From b2e85bff39a411d02f5a167f7bfab376ae9ccb67 Mon Sep 17 00:00:00 2001 From: Tingyu Wang Date: Tue, 19 Sep 2023 13:33:01 -0400 Subject: [PATCH 001/111] Update `cugraph-dgl` conv layers to use improved graph class (#3849) This PR: - Removes the usage of the deprecated `StaticCSC` and `SampledCSC` - Support creating CSR and storing edge information in SparseGraph - clean up unit tests - Adds GATv2Conv layer - Adds `pylibcugraphops` as a dependency of `cugraph-dgl` conda package Authors: - Tingyu Wang (https://github.com/tingyu66) Approvers: - Jake Awe (https://github.com/AyodeAwe) - Vibhu Jawa (https://github.com/VibhuJawa) - Brad Rees (https://github.com/BradReesWork) URL: https://github.com/rapidsai/cugraph/pull/3849 --- conda/recipes/cugraph-dgl/meta.yaml | 1 + .../cugraph_dgl/nn/conv/__init__.py | 2 + .../cugraph-dgl/cugraph_dgl/nn/conv/base.py | 262 +++++++++++++----- .../cugraph_dgl/nn/conv/gatconv.py | 140 +++++++--- .../cugraph_dgl/nn/conv/gatv2conv.py | 249 +++++++++++++++++ .../cugraph_dgl/nn/conv/relgraphconv.py | 70 ++--- .../cugraph_dgl/nn/conv/sageconv.py | 122 ++++---- .../cugraph_dgl/nn/conv/transformerconv.py | 20 +- python/cugraph-dgl/tests/conftest.py | 3 + python/cugraph-dgl/tests/nn/test_gatconv.py | 100 ++++--- python/cugraph-dgl/tests/nn/test_gatv2conv.py | 147 ++++++++++ .../cugraph-dgl/tests/nn/test_relgraphconv.py | 71 +++-- python/cugraph-dgl/tests/nn/test_sageconv.py | 65 +++-- .../cugraph-dgl/tests/nn/test_sparsegraph.py | 28 +- .../tests/nn/test_transformerconv.py | 41 ++- python/cugraph-dgl/tests/test_dataset.py | 2 +- ...ograph.py => test_from_dgl_heterograph.py} | 0 17 files changed, 978 insertions(+), 345 deletions(-) create mode 100644 python/cugraph-dgl/cugraph_dgl/nn/conv/gatv2conv.py create mode 100644 python/cugraph-dgl/tests/nn/test_gatv2conv.py rename python/cugraph-dgl/tests/{test_from_dgl_hetrograph.py => test_from_dgl_heterograph.py} (100%) diff --git a/conda/recipes/cugraph-dgl/meta.yaml b/conda/recipes/cugraph-dgl/meta.yaml index 2fbc6360c04..9e9fcd2faf1 100644 --- a/conda/recipes/cugraph-dgl/meta.yaml +++ b/conda/recipes/cugraph-dgl/meta.yaml @@ -26,6 +26,7 @@ requirements: - dgl >=1.1.0.cu* - numba >=0.57 - numpy >=1.21 + - pylibcugraphops ={{ version }} - python - pytorch diff --git a/python/cugraph-dgl/cugraph_dgl/nn/conv/__init__.py b/python/cugraph-dgl/cugraph_dgl/nn/conv/__init__.py index e5acbf34478..3e7f2f076f0 100644 --- a/python/cugraph-dgl/cugraph_dgl/nn/conv/__init__.py +++ b/python/cugraph-dgl/cugraph_dgl/nn/conv/__init__.py @@ -13,6 +13,7 @@ from .base import SparseGraph from .gatconv import GATConv +from .gatv2conv import GATv2Conv from .relgraphconv import RelGraphConv from .sageconv import SAGEConv from .transformerconv import TransformerConv @@ -20,6 +21,7 @@ __all__ = [ "SparseGraph", "GATConv", + "GATv2Conv", "RelGraphConv", "SAGEConv", "TransformerConv", diff --git a/python/cugraph-dgl/cugraph_dgl/nn/conv/base.py b/python/cugraph-dgl/cugraph_dgl/nn/conv/base.py index 0eeaed29d86..307eb33078e 100644 --- a/python/cugraph-dgl/cugraph_dgl/nn/conv/base.py +++ b/python/cugraph-dgl/cugraph_dgl/nn/conv/base.py @@ -17,38 +17,7 @@ torch = import_optional("torch") ops_torch = import_optional("pylibcugraphops.pytorch") - - -class BaseConv(torch.nn.Module): - r"""An abstract base class for cugraph-ops nn module.""" - - def __init__(self): - super().__init__() - self._cached_offsets_fg = None - - def reset_parameters(self): - r"""Resets all learnable parameters of the module.""" - raise NotImplementedError - - def forward(self, *args): - r"""Runs the forward pass of the module.""" - raise NotImplementedError - - def pad_offsets(self, offsets: torch.Tensor, size: int) -> torch.Tensor: - r"""Pad zero-in-degree nodes to the end of offsets to reach size. This - is used to augment offset tensors from DGL blocks (MFGs) to be - compatible with cugraph-ops full-graph primitives.""" - if self._cached_offsets_fg is None: - self._cached_offsets_fg = torch.empty( - size, dtype=offsets.dtype, device=offsets.device - ) - elif self._cached_offsets_fg.numel() < size: - self._cached_offsets_fg.resize_(size) - - self._cached_offsets_fg[: offsets.numel()] = offsets - self._cached_offsets_fg[offsets.numel() : size] = offsets[-1] - - return self._cached_offsets_fg[:size] +dgl = import_optional("dgl") def compress_ids(ids: torch.Tensor, size: int) -> torch.Tensor: @@ -63,8 +32,9 @@ def decompress_ids(c_ids: torch.Tensor) -> torch.Tensor: class SparseGraph(object): - r"""A god-class to store different sparse formats needed by cugraph-ops - and facilitate sparse format conversions. + r"""A class to create and store different sparse formats needed by + cugraph-ops. It always creates a CSC representation and can provide COO- or + CSR-format if needed. Parameters ---------- @@ -89,25 +59,43 @@ class SparseGraph(object): consists of the sources between `src_indices[cdst_indices[k]]` and `src_indices[cdst_indices[k+1]]`. - dst_ids_is_sorted: bool - Whether `dst_ids` has been sorted in an ascending order. When sorted, - creating CSC layout is much faster. + values: torch.Tensor, optional + Values on the edges. + + is_sorted: bool + Whether the COO inputs (src_ids, dst_ids, values) have been sorted by + `dst_ids` in an ascending order. CSC layout creation is much faster + when sorted. formats: str or tuple of str, optional - The desired sparse formats to create for the graph. + The desired sparse formats to create for the graph. The formats tuple + must include "csc". Default: "csc". reduce_memory: bool, optional When set, the tensors are not required by the desired formats will be - set to `None`. + set to `None`. Default: True. Notes ----- For MFGs (sampled graphs), the node ids must have been renumbered. """ - supported_formats = {"coo": ("src_ids", "dst_ids"), "csc": ("cdst_ids", "src_ids")} - - all_tensors = set(["src_ids", "dst_ids", "csrc_ids", "cdst_ids"]) + supported_formats = { + "coo": ("_src_ids", "_dst_ids"), + "csc": ("_cdst_ids", "_src_ids"), + "csr": ("_csrc_ids", "_dst_ids", "_perm_csc2csr"), + } + + all_tensors = set( + [ + "_src_ids", + "_dst_ids", + "_csrc_ids", + "_cdst_ids", + "_perm_coo2csc", + "_perm_csc2csr", + ] + ) def __init__( self, @@ -116,15 +104,19 @@ def __init__( dst_ids: Optional[torch.Tensor] = None, csrc_ids: Optional[torch.Tensor] = None, cdst_ids: Optional[torch.Tensor] = None, - dst_ids_is_sorted: bool = False, - formats: Optional[Union[str, Tuple[str]]] = None, + values: Optional[torch.Tensor] = None, + is_sorted: bool = False, + formats: Union[str, Tuple[str]] = "csc", reduce_memory: bool = True, ): self._num_src_nodes, self._num_dst_nodes = size - self._dst_ids_is_sorted = dst_ids_is_sorted + self._is_sorted = is_sorted if dst_ids is None and cdst_ids is None: - raise ValueError("One of 'dst_ids' and 'cdst_ids' must be given.") + raise ValueError( + "One of 'dst_ids' and 'cdst_ids' must be given " + "to create a SparseGraph." + ) if src_ids is not None: src_ids = src_ids.contiguous() @@ -148,21 +140,40 @@ def __init__( ) cdst_ids = cdst_ids.contiguous() + if values is not None: + values = values.contiguous() + self._src_ids = src_ids self._dst_ids = dst_ids self._csrc_ids = csrc_ids self._cdst_ids = cdst_ids - self._perm = None + self._values = values + self._perm_coo2csc = None + self._perm_csc2csr = None if isinstance(formats, str): formats = (formats,) - - if formats is not None: - for format_ in formats: - assert format_ in SparseGraph.supported_formats - self.__getattribute__(f"_create_{format_}")() self._formats = formats + if "csc" not in formats: + raise ValueError( + f"{self.__class__.__name__}.formats must contain " + f"'csc', but got {formats}." + ) + + # always create csc first + if self._cdst_ids is None: + if not self._is_sorted: + self._dst_ids, self._perm_coo2csc = torch.sort(self._dst_ids) + self._src_ids = self._src_ids[self._perm_coo2csc] + if self._values is not None: + self._values = self._values[self._perm_coo2csc] + self._cdst_ids = compress_ids(self._dst_ids, self._num_dst_nodes) + + for format_ in formats: + assert format_ in SparseGraph.supported_formats + self.__getattribute__(f"{format_}")() + self._reduce_memory = reduce_memory if reduce_memory: self.reduce_memory() @@ -170,8 +181,6 @@ def __init__( def reduce_memory(self): """Remove the tensors that are not necessary to create the desired sparse formats to reduce memory footprint.""" - - self._perm = None if self._formats is None: return @@ -181,16 +190,22 @@ def reduce_memory(self): for t in SparseGraph.all_tensors.difference(set(tensors_needed)): self.__dict__[t] = None - def _create_coo(self): + def src_ids(self) -> torch.Tensor: + return self._src_ids + + def cdst_ids(self) -> torch.Tensor: + return self._cdst_ids + + def dst_ids(self) -> torch.Tensor: if self._dst_ids is None: self._dst_ids = decompress_ids(self._cdst_ids) + return self._dst_ids - def _create_csc(self): - if self._cdst_ids is None: - if not self._dst_ids_is_sorted: - self._dst_ids, self._perm = torch.sort(self._dst_ids) - self._src_ids = self._src_ids[self._perm] - self._cdst_ids = compress_ids(self._dst_ids, self._num_dst_nodes) + def csrc_ids(self) -> torch.Tensor: + if self._csrc_ids is None: + src_ids, self._perm_csc2csr = torch.sort(self._src_ids) + self._csrc_ids = compress_ids(src_ids, self._num_src_nodes) + return self._csrc_ids def num_src_nodes(self): return self._num_src_nodes @@ -198,21 +213,134 @@ def num_src_nodes(self): def num_dst_nodes(self): return self._num_dst_nodes + def values(self): + return self._values + def formats(self): return self._formats - def coo(self) -> Tuple[torch.Tensor, torch.Tensor]: + def coo(self) -> Tuple[torch.Tensor, torch.Tensor, Optional[torch.Tensor]]: if "coo" not in self.formats(): raise RuntimeError( "The SparseGraph did not create a COO layout. " - "Set 'formats' to include 'coo' when creating the graph." + "Set 'formats' list to include 'coo' when creating the graph." ) - return (self._src_ids, self._dst_ids) + return self.src_ids(), self.dst_ids(), self._values - def csc(self) -> Tuple[torch.Tensor, torch.Tensor]: + def csc(self) -> Tuple[torch.Tensor, torch.Tensor, Optional[torch.Tensor]]: if "csc" not in self.formats(): raise RuntimeError( "The SparseGraph did not create a CSC layout. " - "Set 'formats' to include 'csc' when creating the graph." + "Set 'formats' list to include 'csc' when creating the graph." + ) + return self.cdst_ids(), self.src_ids(), self._values + + def csr(self) -> Tuple[torch.Tensor, torch.Tensor, Optional[torch.Tensor]]: + if "csr" not in self.formats(): + raise RuntimeError( + "The SparseGraph did not create a CSR layout. " + "Set 'formats' list to include 'csr' when creating the graph." + ) + csrc_ids = self.csrc_ids() + dst_ids = self.dst_ids()[self._perm_csc2csr] + value = self._values + if value is not None: + value = value[self._perm_csc2csr] + return csrc_ids, dst_ids, value + + +class BaseConv(torch.nn.Module): + r"""An abstract base class for cugraph-ops nn module.""" + + def __init__(self): + super().__init__() + + def reset_parameters(self): + r"""Resets all learnable parameters of the module.""" + raise NotImplementedError + + def forward(self, *args): + r"""Runs the forward pass of the module.""" + raise NotImplementedError + + def get_cugraph_ops_CSC( + self, + g: Union[SparseGraph, dgl.DGLHeteroGraph], + is_bipartite: bool = False, + max_in_degree: Optional[int] = None, + ) -> ops_torch.CSC: + """Create CSC structure needed by cugraph-ops.""" + + if not isinstance(g, (SparseGraph, dgl.DGLHeteroGraph)): + raise TypeError( + f"The graph has to be either a 'cugraph_dgl.nn.SparseGraph' or " + f"'dgl.DGLHeteroGraph', but got '{type(g)}'." ) - return (self._cdst_ids, self._src_ids) + + # TODO: max_in_degree should default to None in pylibcugraphops + if max_in_degree is None: + max_in_degree = -1 + + if isinstance(g, SparseGraph): + offsets, indices, _ = g.csc() + else: + offsets, indices, _ = g.adj_tensors("csc") + + graph = ops_torch.CSC( + offsets=offsets, + indices=indices, + num_src_nodes=g.num_src_nodes(), + dst_max_in_degree=max_in_degree, + is_bipartite=is_bipartite, + ) + + return graph + + def get_cugraph_ops_HeteroCSC( + self, + g: Union[SparseGraph, dgl.DGLHeteroGraph], + num_edge_types: int, + etypes: Optional[torch.Tensor] = None, + is_bipartite: bool = False, + max_in_degree: Optional[int] = None, + ) -> ops_torch.HeteroCSC: + """Create HeteroCSC structure needed by cugraph-ops.""" + + if not isinstance(g, (SparseGraph, dgl.DGLHeteroGraph)): + raise TypeError( + f"The graph has to be either a 'cugraph_dgl.nn.SparseGraph' or " + f"'dgl.DGLHeteroGraph', but got '{type(g)}'." + ) + + # TODO: max_in_degree should default to None in pylibcugraphops + if max_in_degree is None: + max_in_degree = -1 + + if isinstance(g, SparseGraph): + offsets, indices, etypes = g.csc() + if etypes is None: + raise ValueError( + "SparseGraph must have 'values' to create HeteroCSC. " + "Pass in edge types as 'values' when creating the SparseGraph." + ) + etypes = etypes.int() + else: + if etypes is None: + raise ValueError( + "'etypes' is required when creating HeteroCSC " + "from dgl.DGLHeteroGraph." + ) + offsets, indices, perm = g.adj_tensors("csc") + etypes = etypes[perm].int() + + graph = ops_torch.HeteroCSC( + offsets=offsets, + indices=indices, + edge_types=etypes, + num_src_nodes=g.num_src_nodes(), + num_edge_types=num_edge_types, + dst_max_in_degree=max_in_degree, + is_bipartite=is_bipartite, + ) + + return graph diff --git a/python/cugraph-dgl/cugraph_dgl/nn/conv/gatconv.py b/python/cugraph-dgl/cugraph_dgl/nn/conv/gatconv.py index 239def5b677..8843e61ad89 100644 --- a/python/cugraph-dgl/cugraph_dgl/nn/conv/gatconv.py +++ b/python/cugraph-dgl/cugraph_dgl/nn/conv/gatconv.py @@ -10,13 +10,10 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -"""Torch Module for graph attention network layer using the aggregation -primitives in cugraph-ops""" -# pylint: disable=no-member, arguments-differ, invalid-name, too-many-arguments -from __future__ import annotations + from typing import Optional, Tuple, Union -from cugraph_dgl.nn.conv.base import BaseConv +from cugraph_dgl.nn.conv.base import BaseConv, SparseGraph from cugraph.utilities.utils import import_optional dgl = import_optional("dgl") @@ -32,13 +29,15 @@ class GATConv(BaseConv): Parameters ---------- - in_feats : int, pair of ints + in_feats : int or tuple Input feature size. A pair denotes feature sizes of source and destination nodes. out_feats : int Output feature size. num_heads : int - Number of heads in Multi-Head Attention. + Number of heads in multi-head attention. + feat_drop : float, optional + Dropout rate on feature. Defaults: ``0``. concat : bool, optional If False, the multi-head attentions are averaged instead of concatenated. Default: ``True``. @@ -46,6 +45,15 @@ class GATConv(BaseConv): Edge feature size. Default: ``None``. negative_slope : float, optional LeakyReLU angle of negative slope. Defaults: ``0.2``. + residual : bool, optional + If True, use residual connection. Defaults: ``False``. + allow_zero_in_degree : bool, optional + If there are 0-in-degree nodes in the graph, output for those nodes will + be invalid since no message will be passed to those nodes. This is + harmful for some applications causing silent performance regression. + This module will raise a DGLError if it detects 0-in-degree nodes in + input graph. By setting ``True``, it will suppress the check and let the + users handle it by themselves. Defaults: ``False``. bias : bool, optional If True, learns a bias term. Defaults: ``True``. @@ -81,37 +89,46 @@ class GATConv(BaseConv): [ 1.6477, -1.9986], [ 1.1138, -1.9302]]], device='cuda:0', grad_fn=) """ - MAX_IN_DEGREE_MFG = 200 def __init__( self, in_feats: Union[int, Tuple[int, int]], out_feats: int, num_heads: int, + feat_drop: float = 0.0, concat: bool = True, edge_feats: Optional[int] = None, negative_slope: float = 0.2, + residual: bool = False, + allow_zero_in_degree: bool = False, bias: bool = True, ): super().__init__() self.in_feats = in_feats self.out_feats = out_feats + self.in_feats_src, self.in_feats_dst = dgl.utils.expand_as_pair(in_feats) self.num_heads = num_heads + self.feat_drop = nn.Dropout(feat_drop) self.concat = concat self.edge_feats = edge_feats self.negative_slope = negative_slope + self.allow_zero_in_degree = allow_zero_in_degree if isinstance(in_feats, int): - self.fc = nn.Linear(in_feats, num_heads * out_feats, bias=False) + self.lin = nn.Linear(in_feats, num_heads * out_feats, bias=False) else: - self.fc_src = nn.Linear(in_feats[0], num_heads * out_feats, bias=False) - self.fc_dst = nn.Linear(in_feats[1], num_heads * out_feats, bias=False) + self.lin_src = nn.Linear( + self.in_feats_src, num_heads * out_feats, bias=False + ) + self.lin_dst = nn.Linear( + self.in_feats_dst, num_heads * out_feats, bias=False + ) if edge_feats is not None: - self.fc_edge = nn.Linear(edge_feats, num_heads * out_feats, bias=False) + self.lin_edge = nn.Linear(edge_feats, num_heads * out_feats, bias=False) self.attn_weights = nn.Parameter(torch.Tensor(3 * num_heads * out_feats)) else: - self.register_parameter("fc_edge", None) + self.register_parameter("lin_edge", None) self.attn_weights = nn.Parameter(torch.Tensor(2 * num_heads * out_feats)) if bias and concat: @@ -121,28 +138,40 @@ def __init__( else: self.register_buffer("bias", None) + self.residual = residual and self.in_feats_dst != out_feats * num_heads + if self.residual: + self.lin_res = nn.Linear( + self.in_feats_dst, num_heads * out_feats, bias=bias + ) + else: + self.register_buffer("lin_res", None) + self.reset_parameters() def reset_parameters(self): r"""Reinitialize learnable parameters.""" gain = nn.init.calculate_gain("relu") - if hasattr(self, "fc"): - nn.init.xavier_normal_(self.fc.weight, gain=gain) + if hasattr(self, "lin"): + nn.init.xavier_normal_(self.lin.weight, gain=gain) else: - nn.init.xavier_normal_(self.fc_src.weight, gain=gain) - nn.init.xavier_normal_(self.fc_dst.weight, gain=gain) + nn.init.xavier_normal_(self.lin_src.weight, gain=gain) + nn.init.xavier_normal_(self.lin_dst.weight, gain=gain) nn.init.xavier_normal_( self.attn_weights.view(-1, self.num_heads, self.out_feats), gain=gain ) - if self.fc_edge is not None: - self.fc_edge.reset_parameters() + if self.lin_edge is not None: + self.lin_edge.reset_parameters() + + if self.lin_res is not None: + self.lin_res.reset_parameters() + if self.bias is not None: nn.init.zeros_(self.bias) def forward( self, - g: dgl.DGLHeteroGraph, + g: Union[SparseGraph, dgl.DGLHeteroGraph], nfeat: Union[torch.Tensor, Tuple[torch.Tensor, torch.Tensor]], efeat: Optional[torch.Tensor] = None, max_in_degree: Optional[int] = None, @@ -151,18 +180,17 @@ def forward( Parameters ---------- - graph : DGLGraph + graph : DGLGraph or SparseGraph The graph. nfeat : torch.Tensor Input features of shape :math:`(N, D_{in})`. efeat: torch.Tensor, optional Optional edge features. max_in_degree : int - Maximum in-degree of destination nodes. It is only effective when - :attr:`g` is a :class:`DGLBlock`, i.e., bipartite graph. When - :attr:`g` is generated from a neighbor sampler, the value should be - set to the corresponding :attr:`fanout`. If not given, - :attr:`max_in_degree` will be calculated on-the-fly. + Maximum in-degree of destination nodes. When :attr:`g` is generated + from a neighbor sampler, the value should be set to the corresponding + :attr:`fanout`. This option is used to invoke the MFG-variant of + cugraph-ops kernel. Returns ------- @@ -171,49 +199,63 @@ def forward( :math:`H` is the number of heads, and :math:`D_{out}` is size of output feature. """ - if max_in_degree is None: - max_in_degree = -1 - - bipartite = not isinstance(nfeat, torch.Tensor) - offsets, indices, _ = g.adj_tensors("csc") - - graph = ops_torch.CSC( - offsets=offsets, - indices=indices, - num_src_nodes=g.num_src_nodes(), - dst_max_in_degree=max_in_degree, - is_bipartite=bipartite, + if isinstance(g, dgl.DGLHeteroGraph): + if not self.allow_zero_in_degree: + if (g.in_degrees() == 0).any(): + raise dgl.base.DGLError( + "There are 0-in-degree nodes in the graph, " + "output for those nodes will be invalid. " + "This is harmful for some applications, " + "causing silent performance regression. " + "Adding self-loop on the input graph by " + "calling `g = dgl.add_self_loop(g)` will resolve " + "the issue. Setting ``allow_zero_in_degree`` " + "to be `True` when constructing this module will " + "suppress the check and let the code run." + ) + + bipartite = isinstance(nfeat, (list, tuple)) + + _graph = self.get_cugraph_ops_CSC( + g, is_bipartite=bipartite, max_in_degree=max_in_degree ) + if bipartite: + nfeat = (self.feat_drop(nfeat[0]), self.feat_drop(nfeat[1])) + nfeat_dst_orig = nfeat[1] + else: + nfeat = self.feat_drop(nfeat) + nfeat_dst_orig = nfeat[: g.num_dst_nodes()] + if efeat is not None: - if self.fc_edge is None: + if self.lin_edge is None: raise RuntimeError( f"{self.__class__.__name__}.edge_feats must be set to " f"accept edge features." ) - efeat = self.fc_edge(efeat) + efeat = self.lin_edge(efeat) if bipartite: - if not hasattr(self, "fc_src"): + if not hasattr(self, "lin_src"): raise RuntimeError( f"{self.__class__.__name__}.in_feats must be a pair of " f"integers to allow bipartite node features, but got " f"{self.in_feats}." ) - nfeat_src = self.fc_src(nfeat[0]) - nfeat_dst = self.fc_dst(nfeat[1]) + nfeat_src = self.lin_src(nfeat[0]) + nfeat_dst = self.lin_dst(nfeat[1]) else: - if not hasattr(self, "fc"): + if not hasattr(self, "lin"): raise RuntimeError( f"{self.__class__.__name__}.in_feats is expected to be an " f"integer, but got {self.in_feats}." ) - nfeat = self.fc(nfeat) + nfeat = self.lin(nfeat) out = ops_torch.operators.mha_gat_n2n( (nfeat_src, nfeat_dst) if bipartite else nfeat, self.attn_weights, - graph, + _graph, num_heads=self.num_heads, activation="LeakyReLU", negative_slope=self.negative_slope, @@ -224,6 +266,12 @@ def forward( if self.concat: out = out.view(-1, self.num_heads, self.out_feats) + if self.residual: + res = self.lin_res(nfeat_dst_orig).view(-1, self.num_heads, self.out_feats) + if not self.concat: + res = res.mean(dim=1) + out = out + res + if self.bias is not None: out = out + self.bias diff --git a/python/cugraph-dgl/cugraph_dgl/nn/conv/gatv2conv.py b/python/cugraph-dgl/cugraph_dgl/nn/conv/gatv2conv.py new file mode 100644 index 00000000000..209a5fe1a8d --- /dev/null +++ b/python/cugraph-dgl/cugraph_dgl/nn/conv/gatv2conv.py @@ -0,0 +1,249 @@ +# Copyright (c) 2023, NVIDIA CORPORATION. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from typing import Optional, Tuple, Union + +from cugraph_dgl.nn.conv.base import BaseConv, SparseGraph +from cugraph.utilities.utils import import_optional + +dgl = import_optional("dgl") +torch = import_optional("torch") +nn = import_optional("torch.nn") +ops_torch = import_optional("pylibcugraphops.pytorch") + + +class GATv2Conv(BaseConv): + r"""GATv2 from `How Attentive are Graph Attention Networks? + `__, with the sparse aggregation + accelerated by cugraph-ops. + + Parameters + ---------- + in_feats : int, or pair of ints + Input feature size; i.e, the number of dimensions of :math:`h_i^{(l)}`. + If the layer is to be applied to a unidirectional bipartite graph, `in_feats` + specifies the input feature size on both the source and destination nodes. + If a scalar is given, the source and destination node feature size + would take the same value. + out_feats : int + Output feature size; i.e, the number of dimensions of :math:`h_i^{(l+1)}`. + num_heads : int + Number of heads in Multi-Head Attention. + feat_drop : float, optional + Dropout rate on feature. Defaults: ``0``. + concat : bool, optional + If False, the multi-head attentions are averaged instead of concatenated. + Default: ``True``. + edge_feats : int, optional + Edge feature size. Default: ``None``. + negative_slope : float, optional + LeakyReLU angle of negative slope. Defaults: ``0.2``. + residual : bool, optional + If True, use residual connection. Defaults: ``False``. + allow_zero_in_degree : bool, optional + If there are 0-in-degree nodes in the graph, output for those nodes will + be invalid since no message will be passed to those nodes. This is + harmful for some applications causing silent performance regression. + This module will raise a DGLError if it detects 0-in-degree nodes in + input graph. By setting ``True``, it will suppress the check and let the + users handle it by themselves. Defaults: ``False``. + bias : bool, optional + If set to :obj:`False`, the layer will not learn + an additive bias. (default: :obj:`True`) + share_weights : bool, optional + If set to :obj:`True`, the same matrix for :math:`W_{left}` and + :math:`W_{right}` in the above equations, will be applied to the source + and the target node of every edge. (default: :obj:`False`) + """ + + def __init__( + self, + in_feats: Union[int, Tuple[int, int]], + out_feats: int, + num_heads: int, + feat_drop: float = 0.0, + concat: bool = True, + edge_feats: Optional[int] = None, + negative_slope: float = 0.2, + residual: bool = False, + allow_zero_in_degree: bool = False, + bias: bool = True, + share_weights: bool = False, + ): + super().__init__() + self.in_feats = in_feats + self.out_feats = out_feats + self.in_feats_src, self.in_feats_dst = dgl.utils.expand_as_pair(in_feats) + self.num_heads = num_heads + self.feat_drop = nn.Dropout(feat_drop) + self.concat = concat + self.edge_feats = edge_feats + self.negative_slope = negative_slope + self.allow_zero_in_degree = allow_zero_in_degree + self.share_weights = share_weights + + self.lin_src = nn.Linear(self.in_feats_src, num_heads * out_feats, bias=bias) + if share_weights: + if self.in_feats_src != self.in_feats_dst: + raise ValueError( + f"Input feature size of source and destination " + f"nodes must be identical when share_weights is enabled, " + f"but got {self.in_feats_src} and {self.in_feats_dst}." + ) + self.lin_dst = self.lin_src + else: + self.lin_dst = nn.Linear( + self.in_feats_dst, num_heads * out_feats, bias=bias + ) + + self.attn = nn.Parameter(torch.Tensor(num_heads * out_feats)) + + if edge_feats is not None: + self.lin_edge = nn.Linear(edge_feats, num_heads * out_feats, bias=False) + else: + self.register_parameter("lin_edge", None) + + if bias and concat: + self.bias = nn.Parameter(torch.Tensor(num_heads, out_feats)) + elif bias and not concat: + self.bias = nn.Parameter(torch.Tensor(out_feats)) + else: + self.register_buffer("bias", None) + + self.residual = residual and self.in_feats_dst != out_feats * num_heads + if self.residual: + self.lin_res = nn.Linear( + self.in_feats_dst, num_heads * out_feats, bias=bias + ) + else: + self.register_buffer("lin_res", None) + + self.reset_parameters() + + def reset_parameters(self): + r"""Reinitialize learnable parameters.""" + gain = nn.init.calculate_gain("relu") + nn.init.xavier_normal_(self.lin_src.weight, gain=gain) + nn.init.xavier_normal_(self.lin_dst.weight, gain=gain) + + nn.init.xavier_normal_( + self.attn.view(-1, self.num_heads, self.out_feats), gain=gain + ) + if self.lin_edge is not None: + self.lin_edge.reset_parameters() + + if self.lin_res is not None: + self.lin_res.reset_parameters() + + if self.bias is not None: + nn.init.zeros_(self.bias) + + def forward( + self, + g: Union[SparseGraph, dgl.DGLHeteroGraph], + nfeat: Union[torch.Tensor, Tuple[torch.Tensor, torch.Tensor]], + efeat: Optional[torch.Tensor] = None, + max_in_degree: Optional[int] = None, + ) -> torch.Tensor: + r"""Forward computation. + + Parameters + ---------- + graph : DGLGraph or SparseGraph + The graph. + nfeat : torch.Tensor + Input features of shape :math:`(N, D_{in})`. + efeat: torch.Tensor, optional + Optional edge features. + max_in_degree : int + Maximum in-degree of destination nodes. When :attr:`g` is generated + from a neighbor sampler, the value should be set to the corresponding + :attr:`fanout`. This option is used to invoke the MFG-variant of + cugraph-ops kernel. + + Returns + ------- + torch.Tensor + The output feature of shape :math:`(N, H, D_{out})` where + :math:`H` is the number of heads, and :math:`D_{out}` is size of + output feature. + """ + + if isinstance(g, dgl.DGLHeteroGraph): + if not self.allow_zero_in_degree: + if (g.in_degrees() == 0).any(): + raise dgl.base.DGLError( + "There are 0-in-degree nodes in the graph, " + "output for those nodes will be invalid. " + "This is harmful for some applications, " + "causing silent performance regression. " + "Adding self-loop on the input graph by " + "calling `g = dgl.add_self_loop(g)` will resolve " + "the issue. Setting ``allow_zero_in_degree`` " + "to be `True` when constructing this module will " + "suppress the check and let the code run." + ) + + nfeat_bipartite = isinstance(nfeat, (list, tuple)) + graph_bipartite = nfeat_bipartite or self.share_weights is False + + _graph = self.get_cugraph_ops_CSC( + g, is_bipartite=graph_bipartite, max_in_degree=max_in_degree + ) + + if nfeat_bipartite: + nfeat = (self.feat_drop(nfeat[0]), self.feat_drop(nfeat[1])) + nfeat_dst_orig = nfeat[1] + else: + nfeat = self.feat_drop(nfeat) + nfeat_dst_orig = nfeat[: g.num_dst_nodes()] + + if efeat is not None: + if self.lin_edge is None: + raise RuntimeError( + f"{self.__class__.__name__}.edge_feats must be set to " + f"accept edge features." + ) + efeat = self.lin_edge(efeat) + + if nfeat_bipartite: + nfeat = (self.lin_src(nfeat[0]), self.lin_dst(nfeat[1])) + elif graph_bipartite: + nfeat = (self.lin_src(nfeat), self.lin_dst(nfeat[: g.num_dst_nodes()])) + else: + nfeat = self.lin_src(nfeat) + + out = ops_torch.operators.mha_gat_v2_n2n( + nfeat, + self.attn, + _graph, + num_heads=self.num_heads, + activation="LeakyReLU", + negative_slope=self.negative_slope, + concat_heads=self.concat, + edge_feat=efeat, + )[: g.num_dst_nodes()] + + if self.concat: + out = out.view(-1, self.num_heads, self.out_feats) + + if self.residual: + res = self.lin_res(nfeat_dst_orig).view(-1, self.num_heads, self.out_feats) + if not self.concat: + res = res.mean(dim=1) + out = out + res + + if self.bias is not None: + out = out + self.bias + + return out diff --git a/python/cugraph-dgl/cugraph_dgl/nn/conv/relgraphconv.py b/python/cugraph-dgl/cugraph_dgl/nn/conv/relgraphconv.py index 89e49011cf7..54916674210 100644 --- a/python/cugraph-dgl/cugraph_dgl/nn/conv/relgraphconv.py +++ b/python/cugraph-dgl/cugraph_dgl/nn/conv/relgraphconv.py @@ -10,14 +10,11 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -"""Torch Module for Relational graph convolution layer using the aggregation -primitives in cugraph-ops""" -# pylint: disable=no-member, arguments-differ, invalid-name, too-many-arguments -from __future__ import annotations + import math -from typing import Optional +from typing import Optional, Union -from cugraph_dgl.nn.conv.base import BaseConv +from cugraph_dgl.nn.conv.base import BaseConv, SparseGraph from cugraph.utilities.utils import import_optional dgl = import_optional("dgl") @@ -29,13 +26,8 @@ class RelGraphConv(BaseConv): r"""An accelerated relational graph convolution layer from `Modeling Relational Data with Graph Convolutional Networks - `__ that leverages the highly-optimized - aggregation primitives in cugraph-ops. - - See :class:`dgl.nn.pytorch.conv.RelGraphConv` for mathematical model. - - This module depends on :code:`pylibcugraphops` package, which can be - installed via :code:`conda install -c nvidia pylibcugraphops>=23.02`. + `__, with the sparse aggregation + accelerated by cugraph-ops. Parameters ---------- @@ -84,7 +76,6 @@ class RelGraphConv(BaseConv): [-1.4335, -2.3758], [-1.4331, -2.3295]], device='cuda:0', grad_fn=) """ - MAX_IN_DEGREE_MFG = 500 def __init__( self, @@ -148,7 +139,7 @@ def reset_parameters(self): def forward( self, - g: dgl.DGLHeteroGraph, + g: Union[SparseGraph, dgl.DGLHeteroGraph], feat: torch.Tensor, etypes: torch.Tensor, max_in_degree: Optional[int] = None, @@ -167,49 +158,24 @@ def forward( so any input of other integer types will be casted into int32, thus introducing some overhead. Pass in int32 tensors directly for best performance. - max_in_degree : int, optional - Maximum in-degree of destination nodes. It is only effective when - :attr:`g` is a :class:`DGLBlock`, i.e., bipartite graph. When - :attr:`g` is generated from a neighbor sampler, the value should be - set to the corresponding :attr:`fanout`. If not given, - :attr:`max_in_degree` will be calculated on-the-fly. + max_in_degree : int + Maximum in-degree of destination nodes. When :attr:`g` is generated + from a neighbor sampler, the value should be set to the corresponding + :attr:`fanout`. This option is used to invoke the MFG-variant of + cugraph-ops kernel. Returns ------- torch.Tensor New node features. Shape: :math:`(|V|, D_{out})`. """ - offsets, indices, edge_ids = g.adj_tensors("csc") - edge_types_perm = etypes[edge_ids.long()].int() - - if g.is_block: - if max_in_degree is None: - max_in_degree = g.in_degrees().max().item() - - if max_in_degree < self.MAX_IN_DEGREE_MFG: - _graph = ops_torch.SampledHeteroCSC( - offsets, - indices, - edge_types_perm, - max_in_degree, - g.num_src_nodes(), - self.num_rels, - ) - else: - offsets_fg = self.pad_offsets(offsets, g.num_src_nodes() + 1) - _graph = ops_torch.StaticHeteroCSC( - offsets_fg, - indices, - edge_types_perm, - self.num_rels, - ) - else: - _graph = ops_torch.StaticHeteroCSC( - offsets, - indices, - edge_types_perm, - self.num_rels, - ) + _graph = self.get_cugraph_ops_HeteroCSC( + g, + num_edge_types=self.num_rels, + etypes=etypes, + is_bipartite=False, + max_in_degree=max_in_degree, + ) h = ops_torch.operators.agg_hg_basis_n2n_post( feat, diff --git a/python/cugraph-dgl/cugraph_dgl/nn/conv/sageconv.py b/python/cugraph-dgl/cugraph_dgl/nn/conv/sageconv.py index 60f4c505e19..a3f946d7cb4 100644 --- a/python/cugraph-dgl/cugraph_dgl/nn/conv/sageconv.py +++ b/python/cugraph-dgl/cugraph_dgl/nn/conv/sageconv.py @@ -10,11 +10,8 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -"""Torch Module for GraphSAGE layer using the aggregation primitives in -cugraph-ops""" -# pylint: disable=no-member, arguments-differ, invalid-name, too-many-arguments -from __future__ import annotations -from typing import Optional, Union + +from typing import Optional, Tuple, Union from cugraph_dgl.nn.conv.base import BaseConv, SparseGraph from cugraph.utilities.utils import import_optional @@ -27,22 +24,18 @@ class SAGEConv(BaseConv): r"""An accelerated GraphSAGE layer from `Inductive Representation Learning - on Large Graphs `__ that leverages the - highly-optimized aggregation primitives in cugraph-ops. - - See :class:`dgl.nn.pytorch.conv.SAGEConv` for mathematical model. - - This module depends on :code:`pylibcugraphops` package, which can be - installed via :code:`conda install -c nvidia pylibcugraphops>=23.02`. + on Large Graphs `, with the sparse + aggregation accelerated by cugraph-ops. Parameters ---------- - in_feats : int - Input feature size. + in_feats : int or tuple + Input feature size. If a scalar is given, the source and destination + nodes are required to be the same. out_feats : int Output feature size. aggregator_type : str - Aggregator type to use (``mean``, ``sum``, ``min``, ``max``). + Aggregator type to use ("mean", "sum", "min", "max", "pool", "gcn"). feat_drop : float Dropout rate on features, default: ``0``. bias : bool @@ -68,38 +61,57 @@ class SAGEConv(BaseConv): [-1.1690, 0.1952], [-1.1690, 0.1952]], device='cuda:0', grad_fn=) """ - MAX_IN_DEGREE_MFG = 500 + valid_aggr_types = {"mean", "sum", "min", "max", "pool", "gcn"} def __init__( self, - in_feats: int, + in_feats: Union[int, Tuple[int, int]], out_feats: int, aggregator_type: str = "mean", feat_drop: float = 0.0, bias: bool = True, ): super().__init__() - self.in_feats = in_feats - self.out_feats = out_feats - valid_aggr_types = {"max", "min", "mean", "sum"} - if aggregator_type not in valid_aggr_types: + + if aggregator_type not in self.valid_aggr_types: raise ValueError( - f"Invalid aggregator_type. Must be one of {valid_aggr_types}. " + f"Invalid aggregator_type. Must be one of {self.valid_aggr_types}. " f"But got '{aggregator_type}' instead." ) - self.aggr = aggregator_type + + self.aggregator_type = aggregator_type + self._aggr = aggregator_type + self.in_feats = in_feats + self.out_feats = out_feats + self.in_feats_src, self.in_feats_dst = dgl.utils.expand_as_pair(in_feats) self.feat_drop = nn.Dropout(feat_drop) - self.linear = nn.Linear(2 * in_feats, out_feats, bias=bias) + if self.aggregator_type == "gcn": + self._aggr = "mean" + self.lin = nn.Linear(self.in_feats_src, out_feats, bias=bias) + else: + self.lin = nn.Linear( + self.in_feats_src + self.in_feats_dst, out_feats, bias=bias + ) + + if self.aggregator_type == "pool": + self._aggr = "max" + self.pre_lin = nn.Linear(self.in_feats_src, self.in_feats_src) + else: + self.register_parameter("pre_lin", None) + + self.reset_parameters() def reset_parameters(self): r"""Reinitialize learnable parameters.""" - self.linear.reset_parameters() + self.lin.reset_parameters() + if self.pre_lin is not None: + self.pre_lin.reset_parameters() def forward( self, g: Union[SparseGraph, dgl.DGLHeteroGraph], - feat: torch.Tensor, + feat: Union[torch.Tensor, Tuple[torch.Tensor, torch.Tensor]], max_in_degree: Optional[int] = None, ) -> torch.Tensor: r"""Forward computation. @@ -108,7 +120,7 @@ def forward( ---------- g : DGLGraph or SparseGraph The graph. - feat : torch.Tensor + feat : torch.Tensor or tuple Node features. Shape: :math:`(|V|, D_{in})`. max_in_degree : int Maximum in-degree of destination nodes. When :attr:`g` is generated @@ -121,36 +133,34 @@ def forward( torch.Tensor Output node features. Shape: :math:`(|V|, D_{out})`. """ - if max_in_degree is None: - max_in_degree = -1 - - if isinstance(g, SparseGraph): - assert "csc" in g.formats() - offsets, indices = g.csc() - _graph = ops_torch.CSC( - offsets=offsets, - indices=indices, - num_src_nodes=g.num_src_nodes(), - dst_max_in_degree=max_in_degree, - ) - elif isinstance(g, dgl.DGLHeteroGraph): - offsets, indices, _ = g.adj_tensors("csc") - _graph = ops_torch.CSC( - offsets=offsets, - indices=indices, - num_src_nodes=g.num_src_nodes(), - dst_max_in_degree=max_in_degree, - ) - else: - raise TypeError( - f"The graph has to be either a 'SparseGraph' or " - f"'dgl.DGLHeteroGraph', but got '{type(g)}'." - ) + feat_bipartite = isinstance(feat, (list, tuple)) + graph_bipartite = feat_bipartite or self.aggregator_type == "pool" + + _graph = self.get_cugraph_ops_CSC( + g, is_bipartite=graph_bipartite, max_in_degree=max_in_degree + ) - feat = self.feat_drop(feat) - h = ops_torch.operators.agg_concat_n2n(feat, _graph, self.aggr)[ + if feat_bipartite: + feat = (self.feat_drop(feat[0]), self.feat_drop(feat[1])) + else: + feat = self.feat_drop(feat) + + if self.aggregator_type == "pool": + if feat_bipartite: + feat = (self.pre_lin(feat[0]).relu(), feat[1]) + else: + feat = (self.pre_lin(feat).relu(), feat[: g.num_dst_nodes()]) + # force ctx.needs_input_grad=True in cugraph-ops autograd function + feat[0].requires_grad_() + feat[1].requires_grad_() + + out = ops_torch.operators.agg_concat_n2n(feat, _graph, self._aggr)[ : g.num_dst_nodes() ] - h = self.linear(h) - return h + if self.aggregator_type == "gcn": + out = out[:, : self.in_feats_src] + + out = self.lin(out) + + return out diff --git a/python/cugraph-dgl/cugraph_dgl/nn/conv/transformerconv.py b/python/cugraph-dgl/cugraph_dgl/nn/conv/transformerconv.py index 5cd5fbbaebe..8481b9ee265 100644 --- a/python/cugraph-dgl/cugraph_dgl/nn/conv/transformerconv.py +++ b/python/cugraph-dgl/cugraph_dgl/nn/conv/transformerconv.py @@ -10,9 +10,10 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. + from typing import Optional, Tuple, Union -from cugraph_dgl.nn.conv.base import BaseConv +from cugraph_dgl.nn.conv.base import BaseConv, SparseGraph from cugraph.utilities.utils import import_optional dgl = import_optional("dgl") @@ -114,7 +115,7 @@ def reset_parameters(self): def forward( self, - g: dgl.DGLHeteroGraph, + g: Union[SparseGraph, dgl.DGLHeteroGraph], nfeat: Union[torch.Tensor, Tuple[torch.Tensor, torch.Tensor]], efeat: Optional[torch.Tensor] = None, ) -> torch.Tensor: @@ -130,17 +131,12 @@ def forward( efeat: torch.Tensor, optional Edge feature tensor. Default: ``None``. """ - offsets, indices, _ = g.adj_tensors("csc") - graph = ops_torch.CSC( - offsets=offsets, - indices=indices, - num_src_nodes=g.num_src_nodes(), - is_bipartite=True, - ) - - if isinstance(nfeat, torch.Tensor): + feat_bipartite = isinstance(nfeat, (list, tuple)) + if not feat_bipartite: nfeat = (nfeat, nfeat) + _graph = self.get_cugraph_ops_CSC(g, is_bipartite=True) + query = self.lin_query(nfeat[1][: g.num_dst_nodes()]) key = self.lin_key(nfeat[0]) value = self.lin_value(nfeat[0]) @@ -157,7 +153,7 @@ def forward( key_emb=key, query_emb=query, value_emb=value, - graph=graph, + graph=_graph, num_heads=self.num_heads, concat_heads=self.concat, edge_emb=efeat, diff --git a/python/cugraph-dgl/tests/conftest.py b/python/cugraph-dgl/tests/conftest.py index 6f8690d1140..a3863ed81fa 100644 --- a/python/cugraph-dgl/tests/conftest.py +++ b/python/cugraph-dgl/tests/conftest.py @@ -40,16 +40,19 @@ class SparseGraphData1: nnz = 6 src_ids = torch.IntTensor([0, 1, 2, 3, 2, 5]).cuda() dst_ids = torch.IntTensor([1, 2, 3, 4, 0, 3]).cuda() + values = torch.IntTensor([10, 20, 30, 40, 50, 60]).cuda() # CSR src_ids_sorted_by_src = torch.IntTensor([0, 1, 2, 2, 3, 5]).cuda() dst_ids_sorted_by_src = torch.IntTensor([1, 2, 0, 3, 4, 3]).cuda() csrc_ids = torch.IntTensor([0, 1, 2, 4, 5, 5, 6]).cuda() + values_csr = torch.IntTensor([10, 20, 50, 30, 40, 60]).cuda() # CSC src_ids_sorted_by_dst = torch.IntTensor([2, 0, 1, 5, 2, 3]).cuda() dst_ids_sorted_by_dst = torch.IntTensor([0, 1, 2, 3, 3, 4]).cuda() cdst_ids = torch.IntTensor([0, 1, 2, 3, 5, 6]).cuda() + values_csc = torch.IntTensor([50, 10, 20, 60, 30, 40]).cuda() @pytest.fixture diff --git a/python/cugraph-dgl/tests/nn/test_gatconv.py b/python/cugraph-dgl/tests/nn/test_gatconv.py index 7ed65645a28..ef3047dc2cd 100644 --- a/python/cugraph-dgl/tests/nn/test_gatconv.py +++ b/python/cugraph-dgl/tests/nn/test_gatconv.py @@ -10,69 +10,84 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -# pylint: disable=too-many-arguments, too-many-locals import pytest -try: - import cugraph_dgl -except ModuleNotFoundError: - pytest.skip("cugraph_dgl not available", allow_module_level=True) - -from cugraph.utilities.utils import import_optional +from cugraph_dgl.nn.conv.base import SparseGraph +from cugraph_dgl.nn import GATConv as CuGraphGATConv from .common import create_graph1 -torch = import_optional("torch") -dgl = import_optional("dgl") +dgl = pytest.importorskip("dgl", reason="DGL not available") +torch = pytest.importorskip("torch", reason="PyTorch not available") + +ATOL = 1e-6 @pytest.mark.parametrize("bipartite", [False, True]) @pytest.mark.parametrize("idtype_int", [False, True]) @pytest.mark.parametrize("max_in_degree", [None, 8]) @pytest.mark.parametrize("num_heads", [1, 2, 7]) +@pytest.mark.parametrize("residual", [False, True]) @pytest.mark.parametrize("to_block", [False, True]) -def test_gatconv_equality(bipartite, idtype_int, max_in_degree, num_heads, to_block): - GATConv = dgl.nn.GATConv - CuGraphGATConv = cugraph_dgl.nn.GATConv - device = "cuda" - g = create_graph1().to(device) +@pytest.mark.parametrize("sparse_format", ["coo", "csc", None]) +def test_gatconv_equality( + bipartite, idtype_int, max_in_degree, num_heads, residual, to_block, sparse_format +): + from dgl.nn.pytorch import GATConv + + g = create_graph1().to("cuda") if idtype_int: g = g.int() - if to_block: g = dgl.to_block(g) + size = (g.num_src_nodes(), g.num_dst_nodes()) + if bipartite: in_feats = (10, 3) nfeat = ( - torch.rand(g.num_src_nodes(), in_feats[0], device=device), - torch.rand(g.num_dst_nodes(), in_feats[1], device=device), + torch.rand(g.num_src_nodes(), in_feats[0]).cuda(), + torch.rand(g.num_dst_nodes(), in_feats[1]).cuda(), ) else: in_feats = 10 - nfeat = torch.rand(g.num_src_nodes(), in_feats, device=device) + nfeat = torch.rand(g.num_src_nodes(), in_feats).cuda() out_feats = 2 + if sparse_format == "coo": + sg = SparseGraph( + size=size, src_ids=g.edges()[0], dst_ids=g.edges()[1], formats="csc" + ) + elif sparse_format == "csc": + offsets, indices, _ = g.adj_tensors("csc") + sg = SparseGraph(size=size, src_ids=indices, cdst_ids=offsets, formats="csc") + args = (in_feats, out_feats, num_heads) - kwargs = {"bias": False} + kwargs = {"bias": False, "allow_zero_in_degree": True} - conv1 = GATConv(*args, **kwargs, allow_zero_in_degree=True).to(device) + conv1 = GATConv(*args, **kwargs).cuda() out1 = conv1(g, nfeat) - conv2 = CuGraphGATConv(*args, **kwargs).to(device) + conv2 = CuGraphGATConv(*args, **kwargs).cuda() dim = num_heads * out_feats with torch.no_grad(): conv2.attn_weights.data[:dim] = conv1.attn_l.data.flatten() conv2.attn_weights.data[dim:] = conv1.attn_r.data.flatten() if bipartite: - conv2.fc_src.weight.data = conv1.fc_src.weight.data.detach().clone() - conv2.fc_dst.weight.data = conv1.fc_dst.weight.data.detach().clone() + conv2.lin_src.weight.data = conv1.fc_src.weight.data.detach().clone() + conv2.lin_dst.weight.data = conv1.fc_dst.weight.data.detach().clone() else: - conv2.fc.weight.data = conv1.fc.weight.data.detach().clone() - out2 = conv2(g, nfeat, max_in_degree=max_in_degree) + conv2.lin.weight.data = conv1.fc.weight.data.detach().clone() + if residual and conv2.residual: + conv2.lin_res.weight.data = conv1.fc_res.weight.data.detach().clone() - assert torch.allclose(out1, out2, atol=1e-6) + if sparse_format is not None: + out2 = conv2(sg, nfeat, max_in_degree=max_in_degree) + else: + out2 = conv2(g, nfeat, max_in_degree=max_in_degree) + + assert torch.allclose(out1, out2, atol=ATOL) grad_out1 = torch.rand_like(out1) grad_out2 = grad_out1.clone().detach() @@ -81,18 +96,18 @@ def test_gatconv_equality(bipartite, idtype_int, max_in_degree, num_heads, to_bl if bipartite: assert torch.allclose( - conv1.fc_src.weight.grad, conv2.fc_src.weight.grad, atol=1e-6 + conv1.fc_src.weight.grad, conv2.lin_src.weight.grad, atol=ATOL ) assert torch.allclose( - conv1.fc_dst.weight.grad, conv2.fc_dst.weight.grad, atol=1e-6 + conv1.fc_dst.weight.grad, conv2.lin_dst.weight.grad, atol=ATOL ) else: - assert torch.allclose(conv1.fc.weight.grad, conv2.fc.weight.grad, atol=1e-6) + assert torch.allclose(conv1.fc.weight.grad, conv2.lin.weight.grad, atol=ATOL) assert torch.allclose( torch.cat((conv1.attn_l.grad, conv1.attn_r.grad), dim=0), conv2.attn_weights.grad.view(2, num_heads, out_feats), - atol=1e-6, + atol=ATOL, ) @@ -106,10 +121,7 @@ def test_gatconv_equality(bipartite, idtype_int, max_in_degree, num_heads, to_bl def test_gatconv_edge_feats( bias, bipartite, concat, max_in_degree, num_heads, to_block, use_edge_feats ): - from cugraph_dgl.nn import GATConv - - device = "cuda" - g = create_graph1().to(device) + g = create_graph1().to("cuda") if to_block: g = dgl.to_block(g) @@ -117,24 +129,30 @@ def test_gatconv_edge_feats( if bipartite: in_feats = (10, 3) nfeat = ( - torch.rand(g.num_src_nodes(), in_feats[0], device=device), - torch.rand(g.num_dst_nodes(), in_feats[1], device=device), + torch.rand(g.num_src_nodes(), in_feats[0]).cuda(), + torch.rand(g.num_dst_nodes(), in_feats[1]).cuda(), ) else: in_feats = 10 - nfeat = torch.rand(g.num_src_nodes(), in_feats, device=device) + nfeat = torch.rand(g.num_src_nodes(), in_feats).cuda() out_feats = 2 if use_edge_feats: edge_feats = 3 - efeat = torch.rand(g.num_edges(), edge_feats, device=device) + efeat = torch.rand(g.num_edges(), edge_feats).cuda() else: edge_feats = None efeat = None - conv = GATConv( - in_feats, out_feats, num_heads, concat=concat, edge_feats=edge_feats, bias=bias - ).to(device) + conv = CuGraphGATConv( + in_feats, + out_feats, + num_heads, + concat=concat, + edge_feats=edge_feats, + bias=bias, + allow_zero_in_degree=True, + ).cuda() out = conv(g, nfeat, efeat=efeat, max_in_degree=max_in_degree) grad_out = torch.rand_like(out) diff --git a/python/cugraph-dgl/tests/nn/test_gatv2conv.py b/python/cugraph-dgl/tests/nn/test_gatv2conv.py new file mode 100644 index 00000000000..cc46a6e4b39 --- /dev/null +++ b/python/cugraph-dgl/tests/nn/test_gatv2conv.py @@ -0,0 +1,147 @@ +# Copyright (c) 2023, NVIDIA CORPORATION. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import pytest + +from cugraph_dgl.nn.conv.base import SparseGraph +from cugraph_dgl.nn import GATv2Conv as CuGraphGATv2Conv +from .common import create_graph1 + +dgl = pytest.importorskip("dgl", reason="DGL not available") +torch = pytest.importorskip("torch", reason="PyTorch not available") + +ATOL = 1e-6 + + +@pytest.mark.parametrize("bipartite", [False, True]) +@pytest.mark.parametrize("idtype_int", [False, True]) +@pytest.mark.parametrize("max_in_degree", [None, 8]) +@pytest.mark.parametrize("num_heads", [1, 2, 7]) +@pytest.mark.parametrize("residual", [False, True]) +@pytest.mark.parametrize("to_block", [False, True]) +@pytest.mark.parametrize("sparse_format", ["coo", "csc", None]) +def test_gatv2conv_equality( + bipartite, idtype_int, max_in_degree, num_heads, residual, to_block, sparse_format +): + from dgl.nn.pytorch import GATv2Conv + + g = create_graph1().to("cuda") + + if idtype_int: + g = g.int() + if to_block: + g = dgl.to_block(g) + + size = (g.num_src_nodes(), g.num_dst_nodes()) + + if bipartite: + in_feats = (10, 3) + nfeat = ( + torch.rand(g.num_src_nodes(), in_feats[0]).cuda(), + torch.rand(g.num_dst_nodes(), in_feats[1]).cuda(), + ) + else: + in_feats = 10 + nfeat = torch.rand(g.num_src_nodes(), in_feats).cuda() + out_feats = 2 + + if sparse_format == "coo": + sg = SparseGraph( + size=size, src_ids=g.edges()[0], dst_ids=g.edges()[1], formats="csc" + ) + elif sparse_format == "csc": + offsets, indices, _ = g.adj_tensors("csc") + sg = SparseGraph(size=size, src_ids=indices, cdst_ids=offsets, formats="csc") + + args = (in_feats, out_feats, num_heads) + kwargs = {"bias": False, "allow_zero_in_degree": True} + + conv1 = GATv2Conv(*args, **kwargs).cuda() + out1 = conv1(g, nfeat) + + conv2 = CuGraphGATv2Conv(*args, **kwargs).cuda() + with torch.no_grad(): + conv2.attn.data = conv1.attn.data.flatten() + conv2.lin_src.weight.data = conv1.fc_src.weight.data.detach().clone() + conv2.lin_dst.weight.data = conv1.fc_dst.weight.data.detach().clone() + if residual and conv2.residual: + conv2.lin_res.weight.data = conv1.fc_res.weight.data.detach().clone() + + if sparse_format is not None: + out2 = conv2(sg, nfeat, max_in_degree=max_in_degree) + else: + out2 = conv2(g, nfeat, max_in_degree=max_in_degree) + + assert torch.allclose(out1, out2, atol=ATOL) + + grad_out1 = torch.rand_like(out1) + grad_out2 = grad_out1.clone().detach() + out1.backward(grad_out1) + out2.backward(grad_out2) + + assert torch.allclose( + conv1.fc_src.weight.grad, conv2.lin_src.weight.grad, atol=ATOL + ) + assert torch.allclose( + conv1.fc_dst.weight.grad, conv2.lin_dst.weight.grad, atol=ATOL + ) + + assert torch.allclose(conv1.attn.grad, conv1.attn.grad, atol=ATOL) + + +@pytest.mark.parametrize("bias", [False, True]) +@pytest.mark.parametrize("bipartite", [False, True]) +@pytest.mark.parametrize("concat", [False, True]) +@pytest.mark.parametrize("max_in_degree", [None, 8, 800]) +@pytest.mark.parametrize("num_heads", [1, 2, 7]) +@pytest.mark.parametrize("to_block", [False, True]) +@pytest.mark.parametrize("use_edge_feats", [False, True]) +def test_gatv2conv_edge_feats( + bias, bipartite, concat, max_in_degree, num_heads, to_block, use_edge_feats +): + g = create_graph1().to("cuda") + + if to_block: + g = dgl.to_block(g) + + if bipartite: + in_feats = (10, 3) + nfeat = ( + torch.rand(g.num_src_nodes(), in_feats[0]).cuda(), + torch.rand(g.num_dst_nodes(), in_feats[1]).cuda(), + ) + else: + in_feats = 10 + nfeat = torch.rand(g.num_src_nodes(), in_feats).cuda() + out_feats = 2 + + if use_edge_feats: + edge_feats = 3 + efeat = torch.rand(g.num_edges(), edge_feats).cuda() + else: + edge_feats = None + efeat = None + + conv = CuGraphGATv2Conv( + in_feats, + out_feats, + num_heads, + concat=concat, + edge_feats=edge_feats, + bias=bias, + allow_zero_in_degree=True, + ).cuda() + out = conv(g, nfeat, efeat=efeat, max_in_degree=max_in_degree) + + grad_out = torch.rand_like(out) + out.backward(grad_out) diff --git a/python/cugraph-dgl/tests/nn/test_relgraphconv.py b/python/cugraph-dgl/tests/nn/test_relgraphconv.py index d2ae6a23978..901f9ba1433 100644 --- a/python/cugraph-dgl/tests/nn/test_relgraphconv.py +++ b/python/cugraph-dgl/tests/nn/test_relgraphconv.py @@ -10,20 +10,17 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -# pylint: disable=too-many-arguments, too-many-locals import pytest -try: - import cugraph_dgl -except ModuleNotFoundError: - pytest.skip("cugraph_dgl not available", allow_module_level=True) - -from cugraph.utilities.utils import import_optional +from cugraph_dgl.nn.conv.base import SparseGraph +from cugraph_dgl.nn import RelGraphConv as CuGraphRelGraphConv from .common import create_graph1 -torch = import_optional("torch") -dgl = import_optional("dgl") +dgl = pytest.importorskip("dgl", reason="DGL not available") +torch = pytest.importorskip("torch", reason="PyTorch not available") + +ATOL = 1e-6 @pytest.mark.parametrize("idtype_int", [False, True]) @@ -32,12 +29,17 @@ @pytest.mark.parametrize("regularizer", [None, "basis"]) @pytest.mark.parametrize("self_loop", [False, True]) @pytest.mark.parametrize("to_block", [False, True]) +@pytest.mark.parametrize("sparse_format", ["coo", "csc", None]) def test_relgraphconv_equality( - idtype_int, max_in_degree, num_bases, regularizer, self_loop, to_block + idtype_int, + max_in_degree, + num_bases, + regularizer, + self_loop, + to_block, + sparse_format, ): - RelGraphConv = dgl.nn.RelGraphConv - CuGraphRelGraphConv = cugraph_dgl.nn.RelGraphConv - device = "cuda" + from dgl.nn.pytorch import RelGraphConv in_feat, out_feat, num_rels = 10, 2, 3 args = (in_feat, out_feat, num_rels) @@ -47,34 +49,57 @@ def test_relgraphconv_equality( "bias": False, "self_loop": self_loop, } - g = create_graph1().to(device) - g.edata[dgl.ETYPE] = torch.randint(num_rels, (g.num_edges(),)).to(device) + g = create_graph1().to("cuda") + g.edata[dgl.ETYPE] = torch.randint(num_rels, (g.num_edges(),)).cuda() + if idtype_int: g = g.int() if to_block: g = dgl.to_block(g) - feat = torch.rand(g.num_src_nodes(), in_feat).to(device) + + size = (g.num_src_nodes(), g.num_dst_nodes()) + feat = torch.rand(g.num_src_nodes(), in_feat).cuda() + + if sparse_format == "coo": + sg = SparseGraph( + size=size, + src_ids=g.edges()[0], + dst_ids=g.edges()[1], + values=g.edata[dgl.ETYPE], + formats="csc", + ) + elif sparse_format == "csc": + offsets, indices, perm = g.adj_tensors("csc") + etypes = g.edata[dgl.ETYPE][perm] + sg = SparseGraph( + size=size, src_ids=indices, cdst_ids=offsets, values=etypes, formats="csc" + ) torch.manual_seed(0) - conv1 = RelGraphConv(*args, **kwargs).to(device) + conv1 = RelGraphConv(*args, **kwargs).cuda() torch.manual_seed(0) kwargs["apply_norm"] = False - conv2 = CuGraphRelGraphConv(*args, **kwargs).to(device) + conv2 = CuGraphRelGraphConv(*args, **kwargs).cuda() out1 = conv1(g, feat, g.edata[dgl.ETYPE]) - out2 = conv2(g, feat, g.edata[dgl.ETYPE], max_in_degree=max_in_degree) - assert torch.allclose(out1, out2, atol=1e-06) + + if sparse_format is not None: + out2 = conv2(sg, feat, sg.values(), max_in_degree=max_in_degree) + else: + out2 = conv2(g, feat, g.edata[dgl.ETYPE], max_in_degree=max_in_degree) + + assert torch.allclose(out1, out2, atol=ATOL) grad_out = torch.rand_like(out1) out1.backward(grad_out) out2.backward(grad_out) end = -1 if self_loop else None - assert torch.allclose(conv1.linear_r.W.grad, conv2.W.grad[:end], atol=1e-6) + assert torch.allclose(conv1.linear_r.W.grad, conv2.W.grad[:end], atol=ATOL) if self_loop: - assert torch.allclose(conv1.loop_weight.grad, conv2.W.grad[-1], atol=1e-6) + assert torch.allclose(conv1.loop_weight.grad, conv2.W.grad[-1], atol=ATOL) if regularizer is not None: - assert torch.allclose(conv1.linear_r.coeff.grad, conv2.coeff.grad, atol=1e-6) + assert torch.allclose(conv1.linear_r.coeff.grad, conv2.coeff.grad, atol=ATOL) diff --git a/python/cugraph-dgl/tests/nn/test_sageconv.py b/python/cugraph-dgl/tests/nn/test_sageconv.py index 447bbe49460..e2acf9e6596 100644 --- a/python/cugraph-dgl/tests/nn/test_sageconv.py +++ b/python/cugraph-dgl/tests/nn/test_sageconv.py @@ -10,31 +10,33 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -# pylint: disable=too-many-arguments, too-many-locals import pytest -from cugraph.utilities.utils import import_optional from cugraph_dgl.nn.conv.base import SparseGraph from cugraph_dgl.nn import SAGEConv as CuGraphSAGEConv from .common import create_graph1 -torch = import_optional("torch") -dgl = import_optional("dgl") +dgl = pytest.importorskip("dgl", reason="DGL not available") +torch = pytest.importorskip("torch", reason="PyTorch not available") +ATOL = 1e-6 + +@pytest.mark.parametrize("aggr", ["mean", "pool"]) @pytest.mark.parametrize("bias", [False, True]) +@pytest.mark.parametrize("bipartite", [False, True]) @pytest.mark.parametrize("idtype_int", [False, True]) @pytest.mark.parametrize("max_in_degree", [None, 8]) @pytest.mark.parametrize("to_block", [False, True]) @pytest.mark.parametrize("sparse_format", ["coo", "csc", None]) -def test_SAGEConv_equality(bias, idtype_int, max_in_degree, to_block, sparse_format): - SAGEConv = dgl.nn.SAGEConv - device = "cuda" +def test_sageconv_equality( + aggr, bias, bipartite, idtype_int, max_in_degree, to_block, sparse_format +): + from dgl.nn.pytorch import SAGEConv - in_feat, out_feat = 5, 2 - kwargs = {"aggregator_type": "mean", "bias": bias} - g = create_graph1().to(device) + kwargs = {"aggregator_type": aggr, "bias": bias} + g = create_graph1().to("cuda") if idtype_int: g = g.int() @@ -42,7 +44,17 @@ def test_SAGEConv_equality(bias, idtype_int, max_in_degree, to_block, sparse_for g = dgl.to_block(g) size = (g.num_src_nodes(), g.num_dst_nodes()) - feat = torch.rand(g.num_src_nodes(), in_feat).to(device) + + if bipartite: + in_feats = (5, 3) + feat = ( + torch.rand(size[0], in_feats[0], requires_grad=True).cuda(), + torch.rand(size[1], in_feats[1], requires_grad=True).cuda(), + ) + else: + in_feats = 5 + feat = torch.rand(size[0], in_feats).cuda() + out_feats = 2 if sparse_format == "coo": sg = SparseGraph( @@ -52,39 +64,38 @@ def test_SAGEConv_equality(bias, idtype_int, max_in_degree, to_block, sparse_for offsets, indices, _ = g.adj_tensors("csc") sg = SparseGraph(size=size, src_ids=indices, cdst_ids=offsets, formats="csc") - torch.manual_seed(0) - conv1 = SAGEConv(in_feat, out_feat, **kwargs).to(device) - - torch.manual_seed(0) - conv2 = CuGraphSAGEConv(in_feat, out_feat, **kwargs).to(device) + conv1 = SAGEConv(in_feats, out_feats, **kwargs).cuda() + conv2 = CuGraphSAGEConv(in_feats, out_feats, **kwargs).cuda() + in_feats_src = conv2.in_feats_src with torch.no_grad(): - conv2.linear.weight.data[:, :in_feat] = conv1.fc_neigh.weight.data - conv2.linear.weight.data[:, in_feat:] = conv1.fc_self.weight.data + conv2.lin.weight.data[:, :in_feats_src] = conv1.fc_neigh.weight.data + conv2.lin.weight.data[:, in_feats_src:] = conv1.fc_self.weight.data if bias: - conv2.linear.bias.data[:] = conv1.fc_self.bias.data + conv2.lin.bias.data[:] = conv1.fc_self.bias.data + if aggr == "pool": + conv2.pre_lin.weight.data[:] = conv1.fc_pool.weight.data + conv2.pre_lin.bias.data[:] = conv1.fc_pool.bias.data out1 = conv1(g, feat) if sparse_format is not None: out2 = conv2(sg, feat, max_in_degree=max_in_degree) else: out2 = conv2(g, feat, max_in_degree=max_in_degree) - assert torch.allclose(out1, out2, atol=1e-06) + assert torch.allclose(out1, out2, atol=ATOL) grad_out = torch.rand_like(out1) out1.backward(grad_out) out2.backward(grad_out) assert torch.allclose( conv1.fc_neigh.weight.grad, - conv2.linear.weight.grad[:, :in_feat], - atol=1e-6, + conv2.lin.weight.grad[:, :in_feats_src], + atol=ATOL, ) assert torch.allclose( conv1.fc_self.weight.grad, - conv2.linear.weight.grad[:, in_feat:], - atol=1e-6, + conv2.lin.weight.grad[:, in_feats_src:], + atol=ATOL, ) if bias: - assert torch.allclose( - conv1.fc_self.bias.grad, conv2.linear.bias.grad, atol=1e-6 - ) + assert torch.allclose(conv1.fc_self.bias.grad, conv2.lin.bias.grad, atol=ATOL) diff --git a/python/cugraph-dgl/tests/nn/test_sparsegraph.py b/python/cugraph-dgl/tests/nn/test_sparsegraph.py index 3fb01575d66..09c0df202ff 100644 --- a/python/cugraph-dgl/tests/nn/test_sparsegraph.py +++ b/python/cugraph-dgl/tests/nn/test_sparsegraph.py @@ -19,32 +19,42 @@ def test_coo2csc(sparse_graph_1): data = sparse_graph_1 - values = torch.ones(data.nnz).cuda() + g = SparseGraph( - size=data.size, src_ids=data.src_ids, dst_ids=data.dst_ids, formats="csc" + size=data.size, + src_ids=data.src_ids, + dst_ids=data.dst_ids, + values=data.values, + formats=["csc"], ) - cdst_ids, src_ids = g.csc() + cdst_ids, src_ids, values = g.csc() new = torch.sparse_csc_tensor(cdst_ids, src_ids, values).cuda() old = torch.sparse_coo_tensor( - torch.vstack((data.src_ids, data.dst_ids)), values + torch.vstack((data.src_ids, data.dst_ids)), data.values ).cuda() torch.allclose(new.to_dense(), old.to_dense()) -def test_csc2coo(sparse_graph_1): +def test_csc_input(sparse_graph_1): data = sparse_graph_1 - values = torch.ones(data.nnz).cuda() + g = SparseGraph( size=data.size, src_ids=data.src_ids_sorted_by_dst, cdst_ids=data.cdst_ids, - formats="coo", + values=data.values_csc, + formats=["coo", "csc", "csr"], ) - src_ids, dst_ids = g.coo() + src_ids, dst_ids, values = g.coo() new = torch.sparse_coo_tensor(torch.vstack((src_ids, dst_ids)), values).cuda() old = torch.sparse_csc_tensor( - data.cdst_ids, data.src_ids_sorted_by_dst, values + data.cdst_ids, data.src_ids_sorted_by_dst, data.values_csc ).cuda() torch.allclose(new.to_dense(), old.to_dense()) + + csrc_ids, dst_ids, values = g.csr() + + new = torch.sparse_csr_tensor(csrc_ids, dst_ids, values).cuda() + torch.allclose(new.to_dense(), old.to_dense()) diff --git a/python/cugraph-dgl/tests/nn/test_transformerconv.py b/python/cugraph-dgl/tests/nn/test_transformerconv.py index 00476b9f0bb..b2b69cb35ab 100644 --- a/python/cugraph-dgl/tests/nn/test_transformerconv.py +++ b/python/cugraph-dgl/tests/nn/test_transformerconv.py @@ -13,16 +13,14 @@ import pytest -try: - from cugraph_dgl.nn import TransformerConv -except ModuleNotFoundError: - pytest.skip("cugraph_dgl not available", allow_module_level=True) - -from cugraph.utilities.utils import import_optional +from cugraph_dgl.nn.conv.base import SparseGraph +from cugraph_dgl.nn import TransformerConv from .common import create_graph1 -torch = import_optional("torch") -dgl = import_optional("dgl") +dgl = pytest.importorskip("dgl", reason="DGL not available") +torch = pytest.importorskip("torch", reason="PyTorch not available") + +ATOL = 1e-6 @pytest.mark.parametrize("beta", [False, True]) @@ -32,8 +30,16 @@ @pytest.mark.parametrize("num_heads", [1, 2, 3, 4]) @pytest.mark.parametrize("to_block", [False, True]) @pytest.mark.parametrize("use_edge_feats", [False, True]) -def test_TransformerConv( - beta, bipartite_node_feats, concat, idtype_int, num_heads, to_block, use_edge_feats +@pytest.mark.parametrize("sparse_format", ["coo", "csc", None]) +def test_transformerconv( + beta, + bipartite_node_feats, + concat, + idtype_int, + num_heads, + to_block, + use_edge_feats, + sparse_format, ): device = "cuda" g = create_graph1().to(device) @@ -44,6 +50,15 @@ def test_TransformerConv( if to_block: g = dgl.to_block(g) + size = (g.num_src_nodes(), g.num_dst_nodes()) + if sparse_format == "coo": + sg = SparseGraph( + size=size, src_ids=g.edges()[0], dst_ids=g.edges()[1], formats="csc" + ) + elif sparse_format == "csc": + offsets, indices, _ = g.adj_tensors("csc") + sg = SparseGraph(size=size, src_ids=indices, cdst_ids=offsets, formats="csc") + if bipartite_node_feats: in_node_feats = (5, 3) nfeat = ( @@ -71,6 +86,10 @@ def test_TransformerConv( edge_feats=edge_feats, ).to(device) - out = conv(g, nfeat, efeat) + if sparse_format is not None: + out = conv(sg, nfeat, efeat) + else: + out = conv(g, nfeat, efeat) + grad_out = torch.rand_like(out) out.backward(grad_out) diff --git a/python/cugraph-dgl/tests/test_dataset.py b/python/cugraph-dgl/tests/test_dataset.py index 69d50261e55..5db443dc0d8 100644 --- a/python/cugraph-dgl/tests/test_dataset.py +++ b/python/cugraph-dgl/tests/test_dataset.py @@ -123,6 +123,6 @@ def test_homogeneous_sampled_graphs_from_dataframe(return_type, seed_node): assert dgl_block.num_src_nodes() == cugraph_dgl_graph.num_src_nodes() assert dgl_block.num_dst_nodes() == cugraph_dgl_graph.num_dst_nodes() dgl_offsets, dgl_indices, _ = dgl_block.adj_tensors("csc") - cugraph_offsets, cugraph_indices = cugraph_dgl_graph.csc() + cugraph_offsets, cugraph_indices, _ = cugraph_dgl_graph.csc() assert torch.equal(dgl_offsets.to("cpu"), cugraph_offsets.to("cpu")) assert torch.equal(dgl_indices.to("cpu"), cugraph_indices.to("cpu")) diff --git a/python/cugraph-dgl/tests/test_from_dgl_hetrograph.py b/python/cugraph-dgl/tests/test_from_dgl_heterograph.py similarity index 100% rename from python/cugraph-dgl/tests/test_from_dgl_hetrograph.py rename to python/cugraph-dgl/tests/test_from_dgl_heterograph.py From ed7b1a41fe502c9097c4ac9688f08c1d1e5fd33f Mon Sep 17 00:00:00 2001 From: Chuck Hastings <45364586+ChuckHastings@users.noreply.github.com> Date: Tue, 19 Sep 2023 13:48:52 -0400 Subject: [PATCH 002/111] New mtmg API for integration (#3521) Creating a new API for integrating multi-threaded multi-GPU programs into the cugraph library. This API will extend our OPG (one [process] per GPU) model to support a single process handling multiple GPUs, and will also ultimately support a multi-node configuration where some compute nodes might not have GPUs. closes https://github.com/rapidsai/graph_dl/issues/241 Authors: - Chuck Hastings (https://github.com/ChuckHastings) Approvers: - Seunghwa Kang (https://github.com/seunghwak) URL: https://github.com/rapidsai/cugraph/pull/3521 --- cpp/CMakeLists.txt | 2 + cpp/cmake/thirdparty/get_ucp.cmake | 35 ++ .../mtmg/detail/device_shared_device_span.hpp | 39 ++ .../detail/device_shared_device_vector.hpp | 58 +++ .../mtmg/detail/device_shared_wrapper.hpp | 123 +++++ .../mtmg/detail/per_device_edgelist.hpp | 275 +++++++++++ cpp/include/cugraph/mtmg/edge_property.hpp | 60 +++ .../cugraph/mtmg/edge_property_view.hpp | 33 ++ cpp/include/cugraph/mtmg/edgelist.hpp | 65 +++ cpp/include/cugraph/mtmg/graph.hpp | 136 ++++++ cpp/include/cugraph/mtmg/graph_view.hpp | 34 ++ cpp/include/cugraph/mtmg/handle.hpp | 111 +++++ cpp/include/cugraph/mtmg/instance_manager.hpp | 98 ++++ .../cugraph/mtmg/per_thread_edgelist.hpp | 174 +++++++ cpp/include/cugraph/mtmg/renumber_map.hpp | 40 ++ .../cugraph/mtmg/renumber_map_view.hpp | 32 ++ cpp/include/cugraph/mtmg/resource_manager.hpp | 225 +++++++++ cpp/include/cugraph/mtmg/vertex_result.hpp | 40 ++ .../cugraph/mtmg/vertex_result_view.hpp | 49 ++ cpp/src/link_analysis/pagerank_impl.cuh | 8 +- cpp/src/mtmg/vertex_result.cu | 167 +++++++ cpp/tests/CMakeLists.txt | 8 + cpp/tests/mtmg/threaded_test.cu | 459 ++++++++++++++++++ 23 files changed, 2268 insertions(+), 3 deletions(-) create mode 100644 cpp/cmake/thirdparty/get_ucp.cmake create mode 100644 cpp/include/cugraph/mtmg/detail/device_shared_device_span.hpp create mode 100644 cpp/include/cugraph/mtmg/detail/device_shared_device_vector.hpp create mode 100644 cpp/include/cugraph/mtmg/detail/device_shared_wrapper.hpp create mode 100644 cpp/include/cugraph/mtmg/detail/per_device_edgelist.hpp create mode 100644 cpp/include/cugraph/mtmg/edge_property.hpp create mode 100644 cpp/include/cugraph/mtmg/edge_property_view.hpp create mode 100644 cpp/include/cugraph/mtmg/edgelist.hpp create mode 100644 cpp/include/cugraph/mtmg/graph.hpp create mode 100644 cpp/include/cugraph/mtmg/graph_view.hpp create mode 100644 cpp/include/cugraph/mtmg/handle.hpp create mode 100644 cpp/include/cugraph/mtmg/instance_manager.hpp create mode 100644 cpp/include/cugraph/mtmg/per_thread_edgelist.hpp create mode 100644 cpp/include/cugraph/mtmg/renumber_map.hpp create mode 100644 cpp/include/cugraph/mtmg/renumber_map_view.hpp create mode 100644 cpp/include/cugraph/mtmg/resource_manager.hpp create mode 100644 cpp/include/cugraph/mtmg/vertex_result.hpp create mode 100644 cpp/include/cugraph/mtmg/vertex_result_view.hpp create mode 100644 cpp/src/mtmg/vertex_result.cu create mode 100644 cpp/tests/mtmg/threaded_test.cu diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index 69a488de0b8..a6c26ee3b91 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -166,6 +166,7 @@ endif() include(cmake/thirdparty/get_nccl.cmake) include(cmake/thirdparty/get_cuhornet.cmake) +include(cmake/thirdparty/get_ucp.cmake) if(BUILD_TESTS) include(cmake/thirdparty/get_gtest.cmake) @@ -292,6 +293,7 @@ set(CUGRAPH_SOURCES src/community/triangle_count_mg.cu src/traversal/k_hop_nbrs_sg.cu src/traversal/k_hop_nbrs_mg.cu + src/mtmg/vertex_result.cu ) if(USE_CUGRAPH_OPS) diff --git a/cpp/cmake/thirdparty/get_ucp.cmake b/cpp/cmake/thirdparty/get_ucp.cmake new file mode 100644 index 00000000000..dcc4956a34e --- /dev/null +++ b/cpp/cmake/thirdparty/get_ucp.cmake @@ -0,0 +1,35 @@ +#============================================================================= +# Copyright (c) 2023, NVIDIA CORPORATION. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +#============================================================================= + +function(find_and_configure_ucp) + + if(TARGET UCP::UCP) + return() + endif() + + rapids_find_generate_module(UCP + HEADER_NAMES ucp.h + LIBRARY_NAMES ucp + INCLUDE_SUFFIXES ucp/api + ) + + # Currently UCP has no CMake build-system so we require + # it built and installed on the machine already + rapids_find_package(UCP REQUIRED) + +endfunction() + +find_and_configure_ucp() diff --git a/cpp/include/cugraph/mtmg/detail/device_shared_device_span.hpp b/cpp/include/cugraph/mtmg/detail/device_shared_device_span.hpp new file mode 100644 index 00000000000..37398891370 --- /dev/null +++ b/cpp/include/cugraph/mtmg/detail/device_shared_device_span.hpp @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2023, NVIDIA CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include + +namespace cugraph { +namespace mtmg { +namespace detail { + +/** + * @brief Wrap an object to be available for each GPU + * + * In the MTMG environment we need the ability to manage a collection of objects + * that are associated with a particular GPU, and fetch the objects from an + * arbitrary GPU thread. This object will wrap any object and allow it to be + * accessed from different threads. + */ +template +using device_shared_device_span_t = device_shared_wrapper_t>; + +} // namespace detail +} // namespace mtmg +} // namespace cugraph diff --git a/cpp/include/cugraph/mtmg/detail/device_shared_device_vector.hpp b/cpp/include/cugraph/mtmg/detail/device_shared_device_vector.hpp new file mode 100644 index 00000000000..7f3992b73bd --- /dev/null +++ b/cpp/include/cugraph/mtmg/detail/device_shared_device_vector.hpp @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2023, NVIDIA CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include + +namespace cugraph { +namespace mtmg { +namespace detail { + +/** + * @brief Wrap an object to be available for each GPU + * + * In the MTMG environment we need the ability to manage a collection of objects + * that are associated with a particular GPU, and fetch the objects from an + * arbitrary GPU thread. This object will wrap any object and allow it to be + * accessed from different threads. + */ +template +class device_shared_device_vector_t : public device_shared_wrapper_t> { + using parent_t = detail::device_shared_wrapper_t>; + + public: + /** + * @brief Create a device_shared_device_span (read only view) + */ + auto view() + { + std::lock_guard lock(parent_t::lock_); + + device_shared_device_span_t result; + + std::for_each(parent_t::objects_.begin(), parent_t::objects_.end(), [&result](auto& p) { + result.set(p.first, raft::device_span{p.second.data(), p.second.size()}); + }); + + return result; + } +}; + +} // namespace detail +} // namespace mtmg +} // namespace cugraph diff --git a/cpp/include/cugraph/mtmg/detail/device_shared_wrapper.hpp b/cpp/include/cugraph/mtmg/detail/device_shared_wrapper.hpp new file mode 100644 index 00000000000..c4cacb401af --- /dev/null +++ b/cpp/include/cugraph/mtmg/detail/device_shared_wrapper.hpp @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2023, NVIDIA CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include + +#include +#include + +namespace cugraph { +namespace mtmg { +namespace detail { + +/** + * @brief Wrap an object to be available for each GPU + * + * In the MTMG environment we need the ability to manage a collection of objects + * that are associated with a particular GPU, and fetch the objects from an + * arbitrary GPU thread. This object will wrap any object and allow it to be + * accessed from different threads. + */ +template +class device_shared_wrapper_t { + public: + using wrapped_t = T; + + device_shared_wrapper_t() = default; + device_shared_wrapper_t(device_shared_wrapper_t&& other) : objects_{std::move(other.objects_)} {} + device_shared_wrapper_t& operator=(device_shared_wrapper_t&& other) + { + objects_ = std::move(other.objects_); + return *this; + } + + /** + * @brief Move a wrapped object into the wrapper for this thread + * + * @param handle Handle is used to identify the GPU we associated this object with + * @param obj Wrapped object + */ + void set(cugraph::mtmg::handle_t const& handle, wrapped_t&& obj) + { + std::lock_guard lock(lock_); + + auto pos = objects_.find(handle.get_local_rank()); + CUGRAPH_EXPECTS(pos == objects_.end(), "Cannot overwrite wrapped object"); + + objects_.insert(std::make_pair(handle.get_local_rank(), std::move(obj))); + } + + /** + * @brief Move a wrapped object into the wrapper for this thread + * + * @param local_rank Identify which GPU to associated this object with + * @param obj Wrapped object + */ + void set(int local_rank, wrapped_t&& obj) + { + std::lock_guard lock(lock_); + + auto pos = objects_.find(local_rank); + CUGRAPH_EXPECTS(pos == objects_.end(), "Cannot overwrite wrapped object"); + + objects_.insert(std::make_pair(local_rank, std::move(obj))); + } + + public: + /** + * @brief Get reference to an object for a particular thread + * + * @param handle Handle is used to identify the GPU we associated this object with + * @return Reference to the wrapped object + */ + wrapped_t& get(cugraph::mtmg::handle_t const& handle) + { + std::lock_guard lock(lock_); + + auto pos = objects_.find(handle.get_local_rank()); + CUGRAPH_EXPECTS(pos != objects_.end(), "Uninitialized wrapped object"); + + return pos->second; + } + + /** + * @brief Get the pointer to an object for a particular thread from this wrapper + * + * @param handle Handle is used to identify the GPU we associated this object with + * @return Shared pointer the wrapped object + */ + wrapped_t const& get(cugraph::mtmg::handle_t const& handle) const + { + std::lock_guard lock(lock_); + + auto pos = objects_.find(handle.get_local_rank()); + + CUGRAPH_EXPECTS(pos != objects_.end(), "Uninitialized wrapped object"); + + return pos->second; + } + + protected: + mutable std::mutex lock_{}; + std::map objects_{}; +}; + +} // namespace detail +} // namespace mtmg +} // namespace cugraph diff --git a/cpp/include/cugraph/mtmg/detail/per_device_edgelist.hpp b/cpp/include/cugraph/mtmg/detail/per_device_edgelist.hpp new file mode 100644 index 00000000000..8011146ee4f --- /dev/null +++ b/cpp/include/cugraph/mtmg/detail/per_device_edgelist.hpp @@ -0,0 +1,275 @@ +/* + * Copyright (c) 2023, NVIDIA CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +// FIXME: Could use std::span once compiler supports C++20 +#include + +#include + +namespace cugraph { +namespace mtmg { +namespace detail { + +/** + * @brief An edgelist for each GPU + * + * Manages an edge list for edges associated with a particular GPU. Multiple threads + * can call the append() method, possibly concurrently. To avoid constantly copying + * when the buffers fill up, the class will create a device buffer containing a + * number of elements specified in the constructor. When that device buffer is full + * we will create a new buffer. + * + * When we try and use the edgelist we will consolidate the buffers, since at that + * time we know the entire size required. + * + * Important note, the expectation is that this object will be used in two phases: + * 1) The append() method will be used to fill buffers with edges + * 2) The edges will be consumed to create a graph + * + * These two phases are expected to be disjoint. The calling process is expected to + * manage some barrier so that all threads are guaranteed to be completed before changing + * phases. If an append() call (part of the filling phase) overlaps with calls to + * finalize_buffer(), consolidate_and_shuffle(), get_src(), get_dst(), get_wgt(), + * get_edge_id() and get_edge_type() then the behavior is undefined (data might change + * in some non-deterministic way). + */ +template +class per_device_edgelist_t { + public: + per_device_edgelist_t() = delete; + per_device_edgelist_t(per_device_edgelist_t const&) = delete; + per_device_edgelist_t& operator=(per_device_edgelist_t const&) = delete; + per_device_edgelist_t& operator=(per_device_edgelist_t&&) = delete; + + per_device_edgelist_t(cugraph::mtmg::handle_t const& handle, + size_t device_buffer_size, + bool use_weight, + bool use_edge_id, + bool use_edge_type) + : device_buffer_size_{device_buffer_size}, + current_pos_{0}, + src_{}, + dst_{}, + wgt_{std::nullopt}, + edge_id_{std::nullopt}, + edge_type_{std::nullopt} + { + if (use_weight) { wgt_ = std::make_optional(std::vector>()); } + + if (use_edge_id) { edge_id_ = std::make_optional(std::vector>()); } + + if (use_edge_type) { + edge_type_ = std::make_optional(std::vector>()); + } + + create_new_buffers(handle); + } + + per_device_edgelist_t(per_device_edgelist_t&& other) + : device_buffer_size_{other.device_buffer_size_}, + current_pos_{other.current_pos_}, + src_{std::move(other.src_)}, + dst_{std::move(other.dst_)}, + wgt_{std::move(other.wgt_)}, + edge_id_{std::move(other.edge_id_)}, + edge_type_{std::move(other.edge_type_)} + { + } + + /** + * @brief Append a list of edges to the edge list + * + * @param handle The resource handle + * @param src Source vertex id + * @param dst Destination vertex id + * @param wgt Edge weight + * @param edge_id Edge id + * @param edge_type Edge type + */ + void append(handle_t const& handle, + raft::host_span src, + raft::host_span dst, + std::optional> wgt, + std::optional> edge_id, + std::optional> edge_type) + { + // FIXME: This lock guard could be on a smaller region, but it + // would require more careful coding. The raft::update_device + // calls could be done without the lock if we made a local + // of the values of *.back() and did an increment of current_pos_ + // while we hold the lock. + std::lock_guard lock(lock_); + + size_t count = src.size(); + size_t pos = 0; + + while (count > 0) { + size_t copy_count = std::min(count, (src_.back().size() - current_pos_)); + + raft::update_device( + src_.back().begin() + current_pos_, src.begin() + pos, copy_count, handle.get_stream()); + raft::update_device( + dst_.back().begin() + current_pos_, dst.begin() + pos, copy_count, handle.get_stream()); + if (wgt) + raft::update_device( + wgt_->back().begin() + current_pos_, wgt->begin() + pos, copy_count, handle.get_stream()); + if (edge_id) + raft::update_device(edge_id_->back().begin() + current_pos_, + edge_id->begin() + pos, + copy_count, + handle.get_stream()); + if (edge_type) + raft::update_device(edge_type_->back().begin() + current_pos_, + edge_type->begin() + pos, + copy_count, + handle.get_stream()); + + count -= copy_count; + pos += copy_count; + current_pos_ += copy_count; + + if (current_pos_ == src_.back().size()) { create_new_buffers(handle); } + } + + handle.raft_handle().sync_stream(); + } + + /** + * @brief Mark the edgelist as ready for reading (all writes are complete) + * + * @param handle The resource handle + */ + void finalize_buffer(handle_t const& handle) + { + src_.back().resize(current_pos_, handle.get_stream()); + dst_.back().resize(current_pos_, handle.get_stream()); + if (wgt_) wgt_->back().resize(current_pos_, handle.get_stream()); + if (edge_id_) edge_id_->back().resize(current_pos_, handle.get_stream()); + if (edge_type_) edge_type_->back().resize(current_pos_, handle.get_stream()); + } + + bool use_weight() const { return wgt_.has_value(); } + + bool use_edge_id() const { return edge_id_.has_value(); } + + bool use_edge_type() const { return edge_type_.has_value(); } + + std::vector>& get_src() { return src_; } + std::vector>& get_dst() { return dst_; } + std::optional>>& get_wgt() { return wgt_; } + std::optional>>& get_edge_id() { return edge_id_; } + std::optional>>& get_edge_type() + { + return edge_type_; + } + + /** + * @brief Consolidate edgelists (if necessary) and shuffle to the proper GPU + * + * @param handle The resource handle + */ + void consolidate_and_shuffle(cugraph::mtmg::handle_t const& handle, bool store_transposed) + { + if (src_.size() > 1) { + size_t total_size = std::transform_reduce( + src_.begin(), src_.end(), size_t{0}, std::plus(), [](auto& d_vector) { + return d_vector.size(); + }); + + resize_and_copy_buffers(handle.get_stream(), src_, total_size); + resize_and_copy_buffers(handle.get_stream(), dst_, total_size); + if (wgt_) resize_and_copy_buffers(handle.get_stream(), *wgt_, total_size); + if (edge_id_) resize_and_copy_buffers(handle.get_stream(), *edge_id_, total_size); + if (edge_type_) resize_and_copy_buffers(handle.get_stream(), *edge_type_, total_size); + } + + auto tmp_wgt = wgt_ ? std::make_optional(std::move((*wgt_)[0])) : std::nullopt; + auto tmp_edge_id = edge_id_ ? std::make_optional(std::move((*edge_id_)[0])) : std::nullopt; + auto tmp_edge_type = + edge_type_ ? std::make_optional(std::move((*edge_type_)[0])) : std::nullopt; + + std::tie(store_transposed ? dst_[0] : src_[0], + store_transposed ? src_[0] : dst_[0], + tmp_wgt, + tmp_edge_id, + tmp_edge_type) = + cugraph::detail::shuffle_ext_vertex_pairs_with_values_to_local_gpu_by_edge_partitioning( + handle.raft_handle(), + store_transposed ? std::move(dst_[0]) : std::move(src_[0]), + store_transposed ? std::move(src_[0]) : std::move(dst_[0]), + std::move(tmp_wgt), + std::move(tmp_edge_id), + std::move(tmp_edge_type)); + + if (tmp_wgt) ((*wgt_)[0]) = std::move(*tmp_wgt); + if (tmp_edge_id) ((*edge_id_)[0]) = std::move(*tmp_edge_id); + if (tmp_edge_type) ((*edge_type_)[0]) = std::move(*tmp_edge_type); + } + + private: + template + void resize_and_copy_buffers(rmm::cuda_stream_view stream, + std::vector>& buffer, + size_t total_size) + { + size_t pos = buffer[0].size(); + buffer[0].resize(total_size, stream); + + for (size_t i = 1; i < buffer.size(); ++i) { + raft::copy(buffer[0].data() + pos, buffer[i].data(), buffer[i].size(), stream); + pos += buffer[i].size(); + buffer[i].resize(0, stream); + buffer[i].shrink_to_fit(stream); + } + + std::vector> new_buffer; + new_buffer.push_back(std::move(buffer[0])); + buffer = std::move(new_buffer); + } + + void create_new_buffers(cugraph::mtmg::handle_t const& handle) + { + src_.emplace_back(device_buffer_size_, handle.get_stream()); + dst_.emplace_back(device_buffer_size_, handle.get_stream()); + + if (wgt_) { wgt_->emplace_back(device_buffer_size_, handle.get_stream()); } + + if (edge_id_) { edge_id_->emplace_back(device_buffer_size_, handle.get_stream()); } + + if (edge_type_) { edge_type_->emplace_back(device_buffer_size_, handle.get_stream()); } + + current_pos_ = 0; + } + + mutable std::mutex lock_{}; + + size_t current_pos_{0}; + size_t device_buffer_size_{0}; + + std::vector> src_{}; + std::vector> dst_{}; + std::optional>> wgt_{}; + std::optional>> edge_id_{}; + std::optional>> edge_type_{}; +}; + +} // namespace detail +} // namespace mtmg +} // namespace cugraph diff --git a/cpp/include/cugraph/mtmg/edge_property.hpp b/cpp/include/cugraph/mtmg/edge_property.hpp new file mode 100644 index 00000000000..afa72492b9a --- /dev/null +++ b/cpp/include/cugraph/mtmg/edge_property.hpp @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2023, NVIDIA CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include + +namespace cugraph { +namespace mtmg { + +/** + * @brief Edge property object for each GPU + */ +template +class edge_property_t : public detail::device_shared_wrapper_t< + cugraph::edge_property_t> { + public: + using parent_t = detail::device_shared_wrapper_t< + cugraph::edge_property_t>; + + /** + * @brief Return a edge_property_view_t (read only) + */ + auto view() + { + std::lock_guard lock(parent_t::lock_); + + using edge_t = typename graph_view_t::wrapped_t::edge_type; + using buffer_t = + typename cugraph::edge_property_t::buffer_type; + std::vector buffers{}; + using const_value_iterator_t = decltype(get_dataframe_buffer_cbegin(buffers[0])); + + edge_property_view_t result; + + std::for_each(parent_t::objects_.begin(), parent_t::objects_.end(), [&result](auto& p) { + result.set(p.first, p.second.view()); + }); + + return result; + } +}; + +} // namespace mtmg +} // namespace cugraph diff --git a/cpp/include/cugraph/mtmg/edge_property_view.hpp b/cpp/include/cugraph/mtmg/edge_property_view.hpp new file mode 100644 index 00000000000..c84a6458e1d --- /dev/null +++ b/cpp/include/cugraph/mtmg/edge_property_view.hpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2023, NVIDIA CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include + +namespace cugraph { +namespace mtmg { + +/** + * @brief Edge property object for each GPU + */ +template +using edge_property_view_t = + detail::device_shared_wrapper_t>; + +} // namespace mtmg +} // namespace cugraph diff --git a/cpp/include/cugraph/mtmg/edgelist.hpp b/cpp/include/cugraph/mtmg/edgelist.hpp new file mode 100644 index 00000000000..90c53dfbb64 --- /dev/null +++ b/cpp/include/cugraph/mtmg/edgelist.hpp @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2023, NVIDIA CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include + +namespace cugraph { +namespace mtmg { + +/** + * @brief Edgelist object for each GPU + */ +template +class edgelist_t : public detail::device_shared_wrapper_t< + detail::per_device_edgelist_t> { + public: + /** + * @brief Create a per_device_edgelist for this GPU + */ + void set(handle_t const& handle, + size_t device_buffer_size, + bool use_weight, + bool use_edge_id, + bool use_edge_type) + { + detail::per_device_edgelist_t tmp( + handle, device_buffer_size, use_weight, use_edge_id, use_edge_type); + + detail::device_shared_wrapper_t< + detail::per_device_edgelist_t>::set(handle, + std::move(tmp)); + } + + /** + * @brief Stop inserting edges into this edgelist so we can use the edges + */ + void finalize_buffer(handle_t const& handle) { this->get(handle).finalize_buffer(handle); } + + /** + * @brief Consolidate for the edgelist edges into a single edgelist and then + * shuffle across GPUs. + */ + void consolidate_and_shuffle(cugraph::mtmg::handle_t const& handle, bool store_transposed) + { + this->get(handle).consolidate_and_shuffle(handle, store_transposed); + } +}; + +} // namespace mtmg +} // namespace cugraph diff --git a/cpp/include/cugraph/mtmg/graph.hpp b/cpp/include/cugraph/mtmg/graph.hpp new file mode 100644 index 00000000000..76a2f401425 --- /dev/null +++ b/cpp/include/cugraph/mtmg/graph.hpp @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2023, NVIDIA CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include +#include +#include + +namespace cugraph { +namespace mtmg { + +/** + * @brief Graph object for each GPU + */ +template +class graph_t : public detail::device_shared_wrapper_t< + cugraph::graph_t> { + using parent_t = detail::device_shared_wrapper_t< + cugraph::graph_t>; + + public: + /** + * @brief Create an MTMG graph view (read only) + */ + auto view() + { + std::lock_guard lock(parent_t::lock_); + + cugraph::mtmg::graph_view_t result; + + std::for_each(parent_t::objects_.begin(), parent_t::objects_.end(), [&result](auto& p) { + result.set(p.first, std::move(p.second.view())); + }); + + return result; + } +}; + +/** + * @brief Create an MTMG graph from an edgelist + * + * @param[in] handle Resource handle + * @param[in] edgelist Edgelist + * @param[in] graph_properties Graph properties + * @param[in] renumber If true, renumber graph (must be true for MG) + * @param[out] graph MTMG graph is stored here + * @param[out] edge_weights MTMG edge weights is stored here + * @param[out] edge_ids MTMG edge ids is stored here + * @param[out] edge_types MTMG edge types is stored here + * @param[in] renumber_map MTMG renumber_map is stored here + * @param[in] do_expensive_check A flag to run expensive checks for input arguments (if set to + * `true`). + */ +template +void create_graph_from_edgelist( + handle_t const& handle, + cugraph::mtmg::edgelist_t& edgelist, + graph_properties_t graph_properties, + bool renumber, + cugraph::mtmg::graph_t& graph, + std::optional, + weight_t>>& edge_weights, + std::optional, + edge_id_t>>& edge_ids, + std::optional, + edge_type_t>>& edge_types, + std::optional>& renumber_map, + bool do_expensive_check = false) +{ + if (handle.get_thread_rank() > 0) return; + + CUGRAPH_EXPECTS(renumber_map.has_value() == renumber, + "Renumbering set to true, but no space for renumber map"); + + auto& my_edgelist = edgelist.get(handle); + + CUGRAPH_EXPECTS(my_edgelist.get_src().size() > 0, "Cannot create graph without an edge list"); + CUGRAPH_EXPECTS(my_edgelist.get_src().size() == 1, + "Must consolidate edges into a single list before creating graph"); + + auto [local_graph, local_edge_weights, local_edge_ids, local_edge_types, local_renumber_map] = + cugraph::create_graph_from_edgelist( + handle.raft_handle(), + std::nullopt, + std::move(my_edgelist.get_src()[0]), + std::move(my_edgelist.get_dst()[0]), + my_edgelist.get_wgt() ? std::make_optional(std::move((*my_edgelist.get_wgt())[0])) + : std::nullopt, + my_edgelist.get_edge_id() ? std::make_optional(std::move((*my_edgelist.get_edge_id())[0])) + : std::nullopt, + my_edgelist.get_edge_type() ? std::make_optional(std::move((*my_edgelist.get_edge_type())[0])) + : std::nullopt, + graph_properties, + renumber, + do_expensive_check); + + graph.set(handle, std::move(local_graph)); + if (edge_weights) edge_weights->set(handle, std::move(*local_edge_weights)); + if (edge_ids) edge_ids->set(handle, std::move(*local_edge_ids)); + if (edge_types) edge_types->set(handle, std::move(*local_edge_types)); + if (renumber) renumber_map->set(handle, std::move(*local_renumber_map)); +} + +} // namespace mtmg +} // namespace cugraph diff --git a/cpp/include/cugraph/mtmg/graph_view.hpp b/cpp/include/cugraph/mtmg/graph_view.hpp new file mode 100644 index 00000000000..94347e016ea --- /dev/null +++ b/cpp/include/cugraph/mtmg/graph_view.hpp @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2023, NVIDIA CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include + +namespace cugraph { +namespace mtmg { + +/** + * @brief Graph view for each GPU + */ +template +using graph_view_t = detail::device_shared_wrapper_t< + cugraph::graph_view_t>; + +} // namespace mtmg +} // namespace cugraph diff --git a/cpp/include/cugraph/mtmg/handle.hpp b/cpp/include/cugraph/mtmg/handle.hpp new file mode 100644 index 00000000000..f23bce5aeac --- /dev/null +++ b/cpp/include/cugraph/mtmg/handle.hpp @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2023, NVIDIA CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +namespace cugraph { +namespace mtmg { + +/** + * @brief Resource handler + * + * Multi-threaded resource handler. Every GPU gets a raft::handle object that provides access to + * the GPU resources. In a multi-threaded environment multiple threads will share a particular GPU. + * Following the MPI model, each thread will be assigned to a thread rank. + * + */ +class handle_t { + public: + /** + * @brief Constructor + * + * @param raft_handle Raft handle for the resources + * @param thread_rank Rank for this thread + */ + handle_t(raft::handle_t const& raft_handle, int thread_rank, size_t device_id) + : raft_handle_(raft_handle), + thread_rank_(thread_rank), + local_rank_(raft_handle.get_comms().get_rank()), // FIXME: update for multi-node + device_id_(device_id) + { + } + + /** + * @brief Get the raft handle + * + * @return const reference to a raft handle + */ + raft::handle_t const& raft_handle() const { return raft_handle_; } + + /** + * @brief Get cuda stream + * + * @return cuda stream + */ + rmm::cuda_stream_view get_stream() const + { + return raft_handle_.is_stream_pool_initialized() + ? raft_handle_.get_stream_from_stream_pool(device_id_) + : raft_handle_.get_stream(); + } + + /** + * @brief Get thread rank + * + * @return thread rank + */ + int get_thread_rank() const { return thread_rank_; } + + /** + * @brief Get number of gpus + * + * @return number of gpus + */ + int get_size() const { return raft_handle_.get_comms().get_size(); } + + /** + * @brief Get number of local gpus + * + * @return number of local gpus + */ + // FIXME: wrong for multi-node + int get_local_size() const { return raft_handle_.get_comms().get_size(); } + + /** + * @brief Get gpu rank + * + * @return gpu rank + */ + int get_rank() const { return raft_handle_.get_comms().get_rank(); } + + /** + * @brief Get local gpu rank + * + * @return local gpu rank + */ + int get_local_rank() const { return local_rank_; } + + private: + raft::handle_t const& raft_handle_; + int thread_rank_; + int local_rank_; + size_t device_id_; +}; + +} // namespace mtmg +} // namespace cugraph diff --git a/cpp/include/cugraph/mtmg/instance_manager.hpp b/cpp/include/cugraph/mtmg/instance_manager.hpp new file mode 100644 index 00000000000..8bf62b56f4b --- /dev/null +++ b/cpp/include/cugraph/mtmg/instance_manager.hpp @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2023, NVIDIA CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +#include + +#include + +namespace cugraph { +namespace mtmg { + +/** + * @brief Manages a subset of the cluster for a set of graph computations + */ +class instance_manager_t { + public: + /** + * @brief Constructor + * + * @param handles Vector of RAFT handles, one for each device on this node + */ + instance_manager_t(std::vector>&& handles, + std::vector>&& nccl_comms, + std::vector&& device_ids, + int local_gpu_count) + : thread_counter_{0}, + raft_handle_{std::move(handles)}, + nccl_comms_{std::move(nccl_comms)}, + device_ids_{std::move(device_ids)}, + local_gpu_count_{local_gpu_count} + { + } + + /** + * @brief Get handle + * + * The instance manager will construct a handle appropriate for the thread making + * the request. Threads will be assigned to GPUs in a round-robin fashion to + * spread requesting threads around the GPU resources. + * + * This function will be CPU thread-safe. + * + * @return a handle for this thread. + */ + handle_t get_handle() + { + int local_id = thread_counter_++; + + RAFT_CUDA_TRY(cudaSetDevice(device_ids_[local_id % raft_handle_.size()].value())); + return handle_t(*raft_handle_[local_id % raft_handle_.size()], + local_id / raft_handle_.size(), + static_cast(local_id % raft_handle_.size())); + } + + /** + * @brief Reset the thread counter + * + * After a parallel activity is completed, we need to reset the thread counter so that + * future threads will round robin around the GPUs properly. + */ + void reset_threads() { thread_counter_.store(0); } + + /** + * @brief Number of local GPUs in the instance + */ + int get_local_gpu_count() { return local_gpu_count_; } + + private: + // FIXME: Should this be an std::map<> where the key is the rank? + // On a multi-node system we might have nodes with fewer + // (or no) GPUs, so mapping rank to a handle might be a challenge + // + std::vector> raft_handle_{}; + std::vector> nccl_comms_{}; + std::vector device_ids_{}; + int local_gpu_count_{}; + + std::atomic thread_counter_{0}; +}; + +} // namespace mtmg +} // namespace cugraph diff --git a/cpp/include/cugraph/mtmg/per_thread_edgelist.hpp b/cpp/include/cugraph/mtmg/per_thread_edgelist.hpp new file mode 100644 index 00000000000..b672db48719 --- /dev/null +++ b/cpp/include/cugraph/mtmg/per_thread_edgelist.hpp @@ -0,0 +1,174 @@ +/* + * Copyright (c) 2023, NVIDIA CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include + +namespace cugraph { +namespace mtmg { + +/** + * @brief Supports creating an edgelist from individual host threads + * + * A cugraph edgelist needs to contain all of the edges necessary to create the graph + * stored in GPU memory (distributed across multiple GPUs in a multi-GPU configuration). + * + * This class provides a mechanism for populating the edgelist object from independent CPU threads. + * + * Calls to the append() method will take edges (in CPU host memory) and append them to a local + * buffer. As the local buffer fills, the buffer will be sent to GPU memory using the flush() + * method. This allows the CPU to GPU transfers to be larger (and consequently more efficient). + */ +template +class per_thread_edgelist_t { + public: + per_thread_edgelist_t() = delete; + per_thread_edgelist_t(per_thread_edgelist_t const&) = delete; + + /** + * @brief Only constructor + * + * @param edgelist The edge list this thread_edgelist_t should be associated with + * @param thread_buffer_size Size of the local buffer for accumulating edges on the CPU + */ + per_thread_edgelist_t( + detail::per_device_edgelist_t& edgelist, + size_t thread_buffer_size) + : edgelist_{edgelist}, + current_pos_{0}, + src_(thread_buffer_size), + dst_(thread_buffer_size), + wgt_{std::nullopt}, + edge_id_{std::nullopt}, + edge_type_{std::nullopt} + { + if (edgelist.use_weight()) wgt_ = std::make_optional(std::vector(thread_buffer_size)); + + if (edgelist.use_edge_id()) + edge_id_ = std::make_optional(std::vector(thread_buffer_size)); + + if (edgelist.use_edge_type()) + edge_type_ = std::make_optional(std::vector(thread_buffer_size)); + } + + /** + * @brief Append an edge to the edge list + * + * @param handle The resource handle + * @param src Source vertex id + * @param dst Destination vertex id + * @param wgt Edge weight + * @param edge_id Edge id + * @param edge_type Edge type + */ + void append(handle_t const& handle, + vertex_t src, + vertex_t dst, + std::optional wgt, + std::optional edge_id, + std::optional edge_type) + { + if (current_pos_ == src_.size()) { flush(handle); } + + src_[current_pos_] = src; + dst_[current_pos_] = dst; + if (wgt) (*wgt_)[current_pos_] = *wgt; + if (edge_id) (*edge_id_)[current_pos_] = *edge_id; + if (edge_type) (*edge_type_)[current_pos_] = *edge_type; + + ++current_pos_; + } + + /** + * @brief Append a list of edges to the edge list + * + * @param handle The resource handle + * @param src Source vertex id + * @param dst Destination vertex id + * @param wgt Edge weight + * @param edge_id Edge id + * @param edge_type Edge type + */ + void append(handle_t const& handle, + raft::host_span src, + raft::host_span dst, + std::optional> wgt, + std::optional> edge_id, + std::optional> edge_type) + { + size_t count = src.size(); + size_t pos = 0; + + while (count > 0) { + size_t copy_count = std::min(count, (src_.size() - current_pos_)); + + std::copy(src.begin() + pos, src.begin() + pos + copy_count, src_.begin() + current_pos_); + std::copy(dst.begin() + pos, dst.begin() + pos + copy_count, dst_.begin() + current_pos_); + if (wgt) + std::copy(wgt.begin() + pos, wgt.begin() + pos + copy_count, wgt_->begin() + current_pos_); + if (edge_id) + std::copy(edge_id.begin() + pos, + edge_id.begin() + pos + copy_count, + edge_id_->begin() + current_pos_); + if (edge_type) + std::copy(edge_type.begin() + pos, + edge_type.begin() + pos + copy_count, + edge_type_->begin() + current_pos_); + + if (current_pos_ == src_.size()) { flush(handle); } + + count -= copy_count; + pos += copy_count; + } + } + + /** + * @brief Flush thread data from host to GPU memory + * + * @param handle The resource handle + */ + void flush(handle_t const& handle) + { + edgelist_.append( + handle, + raft::host_span{src_.data(), current_pos_}, + raft::host_span{dst_.data(), current_pos_}, + wgt_ ? std::make_optional(raft::host_span{wgt_->data(), current_pos_}) + : std::nullopt, + edge_id_ ? std::make_optional(raft::host_span{edge_id_->data(), current_pos_}) + : std::nullopt, + edge_type_ + ? std::make_optional(raft::host_span{edge_type_->data(), current_pos_}) + : std::nullopt); + + current_pos_ = 0; + } + + private: + detail::per_device_edgelist_t& edgelist_; + size_t current_pos_{0}; + std::vector src_{}; + std::vector dst_{}; + std::optional> wgt_{}; + std::optional> edge_id_{}; + std::optional> edge_type_{}; +}; + +} // namespace mtmg +} // namespace cugraph diff --git a/cpp/include/cugraph/mtmg/renumber_map.hpp b/cpp/include/cugraph/mtmg/renumber_map.hpp new file mode 100644 index 00000000000..da07d61bd96 --- /dev/null +++ b/cpp/include/cugraph/mtmg/renumber_map.hpp @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2023, NVIDIA CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include + +namespace cugraph { +namespace mtmg { + +/** + * @brief An MTMG device vector for storing a renumber map + */ +template +class renumber_map_t : public detail::device_shared_device_vector_t { + using parent_t = detail::device_shared_device_vector_t; + + public: + /** + * @brief Return a view (read only) of the renumber map + */ + auto view() { return static_cast>(this->parent_t::view()); } +}; + +} // namespace mtmg +} // namespace cugraph diff --git a/cpp/include/cugraph/mtmg/renumber_map_view.hpp b/cpp/include/cugraph/mtmg/renumber_map_view.hpp new file mode 100644 index 00000000000..5ff7ff5e100 --- /dev/null +++ b/cpp/include/cugraph/mtmg/renumber_map_view.hpp @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2023, NVIDIA CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include + +namespace cugraph { +namespace mtmg { + +/** + * @brief An MTMG device span for storing a renumber map + */ +template +using renumber_map_view_t = detail::device_shared_device_span_t; + +} // namespace mtmg +} // namespace cugraph diff --git a/cpp/include/cugraph/mtmg/resource_manager.hpp b/cpp/include/cugraph/mtmg/resource_manager.hpp new file mode 100644 index 00000000000..b4633626e7c --- /dev/null +++ b/cpp/include/cugraph/mtmg/resource_manager.hpp @@ -0,0 +1,225 @@ +/* + * Copyright (c) 2023, NVIDIA CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include + +namespace cugraph { +namespace mtmg { + +/** + * @brief Class for managing local and remote GPU resources for use in + * multi-threaded multi-GPU interface. + * + * Each process in a multi-GPU configuration should have an instance of this + * class. The resource manager object should be configured by calling + * register_local_gpu (or register_remote_gpu once we support a multi-node + * configuration) to allocate resources that can be used in the mtmg space. + * + * When we want to execute some graph computations, we need to create an instance for execution. + * Based on how big a subset of the desired compute resources is desired, we can allocate some + * number of GPUs to the problem (up to the total set of managed resources). + * + * The returned instance can be used to create a graph, execute one or more algorithms, etc. Once + * we are done the caller can delete the instance. + * + * At the moment, the caller is assumed to be responsible for scheduling use of the resources. + * + * For our first release, we will only consider a single node multi-GPU configuration, so the remote + * GPU methods are currently disabled via ifdef. + */ +class resource_manager_t { + public: + /** + * @brief Default constructor + */ + resource_manager_t() {} + + /** + * @brief add a local GPU to the resource manager. + * + * @param rank The rank to assign to the local GPU + * @param device_id The device_id corresponding to this rank + */ + void register_local_gpu(int rank, rmm::cuda_device_id device_id) + { + std::lock_guard lock(lock_); + + CUGRAPH_EXPECTS(local_rank_map_.find(rank) == local_rank_map_.end(), + "cannot register same rank multiple times"); + + int num_gpus_this_node; + RAFT_CUDA_TRY(cudaGetDeviceCount(&num_gpus_this_node)); + + CUGRAPH_EXPECTS((device_id.value() >= 0) && (device_id.value() < num_gpus_this_node), + "device id out of range"); + + local_rank_map_.insert(std::pair(rank, device_id)); + + RAFT_CUDA_TRY(cudaSetDevice(device_id.value())); + + // FIXME: There is a bug in the cuda_memory_resource that results in a Hang. + // using the pool resource as a work-around. + // + // There is a deprecated environment variable: NCCL_LAUNCH_MODE=GROUP + // which should temporarily work around this problem. + // + // Ultimately there should be some RMM parameters passed into this function + // (or the constructor of the object) to configure this behavior +#if 0 + auto per_device_it = per_device_rmm_resources_.insert( + std::pair{rank, std::make_shared()}); +#else + auto const [free, total] = rmm::detail::available_device_memory(); + auto const min_alloc = + rmm::detail::align_down(std::min(free, total / 6), rmm::detail::CUDA_ALLOCATION_ALIGNMENT); + + auto per_device_it = per_device_rmm_resources_.insert( + std::pair{rank, + rmm::mr::make_owning_wrapper( + std::make_shared(), min_alloc)}); +#endif + + rmm::mr::set_per_device_resource(device_id, per_device_it.first->second.get()); + } + + /** + * @brief Create an instance using a subset of the registered resources + * + * The selected set of resources will be configured as an instance manager. + * If @ranks_to_include is a proper subset of the registered resources, + * ranks will be renumbered into the range [0, @p ranks_to_use.size()), making + * it a proper configuration. + * + * @param ranks_to_use a vector containing the ranks to include in the instance. + * Must be a subset of the entire set of available ranks. + * @param instance_manager_id a ncclUniqueId that is shared by all processes participating + * in this instance. All processes must use the same ID in this call, it is up + * to the calling code to share this ID properly before the call. + * + * @return unique pointer to instance manager + */ + std::unique_ptr create_instance_manager( + std::vector ranks_to_include, ncclUniqueId instance_manager_id) const + { + std::for_each( + ranks_to_include.begin(), ranks_to_include.end(), [local_ranks = local_rank_map_](int rank) { + CUGRAPH_EXPECTS(local_ranks.find(rank) != local_ranks.end(), + "requesting inclusion of an invalid rank"); + }); + + std::vector> nccl_comms{}; + std::vector> handles{}; + std::vector device_ids{}; + + nccl_comms.reserve(ranks_to_include.size()); + handles.reserve(ranks_to_include.size()); + device_ids.reserve(ranks_to_include.size()); + + // FIXME: not quite right for multi-node + auto gpu_row_comm_size = static_cast(sqrt(static_cast(ranks_to_include.size()))); + while (ranks_to_include.size() % gpu_row_comm_size != 0) { + --gpu_row_comm_size; + } + + // FIXME: not quite right for multi-node + for (size_t i = 0; i < ranks_to_include.size(); ++i) { + int rank = ranks_to_include[i]; + auto pos = local_rank_map_.find(rank); + RAFT_CUDA_TRY(cudaSetDevice(pos->second.value())); + + raft::handle_t tmp_handle; + + nccl_comms.push_back(std::make_unique()); + handles.push_back( + std::make_unique(tmp_handle, per_device_rmm_resources_.find(rank)->second)); + device_ids.push_back(pos->second); + } + + std::vector running_threads; + + for (size_t i = 0; i < ranks_to_include.size(); ++i) { + running_threads.emplace_back([instance_manager_id, + idx = i, + gpu_row_comm_size, + comm_size = ranks_to_include.size(), + &ranks_to_include, + &local_rank_map = local_rank_map_, + &nccl_comms, + &handles]() { + int rank = ranks_to_include[idx]; + auto pos = local_rank_map.find(rank); + RAFT_CUDA_TRY(cudaSetDevice(pos->second.value())); + + NCCL_TRY(ncclCommInitRank(nccl_comms[idx].get(), comm_size, instance_manager_id, rank)); + + raft::comms::build_comms_nccl_only(handles[idx].get(), *nccl_comms[idx], comm_size, rank); + + cugraph::partition_manager::init_subcomm(*handles[idx], gpu_row_comm_size); + }); + } + + std::for_each(running_threads.begin(), running_threads.end(), [](auto& t) { t.join(); }); + + // FIXME: Update for multi-node + return std::make_unique( + std::move(handles), std::move(nccl_comms), std::move(device_ids), ranks_to_include.size()); + } + + /** + * @brief Get a list of all of the currently registered ranks + * + * @return A copy of the list of ranks. + */ + std::vector registered_ranks() const + { + std::lock_guard lock(lock_); + + // + // C++20 mechanism: + // return std::vector{ std::views::keys(local_rank_map_).begin(), + // std::views::keys(local_rank_map_).end() }; + // Would need a bit more complicated to handle remote_rank_map_ also + // + std::vector registered_ranks(local_rank_map_.size()); + std::transform( + local_rank_map_.begin(), local_rank_map_.end(), registered_ranks.begin(), [](auto pair) { + return pair.first; + }); + + return registered_ranks; + } + + private: + mutable std::mutex lock_{}; + std::map local_rank_map_{}; + std::map> per_device_rmm_resources_{}; +}; + +} // namespace mtmg +} // namespace cugraph diff --git a/cpp/include/cugraph/mtmg/vertex_result.hpp b/cpp/include/cugraph/mtmg/vertex_result.hpp new file mode 100644 index 00000000000..e8999b35aa9 --- /dev/null +++ b/cpp/include/cugraph/mtmg/vertex_result.hpp @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2023, NVIDIA CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include + +namespace cugraph { +namespace mtmg { + +/** + * @brief An MTMG device vector for storing vertex results + */ +template +class vertex_result_t : public detail::device_shared_device_vector_t { + using parent_t = detail::device_shared_device_vector_t; + + public: + /** + * @brief Create a vertex result view (read only) + */ + auto view() { return static_cast>(this->parent_t::view()); } +}; + +} // namespace mtmg +} // namespace cugraph diff --git a/cpp/include/cugraph/mtmg/vertex_result_view.hpp b/cpp/include/cugraph/mtmg/vertex_result_view.hpp new file mode 100644 index 00000000000..7a7070d6f2a --- /dev/null +++ b/cpp/include/cugraph/mtmg/vertex_result_view.hpp @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2023, NVIDIA CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include +#include + +namespace cugraph { +namespace mtmg { + +/** + * @brief An MTMG device span for referencing a vertex result + */ +template +class vertex_result_view_t : public detail::device_shared_device_span_t { + using parent_t = detail::device_shared_device_span_t; + + public: + vertex_result_view_t(parent_t&& other) : parent_t{std::move(other)} {} + + /** + * @brief Gather results from specified vertices into a device vector + */ + template + rmm::device_uvector gather( + handle_t const& handle, + raft::device_span vertices, + cugraph::mtmg::graph_view_t const& graph_view, + std::optional>& renumber_map_view); +}; + +} // namespace mtmg +} // namespace cugraph diff --git a/cpp/src/link_analysis/pagerank_impl.cuh b/cpp/src/link_analysis/pagerank_impl.cuh index 3a84cdedfda..92c70fcff20 100644 --- a/cpp/src/link_analysis/pagerank_impl.cuh +++ b/cpp/src/link_analysis/pagerank_impl.cuh @@ -388,9 +388,11 @@ void pagerank(raft::handle_t const& handle, handle, graph_view, edge_weight_view, - std::make_optional(raft::device_span{ - *precomputed_vertex_out_weight_sums, - static_cast(graph_view.local_vertex_partition_range_size())}), + precomputed_vertex_out_weight_sums + ? std::make_optional(raft::device_span{ + *precomputed_vertex_out_weight_sums, + static_cast(graph_view.local_vertex_partition_range_size())}) + : std::nullopt, personalization_vertices ? std::make_optional(std::make_tuple( raft::device_span{*personalization_vertices, diff --git a/cpp/src/mtmg/vertex_result.cu b/cpp/src/mtmg/vertex_result.cu new file mode 100644 index 00000000000..a669a127f41 --- /dev/null +++ b/cpp/src/mtmg/vertex_result.cu @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2023, NVIDIA CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include + +#include + +#include + +namespace cugraph { +namespace mtmg { + +template +template +rmm::device_uvector vertex_result_view_t::gather( + handle_t const& handle, + raft::device_span vertices, + cugraph::mtmg::graph_view_t const& graph_view, + std::optional>& renumber_map_view) +{ + auto this_gpu_graph_view = graph_view.get(handle); + + rmm::device_uvector local_vertices(vertices.size(), handle.get_stream()); + rmm::device_uvector vertex_gpu_ids(vertices.size(), handle.get_stream()); + rmm::device_uvector vertex_pos(vertices.size(), handle.get_stream()); + rmm::device_uvector result(vertices.size(), handle.get_stream()); + + raft::copy(local_vertices.data(), vertices.data(), vertices.size(), handle.get_stream()); + cugraph::detail::scalar_fill( + handle.get_stream(), vertex_gpu_ids.data(), vertex_gpu_ids.size(), handle.get_rank()); + cugraph::detail::sequence_fill( + handle.get_stream(), vertex_pos.data(), vertex_pos.size(), size_t{0}); + + rmm::device_uvector d_vertex_partition_range_lasts( + this_gpu_graph_view.vertex_partition_range_lasts().size(), handle.get_stream()); + raft::update_device(d_vertex_partition_range_lasts.data(), + this_gpu_graph_view.vertex_partition_range_lasts().data(), + this_gpu_graph_view.vertex_partition_range_lasts().size(), + handle.get_stream()); + + if (renumber_map_view) { + cugraph::renumber_ext_vertices( + handle.raft_handle(), + local_vertices.data(), + local_vertices.size(), + renumber_map_view->get(handle).data(), + this_gpu_graph_view.local_vertex_partition_range_first(), + this_gpu_graph_view.local_vertex_partition_range_last()); + } + + auto const major_comm_size = + handle.raft_handle().get_subcomm(cugraph::partition_manager::major_comm_name()).get_size(); + auto const minor_comm_size = + handle.raft_handle().get_subcomm(cugraph::partition_manager::minor_comm_name()).get_size(); + + std::forward_as_tuple(local_vertices, std::tie(vertex_gpu_ids, vertex_pos), std::ignore) = + groupby_gpu_id_and_shuffle_kv_pairs( + handle.raft_handle().get_comms(), + local_vertices.begin(), + local_vertices.end(), + thrust::make_zip_iterator(vertex_gpu_ids.begin(), vertex_pos.begin()), + cugraph::detail::compute_gpu_id_from_int_vertex_t{ + raft::device_span(d_vertex_partition_range_lasts.data(), + d_vertex_partition_range_lasts.size()), + major_comm_size, + minor_comm_size}, + handle.get_stream()); + + // + // Now gather + // + rmm::device_uvector tmp_result(local_vertices.size(), handle.get_stream()); + + auto& wrapped = this->get(handle); + + auto vertex_partition = vertex_partition_device_view_t( + this_gpu_graph_view.local_vertex_partition_view()); + + auto iter = + thrust::make_transform_iterator(local_vertices.begin(), [vertex_partition] __device__(auto v) { + return vertex_partition.local_vertex_partition_offset_from_vertex_nocheck(v); + }); + + thrust::gather(handle.raft_handle().get_thrust_policy(), + iter, + iter + local_vertices.size(), + wrapped.begin(), + tmp_result.begin()); + + // + // Shuffle back + // + std::forward_as_tuple(std::ignore, std::tie(std::ignore, vertex_pos, tmp_result), std::ignore) = + groupby_gpu_id_and_shuffle_kv_pairs( + handle.raft_handle().get_comms(), + vertex_gpu_ids.begin(), + vertex_gpu_ids.end(), + thrust::make_zip_iterator(local_vertices.begin(), vertex_pos.begin(), tmp_result.begin()), + [] __device__(int gpu) { return gpu; }, + handle.get_stream()); + + // + // Finally, reorder result + // + thrust::scatter(handle.raft_handle().get_thrust_policy(), + tmp_result.begin(), + tmp_result.end(), + vertex_pos.begin(), + result.begin()); + + return result; +} + +template rmm::device_uvector vertex_result_view_t::gather( + handle_t const& handle, + raft::device_span vertices, + cugraph::mtmg::graph_view_t const& graph_view, + std::optional>& renumber_map_view); + +template rmm::device_uvector vertex_result_view_t::gather( + handle_t const& handle, + raft::device_span vertices, + cugraph::mtmg::graph_view_t const& graph_view, + std::optional>& renumber_map_view); + +template rmm::device_uvector vertex_result_view_t::gather( + handle_t const& handle, + raft::device_span vertices, + cugraph::mtmg::graph_view_t const& graph_view, + std::optional>& renumber_map_view); + +template rmm::device_uvector vertex_result_view_t::gather( + handle_t const& handle, + raft::device_span vertices, + cugraph::mtmg::graph_view_t const& graph_view, + std::optional>& renumber_map_view); + +template rmm::device_uvector vertex_result_view_t::gather( + handle_t const& handle, + raft::device_span vertices, + cugraph::mtmg::graph_view_t const& graph_view, + std::optional>& renumber_map_view); + +template rmm::device_uvector vertex_result_view_t::gather( + handle_t const& handle, + raft::device_span vertices, + cugraph::mtmg::graph_view_t const& graph_view, + std::optional>& renumber_map_view); + +} // namespace mtmg +} // namespace cugraph diff --git a/cpp/tests/CMakeLists.txt b/cpp/tests/CMakeLists.txt index 5e1e1d6ace3..f08606df8ea 100644 --- a/cpp/tests/CMakeLists.txt +++ b/cpp/tests/CMakeLists.txt @@ -419,6 +419,14 @@ ConfigureTest(K_HOP_NBRS_TEST traversal/k_hop_nbrs_test.cpp) # - install tests --------------------------------------------------------------------------------- rapids_test_install_relocatable(INSTALL_COMPONENT_SET testing DESTINATION bin/gtests/libcugraph) +################################################################################################### +# - MTMG tests ------------------------------------------------------------------------- +ConfigureTest(MTMG_TEST mtmg/threaded_test.cu) +target_link_libraries(MTMG_TEST + PRIVATE + UCP::UCP + ) + ################################################################################################### # - MG tests -------------------------------------------------------------------------------------- diff --git a/cpp/tests/mtmg/threaded_test.cu b/cpp/tests/mtmg/threaded_test.cu new file mode 100644 index 00000000000..c5dc2d3c7ce --- /dev/null +++ b/cpp/tests/mtmg/threaded_test.cu @@ -0,0 +1,459 @@ +/* + * Copyright (c) 2023, NVIDIA CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include + +#include + +#include + +#include +#include + +struct Multithreaded_Usecase { + bool test_weighted{false}; + bool check_correctness{true}; +}; + +template +class Tests_Multithreaded + : public ::testing::TestWithParam> { + public: + Tests_Multithreaded() {} + + static void SetUpTestCase() {} + static void TearDownTestCase() {} + + virtual void SetUp() {} + virtual void TearDown() {} + + std::vector get_gpu_list() + { + int num_gpus_per_node{1}; + RAFT_CUDA_TRY(cudaGetDeviceCount(&num_gpus_per_node)); + + std::vector gpu_list(num_gpus_per_node); + std::iota(gpu_list.begin(), gpu_list.end(), 0); + + return gpu_list; + } + + template + void run_current_test( + std::tuple const& param, + std::vector gpu_list) + { + using edge_type_t = int32_t; + + constexpr bool renumber = true; + constexpr bool do_expensive_check = false; + + auto [multithreaded_usecase, input_usecase] = param; + + raft::handle_t handle{}; + + result_t constexpr alpha{0.85}; + result_t constexpr epsilon{1e-6}; + + size_t device_buffer_size{64 * 1024 * 1024}; + size_t thread_buffer_size{4 * 1024 * 1024}; + + int num_gpus = gpu_list.size(); + int num_threads = num_gpus * 4; + + cugraph::mtmg::resource_manager_t resource_manager; + + std::for_each(gpu_list.begin(), gpu_list.end(), [&resource_manager](int gpu_id) { + resource_manager.register_local_gpu(gpu_id, rmm::cuda_device_id{gpu_id}); + }); + + ncclUniqueId instance_manager_id; + ncclGetUniqueId(&instance_manager_id); + + auto instance_manager = resource_manager.create_instance_manager( + resource_manager.registered_ranks(), instance_manager_id); + + cugraph::mtmg::edgelist_t edgelist; + cugraph::mtmg::graph_t graph; + cugraph::mtmg::graph_view_t graph_view; + cugraph::mtmg::vertex_result_t pageranks; + std::optional> renumber_map = + std::make_optional>(); + + auto edge_weights = multithreaded_usecase.test_weighted + ? std::make_optional, + weight_t>>() + : std::nullopt; + + // + // Simulate graph creation by spawning threads to walk through the + // local COO and add edges + // + std::vector running_threads; + + // Initialize shared edgelist object, one per GPU + for (int i = 0; i < num_gpus; ++i) { + running_threads.emplace_back([&instance_manager, + &edgelist, + device_buffer_size, + use_weight = true, + use_edge_id = false, + use_edge_type = false]() { + auto thread_handle = instance_manager->get_handle(); + + edgelist.set(thread_handle, device_buffer_size, use_weight, use_edge_id, use_edge_type); + }); + } + + // Wait for CPU threads to complete + std::for_each(running_threads.begin(), running_threads.end(), [](auto& t) { t.join(); }); + running_threads.resize(0); + instance_manager->reset_threads(); + + // Load SG edge list + auto [d_src_v, d_dst_v, d_weights_v, d_vertices_v, is_symmetric] = + input_usecase.template construct_edgelist( + handle, multithreaded_usecase.test_weighted, false, false); + + auto h_src_v = cugraph::test::to_host(handle, d_src_v); + auto h_dst_v = cugraph::test::to_host(handle, d_dst_v); + auto h_weights_v = cugraph::test::to_host(handle, d_weights_v); + auto unique_vertices = cugraph::test::to_host(handle, d_vertices_v); + + // Load edgelist from different threads. We'll use more threads than GPUs here + for (int i = 0; i < num_threads; ++i) { + running_threads.emplace_back([&instance_manager, + thread_buffer_size, + &edgelist, + &h_src_v, + &h_dst_v, + &h_weights_v, + i, + num_threads]() { + auto thread_handle = instance_manager->get_handle(); + cugraph::mtmg::per_thread_edgelist_t + per_thread_edgelist(edgelist.get(thread_handle), thread_buffer_size); + + for (size_t j = i; j < h_src_v.size(); j += num_threads) { +#if 0 + if (h_weights_v) { + thread_edgelist.append( + thread_handle, h_src_v[j], h_dst_v[j], (*h_weights_v)[j], std::nullopt, std::nullopt); + } else { + thread_edgelist.append( + thread_handle, h_src_v[j], h_dst_v[j], std::nullopt, std::nullopt, std::nullopt); + } +#endif + per_thread_edgelist.append( + thread_handle, + h_src_v[j], + h_dst_v[j], + h_weights_v ? std::make_optional((*h_weights_v)[j]) : std::nullopt, + std::nullopt, + std::nullopt); + } + + per_thread_edgelist.flush(thread_handle); + }); + } + + // Wait for CPU threads to complete + std::for_each(running_threads.begin(), running_threads.end(), [](auto& t) { t.join(); }); + running_threads.resize(0); + instance_manager->reset_threads(); + + for (int i = 0; i < num_gpus; ++i) { + running_threads.emplace_back([&instance_manager, + &graph, + &edge_weights, + &edgelist, + &renumber_map, + &pageranks, + is_symmetric = is_symmetric, + renumber, + do_expensive_check]() { + auto thread_handle = instance_manager->get_handle(); + + if (thread_handle.get_thread_rank() > 0) return; + + std::optional, + edge_t>> + edge_ids{std::nullopt}; + std::optional, + int32_t>> + edge_types{std::nullopt}; + + edgelist.finalize_buffer(thread_handle); + edgelist.consolidate_and_shuffle(thread_handle, true); + + cugraph::mtmg:: + create_graph_from_edgelist( + thread_handle, + edgelist, + cugraph::graph_properties_t{is_symmetric, true}, + renumber, + graph, + edge_weights, + edge_ids, + edge_types, + renumber_map, + do_expensive_check); + }); + } + + // Wait for CPU threads to complete + std::for_each(running_threads.begin(), running_threads.end(), [](auto& t) { t.join(); }); + running_threads.resize(0); + instance_manager->reset_threads(); + + graph_view = graph.view(); + + for (int i = 0; i < num_threads; ++i) { + running_threads.emplace_back( + [&instance_manager, &graph_view, &edge_weights, &pageranks, alpha, epsilon]() { + auto thread_handle = instance_manager->get_handle(); + + if (thread_handle.get_thread_rank() > 0) return; + + auto [local_pageranks, metadata] = + cugraph::pagerank( + thread_handle.raft_handle(), + graph_view.get(thread_handle), + edge_weights ? std::make_optional(edge_weights->get(thread_handle).view()) + : std::nullopt, + std::nullopt, + std::nullopt, + std::nullopt, + alpha, + epsilon, + 500, + true); + + pageranks.set(thread_handle, std::move(local_pageranks)); + }); + } + + // Wait for CPU threads to complete + std::for_each(running_threads.begin(), running_threads.end(), [](auto& t) { t.join(); }); + running_threads.resize(0); + instance_manager->reset_threads(); + + std::vector, std::vector>> computed_pageranks_v; + std::mutex computed_pageranks_lock{}; + + auto pageranks_view = pageranks.view(); + auto renumber_map_view = renumber_map ? std::make_optional(renumber_map->view()) : std::nullopt; + + // Load computed_pageranks from different threads. + for (int i = 0; i < num_gpus; ++i) { + running_threads.emplace_back([&instance_manager, + &graph_view, + &renumber_map_view, + &pageranks_view, + &computed_pageranks_lock, + &computed_pageranks_v, + &h_src_v, + &h_dst_v, + &h_weights_v, + &unique_vertices, + i, + num_threads]() { + auto thread_handle = instance_manager->get_handle(); + + auto number_of_vertices = unique_vertices->size(); + + std::vector my_vertex_list; + my_vertex_list.reserve((number_of_vertices + num_threads - 1) / num_threads); + + for (size_t j = i; j < number_of_vertices; j += num_threads) { + my_vertex_list.push_back((*unique_vertices)[j]); + } + + rmm::device_uvector d_my_vertex_list(my_vertex_list.size(), + thread_handle.raft_handle().get_stream()); + raft::update_device(d_my_vertex_list.data(), + my_vertex_list.data(), + my_vertex_list.size(), + thread_handle.raft_handle().get_stream()); + + auto d_my_pageranks = pageranks_view.gather( + thread_handle, + raft::device_span{d_my_vertex_list.data(), d_my_vertex_list.size()}, + graph_view, + renumber_map_view); + + std::vector my_pageranks(d_my_pageranks.size()); + raft::update_host(my_pageranks.data(), + d_my_pageranks.data(), + d_my_pageranks.size(), + thread_handle.raft_handle().get_stream()); + + { + std::lock_guard lock(computed_pageranks_lock); + computed_pageranks_v.push_back( + std::make_tuple(std::move(my_vertex_list), std::move(my_pageranks))); + } + }); + } + + // Wait for CPU threads to complete + std::for_each(running_threads.begin(), running_threads.end(), [](auto& t) { t.join(); }); + running_threads.resize(0); + instance_manager->reset_threads(); + + if (multithreaded_usecase.check_correctness) { + // Want to compare the results in computed_pageranks_v with SG results + cugraph::graph_t sg_graph(handle); + std::optional< + cugraph::edge_property_t, weight_t>> + sg_edge_weights{std::nullopt}; + std::optional> sg_renumber_map{std::nullopt}; + + std::tie(sg_graph, sg_edge_weights, std::ignore, std::ignore, sg_renumber_map) = cugraph:: + create_graph_from_edgelist( + handle, + std::nullopt, + std::move(d_src_v), + std::move(d_dst_v), + std::move(d_weights_v), + std::nullopt, + std::nullopt, + cugraph::graph_properties_t{is_symmetric, true}, + true); + + auto [sg_pageranks, meta] = cugraph::pagerank( + handle, + sg_graph.view(), + sg_edge_weights ? std::make_optional(sg_edge_weights->view()) : std::nullopt, + std::nullopt, + std::nullopt, + std::nullopt, + alpha, + epsilon); + + auto h_sg_pageranks = cugraph::test::to_host(handle, sg_pageranks); + auto h_sg_renumber_map = cugraph::test::to_host(handle, sg_renumber_map); + auto compare_functor = cugraph::test::nearly_equal{ + weight_t{1e-3}, + weight_t{(weight_t{1} / static_cast(h_sg_pageranks.size())) * weight_t{1e-3}}}; + + std::for_each( + computed_pageranks_v.begin(), + computed_pageranks_v.end(), + [h_sg_pageranks, compare_functor, h_sg_renumber_map](auto t1) { + std::for_each( + thrust::make_zip_iterator(std::get<0>(t1).begin(), std::get<1>(t1).begin()), + thrust::make_zip_iterator(std::get<0>(t1).end(), std::get<1>(t1).end()), + [h_sg_pageranks, compare_functor, h_sg_renumber_map](auto t2) { + vertex_t v = thrust::get<0>(t2); + weight_t pr = thrust::get<1>(t2); + + auto pos = std::find(h_sg_renumber_map->begin(), h_sg_renumber_map->end(), v); + auto offset = std::distance(h_sg_renumber_map->begin(), pos); + + ASSERT_TRUE(compare_functor(pr, h_sg_pageranks[offset])) + << "vertex " << v << ", SG result = " << h_sg_pageranks[offset] + << ", mtmg result = " << pr << ", renumber map = " << (*h_sg_renumber_map)[offset]; + }); + }); + } + } +}; + +using Tests_Multithreaded_File = Tests_Multithreaded; +using Tests_Multithreaded_Rmat = Tests_Multithreaded; + +// FIXME: add tests for type combinations +TEST_P(Tests_Multithreaded_File, CheckInt32Int32FloatFloat) +{ + run_current_test( + override_File_Usecase_with_cmd_line_arguments(GetParam()), std::vector{{0, 1}}); +} + +TEST_P(Tests_Multithreaded_Rmat, CheckInt32Int32FloatFloat) +{ + run_current_test( + override_Rmat_Usecase_with_cmd_line_arguments(GetParam()), std::vector{{0, 1}}); +} + +INSTANTIATE_TEST_SUITE_P(file_test, + Tests_Multithreaded_File, + ::testing::Combine( + // enable correctness checks + ::testing::Values(Multithreaded_Usecase{false, true}, + Multithreaded_Usecase{true, true}), + ::testing::Values(cugraph::test::File_Usecase("karate.csv"), + cugraph::test::File_Usecase("dolphins.csv")))); + +INSTANTIATE_TEST_SUITE_P( + rmat_small_test, + Tests_Multithreaded_Rmat, + ::testing::Combine( + // enable correctness checks + ::testing::Values(Multithreaded_Usecase{false, true}, Multithreaded_Usecase{true, true}), + ::testing::Values(cugraph::test::Rmat_Usecase(10, 16, 0.57, 0.19, 0.19, 0, false, false)))); + +INSTANTIATE_TEST_SUITE_P( + file_benchmark_test, /* note that the test filename can be overridden in benchmarking (with + --gtest_filter to select only the file_benchmark_test with a specific + vertex & edge type combination) by command line arguments and do not + include more than one File_Usecase that differ only in filename + (to avoid running same benchmarks more than once) */ + Tests_Multithreaded_File, + ::testing::Combine( + // disable correctness checks + ::testing::Values(Multithreaded_Usecase{false, false}, Multithreaded_Usecase{true, false}), + ::testing::Values(cugraph::test::File_Usecase("test/datasets/karate.mtx")))); + +INSTANTIATE_TEST_SUITE_P( + rmat_benchmark_test, /* note that scale & edge factor can be overridden in benchmarking (with + --gtest_filter to select only the rmat_benchmark_test with a specific + vertex & edge type combination) by command line arguments and do not + include more than one Rmat_Usecase that differ only in scale or edge + factor (to avoid running same benchmarks more than once) */ + Tests_Multithreaded_Rmat, + ::testing::Combine( + // disable correctness checks for large graphs + ::testing::Values(Multithreaded_Usecase{false, false}, Multithreaded_Usecase{true, false}), + ::testing::Values(cugraph::test::Rmat_Usecase(10, 16, 0.57, 0.19, 0.19, 0, false, false)))); + +CUGRAPH_TEST_PROGRAM_MAIN() From 686c3727782c6d303385d7ecdb0330d890e8184d Mon Sep 17 00:00:00 2001 From: Bradley Dice Date: Tue, 19 Sep 2023 14:20:15 -0500 Subject: [PATCH 003/111] Update to clang 16.0.6. (#3859) This PR updates cugraph to use clang 16.0.6. The previous version 16.0.1 has some minor formatting issues affecting several RAPIDS repos. Authors: - Bradley Dice (https://github.com/bdice) Approvers: - Brad Rees (https://github.com/BradReesWork) - Chuck Hastings (https://github.com/ChuckHastings) URL: https://github.com/rapidsai/cugraph/pull/3859 --- .pre-commit-config.yaml | 2 +- cpp/src/components/legacy/scc_matrix.cuh | 2 +- cpp/src/cores/core_number_impl.cuh | 2 +- ...er_v_random_select_transform_outgoing_e.cuh | 4 ++-- cpp/src/sampling/random_walks.cuh | 18 +++++++++--------- cpp/src/structure/renumber_edgelist_impl.cuh | 2 +- cpp/src/traversal/bfs_impl.cuh | 2 +- cpp/tests/prims/mg_extract_transform_e.cu | 4 ++-- cpp/tests/traversal/mg_sssp_test.cpp | 2 +- cpp/tests/traversal/sssp_test.cpp | 2 +- cpp/tests/utilities/test_utilities.hpp | 2 +- 11 files changed, 21 insertions(+), 21 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 0f05aedf1a1..865d06b20e4 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -33,7 +33,7 @@ repos: additional_dependencies: - flake8==6.0.0 - repo: https://github.com/pre-commit/mirrors-clang-format - rev: v16.0.1 + rev: v16.0.6 hooks: - id: clang-format exclude: | diff --git a/cpp/src/components/legacy/scc_matrix.cuh b/cpp/src/components/legacy/scc_matrix.cuh index 3d56bdc5bf4..d044123bed0 100644 --- a/cpp/src/components/legacy/scc_matrix.cuh +++ b/cpp/src/components/legacy/scc_matrix.cuh @@ -68,7 +68,7 @@ struct SCC_Data { SCC_Data(size_t nrows, const IndexT* p_d_r_o, // row_offsets const IndexT* p_d_c_i) - : // column indices + : // column indices nrows_(nrows), p_d_r_o_(p_d_r_o), p_d_c_i_(p_d_c_i), diff --git a/cpp/src/cores/core_number_impl.cuh b/cpp/src/cores/core_number_impl.cuh index b63ae60f052..ea8e2a9c4ee 100644 --- a/cpp/src/cores/core_number_impl.cuh +++ b/cpp/src/cores/core_number_impl.cuh @@ -72,7 +72,7 @@ struct v_to_core_number_t { // a workaround for cudaErrorInvalidDeviceFunction error when device lambda is used template struct mult_degree_by_two_t { - __device__ edge_t operator()(edge_t d) const { return d* edge_t{2}; } + __device__ edge_t operator()(edge_t d) const { return d * edge_t{2}; } }; } // namespace diff --git a/cpp/src/prims/per_v_random_select_transform_outgoing_e.cuh b/cpp/src/prims/per_v_random_select_transform_outgoing_e.cuh index 3375a651982..e6db21f1c7c 100644 --- a/cpp/src/prims/per_v_random_select_transform_outgoing_e.cuh +++ b/cpp/src/prims/per_v_random_select_transform_outgoing_e.cuh @@ -287,7 +287,7 @@ rmm::device_uvector get_sampling_index_without_replacement( #ifndef NO_CUGRAPH_OPS edge_t mid_partition_degree_range_last = static_cast(K * 10); // tuning parameter assert(mid_partition_degree_range_last > K); - size_t high_partition_over_sampling_K = K * 2; // tuning parameter + size_t high_partition_over_sampling_K = K * 2; // tuning parameter assert(high_partition_over_sampling_K > K); rmm::device_uvector sample_nbr_indices(frontier_degrees.size() * K, handle.get_stream()); @@ -883,7 +883,7 @@ per_v_random_select_transform_e(raft::handle_t const& handle, sample_nbr_indices); // neighbor index within an edge partition (note that each vertex's // neighbors are distributed in minor_comm_size partitions) std::optional> sample_key_indices{ - std::nullopt}; // relevant only when (minor_comm_size > 1) + std::nullopt}; // relevant only when (minor_comm_size > 1) auto local_frontier_sample_counts = std::vector{}; auto local_frontier_sample_displacements = std::vector{}; if (minor_comm_size > 1) { diff --git a/cpp/src/sampling/random_walks.cuh b/cpp/src/sampling/random_walks.cuh index 46789c6b8bd..6a7334e9f1a 100644 --- a/cpp/src/sampling/random_walks.cuh +++ b/cpp/src/sampling/random_walks.cuh @@ -197,19 +197,19 @@ struct col_indx_extract_t { void operator()( original::device_vec_t const& d_coalesced_src_v, // in: coalesced vector of vertices original::device_vec_t const& - d_v_col_indx, // in: column indices, given by stepper's random engine + d_v_col_indx, // in: column indices, given by stepper's random engine original::device_vec_t& d_v_next_vertices, // out: set of destination vertices, for next step original::device_vec_t& - d_v_next_weights) // out: set of weights between src and destination vertices, for next step + d_v_next_weights) // out: set of weights between src and destination vertices, for next step const { thrust::transform_if( handle_.get_thrust_policy(), thrust::make_counting_iterator(0), - thrust::make_counting_iterator(num_paths_), // input1 - d_v_col_indx.begin(), // input2 - out_degs_, // stencil + thrust::make_counting_iterator(num_paths_), // input1 + d_v_col_indx.begin(), // input2 + out_degs_, // stencil thrust::make_zip_iterator( thrust::make_tuple(d_v_next_vertices.begin(), d_v_next_weights.begin())), // output [max_depth = max_depth_, @@ -575,9 +575,9 @@ struct random_walker_t { d_crt_out_degs, // |current set of vertex out degrees| = nelems, // to be used as stencil (don't scatter if 0) original::device_vec_t const& - d_sizes, // paths sizes used to provide delta in coalesced paths; - // pre-condition: assumed as updated to reflect new vertex additions; - // also, this is the number of _vertices_ in each path; + d_sizes, // paths sizes used to provide delta in coalesced paths; + // pre-condition: assumed as updated to reflect new vertex additions; + // also, this is the number of _vertices_ in each path; // hence for scattering weights this needs to be adjusted; hence the `adjust` parameter index_t stride, // stride = coalesce block size (max_depth for vertices; max_depth-1 for weights) @@ -762,7 +762,7 @@ random_walks_impl( // pre-allocate num_paths * max_depth; // original::device_vec_t d_coalesced_v(num_paths * max_depth, - stream); // coalesced vertex set + stream); // coalesced vertex set original::device_vec_t d_coalesced_w(num_paths * (max_depth - 1), stream); // coalesced weight set original::device_vec_t d_paths_sz(num_paths, stream); // paths sizes diff --git a/cpp/src/structure/renumber_edgelist_impl.cuh b/cpp/src/structure/renumber_edgelist_impl.cuh index d7381ba71af..6bc19ff4fe1 100644 --- a/cpp/src/structure/renumber_edgelist_impl.cuh +++ b/cpp/src/structure/renumber_edgelist_impl.cuh @@ -86,7 +86,7 @@ struct find_unused_id_t { for (size_t i = worker_id; i < sorted_local_vertices.size() + size_t{1}; i += num_workers) { auto start = (i == size_t{0}) ? std::numeric_limits::lowest() : sorted_local_vertices[i - size_t{1}]; - if (start != std::numeric_limits::max()) { ++start; }; // now inclusive + if (start != std::numeric_limits::max()) { ++start; }; // now inclusive auto end = (i == sorted_local_vertices.size()) ? std::numeric_limits::max() : sorted_local_vertices[i]; // exclusive for (vertex_t v = start; v < end; ++v) { diff --git a/cpp/src/traversal/bfs_impl.cuh b/cpp/src/traversal/bfs_impl.cuh index 0402184bd93..437071569bf 100644 --- a/cpp/src/traversal/bfs_impl.cuh +++ b/cpp/src/traversal/bfs_impl.cuh @@ -73,7 +73,7 @@ struct e_op_t { if (*(prev_visited_flags + packed_bool_offset(dst)) & packed_bool_mask(dst)) { // check if unvisited in previous iterations push = false; - } else { // check if unvisited in this iteration as well + } else { // check if unvisited in this iteration as well auto old = visited_flags.atomic_or(dst, true); push = !old; } diff --git a/cpp/tests/prims/mg_extract_transform_e.cu b/cpp/tests/prims/mg_extract_transform_e.cu index b71fe5ddb5e..bca6471a5bb 100644 --- a/cpp/tests/prims/mg_extract_transform_e.cu +++ b/cpp/tests/prims/mg_extract_transform_e.cu @@ -157,8 +157,8 @@ class Tests_MGExtractTransformE // 1. create MG graph constexpr bool is_multi_gpu = true; - constexpr bool renumber = true; // needs to be true for multi gpu case - constexpr bool store_transposed = false; // needs to be false for using extract_transform_e + constexpr bool renumber = true; // needs to be true for multi gpu case + constexpr bool store_transposed = false; // needs to be false for using extract_transform_e if (cugraph::test::g_perf) { RAFT_CUDA_TRY(cudaDeviceSynchronize()); // for consistent performance measurement handle_->get_comms().barrier(); diff --git a/cpp/tests/traversal/mg_sssp_test.cpp b/cpp/tests/traversal/mg_sssp_test.cpp index b3e96981f96..ea0353c3743 100644 --- a/cpp/tests/traversal/mg_sssp_test.cpp +++ b/cpp/tests/traversal/mg_sssp_test.cpp @@ -214,7 +214,7 @@ class Tests_MGSSSP : public ::testing::TestWithParam> sg_renumber_map, // std::nullopt if the SG graph is not renumbered std::optional> - mg_vertices, // std::nullopt if the entire local vertex partition range is assumed + mg_vertices, // std::nullopt if the entire local vertex partition range is assumed raft::device_span mg_values); template From d93032105ff92a70e28511471444dfcb2557da90 Mon Sep 17 00:00:00 2001 From: Seunghwa Kang <45857425+seunghwak@users.noreply.github.com> Date: Wed, 20 Sep 2023 14:19:56 -0700 Subject: [PATCH 004/111] MFG C++ code bug fix (#3865) cugraph::sort_sampled_edgelist currently returns (label, hop) offsets of all zero. This PR fixes this. Authors: - Seunghwa Kang (https://github.com/seunghwak) Approvers: - Alex Barghi (https://github.com/alexbarghi-nv) - Chuck Hastings (https://github.com/ChuckHastings) URL: https://github.com/rapidsai/cugraph/pull/3865 --- cpp/src/sampling/sampling_post_processing_impl.cuh | 10 ++++++++-- cpp/tests/sampling/sampling_post_processing_test.cu | 11 +++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/cpp/src/sampling/sampling_post_processing_impl.cuh b/cpp/src/sampling/sampling_post_processing_impl.cuh index ff8da72ff35..0c397d91b20 100644 --- a/cpp/src/sampling/sampling_post_processing_impl.cuh +++ b/cpp/src/sampling/sampling_post_processing_impl.cuh @@ -1619,10 +1619,13 @@ renumber_and_sort_sampled_edgelist( (*edgelist_label_hop_offsets).begin(), (*edgelist_label_hop_offsets).end(), size_t{0}); - thrust::for_each( + // FIXME: the device lambda should be placed in cuda::proclaim_return_type() + // once we update CCCL version to 2.x + thrust::transform( handle.get_thrust_policy(), thrust::make_counting_iterator(size_t{0}), thrust::make_counting_iterator(num_labels * num_hops), + (*edgelist_label_hop_offsets).begin(), [edgelist_label_offsets = edgelist_label_offsets ? thrust::make_optional(std::get<0>(*edgelist_label_offsets)) : thrust::nullopt, @@ -1743,10 +1746,13 @@ sort_sampled_edgelist( (*edgelist_label_hop_offsets).begin(), (*edgelist_label_hop_offsets).end(), size_t{0}); - thrust::for_each( + // FIXME: the device lambda should be placed in cuda::proclaim_return_type() + // once we update CCCL version to 2.x + thrust::transform( handle.get_thrust_policy(), thrust::make_counting_iterator(size_t{0}), thrust::make_counting_iterator(num_labels * num_hops), + (*edgelist_label_hop_offsets).begin(), [edgelist_label_offsets = edgelist_label_offsets ? thrust::make_optional(std::get<0>(*edgelist_label_offsets)) : thrust::nullopt, diff --git a/cpp/tests/sampling/sampling_post_processing_test.cu b/cpp/tests/sampling/sampling_post_processing_test.cu index 422fe953b20..e5267d75ac2 100644 --- a/cpp/tests/sampling/sampling_post_processing_test.cu +++ b/cpp/tests/sampling/sampling_post_processing_test.cu @@ -635,6 +635,12 @@ class Tests_SamplingPostProcessing (*renumbered_and_sorted_edgelist_label_hop_offsets).end())) << "Renumbered and sorted edge list (label,hop) offset array values should be " "non-decreasing."; + + ASSERT_TRUE( + (*renumbered_and_sorted_edgelist_label_hop_offsets).back_element(handle.get_stream()) == + renumbered_and_sorted_edgelist_srcs.size()) + << "Renumbered and sorted edge list (label,hop) offset array's last element should " + "coincide with the number of edges."; } if (renumbered_and_sorted_renumber_map_label_offsets) { @@ -1189,6 +1195,11 @@ class Tests_SamplingPostProcessing (*sorted_edgelist_label_hop_offsets).end())) << "Sorted edge list (label,hop) offset array values should be " "non-decreasing."; + + ASSERT_TRUE((*sorted_edgelist_label_hop_offsets).back_element(handle.get_stream()) == + sorted_edgelist_srcs.size()) + << "Sorted edge list (label,hop) offset array's last element should coincide with the " + "number of edges."; } for (size_t i = 0; i < sampling_post_processing_usecase.num_labels; ++i) { From a53ab34b804af2865d2d210b801a759d2ca29bc6 Mon Sep 17 00:00:00 2001 From: Naim <110031745+naimnv@users.noreply.github.com> Date: Thu, 21 Sep 2023 19:39:18 +0200 Subject: [PATCH 005/111] Refactor python code for similarity algos to use latest CAPI (#3828) This PR - refactors python code for similarity algorithms (Jaccard, Sorensen, Overlap) to use latest CAPI - removes legacy cuda c/c++ code and python wrapper around legacy code - update CAPI tests - remove and update python tests Closes #2546 Closes #2547 Closes #2548 Closes #2549 Closes #2749 Authors: - Naim (https://github.com/naimnv) Approvers: - Seunghwa Kang (https://github.com/seunghwak) - Chuck Hastings (https://github.com/ChuckHastings) - Rick Ratzel (https://github.com/rlratzel) URL: https://github.com/rapidsai/cugraph/pull/3828 --- cpp/CMakeLists.txt | 2 - cpp/src/link_prediction/legacy/jaccard.cu | 429 ------------------ cpp/src/link_prediction/legacy/overlap.cu | 425 ----------------- cpp/tests/c_api/mg_similarity_test.c | 51 ++- cpp/tests/c_api/similarity_test.c | 57 +-- python/cugraph/CMakeLists.txt | 1 - .../cugraph/community/induced_subgraph.py | 9 +- .../cugraph/dask/link_prediction/jaccard.py | 2 +- .../cugraph/dask/link_prediction/overlap.py | 2 +- .../cugraph/dask/link_prediction/sorensen.py | 2 +- .../cugraph/cugraph/experimental/__init__.py | 32 +- .../experimental/link_prediction/__init__.py | 13 - .../experimental/link_prediction/jaccard.py | 255 ----------- .../experimental/link_prediction/overlap.py | 223 --------- .../experimental/link_prediction/sorensen.py | 221 --------- .../cugraph/link_prediction/CMakeLists.txt | 22 - .../cugraph/link_prediction/__init__.py | 23 +- .../cugraph/link_prediction/jaccard.pxd | 35 -- .../cugraph/link_prediction/jaccard.py | 208 ++++++--- .../link_prediction/jaccard_wrapper.pyx | 155 ------- .../cugraph/link_prediction/overlap.pxd | 35 -- .../cugraph/link_prediction/overlap.py | 212 +++++++-- .../link_prediction/overlap_wrapper.pyx | 142 ------ .../cugraph/link_prediction/sorensen.py | 223 ++++++--- .../cugraph/link_prediction/wjaccard.py | 100 ++-- .../cugraph/link_prediction/woverlap.py | 76 ++-- .../cugraph/link_prediction/wsorensen.py | 78 ++-- .../cugraph/cugraph/sampling/random_walks.py | 9 +- .../tests/link_prediction/test_jaccard.py | 315 +++++++------ .../tests/link_prediction/test_overlap.py | 152 ++++--- .../tests/link_prediction/test_sorensen.py | 252 ++++++---- .../tests/link_prediction/test_wjaccard.py | 177 -------- .../tests/link_prediction/test_woverlap.py | 171 ------- .../tests/link_prediction/test_wsorensen.py | 181 -------- python/pylibcugraph/pylibcugraph/__init__.py | 7 + .../pylibcugraph/experimental/__init__.py | 19 +- .../pylibcugraph/jaccard_coefficients.pyx | 12 +- .../pylibcugraph/overlap_coefficients.pyx | 10 +- .../pylibcugraph/sorensen_coefficients.pyx | 10 +- 39 files changed, 1129 insertions(+), 3219 deletions(-) delete mode 100644 cpp/src/link_prediction/legacy/jaccard.cu delete mode 100644 cpp/src/link_prediction/legacy/overlap.cu delete mode 100644 python/cugraph/cugraph/experimental/link_prediction/__init__.py delete mode 100644 python/cugraph/cugraph/experimental/link_prediction/jaccard.py delete mode 100644 python/cugraph/cugraph/experimental/link_prediction/overlap.py delete mode 100644 python/cugraph/cugraph/experimental/link_prediction/sorensen.py delete mode 100644 python/cugraph/cugraph/link_prediction/CMakeLists.txt delete mode 100644 python/cugraph/cugraph/link_prediction/jaccard.pxd delete mode 100644 python/cugraph/cugraph/link_prediction/jaccard_wrapper.pyx delete mode 100644 python/cugraph/cugraph/link_prediction/overlap.pxd delete mode 100644 python/cugraph/cugraph/link_prediction/overlap_wrapper.pyx delete mode 100644 python/cugraph/cugraph/tests/link_prediction/test_wjaccard.py delete mode 100644 python/cugraph/cugraph/tests/link_prediction/test_woverlap.py delete mode 100644 python/cugraph/cugraph/tests/link_prediction/test_wsorensen.py diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index a6c26ee3b91..0d7bd86075d 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -196,8 +196,6 @@ set(CUGRAPH_SOURCES src/utilities/path_retrieval.cu src/structure/legacy/graph.cu src/linear_assignment/legacy/hungarian.cu - src/link_prediction/legacy/jaccard.cu - src/link_prediction/legacy/overlap.cu src/link_prediction/jaccard_sg.cu src/link_prediction/sorensen_sg.cu src/link_prediction/overlap_sg.cu diff --git a/cpp/src/link_prediction/legacy/jaccard.cu b/cpp/src/link_prediction/legacy/jaccard.cu deleted file mode 100644 index d0b240e3c77..00000000000 --- a/cpp/src/link_prediction/legacy/jaccard.cu +++ /dev/null @@ -1,429 +0,0 @@ -/* - * Copyright (c) 2019-2022, NVIDIA CORPORATION. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include -#include - -#include -#include - -#include - -namespace cugraph { -namespace detail { - -// Volume of neighboors (*weight_s) -template -__global__ void jaccard_row_sum( - vertex_t n, edge_t const* csrPtr, vertex_t const* csrInd, weight_t const* v, weight_t* work) -{ - vertex_t row; - edge_t start, end, length; - weight_t sum; - - for (row = threadIdx.y + blockIdx.y * blockDim.y; row < n; row += gridDim.y * blockDim.y) { - start = csrPtr[row]; - end = csrPtr[row + 1]; - length = end - start; - - // compute row sums - if (weighted) { - sum = parallel_prefix_sum(length, csrInd + start, v); - if (threadIdx.x == 0) work[row] = sum; - } else { - work[row] = static_cast(length); - } - } -} - -// Volume of intersections (*weight_i) and cumulated volume of neighboors (*weight_s) -template -__global__ void jaccard_is(vertex_t n, - edge_t const* csrPtr, - vertex_t const* csrInd, - weight_t const* v, - weight_t* work, - weight_t* weight_i, - weight_t* weight_s) -{ - edge_t i, j, Ni, Nj; - vertex_t row, col; - vertex_t ref, cur, ref_col, cur_col, match; - weight_t ref_val; - - for (row = threadIdx.z + blockIdx.z * blockDim.z; row < n; row += gridDim.z * blockDim.z) { - for (j = csrPtr[row] + threadIdx.y + blockIdx.y * blockDim.y; j < csrPtr[row + 1]; - j += gridDim.y * blockDim.y) { - col = csrInd[j]; - // find which row has least elements (and call it reference row) - Ni = csrPtr[row + 1] - csrPtr[row]; - Nj = csrPtr[col + 1] - csrPtr[col]; - ref = (Ni < Nj) ? row : col; - cur = (Ni < Nj) ? col : row; - - // compute new sum weights - weight_s[j] = work[row] + work[col]; - - // compute new intersection weights - // search for the element with the same column index in the reference row - for (i = csrPtr[ref] + threadIdx.x + blockIdx.x * blockDim.x; i < csrPtr[ref + 1]; - i += gridDim.x * blockDim.x) { - match = -1; - ref_col = csrInd[i]; - if (weighted) { - ref_val = v[ref_col]; - } else { - ref_val = 1.0; - } - - // binary search (column indices are sorted within each row) - edge_t left = csrPtr[cur]; - edge_t right = csrPtr[cur + 1] - 1; - while (left <= right) { - edge_t middle = (left + right) >> 1; - cur_col = csrInd[middle]; - if (cur_col > ref_col) { - right = middle - 1; - } else if (cur_col < ref_col) { - left = middle + 1; - } else { - match = middle; - break; - } - } - - // if the element with the same column index in the reference row has been found - if (match != -1) { atomicAdd(&weight_i[j], ref_val); } - } - } - } -} - -// Volume of intersections (*weight_i) and cumulated volume of neighboors (*weight_s) -// Using list of node pairs -template -__global__ void jaccard_is_pairs(edge_t num_pairs, - edge_t const* csrPtr, - vertex_t const* csrInd, - vertex_t const* first_pair, - vertex_t const* second_pair, - weight_t const* v, - weight_t* work, - weight_t* weight_i, - weight_t* weight_s) -{ - edge_t i, idx, Ni, Nj, match; - vertex_t row, col, ref, cur, ref_col, cur_col; - weight_t ref_val; - - for (idx = threadIdx.z + blockIdx.z * blockDim.z; idx < num_pairs; - idx += gridDim.z * blockDim.z) { - row = first_pair[idx]; - col = second_pair[idx]; - - // find which row has least elements (and call it reference row) - Ni = csrPtr[row + 1] - csrPtr[row]; - Nj = csrPtr[col + 1] - csrPtr[col]; - ref = (Ni < Nj) ? row : col; - cur = (Ni < Nj) ? col : row; - - // compute new sum weights - weight_s[idx] = work[row] + work[col]; - - // compute new intersection weights - // search for the element with the same column index in the reference row - for (i = csrPtr[ref] + threadIdx.x + blockIdx.x * blockDim.x; i < csrPtr[ref + 1]; - i += gridDim.x * blockDim.x) { - match = -1; - ref_col = csrInd[i]; - if (weighted) { - ref_val = v[ref_col]; - } else { - ref_val = 1.0; - } - - // binary search (column indices are sorted within each row) - edge_t left = csrPtr[cur]; - edge_t right = csrPtr[cur + 1] - 1; - while (left <= right) { - edge_t middle = (left + right) >> 1; - cur_col = csrInd[middle]; - if (cur_col > ref_col) { - right = middle - 1; - } else if (cur_col < ref_col) { - left = middle + 1; - } else { - match = middle; - break; - } - } - - // if the element with the same column index in the reference row has been found - if (match != -1) { atomicAdd(&weight_i[idx], ref_val); } - } - } -} - -// Jaccard weights (*weight) -template -__global__ void jaccard_jw(edge_t e, - weight_t const* weight_i, - weight_t const* weight_s, - weight_t* weight_j) -{ - edge_t j; - weight_t Wi, Ws, Wu; - - for (j = threadIdx.x + blockIdx.x * blockDim.x; j < e; j += gridDim.x * blockDim.x) { - Wi = weight_i[j]; - Ws = weight_s[j]; - Wu = Ws - Wi; - weight_j[j] = (Wi / Wu); - } -} - -template -int jaccard(vertex_t n, - edge_t e, - edge_t const* csrPtr, - vertex_t const* csrInd, - weight_t const* weight_in, - weight_t* work, - weight_t* weight_i, - weight_t* weight_s, - weight_t* weight_j) -{ - rmm::cuda_stream_view stream_view; - dim3 nthreads, nblocks; - int y = 4; - - // setup launch configuration - nthreads.x = 32; - nthreads.y = y; - nthreads.z = 1; - nblocks.x = 1; - nblocks.y = min((n + nthreads.y - 1) / nthreads.y, vertex_t{CUDA_MAX_BLOCKS}); - nblocks.z = 1; - - // launch kernel - jaccard_row_sum - <<>>(n, csrPtr, csrInd, weight_in, work); - - thrust::fill(rmm::exec_policy(stream_view), weight_i, weight_i + e, weight_t{0.0}); - - // setup launch configuration - nthreads.x = 32 / y; - nthreads.y = y; - nthreads.z = 8; - nblocks.x = 1; - nblocks.y = 1; - nblocks.z = min((n + nthreads.z - 1) / nthreads.z, vertex_t{CUDA_MAX_BLOCKS}); // 1; - - // launch kernel - jaccard_is<<>>( - n, csrPtr, csrInd, weight_in, work, weight_i, weight_s); - - // setup launch configuration - nthreads.x = min(e, edge_t{CUDA_MAX_KERNEL_THREADS}); - nthreads.y = 1; - nthreads.z = 1; - nblocks.x = min((e + nthreads.x - 1) / nthreads.x, edge_t{CUDA_MAX_BLOCKS}); - nblocks.y = 1; - nblocks.z = 1; - - // launch kernel - jaccard_jw - <<>>(e, weight_i, weight_s, weight_j); - - return 0; -} - -template -int jaccard_pairs(vertex_t n, - edge_t num_pairs, - edge_t const* csrPtr, - vertex_t const* csrInd, - vertex_t const* first_pair, - vertex_t const* second_pair, - weight_t const* weight_in, - weight_t* work, - weight_t* weight_i, - weight_t* weight_s, - weight_t* weight_j) -{ - dim3 nthreads, nblocks; - int y = 4; - - // setup launch configuration - nthreads.x = 32; - nthreads.y = y; - nthreads.z = 1; - nblocks.x = 1; - nblocks.y = min((n + nthreads.y - 1) / nthreads.y, vertex_t{CUDA_MAX_BLOCKS}); - nblocks.z = 1; - - // launch kernel - jaccard_row_sum - <<>>(n, csrPtr, csrInd, weight_in, work); - cudaDeviceSynchronize(); - - // NOTE: initilized weight_i vector with 0.0 - // fill(num_pairs, weight_i, weight_t{0.0}); - - // setup launch configuration - nthreads.x = 32; - nthreads.y = 1; - nthreads.z = 8; - nblocks.x = 1; - nblocks.y = 1; - nblocks.z = min((n + nthreads.z - 1) / nthreads.z, vertex_t{CUDA_MAX_BLOCKS}); // 1; - - // launch kernel - jaccard_is_pairs<<>>( - num_pairs, csrPtr, csrInd, first_pair, second_pair, weight_in, work, weight_i, weight_s); - - // setup launch configuration - nthreads.x = min(num_pairs, edge_t{CUDA_MAX_KERNEL_THREADS}); - nthreads.y = 1; - nthreads.z = 1; - nblocks.x = min((num_pairs + nthreads.x - 1) / nthreads.x, (edge_t)CUDA_MAX_BLOCKS); - nblocks.y = 1; - nblocks.z = 1; - - // launch kernel - jaccard_jw - <<>>(num_pairs, weight_i, weight_s, weight_j); - - return 0; -} -} // namespace detail - -template -void jaccard(legacy::GraphCSRView const& graph, WT const* weights, WT* result) -{ - CUGRAPH_EXPECTS(result != nullptr, "Invalid input argument: result pointer is NULL"); - - rmm::device_vector weight_i(graph.number_of_edges); - rmm::device_vector weight_s(graph.number_of_edges); - rmm::device_vector work(graph.number_of_vertices); - - if (weights == nullptr) { - cugraph::detail::jaccard(graph.number_of_vertices, - graph.number_of_edges, - graph.offsets, - graph.indices, - weights, - work.data().get(), - weight_i.data().get(), - weight_s.data().get(), - result); - } else { - cugraph::detail::jaccard(graph.number_of_vertices, - graph.number_of_edges, - graph.offsets, - graph.indices, - weights, - work.data().get(), - weight_i.data().get(), - weight_s.data().get(), - result); - } -} - -template -void jaccard_list(legacy::GraphCSRView const& graph, - WT const* weights, - ET num_pairs, - VT const* first, - VT const* second, - WT* result) -{ - CUGRAPH_EXPECTS(result != nullptr, "Invalid input argument: result pointer is NULL"); - CUGRAPH_EXPECTS(first != nullptr, "Invalid input argument: first is NULL"); - CUGRAPH_EXPECTS(second != nullptr, "Invalid input argument: second in NULL"); - - rmm::device_vector weight_i(num_pairs, WT{0.0}); - rmm::device_vector weight_s(num_pairs); - rmm::device_vector work(graph.number_of_vertices); - - if (weights == nullptr) { - cugraph::detail::jaccard_pairs(graph.number_of_vertices, - num_pairs, - graph.offsets, - graph.indices, - first, - second, - weights, - work.data().get(), - weight_i.data().get(), - weight_s.data().get(), - result); - } else { - cugraph::detail::jaccard_pairs(graph.number_of_vertices, - num_pairs, - graph.offsets, - graph.indices, - first, - second, - weights, - work.data().get(), - weight_i.data().get(), - weight_s.data().get(), - result); - } -} - -template void jaccard(legacy::GraphCSRView const&, - float const*, - float*); -template void jaccard( - legacy::GraphCSRView const&, double const*, double*); -template void jaccard(legacy::GraphCSRView const&, - float const*, - float*); -template void jaccard( - legacy::GraphCSRView const&, double const*, double*); -template void jaccard_list( - legacy::GraphCSRView const&, - float const*, - int32_t, - int32_t const*, - int32_t const*, - float*); -template void jaccard_list( - legacy::GraphCSRView const&, - double const*, - int32_t, - int32_t const*, - int32_t const*, - double*); -template void jaccard_list( - legacy::GraphCSRView const&, - float const*, - int64_t, - int64_t const*, - int64_t const*, - float*); -template void jaccard_list( - legacy::GraphCSRView const&, - double const*, - int64_t, - int64_t const*, - int64_t const*, - double*); - -} // namespace cugraph diff --git a/cpp/src/link_prediction/legacy/overlap.cu b/cpp/src/link_prediction/legacy/overlap.cu deleted file mode 100644 index 67d7cd5e4c6..00000000000 --- a/cpp/src/link_prediction/legacy/overlap.cu +++ /dev/null @@ -1,425 +0,0 @@ -/* - * Copyright (c) 2019-2022, NVIDIA CORPORATION. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include -#include -#include - -namespace cugraph { -namespace detail { - -// Volume of neighboors (*weight_s) -// TODO: Identical kernel to jaccard_row_sum!! -template -__global__ void overlap_row_sum( - vertex_t n, edge_t const* csrPtr, vertex_t const* csrInd, weight_t const* v, weight_t* work) -{ - vertex_t row; - edge_t start, end, length; - weight_t sum; - - for (row = threadIdx.y + blockIdx.y * blockDim.y; row < n; row += gridDim.y * blockDim.y) { - start = csrPtr[row]; - end = csrPtr[row + 1]; - length = end - start; - - // compute row sums - if (weighted) { - sum = parallel_prefix_sum(length, csrInd + start, v); - if (threadIdx.x == 0) work[row] = sum; - } else { - work[row] = static_cast(length); - } - } -} - -// Volume of intersections (*weight_i) and cumulated volume of neighboors (*weight_s) -// TODO: Identical kernel to jaccard_row_sum!! -template -__global__ void overlap_is(vertex_t n, - edge_t const* csrPtr, - vertex_t const* csrInd, - weight_t const* v, - weight_t* work, - weight_t* weight_i, - weight_t* weight_s) -{ - edge_t i, j, Ni, Nj; - vertex_t row, col; - vertex_t ref, cur, ref_col, cur_col, match; - weight_t ref_val; - - for (row = threadIdx.z + blockIdx.z * blockDim.z; row < n; row += gridDim.z * blockDim.z) { - for (j = csrPtr[row] + threadIdx.y + blockIdx.y * blockDim.y; j < csrPtr[row + 1]; - j += gridDim.y * blockDim.y) { - col = csrInd[j]; - // find which row has least elements (and call it reference row) - Ni = csrPtr[row + 1] - csrPtr[row]; - Nj = csrPtr[col + 1] - csrPtr[col]; - ref = (Ni < Nj) ? row : col; - cur = (Ni < Nj) ? col : row; - - // compute new sum weights - weight_s[j] = min(work[row], work[col]); - - // compute new intersection weights - // search for the element with the same column index in the reference row - for (i = csrPtr[ref] + threadIdx.x + blockIdx.x * blockDim.x; i < csrPtr[ref + 1]; - i += gridDim.x * blockDim.x) { - match = -1; - ref_col = csrInd[i]; - if (weighted) { - ref_val = v[ref_col]; - } else { - ref_val = 1.0; - } - - // binary search (column indices are sorted within each row) - edge_t left = csrPtr[cur]; - edge_t right = csrPtr[cur + 1] - 1; - while (left <= right) { - edge_t middle = (left + right) >> 1; - cur_col = csrInd[middle]; - if (cur_col > ref_col) { - right = middle - 1; - } else if (cur_col < ref_col) { - left = middle + 1; - } else { - match = middle; - break; - } - } - - // if the element with the same column index in the reference row has been found - if (match != -1) { atomicAdd(&weight_i[j], ref_val); } - } - } - } -} - -// Volume of intersections (*weight_i) and cumulated volume of neighboors (*weight_s) -// Using list of node pairs -// NOTE: NOT the same as jaccard -template -__global__ void overlap_is_pairs(edge_t num_pairs, - edge_t const* csrPtr, - vertex_t const* csrInd, - vertex_t const* first_pair, - vertex_t const* second_pair, - weight_t const* v, - weight_t* work, - weight_t* weight_i, - weight_t* weight_s) -{ - edge_t i, idx, Ni, Nj, match; - vertex_t row, col, ref, cur, ref_col, cur_col; - weight_t ref_val; - - for (idx = threadIdx.z + blockIdx.z * blockDim.z; idx < num_pairs; - idx += gridDim.z * blockDim.z) { - row = first_pair[idx]; - col = second_pair[idx]; - - // find which row has least elements (and call it reference row) - Ni = csrPtr[row + 1] - csrPtr[row]; - Nj = csrPtr[col + 1] - csrPtr[col]; - ref = (Ni < Nj) ? row : col; - cur = (Ni < Nj) ? col : row; - - // compute new sum weights - weight_s[idx] = min(work[row], work[col]); - - // compute new intersection weights - // search for the element with the same column index in the reference row - for (i = csrPtr[ref] + threadIdx.x + blockIdx.x * blockDim.x; i < csrPtr[ref + 1]; - i += gridDim.x * blockDim.x) { - match = -1; - ref_col = csrInd[i]; - if (weighted) { - ref_val = v[ref_col]; - } else { - ref_val = 1.0; - } - - // binary search (column indices are sorted within each row) - edge_t left = csrPtr[cur]; - edge_t right = csrPtr[cur + 1] - 1; - while (left <= right) { - edge_t middle = (left + right) >> 1; - cur_col = csrInd[middle]; - if (cur_col > ref_col) { - right = middle - 1; - } else if (cur_col < ref_col) { - left = middle + 1; - } else { - match = middle; - break; - } - } - - // if the element with the same column index in the reference row has been found - if (match != -1) { atomicAdd(&weight_i[idx], ref_val); } - } - } -} - -// Overlap weights (*weight) -template -__global__ void overlap_jw(edge_t e, - edge_t const* csrPtr, - vertex_t const* csrInd, - weight_t* weight_i, - weight_t* weight_s, - weight_t* weight_j) -{ - edge_t j; - weight_t Wi, Wu; - - for (j = threadIdx.x + blockIdx.x * blockDim.x; j < e; j += gridDim.x * blockDim.x) { - Wi = weight_i[j]; - Wu = weight_s[j]; - weight_j[j] = (Wi / Wu); - } -} - -template -int overlap(vertex_t n, - edge_t e, - edge_t const* csrPtr, - vertex_t const* csrInd, - weight_t const* weight_in, - weight_t* work, - weight_t* weight_i, - weight_t* weight_s, - weight_t* weight_j) -{ - dim3 nthreads, nblocks; - int y = 4; - - // setup launch configuration - nthreads.x = 32; - nthreads.y = y; - nthreads.z = 1; - nblocks.x = 1; - nblocks.y = min((n + nthreads.y - 1) / nthreads.y, vertex_t{CUDA_MAX_BLOCKS}); - nblocks.z = 1; - - // launch kernel - overlap_row_sum - <<>>(n, csrPtr, csrInd, weight_in, work); - cudaDeviceSynchronize(); - fill(e, weight_i, weight_t{0.0}); - - // setup launch configuration - nthreads.x = 32 / y; - nthreads.y = y; - nthreads.z = 8; - nblocks.x = 1; - nblocks.y = 1; - nblocks.z = min((n + nthreads.z - 1) / nthreads.z, vertex_t{CUDA_MAX_BLOCKS}); // 1; - - // launch kernel - overlap_is - <<>>(n, csrPtr, csrInd, weight_in, work, weight_i, weight_s); - - // setup launch configuration - nthreads.x = min(e, edge_t{CUDA_MAX_KERNEL_THREADS}); - nthreads.y = 1; - nthreads.z = 1; - nblocks.x = min((e + nthreads.x - 1) / nthreads.x, edge_t{CUDA_MAX_BLOCKS}); - nblocks.y = 1; - nblocks.z = 1; - - // launch kernel - overlap_jw - <<>>(e, csrPtr, csrInd, weight_i, weight_s, weight_j); - - return 0; -} - -template -int overlap_pairs(vertex_t n, - edge_t num_pairs, - edge_t const* csrPtr, - vertex_t const* csrInd, - vertex_t const* first_pair, - vertex_t const* second_pair, - weight_t const* weight_in, - weight_t* work, - weight_t* weight_i, - weight_t* weight_s, - weight_t* weight_j) -{ - dim3 nthreads, nblocks; - int y = 4; - - // setup launch configuration - nthreads.x = 32; - nthreads.y = y; - nthreads.z = 1; - nblocks.x = 1; - nblocks.y = min((n + nthreads.y - 1) / nthreads.y, vertex_t{CUDA_MAX_BLOCKS}); - nblocks.z = 1; - // launch kernel - - overlap_row_sum - <<>>(n, csrPtr, csrInd, weight_in, work); - cudaDeviceSynchronize(); - fill(num_pairs, weight_i, weight_t{0.0}); - // setup launch configuration - nthreads.x = 32; - nthreads.y = 1; - nthreads.z = 8; - nblocks.x = 1; - nblocks.y = 1; - nblocks.z = min((n + nthreads.z - 1) / nthreads.z, vertex_t{CUDA_MAX_BLOCKS}); // 1; - - // launch kernel - overlap_is_pairs<<>>( - num_pairs, csrPtr, csrInd, first_pair, second_pair, weight_in, work, weight_i, weight_s); - - // setup launch configuration - nthreads.x = min(num_pairs, edge_t{CUDA_MAX_KERNEL_THREADS}); - nthreads.y = 1; - nthreads.z = 1; - nblocks.x = min((num_pairs + nthreads.x - 1) / nthreads.x, edge_t{CUDA_MAX_BLOCKS}); - nblocks.y = 1; - nblocks.z = 1; - // launch kernel - - overlap_jw - <<>>(num_pairs, csrPtr, csrInd, weight_i, weight_s, weight_j); - - return 0; -} -} // namespace detail - -template -void overlap(legacy::GraphCSRView const& graph, WT const* weights, WT* result) -{ - CUGRAPH_EXPECTS(result != nullptr, "Invalid input argument: result pointer is NULL"); - - rmm::device_vector weight_i(graph.number_of_edges); - rmm::device_vector weight_s(graph.number_of_edges); - rmm::device_vector work(graph.number_of_vertices); - - if (weights == nullptr) { - cugraph::detail::overlap(graph.number_of_vertices, - graph.number_of_edges, - graph.offsets, - graph.indices, - weights, - work.data().get(), - weight_i.data().get(), - weight_s.data().get(), - result); - } else { - cugraph::detail::overlap(graph.number_of_vertices, - graph.number_of_edges, - graph.offsets, - graph.indices, - weights, - work.data().get(), - weight_i.data().get(), - weight_s.data().get(), - result); - } -} - -template -void overlap_list(legacy::GraphCSRView const& graph, - WT const* weights, - ET num_pairs, - VT const* first, - VT const* second, - WT* result) -{ - CUGRAPH_EXPECTS(result != nullptr, "Invalid input argument: result pointer is NULL"); - CUGRAPH_EXPECTS(first != nullptr, "Invalid input argument: first column is NULL"); - CUGRAPH_EXPECTS(second != nullptr, "Invalid input argument: second column is NULL"); - - rmm::device_vector weight_i(num_pairs); - rmm::device_vector weight_s(num_pairs); - rmm::device_vector work(graph.number_of_vertices); - - if (weights == nullptr) { - cugraph::detail::overlap_pairs(graph.number_of_vertices, - num_pairs, - graph.offsets, - graph.indices, - first, - second, - weights, - work.data().get(), - weight_i.data().get(), - weight_s.data().get(), - result); - } else { - cugraph::detail::overlap_pairs(graph.number_of_vertices, - num_pairs, - graph.offsets, - graph.indices, - first, - second, - weights, - work.data().get(), - weight_i.data().get(), - weight_s.data().get(), - result); - } -} - -template void overlap(legacy::GraphCSRView const&, - float const*, - float*); -template void overlap( - legacy::GraphCSRView const&, double const*, double*); -template void overlap(legacy::GraphCSRView const&, - float const*, - float*); -template void overlap( - legacy::GraphCSRView const&, double const*, double*); -template void overlap_list( - legacy::GraphCSRView const&, - float const*, - int32_t, - int32_t const*, - int32_t const*, - float*); -template void overlap_list( - legacy::GraphCSRView const&, - double const*, - int32_t, - int32_t const*, - int32_t const*, - double*); -template void overlap_list( - legacy::GraphCSRView const&, - float const*, - int64_t, - int64_t const*, - int64_t const*, - float*); -template void overlap_list( - legacy::GraphCSRView const&, - double const*, - int64_t, - int64_t const*, - int64_t const*, - double*); - -} // namespace cugraph diff --git a/cpp/tests/c_api/mg_similarity_test.c b/cpp/tests/c_api/mg_similarity_test.c index 0ac160245ab..336f6c50519 100644 --- a/cpp/tests/c_api/mg_similarity_test.c +++ b/cpp/tests/c_api/mg_similarity_test.c @@ -160,15 +160,16 @@ int test_jaccard(const cugraph_resource_handle_t* handle) int test_weighted_jaccard(const cugraph_resource_handle_t* handle) { size_t num_edges = 16; - size_t num_vertices = 6; - size_t num_pairs = 10; + size_t num_vertices = 7; + size_t num_pairs = 3; - vertex_t h_src[] = {0, 1, 1, 2, 2, 2, 3, 4, 1, 3, 4, 0, 1, 3, 5, 5}; - vertex_t h_dst[] = {1, 3, 4, 0, 1, 3, 5, 5, 0, 1, 1, 2, 2, 2, 3, 4}; - weight_t h_wgt[] = {0.1f, 2.1f, 1.1f, 5.1f, 3.1f, 4.1f, 7.2f, 3.2f}; - vertex_t h_first[] = {0, 0, 0, 1, 1, 1, 2, 2, 2, 3}; - vertex_t h_second[] = {1, 3, 4, 2, 3, 5, 3, 4, 5, 4}; - weight_t h_result[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // TODO: Fill in + vertex_t h_src[] = {0, 1, 2, 0, 1, 2, 3, 3, 3, 4, 4, 4, 0, 5, 2, 6}; + vertex_t h_dst[] = {3, 3, 3, 4, 4, 4, 0, 1, 2, 0, 1, 2, 5, 0, 6, 2}; + weight_t h_wgt[] = {0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 3.5, 4.0, 4.0}; + + vertex_t h_first[] = {0, 0, 1}; + vertex_t h_second[] = {1, 2, 3}; + weight_t h_result[] = {0.357143, 0.208333, 0.0}; return generic_similarity_test(handle, h_src, @@ -216,15 +217,16 @@ int test_sorensen(const cugraph_resource_handle_t* handle) int test_weighted_sorensen(const cugraph_resource_handle_t* handle) { size_t num_edges = 16; - size_t num_vertices = 6; - size_t num_pairs = 10; + size_t num_vertices = 7; + size_t num_pairs = 3; - vertex_t h_src[] = {0, 1, 1, 2, 2, 2, 3, 4, 1, 3, 4, 0, 1, 3, 5, 5}; - vertex_t h_dst[] = {1, 3, 4, 0, 1, 3, 5, 5, 0, 1, 1, 2, 2, 2, 3, 4}; - weight_t h_wgt[] = {0.1f, 2.1f, 1.1f, 5.1f, 3.1f, 4.1f, 7.2f, 3.2f}; - vertex_t h_first[] = {0, 0, 0, 1, 1, 1, 2, 2, 2, 3}; - vertex_t h_second[] = {1, 3, 4, 2, 3, 5, 3, 4, 5, 4}; - weight_t h_result[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // TODO: Fill in + vertex_t h_src[] = {0, 1, 2, 0, 1, 2, 3, 3, 3, 4, 4, 4, 0, 5, 2, 6}; + vertex_t h_dst[] = {3, 3, 3, 4, 4, 4, 0, 1, 2, 0, 1, 2, 5, 0, 6, 2}; + weight_t h_wgt[] = {0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 3.5, 4.0, 4.0}; + + vertex_t h_first[] = {0, 0, 1}; + vertex_t h_second[] = {1, 2, 3}; + weight_t h_result[] = {0.526316, 0.344828, 0.000000}; return generic_similarity_test(handle, h_src, @@ -272,15 +274,16 @@ int test_overlap(const cugraph_resource_handle_t* handle) int test_weighted_overlap(const cugraph_resource_handle_t* handle) { size_t num_edges = 16; - size_t num_vertices = 6; - size_t num_pairs = 10; + size_t num_vertices = 7; + size_t num_pairs = 3; - vertex_t h_src[] = {0, 1, 1, 2, 2, 2, 3, 4, 1, 3, 4, 0, 1, 3, 5, 5}; - vertex_t h_dst[] = {1, 3, 4, 0, 1, 3, 5, 5, 0, 1, 1, 2, 2, 2, 3, 4}; - weight_t h_wgt[] = {0.1f, 2.1f, 1.1f, 5.1f, 3.1f, 4.1f, 7.2f, 3.2f}; - vertex_t h_first[] = {0, 0, 0, 1, 1, 1, 2, 2, 2, 3}; - vertex_t h_second[] = {1, 3, 4, 2, 3, 5, 3, 4, 5, 4}; - weight_t h_result[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // TODO: Fill in + vertex_t h_src[] = {0, 1, 2, 0, 1, 2, 3, 3, 3, 4, 4, 4, 0, 5, 2, 6}; + vertex_t h_dst[] = {3, 3, 3, 4, 4, 4, 0, 1, 2, 0, 1, 2, 5, 0, 6, 2}; + weight_t h_wgt[] = {0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 3.5, 4.0, 4.0}; + + vertex_t h_first[] = {0, 0, 1}; + vertex_t h_second[] = {1, 2, 3}; + weight_t h_result[] = {0.714286, 0.416667, 0.000000}; return generic_similarity_test(handle, h_src, diff --git a/cpp/tests/c_api/similarity_test.c b/cpp/tests/c_api/similarity_test.c index 20af3f3eccd..52f849ccd28 100644 --- a/cpp/tests/c_api/similarity_test.c +++ b/cpp/tests/c_api/similarity_test.c @@ -161,15 +161,16 @@ int test_jaccard() int test_weighted_jaccard() { size_t num_edges = 16; - size_t num_vertices = 6; - size_t num_pairs = 10; + size_t num_vertices = 7; + size_t num_pairs = 3; - vertex_t h_src[] = {0, 1, 1, 2, 2, 2, 3, 4, 1, 3, 4, 0, 1, 3, 5, 5}; - vertex_t h_dst[] = {1, 3, 4, 0, 1, 3, 5, 5, 0, 1, 1, 2, 2, 2, 3, 4}; - weight_t h_wgt[] = {0.1f, 2.1f, 1.1f, 5.1f, 3.1f, 4.1f, 7.2f, 3.2f}; - vertex_t h_first[] = {0, 0, 0, 1, 1, 1, 2, 2, 2, 3}; - vertex_t h_second[] = {1, 3, 4, 2, 3, 5, 3, 4, 5, 4}; - weight_t h_result[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // TODO: Fill in + vertex_t h_src[] = {0, 1, 2, 0, 1, 2, 3, 3, 3, 4, 4, 4, 0, 5, 2, 6}; + vertex_t h_dst[] = {3, 3, 3, 4, 4, 4, 0, 1, 2, 0, 1, 2, 5, 0, 6, 2}; + weight_t h_wgt[] = {0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 3.5, 4.0, 4.0}; + + vertex_t h_first[] = {0, 0, 1}; + vertex_t h_second[] = {1, 2, 3}; + weight_t h_result[] = {0.357143, 0.208333, 0.0}; return generic_similarity_test(h_src, h_dst, @@ -215,15 +216,16 @@ int test_sorensen() int test_weighted_sorensen() { size_t num_edges = 16; - size_t num_vertices = 6; - size_t num_pairs = 10; + size_t num_vertices = 7; + size_t num_pairs = 3; - vertex_t h_src[] = {0, 1, 1, 2, 2, 2, 3, 4, 1, 3, 4, 0, 1, 3, 5, 5}; - vertex_t h_dst[] = {1, 3, 4, 0, 1, 3, 5, 5, 0, 1, 1, 2, 2, 2, 3, 4}; - weight_t h_wgt[] = {0.1f, 2.1f, 1.1f, 5.1f, 3.1f, 4.1f, 7.2f, 3.2f}; - vertex_t h_first[] = {0, 0, 0, 1, 1, 1, 2, 2, 2, 3}; - vertex_t h_second[] = {1, 3, 4, 2, 3, 5, 3, 4, 5, 4}; - weight_t h_result[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // TODO: Fill in + vertex_t h_src[] = {0, 1, 2, 0, 1, 2, 3, 3, 3, 4, 4, 4, 0, 5, 2, 6}; + vertex_t h_dst[] = {3, 3, 3, 4, 4, 4, 0, 1, 2, 0, 1, 2, 5, 0, 6, 2}; + weight_t h_wgt[] = {0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 3.5, 4.0, 4.0}; + + vertex_t h_first[] = {0, 0, 1}; + vertex_t h_second[] = {1, 2, 3}; + weight_t h_result[] = {0.526316, 0.344828, 0.000000}; return generic_similarity_test(h_src, h_dst, @@ -269,15 +271,16 @@ int test_overlap() int test_weighted_overlap() { size_t num_edges = 16; - size_t num_vertices = 6; - size_t num_pairs = 10; + size_t num_vertices = 7; + size_t num_pairs = 3; - vertex_t h_src[] = {0, 1, 1, 2, 2, 2, 3, 4, 1, 3, 4, 0, 1, 3, 5, 5}; - vertex_t h_dst[] = {1, 3, 4, 0, 1, 3, 5, 5, 0, 1, 1, 2, 2, 2, 3, 4}; - weight_t h_wgt[] = {0.1f, 2.1f, 1.1f, 5.1f, 3.1f, 4.1f, 7.2f, 3.2f}; - vertex_t h_first[] = {0, 0, 0, 1, 1, 1, 2, 2, 2, 3}; - vertex_t h_second[] = {1, 3, 4, 2, 3, 5, 3, 4, 5, 4}; - weight_t h_result[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // TODO: Fill in + vertex_t h_src[] = {0, 1, 2, 0, 1, 2, 3, 3, 3, 4, 4, 4, 0, 5, 2, 6}; + vertex_t h_dst[] = {3, 3, 3, 4, 4, 4, 0, 1, 2, 0, 1, 2, 5, 0, 6, 2}; + weight_t h_wgt[] = {0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 3.5, 4.0, 4.0}; + + vertex_t h_first[] = {0, 0, 1}; + vertex_t h_second[] = {1, 2, 3}; + weight_t h_result[] = {0.714286, 0.416667, 0.000000}; return generic_similarity_test(h_src, h_dst, @@ -301,8 +304,8 @@ int main(int argc, char** argv) result |= RUN_TEST(test_jaccard); result |= RUN_TEST(test_sorensen); result |= RUN_TEST(test_overlap); - // result |= RUN_TEST(test_weighted_jaccard); - // result |= RUN_TEST(test_weighted_sorensen); - // result |= RUN_TEST(test_weighted_overlap); + result |= RUN_TEST(test_weighted_jaccard); + result |= RUN_TEST(test_weighted_sorensen); + result |= RUN_TEST(test_weighted_overlap); return result; } diff --git a/python/cugraph/CMakeLists.txt b/python/cugraph/CMakeLists.txt index f3b28623b12..ecfcb9b219f 100644 --- a/python/cugraph/CMakeLists.txt +++ b/python/cugraph/CMakeLists.txt @@ -89,7 +89,6 @@ add_subdirectory(cugraph/dask/structure) add_subdirectory(cugraph/internals) add_subdirectory(cugraph/layout) add_subdirectory(cugraph/linear_assignment) -add_subdirectory(cugraph/link_prediction) add_subdirectory(cugraph/structure) add_subdirectory(cugraph/tree) add_subdirectory(cugraph/utilities) diff --git a/python/cugraph/cugraph/community/induced_subgraph.py b/python/cugraph/cugraph/community/induced_subgraph.py index 29fe2f29c1e..3a901199b01 100644 --- a/python/cugraph/cugraph/community/induced_subgraph.py +++ b/python/cugraph/cugraph/community/induced_subgraph.py @@ -25,11 +25,10 @@ ) from cugraph.utilities.utils import import_optional -# FIXME: the networkx.Graph type used in the type annotation for -# induced_subgraph() is specified using a string literal to avoid depending on -# and importing networkx. Instead, networkx is imported optionally, which may -# cause a problem for a type checker if run in an environment where networkx is -# not installed. +# FIXME: the networkx.Graph type used in type annotations is specified +# using a string literal to avoid depending on and importing networkx. +# Instead, networkx is imported optionally, which may cause a problem +# for a type checker if run in an environment where networkx is not installed. networkx = import_optional("networkx") diff --git a/python/cugraph/cugraph/dask/link_prediction/jaccard.py b/python/cugraph/cugraph/dask/link_prediction/jaccard.py index b3d688584a0..218e6206fc3 100644 --- a/python/cugraph/cugraph/dask/link_prediction/jaccard.py +++ b/python/cugraph/cugraph/dask/link_prediction/jaccard.py @@ -20,7 +20,7 @@ from cugraph.dask.common.input_utils import get_distributed_data from cugraph.utilities import renumber_vertex_pair -from pylibcugraph.experimental import ( +from pylibcugraph import ( jaccard_coefficients as pylibcugraph_jaccard_coefficients, ) from pylibcugraph import ResourceHandle diff --git a/python/cugraph/cugraph/dask/link_prediction/overlap.py b/python/cugraph/cugraph/dask/link_prediction/overlap.py index c47aeef3c72..5540be28fd1 100644 --- a/python/cugraph/cugraph/dask/link_prediction/overlap.py +++ b/python/cugraph/cugraph/dask/link_prediction/overlap.py @@ -20,7 +20,7 @@ from cugraph.dask.common.input_utils import get_distributed_data from cugraph.utilities import renumber_vertex_pair -from pylibcugraph.experimental import ( +from pylibcugraph import ( overlap_coefficients as pylibcugraph_overlap_coefficients, ) from pylibcugraph import ResourceHandle diff --git a/python/cugraph/cugraph/dask/link_prediction/sorensen.py b/python/cugraph/cugraph/dask/link_prediction/sorensen.py index bb5a3f44f39..24295ac330c 100644 --- a/python/cugraph/cugraph/dask/link_prediction/sorensen.py +++ b/python/cugraph/cugraph/dask/link_prediction/sorensen.py @@ -20,7 +20,7 @@ from cugraph.dask.common.input_utils import get_distributed_data from cugraph.utilities import renumber_vertex_pair -from pylibcugraph.experimental import ( +from pylibcugraph import ( sorensen_coefficients as pylibcugraph_sorensen_coefficients, ) from pylibcugraph import ResourceHandle diff --git a/python/cugraph/cugraph/experimental/__init__.py b/python/cugraph/cugraph/experimental/__init__.py index b96b760e634..2309a529047 100644 --- a/python/cugraph/cugraph/experimental/__init__.py +++ b/python/cugraph/cugraph/experimental/__init__.py @@ -48,30 +48,22 @@ experimental_warning_wrapper(EXPERIMENTAL__find_bicliques) ) -from cugraph.experimental.link_prediction.jaccard import ( - EXPERIMENTAL__jaccard, - EXPERIMENTAL__jaccard_coefficient, -) +from cugraph.gnn.data_loading import EXPERIMENTAL__BulkSampler -jaccard = experimental_warning_wrapper(EXPERIMENTAL__jaccard) -jaccard_coefficient = experimental_warning_wrapper(EXPERIMENTAL__jaccard_coefficient) +BulkSampler = experimental_warning_wrapper(EXPERIMENTAL__BulkSampler) -from cugraph.experimental.link_prediction.sorensen import ( - EXPERIMENTAL__sorensen, - EXPERIMENTAL__sorensen_coefficient, -) -sorensen = experimental_warning_wrapper(EXPERIMENTAL__sorensen) -sorensen_coefficient = experimental_warning_wrapper(EXPERIMENTAL__sorensen_coefficient) +from cugraph.link_prediction.jaccard import jaccard, jaccard_coefficient -from cugraph.experimental.link_prediction.overlap import ( - EXPERIMENTAL__overlap, - EXPERIMENTAL__overlap_coefficient, -) +jaccard = promoted_experimental_warning_wrapper(jaccard) +jaccard_coefficient = promoted_experimental_warning_wrapper(jaccard_coefficient) -overlap = experimental_warning_wrapper(EXPERIMENTAL__overlap) -overlap_coefficient = experimental_warning_wrapper(EXPERIMENTAL__overlap_coefficient) +from cugraph.link_prediction.sorensen import sorensen, sorensen_coefficient -from cugraph.gnn.data_loading import EXPERIMENTAL__BulkSampler +sorensen = promoted_experimental_warning_wrapper(sorensen) +sorensen_coefficient = promoted_experimental_warning_wrapper(sorensen_coefficient) -BulkSampler = experimental_warning_wrapper(EXPERIMENTAL__BulkSampler) +from cugraph.link_prediction.overlap import overlap, overlap_coefficient + +overlap = promoted_experimental_warning_wrapper(overlap) +overlap_coefficient = promoted_experimental_warning_wrapper(overlap_coefficient) diff --git a/python/cugraph/cugraph/experimental/link_prediction/__init__.py b/python/cugraph/cugraph/experimental/link_prediction/__init__.py deleted file mode 100644 index 081b2ae8260..00000000000 --- a/python/cugraph/cugraph/experimental/link_prediction/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2022-2023, NVIDIA CORPORATION. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/python/cugraph/cugraph/experimental/link_prediction/jaccard.py b/python/cugraph/cugraph/experimental/link_prediction/jaccard.py deleted file mode 100644 index 2eba73b3824..00000000000 --- a/python/cugraph/cugraph/experimental/link_prediction/jaccard.py +++ /dev/null @@ -1,255 +0,0 @@ -# Copyright (c) 2019-2023, NVIDIA CORPORATION. -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from cugraph.utilities import ( - ensure_cugraph_obj_for_nx, - df_edge_score_to_dictionary, - renumber_vertex_pair, -) -import cudf -import warnings - -from pylibcugraph.experimental import ( - jaccard_coefficients as pylibcugraph_jaccard_coefficients, -) -from pylibcugraph import ResourceHandle - - -# FIXME: Move this function to the utility module so that it can be -# shared by other algos -def ensure_valid_dtype(input_graph, vertex_pair): - - vertex_dtype = input_graph.edgelist.edgelist_df.dtypes[0] - vertex_pair_dtypes = vertex_pair.dtypes - - if vertex_pair_dtypes[0] != vertex_dtype or vertex_pair_dtypes[1] != vertex_dtype: - warning_msg = ( - "Jaccard requires 'vertex_pair' to match the graph's 'vertex' type. " - f"input graph's vertex type is: {vertex_dtype} and got " - f"'vertex_pair' of type: {vertex_pair_dtypes}." - ) - warnings.warn(warning_msg, UserWarning) - vertex_pair = vertex_pair.astype(vertex_dtype) - - return vertex_pair - - -def EXPERIMENTAL__jaccard(G, vertex_pair=None, use_weight=False): - """ - Compute the Jaccard similarity between each pair of vertices connected by - an edge, or between arbitrary pairs of vertices specified by the user. - Jaccard similarity is defined between two sets as the ratio of the volume - of their intersection divided by the volume of their union. In the context - of graphs, the neighborhood of a vertex is seen as a set. The Jaccard - similarity weight of each edge represents the strength of connection - between vertices based on the relative similarity of their neighbors. If - first is specified but second is not, or vice versa, an exception will be - thrown. - - NOTE: If the vertex_pair parameter is not specified then the behavior - of cugraph.jaccard is different from the behavior of - networkx.jaccard_coefficient. - - cugraph.jaccard, in the absence of a specified vertex pair list, will - compute the two_hop_neighbors of the entire graph to construct a vertex pair - list and will return the jaccard coefficient for those vertex pairs. This is - not advisable as the vertex_pairs can grow exponentially with respect to the - size of the datasets - - networkx.jaccard_coefficient, in the absence of a specified vertex - pair list, will return an upper triangular dense matrix, excluding - the diagonal as well as vertex pairs that are directly connected - by an edge in the graph, of jaccard coefficients. Technically, networkx - returns a lazy iterator across this upper triangular matrix where - the actual jaccard coefficient is computed when the iterator is - dereferenced. Computing a dense matrix of results is not feasible - if the number of vertices in the graph is large (100,000 vertices - would result in 4.9 billion values in that iterator). - - If your graph is small enough (or you have enough memory and patience) - you can get the interesting (non-zero) values that are part of the networkx - solution by doing the following: - - >>> from cugraph.datasets import karate - >>> G = karate.get_graph(download=True, ignore_weights=True) - >>> pairs = G.get_two_hop_neighbors() - >>> df = cugraph.jaccard(G, pairs) - - But please remember that cugraph will fill the dataframe with the entire - solution you request, so you'll need enough memory to store the 2-hop - neighborhood dataframe. - - - Parameters - ---------- - G : cugraph.Graph - cuGraph Graph instance, should contain the connectivity information - as an edge list (edge weights are not supported yet for this algorithm). The - graph should be undirected where an undirected edge is represented by a - directed edge in both direction. The adjacency list will be computed if - not already present. - - This implementation only supports undirected, unweighted Graph. - - vertex_pair : cudf.DataFrame, optional (default=None) - A GPU dataframe consisting of two columns representing pairs of - vertices. If provided, the jaccard coefficient is computed for the - given vertex pairs. If the vertex_pair is not provided then the - current implementation computes the jaccard coefficient for all - adjacent vertices in the graph. - - use_weight : bool, optional (default=False) - Currently not supported - - Returns - ------- - df : cudf.DataFrame - GPU data frame of size E (the default) or the size of the given pairs - (first, second) containing the Jaccard weights. The ordering is - relative to the adjacency list, or that given by the specified vertex - pairs. - - df['first'] : cudf.Series - The first vertex ID of each pair (will be identical to first if specified). - df['second'] : cudf.Series - The second vertex ID of each pair (will be identical to second if - specified). - df['jaccard_coeff'] : cudf.Series - The computed jaccard coefficient between the first and the second - vertex ID. - - Examples - -------- - >>> from cugraph.datasets import karate - >>> from cugraph.experimental import jaccard as exp_jaccard - >>> G = karate.get_graph(download=True, ignore_weights=True) - >>> df = exp_jaccard(G) - - """ - if G.is_directed(): - raise ValueError("Input must be an undirected Graph.") - - if G.is_weighted(): - raise ValueError("Weighted graphs are currently not supported.") - - if use_weight: - raise ValueError("'use_weight' is currently not supported.") - - if vertex_pair is None: - # Call two_hop neighbor of the entire graph - vertex_pair = G.get_two_hop_neighbors() - - v_p_num_col = len(vertex_pair.columns) - - if isinstance(vertex_pair, cudf.DataFrame): - vertex_pair = renumber_vertex_pair(G, vertex_pair) - vertex_pair = ensure_valid_dtype(G, vertex_pair) - src_col_name = vertex_pair.columns[0] - dst_col_name = vertex_pair.columns[1] - first = vertex_pair[src_col_name] - second = vertex_pair[dst_col_name] - - elif vertex_pair is not None: - raise ValueError("vertex_pair must be a cudf dataframe") - - use_weight = False - first, second, jaccard_coeff = pylibcugraph_jaccard_coefficients( - resource_handle=ResourceHandle(), - graph=G._plc_graph, - first=first, - second=second, - use_weight=use_weight, - do_expensive_check=False, - ) - - if G.renumbered: - vertex_pair = G.unrenumber(vertex_pair, src_col_name, preserve_order=True) - vertex_pair = G.unrenumber(vertex_pair, dst_col_name, preserve_order=True) - - if v_p_num_col == 2: - # single column vertex - vertex_pair = vertex_pair.rename( - columns={src_col_name: "first", dst_col_name: "second"} - ) - - df = vertex_pair - df["jaccard_coeff"] = cudf.Series(jaccard_coeff) - - return df - - -def EXPERIMENTAL__jaccard_coefficient(G, ebunch=None, use_weight=False): - """ - For NetworkX Compatability. See `jaccard` - - Parameters - ---------- - graph : cugraph.Graph - cuGraph Graph instance, should contain the connectivity information - as an edge list (edge weights are not supported yet for this algorithm). The - graph should be undirected where an undirected edge is represented by a - directed edge in both direction. The adjacency list will be computed if - not already present. - - ebunch : cudf.DataFrame, optional (default=None) - A GPU dataframe consisting of two columns representing pairs of - vertices. If provided, the jaccard coefficient is computed for the - given vertex pairs. If the vertex_pair is not provided then the - current implementation computes the jaccard coefficient for all - adjacent vertices in the graph. - - use_weight : bool, optional (default=False) - Currently not supported - - Returns - ------- - df : cudf.DataFrame - GPU data frame of size E (the default) or the size of the given pairs - (first, second) containing the Jaccard weights. The ordering is - relative to the adjacency list, or that given by the specified vertex - pairs. - - ddf['first']: dask_cudf.Series - The first vertex ID of each pair (will be identical to first if specified). - ddf['second']: dask_cudf.Series - The second vertex ID of each pair (will be identical to second if - specified). - ddf['jaccard_coeff']: dask_cudf.Series - The computed jaccard coefficient between the first and the second - vertex ID. - - Examples - -------- - >>> from cugraph.datasets import karate - >>> from cugraph.experimental import jaccard_coefficient as exp_jaccard_coefficient - >>> G = karate.get_graph(download=True, ignore_weights=True) - >>> df = exp_jaccard_coefficient(G) - - """ - vertex_pair = None - - G, isNx = ensure_cugraph_obj_for_nx(G) - - # FIXME: What is the logic behind this since the docstrings mention that 'G' and - # 'ebunch'(if not None) are respectively of type cugraph.Graph and cudf.DataFrame? - if isNx is True and ebunch is not None: - vertex_pair = cudf.DataFrame(ebunch) - - df = EXPERIMENTAL__jaccard(G, vertex_pair) - - if isNx is True: - df = df_edge_score_to_dictionary( - df, k="jaccard_coeff", src="first", dst="second" - ) - - return df diff --git a/python/cugraph/cugraph/experimental/link_prediction/overlap.py b/python/cugraph/cugraph/experimental/link_prediction/overlap.py deleted file mode 100644 index 0981ced4835..00000000000 --- a/python/cugraph/cugraph/experimental/link_prediction/overlap.py +++ /dev/null @@ -1,223 +0,0 @@ -# Copyright (c) 2019-2023, NVIDIA CORPORATION. -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from cugraph.utilities import ( - ensure_cugraph_obj_for_nx, - df_edge_score_to_dictionary, - renumber_vertex_pair, -) -import cudf -import warnings - -from pylibcugraph.experimental import ( - overlap_coefficients as pylibcugraph_overlap_coefficients, -) -from pylibcugraph import ResourceHandle - - -# FIXME: Move this function to the utility module so that it can be -# shared by other algos -def ensure_valid_dtype(input_graph, vertex_pair): - - vertex_dtype = input_graph.edgelist.edgelist_df.dtypes[0] - vertex_pair_dtypes = vertex_pair.dtypes - - if vertex_pair_dtypes[0] != vertex_dtype or vertex_pair_dtypes[1] != vertex_dtype: - warning_msg = ( - "Overlap requires 'vertex_pair' to match the graph's 'vertex' type. " - f"input graph's vertex type is: {vertex_dtype} and got " - f"'vertex_pair' of type: {vertex_pair_dtypes}." - ) - warnings.warn(warning_msg, UserWarning) - vertex_pair = vertex_pair.astype(vertex_dtype) - - return vertex_pair - - -def EXPERIMENTAL__overlap_coefficient(G, ebunch=None, use_weight=False): - """ - For NetworkX Compatability. See `overlap` - - Parameters - ---------- - G : cugraph.Graph - cuGraph Graph instance, should contain the connectivity information - as an edge list (edge weights are not supported yet for this algorithm). The - graph should be undirected where an undirected edge is represented by a - directed edge in both direction. The adjacency list will be computed if - not already present. - - ebunch : cudf.DataFrame, optional (default=None) - A GPU dataframe consisting of two columns representing pairs of - vertices. If provided, the Overlap coefficient is computed for the - given vertex pairs. If the vertex_pair is not provided then the - current implementation computes the overlap coefficient for all - adjacent vertices in the graph. - - use_weight : bool, optional (default=False) - Currently not supported - - Returns - ------- - df : cudf.DataFrame - GPU data frame of size E (the default) or the size of the given pairs - (first, second) containing the overlap weights. The ordering is - relative to the adjacency list, or that given by the specified vertex - pairs. - - ddf['first']: dask_cudf.Series - The first vertex ID of each pair (will be identical to first if specified). - ddf['second']: dask_cudf.Series - The second vertex ID of each pair (will be identical to second if - specified). - ddf['overlap_coeff']: dask_cudf.Series - The computed overlap coefficient between the first and the second - vertex ID. - - Examples - -------- - >>> from cugraph.datasets import karate - >>> from cugraph.experimental import overlap_coefficient as exp_overlap_coefficient - >>> G = karate.get_graph(download=True, ignore_weights=True) - >>> df = exp_overlap_coefficient(G) - """ - vertex_pair = None - - G, isNx = ensure_cugraph_obj_for_nx(G) - - # FIXME: What is the logic behind this since the docstrings mention that 'G' and - # 'ebunch'(if not None) are respectively of type cugraph.Graph and cudf.DataFrame? - if isNx is True and ebunch is not None: - vertex_pair = cudf.DataFrame(ebunch) - - df = EXPERIMENTAL__overlap(G, vertex_pair) - - if isNx is True: - df = df_edge_score_to_dictionary( - df, k="overlap_coeff", src="first", dst="second" - ) - - return df - - -def EXPERIMENTAL__overlap(G, vertex_pair=None, use_weight=False): - """ - Compute the Overlap Coefficient between each pair of vertices connected by - an edge, or between arbitrary pairs of vertices specified by the user. - Overlap Coefficient is defined between two sets as the ratio of the volume - of their intersection divided by the smaller of their two volumes. In the - context of graphs, the neighborhood of a vertex is seen as a set. The - Overlap Coefficient weight of each edge represents the strength of - connection between vertices based on the relative similarity of their - neighbors. If first is specified but second is not, or vice versa, an - exception will be thrown. - - cugraph.overlap, in the absence of a specified vertex pair list, will - compute the two_hop_neighbors of the entire graph to construct a vertex pair - list and will return the overlap coefficient for those vertex pairs. This is - not advisable as the vertex_pairs can grow exponentially with respect to the - size of the datasets - - Parameters - ---------- - G : cugraph.Graph - cuGraph Graph instance, should contain the connectivity information - as an edge list (edge weights are not supported yet for this algorithm). The - adjacency list will be computed if not already present. - - This implementation only supports undirected, unweighted Graph. - - vertex_pair : cudf.DataFrame, optional (default=None) - A GPU dataframe consisting of two columns representing pairs of - vertices. If provided, the overlap coefficient is computed for the - given vertex pairs, else, it is computed for all vertex pairs. - - use_weight : bool, optional (default=False) - Currently not supported - - Returns - ------- - df : cudf.DataFrame - GPU data frame of size E (the default) or the size of the given pairs - (first, second) containing the Overlap coefficients. The ordering is - relative to the adjacency list, or that given by the specified vertex - pairs. - - df['first'] : cudf.Series - The first vertex ID of each pair (will be identical to first if specified). - df['second'] : cudf.Series - The second vertex ID of each pair (will be identical to second if - specified). - df['overlap_coeff'] : cudf.Series - The computed overlap coefficient between the first and the second - vertex ID. - - Examples - -------- - >>> from cugraph.datasets import karate - >>> from cugraph.experimental import overlap as exp_overlap - >>> G = karate.get_graph(download=True, ignore_weights=True) - >>> df = exp_overlap(G) - - """ - - if G.is_directed(): - raise ValueError("Input must be an undirected Graph.") - - if G.is_weighted(): - raise ValueError("Weighted graphs are currently not supported.") - - if use_weight: - raise ValueError("'use_weight' is currently not supported.") - - if vertex_pair is None: - # Call two_hop neighbor of the entire graph - vertex_pair = G.get_two_hop_neighbors() - - v_p_num_col = len(vertex_pair.columns) - - if isinstance(vertex_pair, cudf.DataFrame): - vertex_pair = renumber_vertex_pair(G, vertex_pair) - vertex_pair = ensure_valid_dtype(G, vertex_pair) - src_col_name = vertex_pair.columns[0] - dst_col_name = vertex_pair.columns[1] - first = vertex_pair[src_col_name] - second = vertex_pair[dst_col_name] - - elif vertex_pair is not None: - raise ValueError("vertex_pair must be a cudf dataframe") - - use_weight = False - first, second, overlap_coeff = pylibcugraph_overlap_coefficients( - resource_handle=ResourceHandle(), - graph=G._plc_graph, - first=first, - second=second, - use_weight=use_weight, - do_expensive_check=False, - ) - - if G.renumbered: - vertex_pair = G.unrenumber(vertex_pair, src_col_name, preserve_order=True) - vertex_pair = G.unrenumber(vertex_pair, dst_col_name, preserve_order=True) - - if v_p_num_col == 2: - # single column vertex - vertex_pair = vertex_pair.rename( - columns={src_col_name: "first", dst_col_name: "second"} - ) - - df = vertex_pair - df["overlap_coeff"] = cudf.Series(overlap_coeff) - - return df diff --git a/python/cugraph/cugraph/experimental/link_prediction/sorensen.py b/python/cugraph/cugraph/experimental/link_prediction/sorensen.py deleted file mode 100644 index ed27e4813d3..00000000000 --- a/python/cugraph/cugraph/experimental/link_prediction/sorensen.py +++ /dev/null @@ -1,221 +0,0 @@ -# Copyright (c) 2021-2023, NVIDIA CORPORATION. -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from cugraph.utilities import ( - ensure_cugraph_obj_for_nx, - df_edge_score_to_dictionary, - renumber_vertex_pair, -) -import cudf -import warnings -from pylibcugraph.experimental import ( - sorensen_coefficients as pylibcugraph_sorensen_coefficients, -) -from pylibcugraph import ResourceHandle - - -# FIXME: Move this function to the utility module so that it can be -# shared by other algos -def ensure_valid_dtype(input_graph, vertex_pair): - - vertex_dtype = input_graph.edgelist.edgelist_df.dtypes[0] - vertex_pair_dtypes = vertex_pair.dtypes - - if vertex_pair_dtypes[0] != vertex_dtype or vertex_pair_dtypes[1] != vertex_dtype: - warning_msg = ( - "Sorensen requires 'vertex_pair' to match the graph's 'vertex' type. " - f"input graph's vertex type is: {vertex_dtype} and got " - f"'vertex_pair' of type: {vertex_pair_dtypes}." - ) - warnings.warn(warning_msg, UserWarning) - vertex_pair = vertex_pair.astype(vertex_dtype) - - return vertex_pair - - -def EXPERIMENTAL__sorensen(G, vertex_pair=None, use_weight=False): - """ - Compute the Sorensen coefficient between each pair of vertices connected by - an edge, or between arbitrary pairs of vertices specified by the user. - Sorensen coefficient is defined between two sets as the ratio of twice the - volume of their intersection divided by the volume of each set. - If first is specified but second is not, or vice versa, an exception will - be thrown. - - cugraph.sorensen, in the absence of a specified vertex pair list, will - compute the two_hop_neighbors of the entire graph to construct a vertex pair - list and will return the sorensen coefficient for those vertex pairs. This is - not advisable as the vertex_pairs can grow exponentially with respect to the - size of the datasets - - Parameters - ---------- - G : cugraph.Graph - cuGraph Graph instance, should contain the connectivity information - as an edge list (edge weights are not supported yet for this algorithm). The - graph should be undirected where an undirected edge is represented by a - directed edge in both direction. The adjacency list will be computed if - not already present. - - This implementation only supports undirected, unweighted Graph. - - vertex_pair : cudf.DataFrame, optional (default=None) - A GPU dataframe consisting of two columns representing pairs of - vertices. If provided, the Sorensen coefficient is computed for the - given vertex pairs. If the vertex_pair is not provided then the - current implementation computes the Sorensen coefficient for all - adjacent vertices in the graph. - - use_weight : bool, optional (default=False) - Currently not supported - - Returns - ------- - df : cudf.DataFrame - GPU data frame of size E (the default) or the size of the given pairs - (first, second) containing the Sorensen index. The ordering is - relative to the adjacency list, or that given by the specified vertex - pairs. - - df['first'] : cudf.Series - The first vertex ID of each pair (will be identical to first if specified). - df['second'] : cudf.Series - The second vertex ID of each pair (will be identical to second if - specified). - df['sorensen_coeff'] : cudf.Series - The computed sorensen coefficient between the first and the second - vertex ID. - - Examples - -------- - >>> from cugraph.datasets import karate - >>> from cugraph.experimental import sorensen as exp_sorensen - >>> G = karate.get_graph(download=True, ignore_weights=True) - >>> df = exp_sorensen(G) - - """ - if G.is_directed(): - raise ValueError("Input must be an undirected Graph.") - - if G.is_weighted(): - raise ValueError("Weighted graphs are currently not supported.") - - if use_weight: - raise ValueError("'use_weight' is currently not supported.") - - if vertex_pair is None: - # Call two_hop neighbor of the entire graph - vertex_pair = G.get_two_hop_neighbors() - - v_p_num_col = len(vertex_pair.columns) - - if isinstance(vertex_pair, cudf.DataFrame): - vertex_pair = renumber_vertex_pair(G, vertex_pair) - vertex_pair = ensure_valid_dtype(G, vertex_pair) - src_col_name = vertex_pair.columns[0] - dst_col_name = vertex_pair.columns[1] - first = vertex_pair[src_col_name] - second = vertex_pair[dst_col_name] - - elif vertex_pair is not None: - raise ValueError("vertex_pair must be a cudf dataframe") - - use_weight = False - first, second, sorensen_coeff = pylibcugraph_sorensen_coefficients( - resource_handle=ResourceHandle(), - graph=G._plc_graph, - first=first, - second=second, - use_weight=use_weight, - do_expensive_check=False, - ) - - if G.renumbered: - vertex_pair = G.unrenumber(vertex_pair, src_col_name, preserve_order=True) - vertex_pair = G.unrenumber(vertex_pair, dst_col_name, preserve_order=True) - - if v_p_num_col == 2: - # single column vertex - vertex_pair = vertex_pair.rename( - columns={src_col_name: "first", dst_col_name: "second"} - ) - - df = vertex_pair - df["sorensen_coeff"] = cudf.Series(sorensen_coeff) - - return df - - -def EXPERIMENTAL__sorensen_coefficient(G, ebunch=None, use_weight=False): - """ - For NetworkX Compatability. See `sorensen` - - Parameters - ---------- - G : cugraph.Graph - cuGraph Graph instance, should contain the connectivity information - as an edge list (edge weights are not used for this algorithm). The - graph should be undirected where an undirected edge is represented by a - directed edge in both direction. The adjacency list will be computed if - not already present. - ebunch : cudf.DataFrame, optional (default=None) - A GPU dataframe consisting of two columns representing pairs of - vertices. If provided, the sorensen coefficient is computed for the - given vertex pairs. If the vertex_pair is not provided then the - current implementation computes the sorensen coefficient for all - adjacent vertices in the graph. - use_weight : bool, optional (default=False) - Currently not supported - - Returns - ------- - df : cudf.DataFrame - GPU data frame of size E (the default) or the size of the given pairs - (first, second) containing the Sorensen weights. The ordering is - relative to the adjacency list, or that given by the specified vertex - pairs. - - df['first'] : cudf.Series - The first vertex ID of each pair (will be identical to first if specified). - df['second'] : cudf.Series - The second vertex ID of each pair (will be identical to second if - specified). - df['sorensen_coeff'] : cudf.Series - The computed sorensen coefficient between the first and the second - vertex ID. - - Examples - -------- - >>> from cugraph.datasets import karate - >>> from cugraph.experimental import sorensen_coefficient as exp_sorensen_coef - >>> G = karate.get_graph(download=True, ignore_weights=True) - >>> df = exp_sorensen_coef(G) - - """ - vertex_pair = None - - G, isNx = ensure_cugraph_obj_for_nx(G) - - # FIXME: What is the logic behind this since the docstrings mention that 'G' and - # 'ebunch'(if not None) are respectively of type cugraph.Graph and cudf.DataFrame? - if isNx is True and ebunch is not None: - vertex_pair = cudf.DataFrame(ebunch) - - df = EXPERIMENTAL__sorensen(G, vertex_pair) - - if isNx is True: - df = df_edge_score_to_dictionary( - df, k="sorensen_coeff", src="first", dst="second" - ) - - return df diff --git a/python/cugraph/cugraph/link_prediction/CMakeLists.txt b/python/cugraph/cugraph/link_prediction/CMakeLists.txt deleted file mode 100644 index a117cf9afc3..00000000000 --- a/python/cugraph/cugraph/link_prediction/CMakeLists.txt +++ /dev/null @@ -1,22 +0,0 @@ -# ============================================================================= -# Copyright (c) 2022, NVIDIA CORPORATION. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except -# in compliance with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software distributed under the License -# is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express -# or implied. See the License for the specific language governing permissions and limitations under -# the License. -# ============================================================================= - -set(cython_sources jaccard_wrapper.pyx overlap_wrapper.pyx) -set(linked_libraries cugraph::cugraph) -rapids_cython_create_modules( - CXX - SOURCE_FILES "${cython_sources}" - LINKED_LIBRARIES "${linked_libraries}" MODULE_PREFIX link_prediction_ - ASSOCIATED_TARGETS cugraph -) diff --git a/python/cugraph/cugraph/link_prediction/__init__.py b/python/cugraph/cugraph/link_prediction/__init__.py index a6911d3b8ae..a8517ee7c0f 100644 --- a/python/cugraph/cugraph/link_prediction/__init__.py +++ b/python/cugraph/cugraph/link_prediction/__init__.py @@ -1,4 +1,4 @@ -# Copyright (c) 2019-2021, NVIDIA CORPORATION. +# Copyright (c) 2019-2023, NVIDIA CORPORATION. # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at @@ -11,13 +11,26 @@ # See the License for the specific language governing permissions and # limitations under the License. + +from cugraph.utilities.api_tools import deprecated_warning_wrapper from cugraph.link_prediction.jaccard import jaccard from cugraph.link_prediction.jaccard import jaccard_coefficient + +from cugraph.link_prediction.sorensen import sorensen +from cugraph.link_prediction.sorensen import sorensen_coefficient + from cugraph.link_prediction.overlap import overlap +from cugraph.link_prediction.overlap import overlap_coefficient + +# To be deprecated from cugraph.link_prediction.wjaccard import jaccard_w + +jaccard_w = deprecated_warning_wrapper(jaccard_w) + from cugraph.link_prediction.woverlap import overlap_w + +overlap_w = deprecated_warning_wrapper(overlap_w) + from cugraph.link_prediction.wsorensen import sorensen_w -from cugraph.link_prediction.jaccard import jaccard_coefficient -from cugraph.link_prediction.sorensen import sorensen_coefficient -from cugraph.link_prediction.sorensen import sorensen -from cugraph.link_prediction.overlap import overlap_coefficient + +sorensen_w = deprecated_warning_wrapper(sorensen_w) diff --git a/python/cugraph/cugraph/link_prediction/jaccard.pxd b/python/cugraph/cugraph/link_prediction/jaccard.pxd deleted file mode 100644 index 9e8c82ec3d8..00000000000 --- a/python/cugraph/cugraph/link_prediction/jaccard.pxd +++ /dev/null @@ -1,35 +0,0 @@ -# Copyright (c) 2019-2021, NVIDIA CORPORATION. -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# cython: profile=False -# distutils: language = c++ -# cython: embedsignature = True -# cython: language_level = 3 - -from cugraph.structure.graph_primtypes cimport * - - -cdef extern from "cugraph/algorithms.hpp" namespace "cugraph": - - cdef void jaccard[VT,ET,WT]( - const GraphCSRView[VT,ET,WT] &graph, - const WT *weights, - WT *result) except + - - cdef void jaccard_list[VT,ET,WT]( - const GraphCSRView[VT,ET,WT] &graph, - const WT *weights, - ET num_pairs, - const VT *first, - const VT *second, - WT *result) except + diff --git a/python/cugraph/cugraph/link_prediction/jaccard.py b/python/cugraph/cugraph/link_prediction/jaccard.py index 334d57f9d80..27bfa58e6b0 100644 --- a/python/cugraph/cugraph/link_prediction/jaccard.py +++ b/python/cugraph/cugraph/link_prediction/jaccard.py @@ -11,16 +11,54 @@ # See the License for the specific language governing permissions and # limitations under the License. -import cudf -from cugraph.link_prediction import jaccard_wrapper from cugraph.utilities import ( ensure_cugraph_obj_for_nx, df_edge_score_to_dictionary, renumber_vertex_pair, ) +import cudf +import warnings +from typing import Union, Iterable + +from pylibcugraph import ( + jaccard_coefficients as pylibcugraph_jaccard_coefficients, +) +from pylibcugraph import ResourceHandle + +from cugraph.structure import Graph +from cugraph.utilities.utils import import_optional + +# FIXME: the networkx.Graph type used in type annotations is specified +# using a string literal to avoid depending on and importing networkx. +# Instead, networkx is imported optionally, which may cause a problem +# for a type checker if run in an environment where networkx is not installed. +networkx = import_optional("networkx") + + +# FIXME: Move this function to the utility module so that it can be +# shared by other algos +def ensure_valid_dtype(input_graph, vertex_pair): + vertex_dtype = input_graph.edgelist.edgelist_df.dtypes[0] + vertex_pair_dtypes = vertex_pair.dtypes + + if vertex_pair_dtypes[0] != vertex_dtype or vertex_pair_dtypes[1] != vertex_dtype: + warning_msg = ( + "Jaccard requires 'vertex_pair' to match the graph's 'vertex' type. " + f"input graph's vertex type is: {vertex_dtype} and got " + f"'vertex_pair' of type: {vertex_pair_dtypes}." + ) + warnings.warn(warning_msg, UserWarning) + vertex_pair = vertex_pair.astype(vertex_dtype) + + return vertex_pair -def jaccard(input_graph, vertex_pair=None, do_expensive_check=True): +def jaccard( + input_graph: Graph, + vertex_pair: cudf.DataFrame = None, + do_expensive_check: bool = False, # deprecated + use_weight: bool = False, +): """ Compute the Jaccard similarity between each pair of vertices connected by an edge, or between arbitrary pairs of vertices specified by the user. @@ -36,13 +74,11 @@ def jaccard(input_graph, vertex_pair=None, do_expensive_check=True): of cugraph.jaccard is different from the behavior of networkx.jaccard_coefficient. - This algorithm doesn't currently support datasets with vertices that - are not (re)numebred vertices from 0 to V-1 where V is the total number of - vertices as this creates isolated vertices. - cugraph.jaccard, in the absence of a specified vertex pair list, will - use the edges of the graph to construct a vertex pair list and will - return the jaccard coefficient for those vertex pairs. + compute the two_hop_neighbors of the entire graph to construct a vertex pair + list and will return the jaccard coefficient for those vertex pairs. This is + not advisable as the vertex_pairs can grow exponentially with respect to the + size of the datasets networkx.jaccard_coefficient, in the absence of a specified vertex pair list, will return an upper triangular dense matrix, excluding @@ -59,9 +95,9 @@ def jaccard(input_graph, vertex_pair=None, do_expensive_check=True): solution by doing the following: >>> from cugraph.datasets import karate - >>> G = karate.get_graph(download=True) - >>> pairs = G.get_two_hop_neighbors() - >>> df = cugraph.jaccard(G, pairs) + >>> input_graph = karate.get_graph(download=True, ignore_weights=True) + >>> pairs = input_graph.get_two_hop_neighbors() + >>> df = cugraph.jaccard(input_graph, pairs) But please remember that cugraph will fill the dataframe with the entire solution you request, so you'll need enough memory to store the 2-hop @@ -72,10 +108,11 @@ def jaccard(input_graph, vertex_pair=None, do_expensive_check=True): ---------- input_graph : cugraph.Graph cuGraph Graph instance, should contain the connectivity information - as an edge list (edge weights are not used for this algorithm). The - graph should be undirected where an undirected edge is represented by a - directed edge in both direction. The adjacency list will be computed if - not already present. + as an edge list. The graph should be undirected where an undirected + edge is represented by a directed edge in both direction.The adjacency + list will be computed if not already present. + + This implementation only supports undirected, non-multi Graphs. vertex_pair : cudf.DataFrame, optional (default=None) A GPU dataframe consisting of two columns representing pairs of @@ -84,9 +121,20 @@ def jaccard(input_graph, vertex_pair=None, do_expensive_check=True): current implementation computes the jaccard coefficient for all adjacent vertices in the graph. - do_expensive_check: bool (default=True) - When set to True, check if the vertices in the graph are (re)numbered - from 0 to V-1 where V is the total number of vertices. + do_expensive_check : bool, optional (default=False) + Deprecated. + + This option added a check to ensure integer vertex IDs are sequential + values from 0 to V-1. That check is now redundant because cugraph + unconditionally renumbers and un-renumbers integer vertex IDs for + optimal performance, therefore this option is deprecated and will be + removed in a future version. + + use_weight : bool, optional (default=False) + Flag to indicate whether to compute weighted jaccard (if use_weight==True) + or un-weighted jaccard (if use_weight==False). + 'input_graph' must be weighted if 'use_weight=True'. + Returns ------- @@ -99,7 +147,7 @@ def jaccard(input_graph, vertex_pair=None, do_expensive_check=True): df['first'] : cudf.Series The first vertex ID of each pair (will be identical to first if specified). df['second'] : cudf.Series - the second vertex ID of each pair (will be identical to second if + The second vertex ID of each pair (will be identical to second if specified). df['jaccard_coeff'] : cudf.Series The computed Jaccard coefficient between the first and the second @@ -108,65 +156,101 @@ def jaccard(input_graph, vertex_pair=None, do_expensive_check=True): Examples -------- >>> from cugraph.datasets import karate - >>> G = karate.get_graph(download=True) - >>> df = cugraph.jaccard(G) + >>> from cugraph import jaccard + >>> input_graph = karate.get_graph(download=True, ignore_weights=True) + >>> df = jaccard(input_graph) """ if do_expensive_check: - if not input_graph.renumbered: - input_df = input_graph.edgelist.edgelist_df[["src", "dst"]] - max_vertex = input_df.max().max() - expected_nodes = cudf.Series(range(0, max_vertex + 1, 1)).astype( - input_df.dtypes[0] - ) - nodes = ( - cudf.concat([input_df["src"], input_df["dst"]]) - .unique() - .sort_values() - .reset_index(drop=True) - ) - if not expected_nodes.equals(nodes): - raise ValueError("Unrenumbered vertices are not supported.") + warnings.warn( + "do_expensive_check is deprecated since vertex IDs are no longer " + "required to be consecutively numbered", + FutureWarning, + ) if input_graph.is_directed(): raise ValueError("Input must be an undirected Graph.") - if type(vertex_pair) == cudf.DataFrame: + + if vertex_pair is None: + # Call two_hop neighbor of the entire graph + vertex_pair = input_graph.get_two_hop_neighbors() + + v_p_num_col = len(vertex_pair.columns) + + if isinstance(vertex_pair, cudf.DataFrame): vertex_pair = renumber_vertex_pair(input_graph, vertex_pair) + vertex_pair = ensure_valid_dtype(input_graph, vertex_pair) + src_col_name = vertex_pair.columns[0] + dst_col_name = vertex_pair.columns[1] + first = vertex_pair[src_col_name] + second = vertex_pair[dst_col_name] + elif vertex_pair is not None: - raise ValueError("vertex_pair must be a cudf dataframe") + raise ValueError("vertex_pair must be a cudf Dataframe") - df = jaccard_wrapper.jaccard(input_graph, None, vertex_pair) + first, second, jaccard_coeff = pylibcugraph_jaccard_coefficients( + resource_handle=ResourceHandle(), + graph=input_graph._plc_graph, + first=first, + second=second, + use_weight=use_weight, + do_expensive_check=False, + ) if input_graph.renumbered: - df = input_graph.unrenumber(df, "first") - df = input_graph.unrenumber(df, "second") + vertex_pair = input_graph.unrenumber( + vertex_pair, src_col_name, preserve_order=True + ) + vertex_pair = input_graph.unrenumber( + vertex_pair, dst_col_name, preserve_order=True + ) + + if v_p_num_col == 2: + # single column vertex + vertex_pair = vertex_pair.rename( + columns={src_col_name: "first", dst_col_name: "second"} + ) + + df = vertex_pair + df["jaccard_coeff"] = cudf.Series(jaccard_coeff) return df -def jaccard_coefficient(G, ebunch=None, do_expensive_check=True): +def jaccard_coefficient( + G: Union[Graph, "networkx.Graph"], + ebunch: Union[cudf.DataFrame, Iterable[Union[int, str, float]]] = None, + do_expensive_check: bool = False, # deprecated +): """ For NetworkX Compatability. See `jaccard` - NOTE: This algorithm doesn't currently support datasets with vertices that - are not (re)numebred vertices from 0 to V-1 where V is the total number of - vertices as this creates isolated vertices. - Parameters ---------- - graph : cugraph.Graph - cuGraph Graph instance, should contain the connectivity information - as an edge list (edge weights are not used for this algorithm). The - graph should be undirected where an undirected edge is represented by a - directed edge in both direction. The adjacency list will be computed if - not already present. + G : cugraph.Graph or NetworkX.Graph + cuGraph or NetworkX Graph instance, should contain the connectivity + information as an edge list. The graph should be undirected where an + undirected edge is represented by a directed edge in both direction. + The adjacency list will be computed if not already present. - ebunch : cudf.DataFrame, optional (default=None) + This implementation only supports undirected, non-multi Graphs. + + ebunch : cudf.DataFrame or iterable of node pairs, optional (default=None) A GPU dataframe consisting of two columns representing pairs of - vertices. If provided, the jaccard coefficient is computed for the - given vertex pairs. If the vertex_pair is not provided then the - current implementation computes the jaccard coefficient for all - adjacent vertices in the graph. + vertices or iterable of 2-tuples (u, v) where u and v are nodes in + the graph. + + If provided, the Overlap coefficient is computed for the given vertex + pairs. Otherwise, the current implementation computes the overlap + coefficient for all adjacent vertices in the graph. + + do_expensive_check : bool, optional (default=False) + Deprecated. + This option added a check to ensure integer vertex IDs are sequential + values from 0 to V-1. That check is now redundant because cugraph + unconditionally renumbers and un-renumbers integer vertex IDs for + optimal performance, therefore this option is deprecated and will be + removed in a future version. Returns ------- @@ -188,10 +272,18 @@ def jaccard_coefficient(G, ebunch=None, do_expensive_check=True): Examples -------- >>> from cugraph.datasets import karate + >>> from cugraph import jaccard_coefficient >>> G = karate.get_graph(download=True) - >>> df = cugraph.jaccard_coefficient(G) + >>> df = jaccard_coefficient(G) """ + if do_expensive_check: + warnings.warn( + "do_expensive_check is deprecated since vertex IDs are no longer " + "required to be consecutively numbered", + FutureWarning, + ) + vertex_pair = None G, isNx = ensure_cugraph_obj_for_nx(G) diff --git a/python/cugraph/cugraph/link_prediction/jaccard_wrapper.pyx b/python/cugraph/cugraph/link_prediction/jaccard_wrapper.pyx deleted file mode 100644 index e66d8bf0b5c..00000000000 --- a/python/cugraph/cugraph/link_prediction/jaccard_wrapper.pyx +++ /dev/null @@ -1,155 +0,0 @@ -# Copyright (c) 2019-2022, NVIDIA CORPORATION. -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# cython: profile=False -# distutils: language = c++ -# cython: embedsignature = True -# cython: language_level = 3 - -from cugraph.link_prediction.jaccard cimport jaccard as c_jaccard -from cugraph.link_prediction.jaccard cimport jaccard_list as c_jaccard_list -from cugraph.structure.graph_primtypes cimport * -from cugraph.structure import graph_primtypes_wrapper -from libc.stdint cimport uintptr_t -import cudf -import numpy as np - - -def jaccard(input_graph, weights_arr=None, vertex_pair=None): - """ - Call jaccard or jaccard_list - """ - offsets = None - indices = None - - if input_graph.adjlist: - [offsets, indices] = graph_primtypes_wrapper.datatype_cast([input_graph.adjlist.offsets, - input_graph.adjlist.indices], [np.int32]) - elif input_graph.transposedadjlist: - # - # NOTE: jaccard ONLY operates on an undirected graph, so CSR and CSC should be - # equivalent. The undirected check has already happened, so we'll just use - # the CSC as if it were CSR. - # - [offsets, indices] = graph_primtypes_wrapper.datatype_cast([input_graph.transposedadjlist.offsets, - input_graph.transposedadjlist.indices], [np.int32]) - else: - input_graph.view_adj_list() - [offsets, indices] = graph_primtypes_wrapper.datatype_cast([input_graph.adjlist.offsets, - input_graph.adjlist.indices], [np.int32]) - - num_verts = input_graph.number_of_vertices() - num_edges = input_graph.number_of_edges(directed_edges=True) - - first = None - second = None - - cdef uintptr_t c_result_col = NULL - cdef uintptr_t c_first_col = NULL - cdef uintptr_t c_second_col = NULL - cdef uintptr_t c_src_index_col = NULL - cdef uintptr_t c_dst_index_col = NULL - cdef uintptr_t c_weights = NULL - cdef uintptr_t c_offsets = offsets.__cuda_array_interface__['data'][0] - cdef uintptr_t c_indices = indices.__cuda_array_interface__['data'][0] - - cdef GraphCSRView[int,int,float] graph_float - cdef GraphCSRView[int,int,double] graph_double - - weight_type = np.float32 - - if weights_arr is not None: - [weights] = graph_primtypes_wrapper.datatype_cast([weights_arr], [np.float32, np.float64]) - c_weights = weights.__cuda_array_interface__['data'][0] - weight_type = weights.dtype - - if type(vertex_pair) == cudf.DataFrame: - result_size = len(vertex_pair) - result = cudf.Series(np.ones(result_size, dtype=weight_type)) - c_result_col = result.__cuda_array_interface__['data'][0] - - df = cudf.DataFrame() - df['jaccard_coeff'] = result - - cols = vertex_pair.columns.to_list() - first = vertex_pair[cols[0]].astype(np.int32) - second = vertex_pair[cols[1]].astype(np.int32) - - # FIXME: multi column support - df['first'] = first - df['second'] = second - c_first_col = first.__cuda_array_interface__['data'][0] - c_second_col = second.__cuda_array_interface__['data'][0] - - if weight_type == np.float32: - graph_float = GraphCSRView[int,int,float](c_offsets, c_indices, - c_weights, num_verts, num_edges) - c_jaccard_list[int,int,float](graph_float, - c_weights, - result_size, - c_first_col, - c_second_col, - c_result_col) - else: - graph_double = GraphCSRView[int,int,double](c_offsets, c_indices, - c_weights, num_verts, num_edges) - c_jaccard_list[int,int,double](graph_double, - c_weights, - result_size, - c_first_col, - c_second_col, - c_result_col) - - return df - else: - # error check performed in jaccard.py - assert vertex_pair is None - - df = cudf.DataFrame() - df['first'] = cudf.Series(np.zeros(num_edges, indices.dtype)) - df['second'] = indices - - c_src_index_col = df['first'].__cuda_array_interface__['data'][0] - - if weight_type == np.float32: - df['jaccard_coeff'] = cudf.Series(np.ones(num_edges, dtype=np.float32), - nan_as_null=False) - c_result_col = df['jaccard_coeff'].__cuda_array_interface__['data'][0] - - graph_float = GraphCSRView[int,int,float](c_offsets, - c_indices, - c_weights, - num_verts, - num_edges) - c_jaccard[int,int,float](graph_float, - c_weights, - c_result_col) - - graph_float.get_source_indices(c_src_index_col) - else: - df['jaccard_coeff'] = cudf.Series(np.ones(num_edges, dtype=np.float64), - nan_as_null=False) - c_result_col = df['jaccard_coeff'].__cuda_array_interface__['data'][0] - - graph_double = GraphCSRView[int,int,double](c_offsets, - c_indices, - c_weights, - num_verts, - num_edges) - c_jaccard[int,int,double](graph_double, - c_weights, - c_result_col) - - graph_double.get_source_indices(c_src_index_col) - - return df diff --git a/python/cugraph/cugraph/link_prediction/overlap.pxd b/python/cugraph/cugraph/link_prediction/overlap.pxd deleted file mode 100644 index f0654472587..00000000000 --- a/python/cugraph/cugraph/link_prediction/overlap.pxd +++ /dev/null @@ -1,35 +0,0 @@ -# Copyright (c) 2019-2021, NVIDIA CORPORATION. -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# cython: profile=False -# distutils: language = c++ -# cython: embedsignature = True -# cython: language_level = 3 - -from cugraph.structure.graph_primtypes cimport * - - -cdef extern from "cugraph/algorithms.hpp" namespace "cugraph": - - cdef void overlap[VT,ET,WT]( - const GraphCSRView[VT,ET,WT] &graph, - const WT *weights, - WT *result) except + - - cdef void overlap_list[VT,ET,WT]( - const GraphCSRView[VT,ET,WT] &graph, - const WT *weights, - ET num_pairs, - const VT *first, - const VT *second, - WT *result) except + diff --git a/python/cugraph/cugraph/link_prediction/overlap.py b/python/cugraph/cugraph/link_prediction/overlap.py index 9bb7b76b0ca..3a25526679c 100644 --- a/python/cugraph/cugraph/link_prediction/overlap.py +++ b/python/cugraph/cugraph/link_prediction/overlap.py @@ -11,28 +11,120 @@ # See the License for the specific language governing permissions and # limitations under the License. -from cugraph.link_prediction import overlap_wrapper -import cudf from cugraph.utilities import ( ensure_cugraph_obj_for_nx, df_edge_score_to_dictionary, renumber_vertex_pair, ) +import cudf +import warnings +from typing import Union, Iterable + +from pylibcugraph import ( + overlap_coefficients as pylibcugraph_overlap_coefficients, +) +from pylibcugraph import ResourceHandle + +from cugraph.structure import Graph +from cugraph.utilities.utils import import_optional + +# FIXME: the networkx.Graph type used in type annotations is specified +# using a string literal to avoid depending on and importing networkx. +# Instead, networkx is imported optionally, which may cause a problem +# for a type checker if run in an environment where networkx is not installed. +networkx = import_optional("networkx") + +# FIXME: Move this function to the utility module so that it can be +# shared by other algos +def ensure_valid_dtype(input_graph, vertex_pair): + vertex_dtype = input_graph.edgelist.edgelist_df.dtypes[0] + vertex_pair_dtypes = vertex_pair.dtypes -def overlap_coefficient(G, ebunch=None, do_expensive_check=True): + if vertex_pair_dtypes[0] != vertex_dtype or vertex_pair_dtypes[1] != vertex_dtype: + warning_msg = ( + "Overlap requires 'vertex_pair' to match the graph's 'vertex' type. " + f"input graph's vertex type is: {vertex_dtype} and got " + f"'vertex_pair' of type: {vertex_pair_dtypes}." + ) + warnings.warn(warning_msg, UserWarning) + vertex_pair = vertex_pair.astype(vertex_dtype) + + return vertex_pair + + +def overlap_coefficient( + G: Union[Graph, "networkx.Graph"], + ebunch: Union[cudf.DataFrame, Iterable[Union[int, str, float]]] = None, + do_expensive_check: bool = False, # deprecated +): """ - For NetworkX Compatability. See `overlap` + Compute overlap coefficient. + + Parameters + ---------- + G : cugraph.Graph or NetworkX.Graph + cuGraph or NetworkX Graph instance, should contain the connectivity + information as an edge list. The graph should be undirected where an + undirected edge is represented by a directed edge in both direction. + The adjacency list will be computed if not already present. + + This implementation only supports undirected, non-multi edge Graph. + + ebunch : cudf.DataFrame or iterable of node pairs, optional (default=None) + A GPU dataframe consisting of two columns representing pairs of + vertices or iterable of 2-tuples (u, v) where u and v are nodes in + the graph. + + If provided, the Overlap coefficient is computed for the given vertex + pairs. Otherwise, the current implementation computes the overlap + coefficient for all adjacent vertices in the graph. + + do_expensive_check : bool, optional (default=False) + Deprecated. + This option added a check to ensure integer vertex IDs are sequential + values from 0 to V-1. That check is now redundant because cugraph + unconditionally renumbers and un-renumbers integer vertex IDs for + optimal performance, therefore this option is deprecated and will be + removed in a future version. + + Returns + ------- + df : cudf.DataFrame + GPU data frame of size E (the default) or the size of the given pairs + (first, second) containing the overlap weights. The ordering is + relative to the adjacency list, or that given by the specified vertex + pairs. - NOTE: This algorithm doesn't currently support datasets with vertices that - are not (re)numebred vertices from 0 to V-1 where V is the total number of - vertices as this creates isolated vertices. + ddf['first']: dask_cudf.Series + The first vertex ID of each pair (will be identical to first if specified). + ddf['second']: dask_cudf.Series + The second vertex ID of each pair (will be identical to second if + specified). + ddf['overlap_coeff']: dask_cudf.Series + The computed overlap coefficient between the first and the second + vertex ID. + Examples + -------- + >>> from cugraph.datasets import karate + >>> from cugraph import overlap_coefficient + >>> G = karate.get_graph(download=True, ignore_weights=True) + >>> df = overlap_coefficient(G) """ + if do_expensive_check: + warnings.warn( + "do_expensive_check is deprecated since vertex IDs are no longer " + "required to be consecutively numbered", + FutureWarning, + ) + vertex_pair = None G, isNx = ensure_cugraph_obj_for_nx(G) + # FIXME: What is the logic behind this since the docstrings mention that 'G' and + # 'ebunch'(if not None) are respectively of type cugraph.Graph and cudf.DataFrame? if isNx is True and ebunch is not None: vertex_pair = cudf.DataFrame(ebunch) @@ -46,7 +138,12 @@ def overlap_coefficient(G, ebunch=None, do_expensive_check=True): return df -def overlap(input_graph, vertex_pair=None, do_expensive_check=True): +def overlap( + input_graph: Graph, + vertex_pair: cudf.DataFrame = None, + do_expensive_check: bool = False, # deprecated + use_weight: bool = False, +): """ Compute the Overlap Coefficient between each pair of vertices connected by an edge, or between arbitrary pairs of vertices specified by the user. @@ -58,25 +155,39 @@ def overlap(input_graph, vertex_pair=None, do_expensive_check=True): neighbors. If first is specified but second is not, or vice versa, an exception will be thrown. - NOTE: This algorithm doesn't currently support datasets with vertices that - are not (re)numebred vertices from 0 to V-1 where V is the total number of - vertices as this creates isolated vertices. + cugraph.overlap, in the absence of a specified vertex pair list, will + compute the two_hop_neighbors of the entire graph to construct a vertex pair + list and will return the overlap coefficient for those vertex pairs. This is + not advisable as the vertex_pairs can grow exponentially with respect to the + size of the datasets Parameters ---------- input_graph : cugraph.Graph cuGraph Graph instance, should contain the connectivity information - as an edge list (edge weights are not used for this algorithm). The - adjacency list will be computed if not already present. + as an edge list. The adjacency list will be computed if not already + present. + This implementation only supports undirected, non-multi edge Graph. vertex_pair : cudf.DataFrame, optional (default=None) A GPU dataframe consisting of two columns representing pairs of vertices. If provided, the overlap coefficient is computed for the given vertex pairs, else, it is computed for all vertex pairs. - do_expensive_check: bool (default=True) - When set to True, check if the vertices in the graph are (re)numbered - from 0 to V-1 where V is the total number of vertices. + do_expensive_check : bool, optional (default=False) + Deprecated. + This option added a check to ensure integer vertex IDs are sequential + values from 0 to V-1. That check is now redundant because cugraph + unconditionally renumbers and un-renumbers integer vertex IDs for + optimal performance, therefore this option is deprecated and will be + removed in a future version. + + use_weight : bool, optional (default=False) + Flag to indicate whether to compute weighted overlap (if use_weight==True) + or un-weighted overlap (if use_weight==False). + 'input_graph' must be weighted if 'use_weight=True'. + + Returns ------- @@ -98,35 +209,62 @@ def overlap(input_graph, vertex_pair=None, do_expensive_check=True): Examples -------- >>> from cugraph.datasets import karate - >>> G = karate.get_graph(download=True) - >>> df = cugraph.overlap(G) + >>> from cugraph import overlap + >>> input_graph = karate.get_graph(download=True, ignore_weights=True) + >>> df = overlap(input_graph) """ if do_expensive_check: - if not input_graph.renumbered: - input_df = input_graph.edgelist.edgelist_df[["src", "dst"]] - max_vertex = input_df.max().max() - expected_nodes = cudf.Series(range(0, max_vertex + 1, 1)).astype( - input_df.dtypes[0] - ) - nodes = ( - cudf.concat([input_df["src"], input_df["dst"]]) - .unique() - .sort_values() - .reset_index(drop=True) - ) - if not expected_nodes.equals(nodes): - raise ValueError("Unrenumbered vertices are not supported.") - - if type(vertex_pair) == cudf.DataFrame: + warnings.warn( + "do_expensive_check is deprecated since vertex IDs are no longer " + "required to be consecutively numbered", + FutureWarning, + ) + + if input_graph.is_directed(): + raise ValueError("Input must be an undirected Graph.") + + if vertex_pair is None: + # Call two_hop neighbor of the entire graph + vertex_pair = input_graph.get_two_hop_neighbors() + + v_p_num_col = len(vertex_pair.columns) + + if isinstance(vertex_pair, cudf.DataFrame): vertex_pair = renumber_vertex_pair(input_graph, vertex_pair) + vertex_pair = ensure_valid_dtype(input_graph, vertex_pair) + src_col_name = vertex_pair.columns[0] + dst_col_name = vertex_pair.columns[1] + first = vertex_pair[src_col_name] + second = vertex_pair[dst_col_name] + elif vertex_pair is not None: raise ValueError("vertex_pair must be a cudf dataframe") - df = overlap_wrapper.overlap(input_graph, None, vertex_pair) + first, second, overlap_coeff = pylibcugraph_overlap_coefficients( + resource_handle=ResourceHandle(), + graph=input_graph._plc_graph, + first=first, + second=second, + use_weight=use_weight, + do_expensive_check=False, + ) if input_graph.renumbered: - df = input_graph.unrenumber(df, "first") - df = input_graph.unrenumber(df, "second") + vertex_pair = input_graph.unrenumber( + vertex_pair, src_col_name, preserve_order=True + ) + vertex_pair = input_graph.unrenumber( + vertex_pair, dst_col_name, preserve_order=True + ) + + if v_p_num_col == 2: + # single column vertex + vertex_pair = vertex_pair.rename( + columns={src_col_name: "first", dst_col_name: "second"} + ) + + df = vertex_pair + df["overlap_coeff"] = cudf.Series(overlap_coeff) return df diff --git a/python/cugraph/cugraph/link_prediction/overlap_wrapper.pyx b/python/cugraph/cugraph/link_prediction/overlap_wrapper.pyx deleted file mode 100644 index 0f61460a72f..00000000000 --- a/python/cugraph/cugraph/link_prediction/overlap_wrapper.pyx +++ /dev/null @@ -1,142 +0,0 @@ -# Copyright (c) 2019-2022, NVIDIA CORPORATION. -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# cython: profile=False -# distutils: language = c++ -# cython: embedsignature = True -# cython: language_level = 3 - -from cugraph.link_prediction.overlap cimport overlap as c_overlap -from cugraph.link_prediction.overlap cimport overlap_list as c_overlap_list -from cugraph.structure.graph_primtypes cimport * -from cugraph.structure import graph_primtypes_wrapper -from libc.stdint cimport uintptr_t -import cudf -import numpy as np - - -def overlap(input_graph, weights_arr=None, vertex_pair=None): - """ - Call overlap or overlap_list - """ - - if not input_graph.adjlist: - input_graph.view_adj_list() - - [offsets, indices] = graph_primtypes_wrapper.datatype_cast([input_graph.adjlist.offsets, input_graph.adjlist.indices], [np.int32]) - - num_verts = input_graph.number_of_vertices() - num_edges = input_graph.number_of_edges(directed_edges=True) - - first = None - second = None - - cdef uintptr_t c_result_col = NULL - cdef uintptr_t c_first_col = NULL - cdef uintptr_t c_second_col = NULL - cdef uintptr_t c_src_index_col = NULL - cdef uintptr_t c_dst_index_col = NULL - cdef uintptr_t c_weights = NULL - cdef uintptr_t c_offsets = offsets.__cuda_array_interface__['data'][0] - cdef uintptr_t c_indices = indices.__cuda_array_interface__['data'][0] - - cdef GraphCSRView[int,int,float] graph_float - cdef GraphCSRView[int,int,double] graph_double - - weight_type = np.float32 - - if weights_arr is not None: - [weights] = graph_primtypes_wrapper.datatype_cast([weights_arr], [np.float32, np.float64]) - c_weights = weights.__cuda_array_interface__['data'][0] - weight_type = weights.dtype - - if type(vertex_pair) == cudf.DataFrame: - result_size = len(vertex_pair) - result = cudf.Series(np.ones(result_size, dtype=np.float32)) - c_result_col = result.__cuda_array_interface__['data'][0] - - df = cudf.DataFrame() - df['overlap_coeff'] = result - - cols = vertex_pair.columns.to_list() - first = vertex_pair[cols[0]] - second = vertex_pair[cols[1]] - - # FIXME: multi column support - df['first'] = first - df['second'] = second - c_first_col = first.__cuda_array_interface__['data'][0] - c_second_col = second.__cuda_array_interface__['data'][0] - - if weight_type == np.float32: - graph_float = GraphCSRView[int,int,float](c_offsets, c_indices, - c_weights, num_verts, num_edges) - c_overlap_list[int,int,float](graph_float, - c_weights, - result_size, - c_first_col, - c_second_col, - c_result_col) - else: - graph_double = GraphCSRView[int,int,double](c_offsets, c_indices, - c_weights, num_verts, num_edges) - c_overlap_list[int,int,double](graph_double, - c_weights, - result_size, - c_first_col, - c_second_col, - c_result_col) - - return df - else: - # error check performed in overlap.py - assert vertex_pair is None - - df = cudf.DataFrame() - df['first'] = cudf.Series(np.zeros(num_edges, indices.dtype)) - df['second'] = indices - - c_src_index_col = df['first'].__cuda_array_interface__['data'][0] - - if weight_type == np.float32: - df['overlap_coeff'] = cudf.Series(np.ones(num_edges, dtype=np.float32), - nan_as_null=False) - c_result_col = df['overlap_coeff'].__cuda_array_interface__['data'][0] - - graph_float = GraphCSRView[int,int,float](c_offsets, - c_indices, - c_weights, - num_verts, - num_edges) - c_overlap[int,int,float](graph_float, - c_weights, - c_result_col) - - graph_float.get_source_indices(c_src_index_col) - else: - df['overlap_coeff'] = cudf.Series(np.ones(num_edges, dtype=np.float64), - nan_as_null=False) - c_result_col = df['overlap_coeff'].__cuda_array_interface__['data'][0] - - graph_double = GraphCSRView[int,int,double](c_offsets, - c_indices, - c_weights, - num_verts, - num_edges) - c_overlap[int,int,double](graph_double, - c_weights, - c_result_col) - - graph_double.get_source_indices(c_src_index_col) - - return df diff --git a/python/cugraph/cugraph/link_prediction/sorensen.py b/python/cugraph/cugraph/link_prediction/sorensen.py index ef2bd8d674d..a8ccced1e68 100644 --- a/python/cugraph/cugraph/link_prediction/sorensen.py +++ b/python/cugraph/cugraph/link_prediction/sorensen.py @@ -11,17 +11,54 @@ # See the License for the specific language governing permissions and # limitations under the License. -import cudf -from cugraph.structure.graph_classes import Graph -from cugraph.link_prediction import jaccard_wrapper from cugraph.utilities import ( ensure_cugraph_obj_for_nx, df_edge_score_to_dictionary, renumber_vertex_pair, ) +import cudf +import warnings +from typing import Union, Iterable + +from pylibcugraph import ( + sorensen_coefficients as pylibcugraph_sorensen_coefficients, +) +from pylibcugraph import ResourceHandle + +from cugraph.structure import Graph +from cugraph.utilities.utils import import_optional + +# FIXME: the networkx.Graph type used in type annotations is specified +# using a string literal to avoid depending on and importing networkx. +# Instead, networkx is imported optionally, which may cause a problem +# for a type checker if run in an environment where networkx is not installed. +networkx = import_optional("networkx") + + +# FIXME: Move this function to the utility module so that it can be +# shared by other algos +def ensure_valid_dtype(input_graph, vertex_pair): + vertex_dtype = input_graph.edgelist.edgelist_df.dtypes[0] + vertex_pair_dtypes = vertex_pair.dtypes + + if vertex_pair_dtypes[0] != vertex_dtype or vertex_pair_dtypes[1] != vertex_dtype: + warning_msg = ( + "Sorensen requires 'vertex_pair' to match the graph's 'vertex' type. " + f"input graph's vertex type is: {vertex_dtype} and got " + f"'vertex_pair' of type: {vertex_pair_dtypes}." + ) + warnings.warn(warning_msg, UserWarning) + vertex_pair = vertex_pair.astype(vertex_dtype) + + return vertex_pair -def sorensen(input_graph, vertex_pair=None, do_expensive_check=True): +def sorensen( + input_graph: Graph, + vertex_pair: cudf.DataFrame = None, + do_expensive_check: bool = False, # deprecated + use_weight: bool = False, +): """ Compute the Sorensen coefficient between each pair of vertices connected by an edge, or between arbitrary pairs of vertices specified by the user. @@ -30,22 +67,20 @@ def sorensen(input_graph, vertex_pair=None, do_expensive_check=True): If first is specified but second is not, or vice versa, an exception will be thrown. - NOTE: This algorithm doesn't currently support datasets with vertices that - are not (re)numebred vertices from 0 to V-1 where V is the total number of - vertices as this creates isolated vertices. - cugraph.sorensen, in the absence of a specified vertex pair list, will - use the edges of the graph to construct a vertex pair list and will - return the sorensen coefficient for those vertex pairs. + compute the two_hop_neighbors of the entire graph to construct a vertex pair + list and will return the sorensen coefficient for those vertex pairs. This is + not advisable as the vertex_pairs can grow exponentially with respect to the + size of the datasets Parameters ---------- input_graph : cugraph.Graph cuGraph Graph instance, should contain the connectivity information - as an edge list (edge weights are not used for this algorithm). The - graph should be undirected where an undirected edge is represented by a - directed edge in both direction. The adjacency list will be computed if - not already present. + as an edge list. The adjacency list will be computed if not already + present. + + This implementation only supports undirected, non-multi edge Graph. vertex_pair : cudf.DataFrame, optional (default=None) A GPU dataframe consisting of two columns representing pairs of @@ -54,9 +89,18 @@ def sorensen(input_graph, vertex_pair=None, do_expensive_check=True): current implementation computes the Sorensen coefficient for all adjacent vertices in the graph. - do_expensive_check: bool (default=True) - When set to True, check if the vertices in the graph are (re)numbered - from 0 to V-1 where V is the total number of vertices. + do_expensive_check : bool, optional (default=False) + Deprecated. + This option added a check to ensure integer vertex IDs are sequential + values from 0 to V-1. That check is now redundant because cugraph + unconditionally renumbers and un-renumbers integer vertex IDs for + optimal performance, therefore this option is deprecated and will be + removed in a future version. + + use_weight : bool, optional (default=False) + Flag to indicate whether to compute weighted sorensen (if use_weight==True) + or un-weighted sorensen (if use_weight==False). + 'input_graph' must be weighted if 'use_weight=True'. Returns ------- @@ -67,79 +111,112 @@ def sorensen(input_graph, vertex_pair=None, do_expensive_check=True): pairs. df['first'] : cudf.Series - The first vertex ID of each pair (will be identical to first if specified) - + The first vertex ID of each pair (will be identical to first if specified). df['second'] : cudf.Series The second vertex ID of each pair (will be identical to second if - specified) - + specified). df['sorensen_coeff'] : cudf.Series - The computed Sorensen coefficient between the first and the second + The computed sorensen coefficient between the first and the second vertex ID. Examples -------- >>> from cugraph.datasets import karate - >>> G = karate.get_graph(download=True) - >>> df = cugraph.sorensen(G) + >>> from cugraph import sorensen + >>> input_graph = karate.get_graph(download=True, ignore_weights=True) + >>> df = sorensen(input_graph) """ if do_expensive_check: - if not input_graph.renumbered: - input_df = input_graph.edgelist.edgelist_df[["src", "dst"]] - max_vertex = input_df.max().max() - expected_nodes = cudf.Series(range(0, max_vertex + 1, 1)).astype( - input_df.dtypes[0] - ) - nodes = ( - cudf.concat([input_df["src"], input_df["dst"]]) - .unique() - .sort_values() - .reset_index(drop=True) - ) - if not expected_nodes.equals(nodes): - raise ValueError("Unrenumbered vertices are not supported.") - - if type(input_graph) is not Graph: - raise TypeError("input graph must a Graph") - - if type(vertex_pair) == cudf.DataFrame: + warnings.warn( + "do_expensive_check is deprecated since vertex IDs are no longer " + "required to be consecutively numbered", + FutureWarning, + ) + + if input_graph.is_directed(): + raise ValueError("Input must be an undirected Graph.") + + if vertex_pair is None: + # Call two_hop neighbor of the entire graph + vertex_pair = input_graph.get_two_hop_neighbors() + + v_p_num_col = len(vertex_pair.columns) + + if isinstance(vertex_pair, cudf.DataFrame): vertex_pair = renumber_vertex_pair(input_graph, vertex_pair) + vertex_pair = ensure_valid_dtype(input_graph, vertex_pair) + src_col_name = vertex_pair.columns[0] + dst_col_name = vertex_pair.columns[1] + first = vertex_pair[src_col_name] + second = vertex_pair[dst_col_name] + elif vertex_pair is not None: raise ValueError("vertex_pair must be a cudf dataframe") - df = jaccard_wrapper.jaccard(input_graph, None, vertex_pair) - df.jaccard_coeff = (2 * df.jaccard_coeff) / (1 + df.jaccard_coeff) - df.rename({"jaccard_coeff": "sorensen_coeff"}, axis=1, inplace=True) + first, second, sorensen_coeff = pylibcugraph_sorensen_coefficients( + resource_handle=ResourceHandle(), + graph=input_graph._plc_graph, + first=first, + second=second, + use_weight=use_weight, + do_expensive_check=False, + ) + if input_graph.renumbered: - df = input_graph.unrenumber(df, "first") - df = input_graph.unrenumber(df, "second") + vertex_pair = input_graph.unrenumber( + vertex_pair, src_col_name, preserve_order=True + ) + vertex_pair = input_graph.unrenumber( + vertex_pair, dst_col_name, preserve_order=True + ) + + if v_p_num_col == 2: + # single column vertex + vertex_pair = vertex_pair.rename( + columns={src_col_name: "first", dst_col_name: "second"} + ) + + df = vertex_pair + df["sorensen_coeff"] = cudf.Series(sorensen_coeff) return df -def sorensen_coefficient(G, ebunch=None, do_expensive_check=True): +def sorensen_coefficient( + G: Union[Graph, "networkx.Graph"], + ebunch: Union[cudf.DataFrame, Iterable[Union[int, str, float]]] = None, + do_expensive_check: bool = False, # deprecated +): """ - For NetworkX Compatability. See `sorensen` - - NOTE: This algorithm doesn't currently support datasets with vertices that - are not (re)numebred vertices from 0 to V-1 where V is the total number of - vertices as this creates isolated vertices. + Compute sorensen coefficient. Parameters ---------- - G : cugraph.Graph - cuGraph Graph instance, should contain the connectivity information - as an edge list (edge weights are not used for this algorithm). The - graph should be undirected where an undirected edge is represented by a - directed edge in both direction. The adjacency list will be computed if - not already present. - ebunch : cudf.DataFrame, optional (default=None) + G : cugraph.Graph or NetworkX.Graph + cuGraph or NetworkX Graph instance, should contain the connectivity + information as an edge list. The graph should be undirected where an + undirected edge is represented by a directed edge in both direction. + The adjacency list will be computed if not already present. + + This implementation only supports undirected, non-multi Graphs. + + ebunch : cudf.DataFrame or iterable of node pairs, optional (default=None) A GPU dataframe consisting of two columns representing pairs of - vertices. If provided, the sorensen coefficient is computed for the - given vertex pairs. If the vertex_pair is not provided then the - current implementation computes the sorensen coefficient for all - adjacent vertices in the graph. + vertices or iterable of 2-tuples (u, v) where u and v are nodes in + the graph. + + If provided, the Overlap coefficient is computed for the given vertex + pairs. Otherwise, the current implementation computes the overlap + coefficient for all adjacent vertices in the graph. + + do_expensive_check : bool, optional (default=False) + Deprecated. + This option added a check to ensure integer vertex IDs are sequential + values from 0 to V-1. That check is now redundant because cugraph + unconditionally renumbers and un-renumbers integer vertex IDs for + optimal performance, therefore this option is deprecated and will be + removed in a future version. Returns ------- @@ -152,7 +229,7 @@ def sorensen_coefficient(G, ebunch=None, do_expensive_check=True): df['first'] : cudf.Series The first vertex ID of each pair (will be identical to first if specified). df['second'] : cudf.Series - the second vertex ID of each pair (will be identical to second if + The second vertex ID of each pair (will be identical to second if specified). df['sorensen_coeff'] : cudf.Series The computed Sorensen coefficient between the first and the second @@ -161,14 +238,24 @@ def sorensen_coefficient(G, ebunch=None, do_expensive_check=True): Examples -------- >>> from cugraph.datasets import karate - >>> G = karate.get_graph(download=True) - >>> df = cugraph.sorensen_coefficient(G) + >>> from cugraph import sorensen_coefficient + >>> G = karate.get_graph(download=True, ignore_weights=True) + >>> df = sorensen_coefficient(G) """ + if do_expensive_check: + warnings.warn( + "do_expensive_check is deprecated since vertex IDs are no longer " + "required to be consecutively numbered", + FutureWarning, + ) + vertex_pair = None G, isNx = ensure_cugraph_obj_for_nx(G) + # FIXME: What is the logic behind this since the docstrings mention that 'G' and + # 'ebunch'(if not None) are respectively of type cugraph.Graph and cudf.DataFrame? if isNx is True and ebunch is not None: vertex_pair = cudf.DataFrame(ebunch) diff --git a/python/cugraph/cugraph/link_prediction/wjaccard.py b/python/cugraph/cugraph/link_prediction/wjaccard.py index e3486473fe5..ec538bbc0ed 100644 --- a/python/cugraph/cugraph/link_prediction/wjaccard.py +++ b/python/cugraph/cugraph/link_prediction/wjaccard.py @@ -11,13 +11,45 @@ # See the License for the specific language governing permissions and # limitations under the License. -from cugraph.structure.graph_classes import Graph -from cugraph.link_prediction import jaccard_wrapper +from cugraph.link_prediction import jaccard import cudf -from cugraph.utilities import renumber_vertex_pair +import warnings +from cugraph.structure import Graph +from cugraph.utilities.utils import import_optional -def jaccard_w(input_graph, weights, vertex_pair=None, do_expensive_check=True): +# FIXME: the networkx.Graph type used in type annotations is specified +# using a string literal to avoid depending on and importing networkx. +# Instead, networkx is imported optionally, which may cause a problem +# for a type checker if run in an environment where networkx is not installed. +networkx = import_optional("networkx") + + +# FIXME: Move this function to the utility module so that it can be +# shared by other algos +def ensure_valid_dtype(input_graph, vertex_pair): + + vertex_dtype = input_graph.edgelist.edgelist_df.dtypes[0] + vertex_pair_dtypes = vertex_pair.dtypes + + if vertex_pair_dtypes[0] != vertex_dtype or vertex_pair_dtypes[1] != vertex_dtype: + warning_msg = ( + "Jaccard requires 'vertex_pair' to match the graph's 'vertex' type. " + f"input graph's vertex type is: {vertex_dtype} and got " + f"'vertex_pair' of type: {vertex_pair_dtypes}." + ) + warnings.warn(warning_msg, UserWarning) + vertex_pair = vertex_pair.astype(vertex_dtype) + + return vertex_pair + + +def jaccard_w( + input_graph: Graph, + weights: cudf.DataFrame = None, # deprecated + vertex_pair: cudf.DataFrame = None, + do_expensive_check: bool = False, # deprecated +): """ Compute the weighted Jaccard similarity between each pair of vertices connected by an edge, or between arbitrary pairs of vertices specified by @@ -55,9 +87,13 @@ def jaccard_w(input_graph, weights, vertex_pair=None, do_expensive_check=True): vertices. If provided, the jaccard coefficient is computed for the given vertex pairs, else, it is computed for all vertex pairs. - do_expensive_check: bool (default=True) - When set to True, check if the vertices in the graph are (re)numbered - from 0 to V-1 where V is the total number of vertices. + do_expensive_check : bool, optional (default=False) + Deprecated. + This option added a check to ensure integer vertex IDs are sequential + values from 0 to V-1. That check is now redundant because cugraph + unconditionally renumbers and un-renumbers integer vertex IDs for + optimal performance, therefore this option is deprecated and will be + removed in a future version. Returns ------- @@ -95,47 +131,9 @@ def jaccard_w(input_graph, weights, vertex_pair=None, do_expensive_check=True): >>> df = cugraph.jaccard_w(G, weights) """ - if do_expensive_check: - if not input_graph.renumbered: - input_df = input_graph.edgelist.edgelist_df[["src", "dst"]] - max_vertex = input_df.max().max() - expected_nodes = cudf.Series(range(0, max_vertex + 1, 1)).astype( - input_df.dtypes[0] - ) - nodes = ( - cudf.concat([input_df["src"], input_df["dst"]]) - .unique() - .sort_values() - .reset_index(drop=True) - ) - if not expected_nodes.equals(nodes): - raise ValueError("Unrenumbered vertices are not supported.") - - if type(input_graph) is not Graph: - raise TypeError("input graph must a Graph") - - if type(vertex_pair) == cudf.DataFrame: - vertex_pair = renumber_vertex_pair(input_graph, vertex_pair) - elif vertex_pair is not None: - raise ValueError("vertex_pair must be a cudf dataframe") - - if input_graph.renumbered: - # The 'vertex' column of the cudf 'weights' also needs to be renumbered - # if the graph was renumbered - vertex_size = input_graph.vertex_column_size() - # single-column vertices i.e only one src and dst columns - if vertex_size == 1: - weights = input_graph.add_internal_vertex_id(weights, "vertex", "vertex") - # multi-column vertices i.e more than one src and dst columns - else: - cols = weights.columns[:vertex_size].to_list() - weights = input_graph.add_internal_vertex_id(weights, "vertex", cols) - - jaccard_weights = weights["weight"] - df = jaccard_wrapper.jaccard(input_graph, jaccard_weights, vertex_pair) - - if input_graph.renumbered: - df = input_graph.unrenumber(df, "first") - df = input_graph.unrenumber(df, "second") - - return df + warning_msg = ( + "jaccard_w is deprecated. To compute weighted jaccard, please use " + "jaccard(input_graph, vertex_pair=False, use_weight=True)" + ) + warnings.warn(warning_msg, FutureWarning) + return jaccard(input_graph, vertex_pair, do_expensive_check, use_weight=True) diff --git a/python/cugraph/cugraph/link_prediction/woverlap.py b/python/cugraph/cugraph/link_prediction/woverlap.py index d7ebc5fc684..5f43ad0670b 100644 --- a/python/cugraph/cugraph/link_prediction/woverlap.py +++ b/python/cugraph/cugraph/link_prediction/woverlap.py @@ -11,12 +11,26 @@ # See the License for the specific language governing permissions and # limitations under the License. -from cugraph.link_prediction import overlap_wrapper +from cugraph.link_prediction import overlap import cudf -from cugraph.utilities import renumber_vertex_pair +import warnings +from cugraph.structure import Graph +from cugraph.utilities.utils import import_optional -def overlap_w(input_graph, weights, vertex_pair=None, do_expensive_check=True): +# FIXME: the networkx.Graph type used in type annotations is specified +# using a string literal to avoid depending on and importing networkx. +# Instead, networkx is imported optionally, which may cause a problem +# for a type checker if run in an environment where networkx is not installed. +networkx = import_optional("networkx") + + +def overlap_w( + input_graph: Graph, + weights: cudf.DataFrame = None, # deprecated + vertex_pair: cudf.DataFrame = None, + do_expensive_check: bool = False, # deprecated +): """ Compute the weighted Overlap Coefficient between each pair of vertices connected by an edge, or between arbitrary pairs of vertices specified by @@ -55,9 +69,13 @@ def overlap_w(input_graph, weights, vertex_pair=None, do_expensive_check=True): vertices. If provided, the overlap coefficient is computed for the given vertex pairs, else, it is computed for all vertex pairs. - do_expensive_check: bool (default=True) - When set to True, check if the vertices in the graph are (re)numbered - from 0 to V-1 where V is the total number of vertices. + do_expensive_check : bool, optional (default=False) + Deprecated. + This option added a check to ensure integer vertex IDs are sequential + values from 0 to V-1. That check is now redundant because cugraph + unconditionally renumbers and un-renumbers integer vertex IDs for + optimal performance, therefore this option is deprecated and will be + removed in a future version. Returns ------- @@ -96,43 +114,9 @@ def overlap_w(input_graph, weights, vertex_pair=None, do_expensive_check=True): ... len(weights['vertex']))] >>> df = cugraph.overlap_w(G, weights) """ - if do_expensive_check: - if not input_graph.renumbered: - input_df = input_graph.edgelist.edgelist_df[["src", "dst"]] - max_vertex = input_df.max().max() - expected_nodes = cudf.Series(range(0, max_vertex + 1, 1)).astype( - input_df.dtypes[0] - ) - nodes = ( - cudf.concat([input_df["src"], input_df["dst"]]) - .unique() - .sort_values() - .reset_index(drop=True) - ) - if not expected_nodes.equals(nodes): - raise ValueError("Unrenumbered vertices are not supported.") - - if type(vertex_pair) == cudf.DataFrame: - vertex_pair = renumber_vertex_pair(input_graph, vertex_pair) - elif vertex_pair is not None: - raise ValueError("vertex_pair must be a cudf dataframe") - - if input_graph.renumbered: - vertex_size = input_graph.vertex_column_size() - if vertex_size == 1: - weights = input_graph.add_internal_vertex_id(weights, "vertex", "vertex") - else: - cols = weights.columns[:vertex_size].to_list() - weights = input_graph.add_internal_vertex_id(weights, "vertex", cols) - - overlap_weights = weights["weight"] - - overlap_weights = overlap_weights.astype("float32") - - df = overlap_wrapper.overlap(input_graph, overlap_weights, vertex_pair) - - if input_graph.renumbered: - df = input_graph.unrenumber(df, "first") - df = input_graph.unrenumber(df, "second") - - return df + warning_msg = ( + " overlap_w is deprecated. To compute weighted overlap, please use " + "overlap(input_graph, vertex_pair=False, use_weight=True)" + ) + warnings.warn(warning_msg, FutureWarning) + return overlap(input_graph, vertex_pair, do_expensive_check, use_weight=True) diff --git a/python/cugraph/cugraph/link_prediction/wsorensen.py b/python/cugraph/cugraph/link_prediction/wsorensen.py index 8337b4602de..ff502b36837 100644 --- a/python/cugraph/cugraph/link_prediction/wsorensen.py +++ b/python/cugraph/cugraph/link_prediction/wsorensen.py @@ -11,13 +11,26 @@ # See the License for the specific language governing permissions and # limitations under the License. -from cugraph.structure.graph_classes import Graph -from cugraph.link_prediction import jaccard_wrapper +from cugraph.link_prediction import sorensen import cudf -from cugraph.utilities import renumber_vertex_pair +import warnings +from cugraph.structure import Graph +from cugraph.utilities.utils import import_optional -def sorensen_w(input_graph, weights, vertex_pair=None, do_expensive_check=True): +# FIXME: the networkx.Graph type used in type annotations is specified +# using a string literal to avoid depending on and importing networkx. +# Instead, networkx is imported optionally, which may cause a problem +# for a type checker if run in an environment where networkx is not installed. +networkx = import_optional("networkx") + + +def sorensen_w( + input_graph: Graph, + weights: cudf.DataFrame = None, # deprecated + vertex_pair: cudf.DataFrame = None, + do_expensive_check: bool = False, # deprecated +): """ Compute the weighted Sorensen similarity between each pair of vertices connected by an edge, or between arbitrary pairs of vertices specified by @@ -51,9 +64,13 @@ def sorensen_w(input_graph, weights, vertex_pair=None, do_expensive_check=True): vertices. If provided, the sorensen coefficient is computed for the given vertex pairs, else, it is computed for all vertex pairs. - do_expensive_check: bool (default=True) - When set to True, check if the vertices in the graph are (re)numbered - from 0 to V-1 where V is the total number of vertices. + do_expensive_check : bool, optional (default=False) + Deprecated. + This option added a check to ensure integer vertex IDs are sequential + values from 0 to V-1. That check is now redundant because cugraph + unconditionally renumbers and un-renumbers integer vertex IDs for + optimal performance, therefore this option is deprecated and will be + removed in a future version. Returns ------- @@ -93,44 +110,9 @@ def sorensen_w(input_graph, weights, vertex_pair=None, do_expensive_check=True): >>> df = cugraph.sorensen_w(G, weights) """ - if do_expensive_check: - if not input_graph.renumbered: - input_df = input_graph.edgelist.edgelist_df[["src", "dst"]] - max_vertex = input_df.max().max() - expected_nodes = cudf.Series(range(0, max_vertex + 1, 1)).astype( - input_df.dtypes[0] - ) - nodes = ( - cudf.concat([input_df["src"], input_df["dst"]]) - .unique() - .sort_values() - .reset_index(drop=True) - ) - if not expected_nodes.equals(nodes): - raise ValueError("Unrenumbered vertices are not supported.") - - if type(input_graph) is not Graph: - raise TypeError("input graph must a Graph") - - if type(vertex_pair) == cudf.DataFrame: - vertex_pair = renumber_vertex_pair(input_graph, vertex_pair) - elif vertex_pair is not None: - raise ValueError("vertex_pair must be a cudf dataframe") - - if input_graph.renumbered: - vertex_size = input_graph.vertex_column_size() - if vertex_size == 1: - weights = input_graph.add_internal_vertex_id(weights, "vertex", "vertex") - else: - cols = weights.columns[:vertex_size].to_list() - weights = input_graph.add_internal_vertex_id(weights, "vertex", cols) - jaccard_weights = weights["weight"] - df = jaccard_wrapper.jaccard(input_graph, jaccard_weights, vertex_pair) - df.jaccard_coeff = (2 * df.jaccard_coeff) / (1 + df.jaccard_coeff) - df.rename({"jaccard_coeff": "sorensen_coeff"}, axis=1, inplace=True) - - if input_graph.renumbered: - df = input_graph.unrenumber(df, "first") - df = input_graph.unrenumber(df, "second") - - return df + warning_msg = ( + "sorensen_w is deprecated. To compute weighted sorensen, please use " + "sorensen(input_graph, vertex_pair=False, use_weight=True)" + ) + warnings.warn(warning_msg, FutureWarning) + return sorensen(input_graph, vertex_pair, use_weight=True) diff --git a/python/cugraph/cugraph/sampling/random_walks.py b/python/cugraph/cugraph/sampling/random_walks.py index 015c05d1b08..7b04dba82a5 100644 --- a/python/cugraph/cugraph/sampling/random_walks.py +++ b/python/cugraph/cugraph/sampling/random_walks.py @@ -25,11 +25,10 @@ from cugraph.utilities.utils import import_optional from typing import Union, Tuple -# FIXME: the networkx.Graph type used in the type annotation for -# induced_subgraph() is specified using a string literal to avoid depending on -# and importing networkx. Instead, networkx is imported optionally, which may -# cause a problem for a type checker if run in an environment where networkx is -# not installed. +# FIXME: the networkx.Graph type used in type annotations is specified +# using a string literal to avoid depending on and importing networkx. +# Instead, networkx is imported optionally, which may cause a problem +# for a type checker if run in an environment where networkx is not installed. networkx = import_optional("networkx") diff --git a/python/cugraph/cugraph/tests/link_prediction/test_jaccard.py b/python/cugraph/cugraph/tests/link_prediction/test_jaccard.py index cd883fb88f2..7ce7d263eda 100644 --- a/python/cugraph/cugraph/tests/link_prediction/test_jaccard.py +++ b/python/cugraph/cugraph/tests/link_prediction/test_jaccard.py @@ -11,6 +11,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +# FIXME: Can we use global variables for column names instead of hardcoded ones? + import gc import pytest @@ -20,12 +22,19 @@ import cugraph from cugraph.datasets import netscience from cugraph.testing import utils, UNDIRECTED_DATASETS -from cugraph.experimental import jaccard as exp_jaccard -from cudf.testing import assert_series_equal, assert_frame_equal -from cugraph.experimental import jaccard_coefficient as exp_jaccard_coefficient - +from cudf.testing import assert_series_equal +from cudf.testing.testing import assert_frame_equal -print("Networkx version : {} ".format(nx.__version__)) +SRC_COL = "0" +DST_COL = "1" +VERTEX_PAIR_FIRST_COL = "first" +VERTEX_PAIR_SECOND_COL = "second" +JACCARD_COEFF_COL = "jaccard_coeff" +EDGE_ATT_COL = "weight" +MULTI_COL_SRC_0_COL = "src_0" +MULTI_COL_DST_0_COL = "dst_0" +MULTI_COL_SRC_1_COL = "src_1" +MULTI_COL_DST_1_COL = "dst_1" # ============================================================================= @@ -38,65 +47,79 @@ def setup_function(): # ============================================================================= # Helper functions # ============================================================================= -def compare_jaccard_two_hop(G, Gnx, edgevals=True): + + +def compare_jaccard_two_hop(G, Gnx, use_weight=False): """ Compute both cugraph and nx jaccard after extracting the two hop neighbors from G and compare both results """ pairs = ( G.get_two_hop_neighbors() - .sort_values(["first", "second"]) + .sort_values([VERTEX_PAIR_FIRST_COL, VERTEX_PAIR_SECOND_COL]) .reset_index(drop=True) ) - nx_pairs = list(pairs.to_records(index=False)) - preds = nx.jaccard_coefficient(Gnx, nx_pairs) - nx_coeff = [] - for u, v, p in preds: - # print(u, " ", v, " ", p) - nx_coeff.append(p) df = cugraph.jaccard(G, pairs) - df = df.sort_values(by=["first", "second"]).reset_index(drop=True) - if not edgevals: - # experimental jaccard currently only supports unweighted graphs - df_exp = exp_jaccard(G, pairs) - df_exp = df_exp.sort_values(by=["first", "second"]).reset_index(drop=True) - assert_frame_equal(df, df_exp, check_dtype=False, check_like=True) + df = df.sort_values(by=[VERTEX_PAIR_FIRST_COL, VERTEX_PAIR_SECOND_COL]).reset_index( + drop=True + ) - assert len(nx_coeff) == len(df) - for i in range(len(df)): - diff = abs(nx_coeff[i] - df["jaccard_coeff"].iloc[i]) - assert diff < 1.0e-6 + if not use_weight: + nx_pairs = list(pairs.to_records(index=False)) + preds = nx.jaccard_coefficient(Gnx, nx_pairs) + nx_coeff = [] + for u, v, p in preds: + nx_coeff.append(p) + + assert len(nx_coeff) == len(df) + for i in range(len(df)): + diff = abs(nx_coeff[i] - df[JACCARD_COEFF_COL].iloc[i]) + assert diff < 1.0e-6 + else: + # FIXME: compare results against resultset api + pass -def cugraph_call(benchmark_callable, graph_file, edgevals=False, input_df=None): +def cugraph_call(benchmark_callable, graph_file, input_df=None, use_weight=False): G = cugraph.Graph() - G = graph_file.get_graph(ignore_weights=not edgevals) + G = graph_file.get_graph(ignore_weights=not use_weight) # If no vertex_pair is passed as input, 'cugraph.jaccard' will # compute the 'jaccard_similarity' with the two_hop_neighbor of the # entire graph while nx compute with the one_hop_neighbor. For better # comparaison, get the one_hop_neighbor of the entire graph for 'cugraph.jaccard' # and pass it as vertex_pair - vertex_pair = input_df.rename(columns={"0": "first", "1": "second"}) - vertex_pair = vertex_pair[["first", "second"]] + if isinstance(input_df, cudf.DataFrame): + vertex_pair = input_df.rename( + columns={SRC_COL: VERTEX_PAIR_FIRST_COL, DST_COL: VERTEX_PAIR_SECOND_COL} + ) + vertex_pair = vertex_pair[[VERTEX_PAIR_FIRST_COL, VERTEX_PAIR_SECOND_COL]] + else: + vertex_pair = cudf.DataFrame( + columns=[VERTEX_PAIR_FIRST_COL, VERTEX_PAIR_SECOND_COL], + dtype=G.edgelist.edgelist_df["src"].dtype, + ) # cugraph Jaccard Call - df = benchmark_callable(cugraph.jaccard, G, vertex_pair=vertex_pair) + df = benchmark_callable( + cugraph.jaccard, G, vertex_pair=vertex_pair, use_weight=use_weight + ) - df = df.sort_values(["first", "second"]).reset_index(drop=True) + df = df.sort_values([VERTEX_PAIR_FIRST_COL, VERTEX_PAIR_SECOND_COL]).reset_index( + drop=True + ) return ( - df["first"].to_numpy(), - df["second"].to_numpy(), - df["jaccard_coeff"].to_numpy(), + df[VERTEX_PAIR_FIRST_COL].to_numpy(), + df[VERTEX_PAIR_SECOND_COL].to_numpy(), + df[JACCARD_COEFF_COL].to_numpy(), ) def networkx_call(M, benchmark_callable=None): - - sources = M["0"] - destinations = M["1"] + sources = M[SRC_COL] + destinations = M[DST_COL] edges = [] for i in range(len(M)): edges.append((sources[i], destinations[i])) @@ -108,7 +131,11 @@ def networkx_call(M, benchmark_callable=None): print("Format conversion ... ") Gnx = nx.from_pandas_edgelist( - M, source="0", target="1", edge_attr="weight", create_using=nx.Graph() + M, + source=SRC_COL, + target=DST_COL, + edge_attr=EDGE_ATT_COL, + create_using=nx.Graph(), ) # Networkx Jaccard Call @@ -144,118 +171,130 @@ def read_csv(request): @pytest.mark.sg -def test_jaccard(read_csv, gpubenchmark): - +@pytest.mark.parametrize("use_weight", [False, True]) +def test_jaccard(read_csv, gpubenchmark, use_weight): M_cu, M, graph_file = read_csv - cu_src, cu_dst, cu_coeff = cugraph_call(gpubenchmark, graph_file, input_df=M_cu) - nx_src, nx_dst, nx_coeff = networkx_call(M) + cu_src, cu_dst, cu_coeff = cugraph_call( + gpubenchmark, graph_file, input_df=M_cu, use_weight=use_weight + ) + if not use_weight: + nx_src, nx_dst, nx_coeff = networkx_call(M) - # Calculating mismatch - err = 0 - tol = 1.0e-06 + # Calculating mismatch + err = 0 + tol = 1.0e-06 - assert len(cu_coeff) == len(nx_coeff) - for i in range(len(cu_coeff)): - if abs(cu_coeff[i] - nx_coeff[i]) > tol * 1.1: - err += 1 + assert len(cu_coeff) == len(nx_coeff) + for i in range(len(cu_coeff)): + if abs(cu_coeff[i] - nx_coeff[i]) > tol * 1.1: + err += 1 - print("Mismatches: %d" % err) - assert err == 0 + print("Mismatches: %d" % err) + assert err == 0 + else: + G = graph_file.get_graph() + res_w_jaccard = cugraph.jaccard_w(G, vertex_pair=M_cu[[SRC_COL, DST_COL]]) + res_w_jaccard = res_w_jaccard.sort_values( + [VERTEX_PAIR_FIRST_COL, VERTEX_PAIR_SECOND_COL] + ).reset_index(drop=True) + res_jaccard = cudf.DataFrame() + res_jaccard[VERTEX_PAIR_FIRST_COL] = cu_src + res_jaccard[VERTEX_PAIR_SECOND_COL] = cu_dst + res_jaccard[JACCARD_COEFF_COL] = cu_coeff + assert_frame_equal( + res_w_jaccard, res_jaccard, check_dtype=False, check_like=True + ) + + # FIXME: compare weighted jaccard results against resultset api @pytest.mark.sg -def test_directed_graph_check(read_csv): +@pytest.mark.parametrize("use_weight", [False, True]) +def test_directed_graph_check(read_csv, use_weight): _, M, _ = read_csv cu_M = cudf.DataFrame() - cu_M["src_0"] = cudf.Series(M["0"]) - cu_M["dst_0"] = cudf.Series(M["1"]) - cu_M["src_1"] = cu_M["src_0"] + 1000 - cu_M["dst_1"] = cu_M["dst_0"] + 1000 + cu_M[SRC_COL] = cudf.Series(M[SRC_COL]) + cu_M[DST_COL] = cudf.Series(M[DST_COL]) + if use_weight: + cu_M[EDGE_ATT_COL] = cudf.Series(M[EDGE_ATT_COL]) + G1 = cugraph.Graph(directed=True) - G1.from_cudf_edgelist( - cu_M, source=["src_0", "src_1"], destination=["dst_0", "dst_1"] - ) + weight = EDGE_ATT_COL if use_weight else None + G1.from_cudf_edgelist(cu_M, source=SRC_COL, destination=DST_COL, weight=weight) + + vertex_pair = cu_M[[SRC_COL, DST_COL]] - vertex_pair = cu_M[["src_0", "src_1", "dst_0", "dst_1"]] vertex_pair = vertex_pair[:5] with pytest.raises(ValueError): - cugraph.jaccard(G1, vertex_pair) + cugraph.jaccard(G1, vertex_pair, use_weight) @pytest.mark.sg def test_nx_jaccard_time(read_csv, gpubenchmark): - _, M, _ = read_csv nx_src, nx_dst, nx_coeff = networkx_call(M, gpubenchmark) @pytest.mark.sg @pytest.mark.parametrize("graph_file", [netscience]) -@pytest.mark.skip(reason="Skipping because this datasets is unrenumbered") -def test_jaccard_edgevals(gpubenchmark, graph_file): +@pytest.mark.parametrize("use_weight", [False, True]) +def test_jaccard_edgevals(gpubenchmark, graph_file, use_weight): dataset_path = netscience.get_path() M = utils.read_csv_for_nx(dataset_path) M_cu = utils.read_csv_file(dataset_path) cu_src, cu_dst, cu_coeff = cugraph_call( - gpubenchmark, netscience, edgevals=True, input_df=M_cu + gpubenchmark, netscience, input_df=M_cu, use_weight=use_weight ) - nx_src, nx_dst, nx_coeff = networkx_call(M) + if not use_weight: + nx_src, nx_dst, nx_coeff = networkx_call(M) - # Calculating mismatch - err = 0 - tol = 1.0e-06 - - assert len(cu_coeff) == len(nx_coeff) - for i in range(len(cu_coeff)): - if abs(cu_coeff[i] - nx_coeff[i]) > tol * 1.1: - err += 1 - - print("Mismatches: %d" % err) - assert err == 0 + # Calculating mismatch + err = 0 + tol = 1.0e-06 + assert len(cu_coeff) == len(nx_coeff) + for i in range(len(cu_coeff)): + if abs(cu_coeff[i] - nx_coeff[i]) > tol * 1.1: + err += 1 -@pytest.mark.sg -def test_jaccard_two_hop(read_csv): - - _, M, graph_file = read_csv - - Gnx = nx.from_pandas_edgelist(M, source="0", target="1", create_using=nx.Graph()) - G = graph_file.get_graph(ignore_weights=True) - - compare_jaccard_two_hop(G, Gnx) + print("Mismatches: %d" % err) + assert err == 0 + else: + # FIXME: compare results against resultset api + pass @pytest.mark.sg -def test_jaccard_two_hop_edge_vals(read_csv): - +@pytest.mark.parametrize("use_weight", [False, True]) +def test_jaccard_two_hop(read_csv, use_weight): _, M, graph_file = read_csv Gnx = nx.from_pandas_edgelist( - M, source="0", target="1", edge_attr="weight", create_using=nx.Graph() + M, source=SRC_COL, target=DST_COL, create_using=nx.Graph() ) + G = graph_file.get_graph(ignore_weights=not use_weight) - G = graph_file.get_graph() - - compare_jaccard_two_hop(G, Gnx, edgevals=True) + compare_jaccard_two_hop(G, Gnx, use_weight) @pytest.mark.sg def test_jaccard_nx(read_csv): - M_cu, M, _ = read_csv - Gnx = nx.from_pandas_edgelist(M, source="0", target="1", create_using=nx.Graph()) + Gnx = nx.from_pandas_edgelist( + M, source=SRC_COL, target=DST_COL, create_using=nx.Graph() + ) nx_j = nx.jaccard_coefficient(Gnx) nv_js = sorted(nx_j, key=len, reverse=True) - ebunch = M_cu.rename(columns={"0": "first", "1": "second"}) - ebunch = ebunch[["first", "second"]] + ebunch = M_cu.rename( + columns={SRC_COL: VERTEX_PAIR_FIRST_COL, DST_COL: VERTEX_PAIR_SECOND_COL} + ) + ebunch = ebunch[[VERTEX_PAIR_FIRST_COL, VERTEX_PAIR_SECOND_COL]] cg_j = cugraph.jaccard_coefficient(Gnx, ebunch=ebunch) - cg_j_exp = exp_jaccard_coefficient(Gnx, ebunch=ebunch) assert len(nv_js) > len(cg_j) - assert len(nv_js) > len(cg_j_exp) # FIXME: Nx does a full all-pair Jaccard. # cuGraph does a limited 1-hop Jaccard @@ -263,68 +302,58 @@ def test_jaccard_nx(read_csv): @pytest.mark.sg -def test_jaccard_multi_column(read_csv): - - _, M, _ = read_csv +@pytest.mark.parametrize("graph_file", UNDIRECTED_DATASETS) +@pytest.mark.parametrize("use_weight", [False, True]) +def test_jaccard_multi_column(graph_file, use_weight): + dataset_path = graph_file.get_path() + M = utils.read_csv_for_nx(dataset_path) cu_M = cudf.DataFrame() - cu_M["src_0"] = cudf.Series(M["0"]) - cu_M["dst_0"] = cudf.Series(M["1"]) - cu_M["src_1"] = cu_M["src_0"] + 1000 - cu_M["dst_1"] = cu_M["dst_0"] + 1000 + cu_M[MULTI_COL_SRC_0_COL] = cudf.Series(M[SRC_COL]) + cu_M[MULTI_COL_DST_0_COL] = cudf.Series(M[DST_COL]) + cu_M[MULTI_COL_SRC_1_COL] = cu_M[MULTI_COL_SRC_0_COL] + 1000 + cu_M[MULTI_COL_DST_1_COL] = cu_M[MULTI_COL_DST_0_COL] + 1000 + if use_weight: + cu_M[EDGE_ATT_COL] = cudf.Series(M[EDGE_ATT_COL]) + G1 = cugraph.Graph() + weight = EDGE_ATT_COL if use_weight else None G1.from_cudf_edgelist( - cu_M, source=["src_0", "src_1"], destination=["dst_0", "dst_1"] + cu_M, + source=[MULTI_COL_SRC_0_COL, MULTI_COL_SRC_1_COL], + destination=[MULTI_COL_DST_0_COL, MULTI_COL_DST_1_COL], + weight=weight, ) - vertex_pair = cu_M[["src_0", "src_1", "dst_0", "dst_1"]] + vertex_pair = cu_M[ + [ + MULTI_COL_SRC_0_COL, + MULTI_COL_SRC_1_COL, + MULTI_COL_DST_0_COL, + MULTI_COL_DST_1_COL, + ] + ] vertex_pair = vertex_pair[:5] - df_res = cugraph.jaccard(G1, vertex_pair) - df_plc_exp = exp_jaccard(G1, vertex_pair) - - df_plc_exp = df_plc_exp.rename( - columns={ - "0_src": "0_source", - "0_dst": "0_destination", - "1_src": "1_source", - "1_dst": "1_destination", - } - ) - - jaccard_res = df_res["jaccard_coeff"].sort_values().reset_index(drop=True) - jaccard_plc_exp = df_plc_exp["jaccard_coeff"].sort_values().reset_index(drop=True) - assert_series_equal(jaccard_res, jaccard_plc_exp) + df_multi_col_res = cugraph.jaccard(G1, vertex_pair) G2 = cugraph.Graph() - G2.from_cudf_edgelist(cu_M, source="src_0", destination="dst_0") - df_exp = cugraph.jaccard(G2, vertex_pair[["src_0", "dst_0"]]) + G2.from_cudf_edgelist( + cu_M, source=MULTI_COL_SRC_0_COL, destination=MULTI_COL_DST_0_COL, weight=weight + ) + df_single_col_res = cugraph.jaccard( + G2, vertex_pair[[MULTI_COL_SRC_0_COL, MULTI_COL_DST_0_COL]] + ) # Calculating mismatch - actual = df_res.sort_values("0_first").reset_index() - expected = df_exp.sort_values("first").reset_index() - assert_series_equal(actual["jaccard_coeff"], expected["jaccard_coeff"]) + actual = df_multi_col_res.sort_values("0_src").reset_index() + expected = df_single_col_res.sort_values(VERTEX_PAIR_FIRST_COL).reset_index() + assert_series_equal(actual[JACCARD_COEFF_COL], expected[JACCARD_COEFF_COL]) @pytest.mark.sg -def test_weighted_exp_jaccard(): +def test_weighted_jaccard(): karate = UNDIRECTED_DATASETS[0] - G = karate.get_graph() - with pytest.raises(ValueError): - exp_jaccard(G) - G = karate.get_graph(ignore_weights=True) - use_weight = True - with pytest.raises(ValueError): - exp_jaccard(G, use_weight=use_weight) - - -@pytest.mark.sg -def test_invalid_datasets_jaccard(): - karate = UNDIRECTED_DATASETS[0] - df = karate.get_edgelist() - df = df.add(1) - G = cugraph.Graph(directed=False) - G.from_cudf_edgelist(df, source="src", destination="dst") with pytest.raises(ValueError): - cugraph.jaccard(G) + cugraph.jaccard(G, use_weight=True) diff --git a/python/cugraph/cugraph/tests/link_prediction/test_overlap.py b/python/cugraph/cugraph/tests/link_prediction/test_overlap.py index 586d534cd42..e24deaa61ac 100644 --- a/python/cugraph/cugraph/tests/link_prediction/test_overlap.py +++ b/python/cugraph/cugraph/tests/link_prediction/test_overlap.py @@ -20,8 +20,19 @@ import cudf import cugraph from cugraph.testing import utils, UNDIRECTED_DATASETS -from cugraph.experimental import overlap as exp_overlap -from cudf.testing import assert_series_equal, assert_frame_equal +from cudf.testing import assert_series_equal +from cudf.testing.testing import assert_frame_equal + +SRC_COL = "0" +DST_COL = "1" +VERTEX_PAIR_FIRST_COL = "first" +VERTEX_PAIR_SECOND_COL = "second" +OVERLAP_COEFF_COL = "overlap_coeff" +EDGE_ATT_COL = "weight" +MULTI_COL_SRC_0_COL = "src_0" +MULTI_COL_DST_0_COL = "dst_0" +MULTI_COL_SRC_1_COL = "src_1" +MULTI_COL_DST_1_COL = "dst_1" # ============================================================================= @@ -35,7 +46,6 @@ def setup_function(): # Helper functions # ============================================================================= def compare_overlap(cu_coeff, cpu_coeff): - assert len(cu_coeff) == len(cpu_coeff) for i in range(len(cu_coeff)): if np.isnan(cpu_coeff[i]): @@ -47,21 +57,21 @@ def compare_overlap(cu_coeff, cpu_coeff): assert diff < 1.0e-6 -def cugraph_call(benchmark_callable, graph_file, pairs, edgevals=False): +def cugraph_call(benchmark_callable, graph_file, pairs, use_weight=False): # Device data G = graph_file.get_graph( - create_using=cugraph.Graph(directed=False), ignore_weights=not edgevals + create_using=cugraph.Graph(directed=False), ignore_weights=not use_weight ) # cugraph Overlap Call df = benchmark_callable(cugraph.overlap, G, pairs) - df = df.sort_values(by=["first", "second"]).reset_index(drop=True) - if not edgevals: - # experimental overlap currently only supports unweighted graphs - df_exp = exp_overlap(G, pairs) - df_exp = df_exp.sort_values(by=["first", "second"]).reset_index(drop=True) - assert_frame_equal(df, df_exp, check_dtype=False, check_like=True) + df = df.sort_values(by=[VERTEX_PAIR_FIRST_COL, VERTEX_PAIR_SECOND_COL]).reset_index( + drop=True + ) + if use_weight: + res_w_overlap = cugraph.overlap_w(G, vertex_pair=pairs) + assert_frame_equal(res_w_overlap, df, check_dtype=False, check_like=True) - return df["overlap_coeff"].to_numpy() + return df[OVERLAP_COEFF_COL].to_numpy() def intersection(a, b, M): @@ -120,8 +130,10 @@ def read_csv(request): dataset_path = graph_file.get_path() Mnx = utils.read_csv_for_nx(dataset_path) - N = max(max(Mnx["0"]), max(Mnx["1"])) + 1 - M = scipy.sparse.csr_matrix((Mnx.weight, (Mnx["0"], Mnx["1"])), shape=(N, N)) + N = max(max(Mnx[SRC_COL]), max(Mnx[DST_COL])) + 1 + M = scipy.sparse.csr_matrix( + (Mnx.weight, (Mnx[SRC_COL], Mnx[DST_COL])), shape=(N, N) + ) return M, graph_file @@ -135,7 +147,7 @@ def extract_two_hop(read_csv): G = graph_file.get_graph(ignore_weights=True) pairs = ( G.get_two_hop_neighbors() - .sort_values(["first", "second"]) + .sort_values([VERTEX_PAIR_FIRST_COL, VERTEX_PAIR_SECOND_COL]) .reset_index(drop=True) ) @@ -144,93 +156,91 @@ def extract_two_hop(read_csv): # Test @pytest.mark.sg -def test_overlap(gpubenchmark, read_csv, extract_two_hop): - +@pytest.mark.parametrize("use_weight", [False, True]) +def test_overlap(gpubenchmark, read_csv, extract_two_hop, use_weight): M, graph_file = read_csv pairs = extract_two_hop - cu_coeff = cugraph_call(gpubenchmark, graph_file, pairs) - cpu_coeff = cpu_call(M, pairs["first"], pairs["second"]) + cu_coeff = cugraph_call(gpubenchmark, graph_file, pairs, use_weight=use_weight) + cpu_coeff = cpu_call(M, pairs[VERTEX_PAIR_FIRST_COL], pairs[VERTEX_PAIR_SECOND_COL]) compare_overlap(cu_coeff, cpu_coeff) -# Test @pytest.mark.sg -def test_overlap_edge_vals(gpubenchmark, read_csv, extract_two_hop): +@pytest.mark.parametrize("graph_file", UNDIRECTED_DATASETS) +@pytest.mark.parametrize("use_weight", [False, True]) +def test_directed_graph_check(graph_file, use_weight): + M = utils.read_csv_for_nx(graph_file.get_path()) + cu_M = cudf.DataFrame() + cu_M[SRC_COL] = cudf.Series(M[SRC_COL]) + cu_M[DST_COL] = cudf.Series(M[DST_COL]) + if use_weight: + cu_M[EDGE_ATT_COL] = cudf.Series(M[EDGE_ATT_COL]) - M, graph_file = read_csv - pairs = extract_two_hop + G1 = cugraph.Graph(directed=True) + weight = EDGE_ATT_COL if use_weight else None + G1.from_cudf_edgelist(cu_M, source=SRC_COL, destination=DST_COL, weight=weight) - cu_coeff = cugraph_call(gpubenchmark, graph_file, pairs, edgevals=True) - cpu_coeff = cpu_call(M, pairs["first"], pairs["second"]) + vertex_pair = cu_M[[SRC_COL, DST_COL]] - compare_overlap(cu_coeff, cpu_coeff) + vertex_pair = vertex_pair[:5] + with pytest.raises(ValueError): + cugraph.overlap(G1, vertex_pair, use_weight) @pytest.mark.sg @pytest.mark.parametrize("graph_file", UNDIRECTED_DATASETS) -def test_overlap_multi_column(graph_file): +@pytest.mark.parametrize("use_weight", [False, True]) +def test_overlap_multi_column(graph_file, use_weight): dataset_path = graph_file.get_path() M = utils.read_csv_for_nx(dataset_path) cu_M = cudf.DataFrame() - cu_M["src_0"] = cudf.Series(M["0"]) - cu_M["dst_0"] = cudf.Series(M["1"]) - cu_M["src_1"] = cu_M["src_0"] + 1000 - cu_M["dst_1"] = cu_M["dst_0"] + 1000 + cu_M[MULTI_COL_SRC_0_COL] = cudf.Series(M[SRC_COL]) + cu_M[MULTI_COL_DST_0_COL] = cudf.Series(M[DST_COL]) + cu_M[MULTI_COL_SRC_1_COL] = cu_M[MULTI_COL_SRC_0_COL] + 1000 + cu_M[MULTI_COL_DST_1_COL] = cu_M[MULTI_COL_DST_0_COL] + 1000 + if use_weight: + cu_M[EDGE_ATT_COL] = cudf.Series(M[EDGE_ATT_COL]) + G1 = cugraph.Graph() + weight = EDGE_ATT_COL if use_weight else None G1.from_cudf_edgelist( - cu_M, source=["src_0", "src_1"], destination=["dst_0", "dst_1"] + cu_M, + source=[MULTI_COL_SRC_0_COL, MULTI_COL_SRC_1_COL], + destination=[MULTI_COL_DST_0_COL, MULTI_COL_DST_1_COL], + weight=weight, ) - vertex_pair = cu_M[["src_0", "src_1", "dst_0", "dst_1"]] + vertex_pair = cu_M[ + [ + MULTI_COL_SRC_0_COL, + MULTI_COL_SRC_1_COL, + MULTI_COL_DST_0_COL, + MULTI_COL_DST_1_COL, + ] + ] vertex_pair = vertex_pair[:5] - df_res = cugraph.overlap(G1, vertex_pair) - df_plc_exp = exp_overlap(G1, vertex_pair) - - df_plc_exp = df_plc_exp.rename( - columns={ - "0_src": "0_source", - "0_dst": "0_destination", - "1_src": "1_source", - "1_dst": "1_destination", - } - ) - overlap_res = df_res["overlap_coeff"].sort_values().reset_index(drop=True) - overlap_plc_exp = df_plc_exp["overlap_coeff"].sort_values().reset_index(drop=True) - assert_series_equal(overlap_res, overlap_plc_exp) - + df_multi_col_res = cugraph.overlap(G1, vertex_pair, use_weight=use_weight) G2 = cugraph.Graph() - G2.from_cudf_edgelist(cu_M, source="src_0", destination="dst_0") - df_exp = cugraph.overlap(G2, vertex_pair[["src_0", "dst_0"]]) + G2.from_cudf_edgelist( + cu_M, source=MULTI_COL_SRC_0_COL, destination=MULTI_COL_DST_0_COL, weight=weight + ) + df_single_col_res = cugraph.overlap( + G2, vertex_pair[[MULTI_COL_SRC_0_COL, MULTI_COL_DST_0_COL]] + ) # Calculating mismatch - actual = df_res.sort_values("0_first").reset_index() - expected = df_exp.sort_values("first").reset_index() - assert_series_equal(actual["overlap_coeff"], expected["overlap_coeff"]) + actual = df_multi_col_res.sort_values("0_src").reset_index() + expected = df_single_col_res.sort_values(VERTEX_PAIR_FIRST_COL).reset_index() + assert_series_equal(actual[OVERLAP_COEFF_COL], expected[OVERLAP_COEFF_COL]) @pytest.mark.sg -def test_weighted_exp_overlap(): +def test_weighted_overlap(): karate = UNDIRECTED_DATASETS[0] - G = karate.get_graph() - with pytest.raises(ValueError): - exp_overlap(G) - G = karate.get_graph(ignore_weights=True) - use_weight = True - with pytest.raises(ValueError): - exp_overlap(G, use_weight=use_weight) - - -@pytest.mark.sg -def test_invalid_datasets_overlap(): - karate = UNDIRECTED_DATASETS[0] - df = karate.get_edgelist() - df = df.add(1) - G = cugraph.Graph(directed=False) - G.from_cudf_edgelist(df, source="src", destination="dst") with pytest.raises(ValueError): - cugraph.overlap(G) + cugraph.overlap(G, use_weight=True) diff --git a/python/cugraph/cugraph/tests/link_prediction/test_sorensen.py b/python/cugraph/cugraph/tests/link_prediction/test_sorensen.py index 3da33a3e853..6b4074fce30 100644 --- a/python/cugraph/cugraph/tests/link_prediction/test_sorensen.py +++ b/python/cugraph/cugraph/tests/link_prediction/test_sorensen.py @@ -20,11 +20,19 @@ import cugraph from cugraph.testing import utils, UNDIRECTED_DATASETS from cugraph.datasets import netscience -from cugraph.experimental import sorensen as exp_sorensen -from cudf.testing import assert_series_equal, assert_frame_equal +from cudf.testing import assert_series_equal +from cudf.testing.testing import assert_frame_equal - -print("Networkx version : {} ".format(nx.__version__)) +SRC_COL = "0" +DST_COL = "1" +VERTEX_PAIR_FIRST_COL = "first" +VERTEX_PAIR_SECOND_COL = "second" +SORENSEN_COEFF_COL = "sorensen_coeff" +EDGE_ATT_COL = "weight" +MULTI_COL_SRC_0_COL = "src_0" +MULTI_COL_DST_0_COL = "dst_0" +MULTI_COL_SRC_1_COL = "src_1" +MULTI_COL_DST_1_COL = "dst_1" # ============================================================================= @@ -37,68 +45,89 @@ def setup_function(): # ============================================================================= # Helper functions # ============================================================================= -def compare_sorensen_two_hop(G, Gnx, edgevals=False): +def compare_sorensen_two_hop(G, Gnx, use_weight=False): """ Compute both cugraph and nx sorensen after extracting the two hop neighbors from G and compare both results """ pairs = ( G.get_two_hop_neighbors() - .sort_values(["first", "second"]) + .sort_values([VERTEX_PAIR_FIRST_COL, VERTEX_PAIR_SECOND_COL]) .reset_index(drop=True) ) - nx_pairs = [] - nx_pairs = list(pairs.to_records(index=False)) - preds = nx.jaccard_coefficient(Gnx, nx_pairs) - nx_coeff = [] - for u, v, p in preds: + + # print(f'G = {G.edgelist.edgelist_df}') + + df = cugraph.sorensen(G, pairs) + df = df.sort_values(by=[VERTEX_PAIR_FIRST_COL, VERTEX_PAIR_SECOND_COL]).reset_index( + drop=True + ) + + if not use_weight: + nx_pairs = list(pairs.to_records(index=False)) + + # print(f'nx_pairs = {len(nx_pairs)}') + + preds = nx.jaccard_coefficient(Gnx, nx_pairs) + # FIXME: Use known correct values of Sorensen for few graphs, # hardcode it and compare to Cugraph Sorensen to get a more robust test # Conversion from Networkx Jaccard to Sorensen # No networkX equivalent - nx_coeff.append((2 * p) / (1 + p)) - df = cugraph.sorensen(G, pairs) - df = df.sort_values(by=["first", "second"]).reset_index(drop=True) - if not edgevals: - # experimental sorensen currently only supports unweighted graphs - df_exp = exp_sorensen(G, pairs) - df_exp = df_exp.sort_values(by=["first", "second"]).reset_index(drop=True) - assert_frame_equal(df, df_exp, check_dtype=False, check_like=True) - assert len(nx_coeff) == len(df) - for i in range(len(df)): - diff = abs(nx_coeff[i] - df["sorensen_coeff"].iloc[i]) - assert diff < 1.0e-6 - - -def cugraph_call(benchmark_callable, graph_file, edgevals=False, input_df=None): + + nx_coeff = list(map(lambda x: (2 * x[2]) / (1 + x[2]), preds)) + + assert len(nx_coeff) == len(df) + for i in range(len(df)): + diff = abs(nx_coeff[i] - df[SORENSEN_COEFF_COL].iloc[i]) + assert diff < 1.0e-6 + else: + # FIXME: compare results against resultset api + res_w_sorensen = cugraph.sorensen_w(G, vertex_pair=pairs) + res_w_sorensen = res_w_sorensen.sort_values( + [VERTEX_PAIR_FIRST_COL, VERTEX_PAIR_SECOND_COL] + ).reset_index(drop=True) + assert_frame_equal(res_w_sorensen, df, check_dtype=False, check_like=True) + + +def cugraph_call(benchmark_callable, graph_file, input_df=None, use_weight=False): G = cugraph.Graph() - G = graph_file.get_graph(ignore_weights=not edgevals) + G = graph_file.get_graph(ignore_weights=not use_weight) # If no vertex_pair is passed as input, 'cugraph.sorensen' will # compute the 'sorensen_similarity' with the two_hop_neighbor of the # entire graph while nx compute with the one_hop_neighbor. For better # comparaison, get the one_hop_neighbor of the entire graph for 'cugraph.sorensen' # and pass it as vertex_pair - vertex_pair = input_df.rename(columns={"0": "first", "1": "second"}) - vertex_pair = vertex_pair[["first", "second"]] + if isinstance(input_df, cudf.DataFrame): + vertex_pair = input_df.rename( + columns={SRC_COL: VERTEX_PAIR_FIRST_COL, DST_COL: VERTEX_PAIR_SECOND_COL} + ) + vertex_pair = vertex_pair[[VERTEX_PAIR_FIRST_COL, VERTEX_PAIR_SECOND_COL]] + else: + vertex_pair = cudf.DataFrame( + columns=[VERTEX_PAIR_FIRST_COL, VERTEX_PAIR_SECOND_COL], + dtype=G.edgelist.edgelist_df["src"].dtype, + ) # cugraph Sorensen Call df = benchmark_callable(cugraph.sorensen, G, vertex_pair=vertex_pair) - df = df.sort_values(["first", "second"]).reset_index(drop=True) + df = df.sort_values([VERTEX_PAIR_FIRST_COL, VERTEX_PAIR_SECOND_COL]).reset_index( + drop=True + ) return ( - df["first"].to_numpy(), - df["second"].to_numpy(), - df["sorensen_coeff"].to_numpy(), + df[VERTEX_PAIR_FIRST_COL].to_numpy(), + df[VERTEX_PAIR_SECOND_COL].to_numpy(), + df[SORENSEN_COEFF_COL].to_numpy(), ) def networkx_call(M, benchmark_callable=None): - - sources = M["0"] - destinations = M["1"] + sources = M[SRC_COL] + destinations = M[DST_COL] edges = [] for i in range(len(M)): edges.append((sources[i], destinations[i])) @@ -110,7 +139,11 @@ def networkx_call(M, benchmark_callable=None): print("Format conversion ... ") Gnx = nx.from_pandas_edgelist( - M, source="0", target="1", edge_attr="weight", create_using=nx.Graph() + M, + source=SRC_COL, + target=DST_COL, + edge_attr=EDGE_ATT_COL, + create_using=nx.Graph(), ) # Networkx Jaccard Call @@ -149,10 +182,12 @@ def read_csv(request): @pytest.mark.sg -def test_sorensen(gpubenchmark, read_csv): - +@pytest.mark.parametrize("use_weight", [False, True]) +def test_sorensen(gpubenchmark, read_csv, use_weight): M_cu, M, graph_file = read_csv - cu_src, cu_dst, cu_coeff = cugraph_call(gpubenchmark, graph_file, input_df=M_cu) + cu_src, cu_dst, cu_coeff = cugraph_call( + gpubenchmark, graph_file, input_df=M_cu, use_weight=use_weight + ) nx_src, nx_dst, nx_coeff = networkx_call(M) # Calculating mismatch @@ -170,20 +205,42 @@ def test_sorensen(gpubenchmark, read_csv): @pytest.mark.sg def test_nx_sorensen_time(gpubenchmark, read_csv): - _, M, _ = read_csv nx_src, nx_dst, nx_coeff = networkx_call(M, gpubenchmark) +@pytest.mark.sg +@pytest.mark.parametrize("use_weight", [False, True]) +def test_directed_graph_check(read_csv, use_weight): + _, M, _ = read_csv + + cu_M = cudf.DataFrame() + cu_M[SRC_COL] = cudf.Series(M[SRC_COL]) + cu_M[DST_COL] = cudf.Series(M[DST_COL]) + if use_weight: + cu_M[EDGE_ATT_COL] = cudf.Series(M[EDGE_ATT_COL]) + + G1 = cugraph.Graph(directed=True) + weight = EDGE_ATT_COL if use_weight else None + G1.from_cudf_edgelist(cu_M, source=SRC_COL, destination=DST_COL, weight=weight) + + vertex_pair = cu_M[[SRC_COL, DST_COL]] + + vertex_pair = vertex_pair[:5] + with pytest.raises(ValueError): + cugraph.sorensen(G1, vertex_pair, use_weight) + + @pytest.mark.sg @pytest.mark.parametrize("graph_file", [netscience]) +@pytest.mark.parametrize("use_weight", [False, True]) @pytest.mark.skip(reason="Skipping because this datasets is unrenumbered") -def test_sorensen_edgevals(gpubenchmark, graph_file): +def test_sorensen_edgevals(gpubenchmark, graph_file, use_weight): dataset_path = netscience.get_path() M = utils.read_csv_for_nx(dataset_path) M_cu = utils.read_csv_file(dataset_path) cu_src, cu_dst, cu_coeff = cugraph_call( - gpubenchmark, netscience, edgevals=True, input_df=M_cu + gpubenchmark, netscience, input_df=M_cu, use_weight=use_weight ) nx_src, nx_dst, nx_coeff = networkx_call(M) @@ -201,92 +258,89 @@ def test_sorensen_edgevals(gpubenchmark, graph_file): @pytest.mark.sg -def test_sorensen_two_hop(read_csv): - +@pytest.mark.parametrize("use_weight", [False, True]) +def test_sorensen_two_hop(read_csv, use_weight): _, M, graph_file = read_csv - Gnx = nx.from_pandas_edgelist(M, source="0", target="1", create_using=nx.Graph()) - G = graph_file.get_graph(ignore_weights=True) + Gnx = nx.from_pandas_edgelist( + M, source=SRC_COL, target=DST_COL, create_using=nx.Graph() + ) + G = graph_file.get_graph(ignore_weights=not use_weight) - compare_sorensen_two_hop(G, Gnx) + compare_sorensen_two_hop(G, Gnx, use_weight=use_weight) @pytest.mark.sg -def test_sorensen_two_hop_edge_vals(read_csv): - +@pytest.mark.parametrize("use_weight", [False, True]) +def test_sorensen_two_hop_edge_vals(read_csv, use_weight): _, M, graph_file = read_csv Gnx = nx.from_pandas_edgelist( - M, source="0", target="1", edge_attr="weight", create_using=nx.Graph() + M, + source=SRC_COL, + target=DST_COL, + edge_attr=EDGE_ATT_COL, + create_using=nx.Graph(), ) - G = graph_file.get_graph() + G = graph_file.get_graph(ignore_weights=not use_weight) - compare_sorensen_two_hop(G, Gnx, edgevals=True) + compare_sorensen_two_hop(G, Gnx, use_weight=use_weight) @pytest.mark.sg -def test_sorensen_multi_column(read_csv): - - _, M, _ = read_csv +@pytest.mark.parametrize("graph_file", UNDIRECTED_DATASETS) +@pytest.mark.parametrize("use_weight", [False, True]) +def test_sorensen_multi_column(graph_file, use_weight): + dataset_path = graph_file.get_path() + M = utils.read_csv_for_nx(dataset_path) cu_M = cudf.DataFrame() - cu_M["src_0"] = cudf.Series(M["0"]) - cu_M["dst_0"] = cudf.Series(M["1"]) - cu_M["src_1"] = cu_M["src_0"] + 1000 - cu_M["dst_1"] = cu_M["dst_0"] + 1000 + cu_M[MULTI_COL_SRC_0_COL] = cudf.Series(M[SRC_COL]) + cu_M[MULTI_COL_DST_0_COL] = cudf.Series(M[DST_COL]) + cu_M[MULTI_COL_SRC_1_COL] = cu_M[MULTI_COL_SRC_0_COL] + 1000 + cu_M[MULTI_COL_DST_1_COL] = cu_M[MULTI_COL_DST_0_COL] + 1000 + if use_weight: + cu_M[EDGE_ATT_COL] = cudf.Series(M[EDGE_ATT_COL]) + G1 = cugraph.Graph() + weight = EDGE_ATT_COL if use_weight else None G1.from_cudf_edgelist( - cu_M, source=["src_0", "src_1"], destination=["dst_0", "dst_1"] + cu_M, + source=[MULTI_COL_SRC_0_COL, MULTI_COL_SRC_1_COL], + destination=[MULTI_COL_DST_0_COL, MULTI_COL_DST_1_COL], + weight=weight, ) - vertex_pair = cu_M[["src_0", "src_1", "dst_0", "dst_1"]] + vertex_pair = cu_M[ + [ + MULTI_COL_SRC_0_COL, + MULTI_COL_SRC_1_COL, + MULTI_COL_DST_0_COL, + MULTI_COL_DST_1_COL, + ] + ] vertex_pair = vertex_pair[:5] - df_res = cugraph.sorensen(G1, vertex_pair) - df_plc_exp = exp_sorensen(G1, vertex_pair) - - df_plc_exp = df_plc_exp.rename( - columns={ - "0_src": "0_source", - "0_dst": "0_destination", - "1_src": "1_source", - "1_dst": "1_destination", - } - ) - sorensen_res = df_res["sorensen_coeff"].sort_values().reset_index(drop=True) - sorensen_plc_exp = df_plc_exp["sorensen_coeff"].sort_values().reset_index(drop=True) - assert_series_equal(sorensen_res, sorensen_plc_exp) + df_multi_col_res = cugraph.sorensen(G1, vertex_pair) G2 = cugraph.Graph() - G2.from_cudf_edgelist(cu_M, source="src_0", destination="dst_0") - df_exp = cugraph.sorensen(G2, vertex_pair[["src_0", "dst_0"]]) + G2.from_cudf_edgelist( + cu_M, source=MULTI_COL_SRC_0_COL, destination=MULTI_COL_DST_0_COL, weight=weight + ) + df_single_col_res = cugraph.sorensen( + G2, vertex_pair[[MULTI_COL_SRC_0_COL, MULTI_COL_DST_0_COL]] + ) # Calculating mismatch - actual = df_res.sort_values("0_first").reset_index() - expected = df_exp.sort_values("first").reset_index() - assert_series_equal(actual["sorensen_coeff"], expected["sorensen_coeff"]) + actual = df_multi_col_res.sort_values("0_src").reset_index() + expected = df_single_col_res.sort_values(VERTEX_PAIR_FIRST_COL).reset_index() + assert_series_equal(actual[SORENSEN_COEFF_COL], expected[SORENSEN_COEFF_COL]) @pytest.mark.sg -def test_weighted_exp_sorensen(): +def test_weighted_sorensen(): karate = UNDIRECTED_DATASETS[0] - G = karate.get_graph() - with pytest.raises(ValueError): - exp_sorensen(G) - G = karate.get_graph(ignore_weights=True) - use_weight = True - with pytest.raises(ValueError): - exp_sorensen(G, use_weight=use_weight) - - -@pytest.mark.sg -def test_invalid_datasets_sorensen(): - karate = UNDIRECTED_DATASETS[0] - df = karate.get_edgelist() - df = df.add(1) - G = cugraph.Graph(directed=False) - G.from_cudf_edgelist(df, source="src", destination="dst") with pytest.raises(ValueError): - cugraph.sorensen(G) + cugraph.sorensen(G, use_weight=True) diff --git a/python/cugraph/cugraph/tests/link_prediction/test_wjaccard.py b/python/cugraph/cugraph/tests/link_prediction/test_wjaccard.py deleted file mode 100644 index 36a21df46b8..00000000000 --- a/python/cugraph/cugraph/tests/link_prediction/test_wjaccard.py +++ /dev/null @@ -1,177 +0,0 @@ -# Copyright (c) 2019-2023, NVIDIA CORPORATION. -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import gc - -import pytest -import numpy as np -import networkx as nx - -import cudf -import cugraph -from cugraph.testing import utils, UNDIRECTED_DATASETS -from cudf.testing import assert_series_equal - - -print("Networkx version : {} ".format(nx.__version__)) - - -# ============================================================================= -# Pytest Setup / Teardown - called for each test function -# ============================================================================= -def setup_function(): - gc.collect() - - -def cugraph_call(benchmark_callable, graph_file): - # Device data - cu_M = graph_file.get_edgelist() - weight_arr = cudf.Series( - np.ones(max(cu_M["src"].max(), cu_M["dst"].max()) + 1, dtype=np.float32) - ) - weights = cudf.DataFrame() - weights["vertex"] = np.arange(len(weight_arr), dtype=np.int32) - weights["weight"] = weight_arr - - G = graph_file.get_graph(ignore_weights=True) - - # cugraph Jaccard Call - df = benchmark_callable(cugraph.jaccard_w, G, weights) - - df = df.sort_values(["first", "second"]).reset_index(drop=True) - - return df["jaccard_coeff"] - - -def networkx_call(M, benchmark_callable=None): - - sources = M["0"] - destinations = M["1"] - edges = [] - for i in range(len(sources)): - edges.append((sources[i], destinations[i])) - edges.append((destinations[i], sources[i])) - edges = list(dict.fromkeys(edges)) - edges = sorted(edges) - # in NVGRAPH tests we read as CSR and feed as CSC, so here we doing this - # explicitly - print("Format conversion ... ") - - # NetworkX graph - Gnx = nx.from_pandas_edgelist(M, source="0", target="1", create_using=nx.Graph()) - # Networkx Jaccard Call - print("Solving... ") - if benchmark_callable is not None: - preds = benchmark_callable(nx.jaccard_coefficient, Gnx, edges) - else: - preds = nx.jaccard_coefficient(Gnx, edges) - - coeff = [] - for u, v, p in preds: - coeff.append(p) - return coeff - - -# ============================================================================= -# Pytest Fixtures -# ============================================================================= -@pytest.fixture(scope="module", params=UNDIRECTED_DATASETS) -def read_csv(request): - """ - Read csv file for both networkx and cugraph - """ - graph_file = request.param - dataset_path = graph_file.get_path() - M = utils.read_csv_for_nx(dataset_path) - - return M, graph_file - - -@pytest.mark.sg -def test_wjaccard(gpubenchmark, read_csv): - - M, graph_file = read_csv - - cu_coeff = cugraph_call(gpubenchmark, graph_file) - nx_coeff = networkx_call(M) - for i in range(len(cu_coeff)): - diff = abs(nx_coeff[i] - cu_coeff[i]) - assert diff < 1.0e-6 - - -@pytest.mark.sg -def test_nx_wjaccard_time(gpubenchmark, read_csv): - - M, _ = read_csv - networkx_call(M, gpubenchmark) - - -@pytest.mark.sg -def test_wjaccard_multi_column_weights(gpubenchmark, read_csv): - - M, graph_file = read_csv - - cu_coeff = cugraph_call(gpubenchmark, graph_file) - nx_coeff = networkx_call(M) - for i in range(len(cu_coeff)): - diff = abs(nx_coeff[i] - cu_coeff[i]) - assert diff < 1.0e-6 - - -@pytest.mark.sg -def test_wjaccard_multi_column(read_csv): - - M, _ = read_csv - - cu_M = cudf.DataFrame() - cu_M["src_0"] = cudf.Series(M["0"]) - cu_M["dst_0"] = cudf.Series(M["1"]) - cu_M["src_1"] = cu_M["src_0"] + 1000 - cu_M["dst_1"] = cu_M["dst_0"] + 1000 - G1 = cugraph.Graph() - G1.from_cudf_edgelist( - cu_M, source=["src_0", "src_1"], destination=["dst_0", "dst_1"] - ) - - G2 = cugraph.Graph() - G2.from_cudf_edgelist(cu_M, source="src_0", destination="dst_0") - - vertex_pair = cu_M[["src_0", "src_1", "dst_0", "dst_1"]] - vertex_pair = vertex_pair[:5] - - weight_arr = cudf.Series(np.ones(G2.number_of_vertices(), dtype=np.float32)) - weights = cudf.DataFrame() - weights["vertex"] = G2.nodes() - weights["vertex_"] = weights["vertex"] + 1000 - weights["weight"] = weight_arr - - df_res = cugraph.jaccard_w(G1, weights, vertex_pair) - - weights = weights[["vertex", "weight"]] - df_exp = cugraph.jaccard_w(G2, weights, vertex_pair[["src_0", "dst_0"]]) - - # Calculating mismatch - actual = df_res.sort_values("0_first").reset_index() - expected = df_exp.sort_values("first").reset_index() - assert_series_equal(actual["jaccard_coeff"], expected["jaccard_coeff"]) - - -@pytest.mark.sg -def test_invalid_datasets_jaccard_w(): - karate = UNDIRECTED_DATASETS[0] - df = karate.get_edgelist() - df = df.add(1) - G = cugraph.Graph(directed=False) - G.from_cudf_edgelist(df, source="src", destination="dst") - with pytest.raises(ValueError): - cugraph.jaccard_w(G, None) diff --git a/python/cugraph/cugraph/tests/link_prediction/test_woverlap.py b/python/cugraph/cugraph/tests/link_prediction/test_woverlap.py deleted file mode 100644 index 1dffb9fca41..00000000000 --- a/python/cugraph/cugraph/tests/link_prediction/test_woverlap.py +++ /dev/null @@ -1,171 +0,0 @@ -# Copyright (c) 2019-2023, NVIDIA CORPORATION. -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import gc - -import pytest -import scipy -import numpy as np - -import cudf -import cugraph -from cudf.testing import assert_series_equal -from cugraph.testing import utils, UNDIRECTED_DATASETS - - -# ============================================================================= -# Pytest Setup / Teardown - called for each test function -# ============================================================================= -def setup_function(): - gc.collect() - - -def cugraph_call(benchmark_callable, graph_file, pairs): - # Device data - cu_M = graph_file.get_edgelist() - weights_arr = cudf.Series( - np.ones(max(cu_M["src"].max(), cu_M["dst"].max()) + 1, dtype=np.float32) - ) - weights = cudf.DataFrame() - weights["vertex"] = np.arange(len(weights_arr), dtype=np.int32) - weights["weight"] = weights_arr - - G = graph_file.get_graph(create_using=cugraph.Graph(directed=True)) - - # cugraph Overlap Call - df = benchmark_callable(cugraph.overlap_w, G, weights, pairs) - - df = df.sort_values(by=["first", "second"]) - return df["overlap_coeff"].to_numpy() - - -def intersection(a, b, M): - count = 0 - a_idx = M.indptr[a] - b_idx = M.indptr[b] - - while (a_idx < M.indptr[a + 1]) and (b_idx < M.indptr[b + 1]): - a_vertex = M.indices[a_idx] - b_vertex = M.indices[b_idx] - - if a_vertex == b_vertex: - count += 1 - a_idx += 1 - b_idx += 1 - elif a_vertex < b_vertex: - a_idx += 1 - else: - b_idx += 1 - - return count - - -def degree(a, M): - return M.indptr[a + 1] - M.indptr[a] - - -def overlap(a, b, M): - b_sum = degree(b, M) - if b_sum == 0: - return float("NaN") - - i = intersection(a, b, M) - a_sum = degree(a, M) - total = min(a_sum, b_sum) - return i / total - - -def cpu_call(M, first, second): - result = [] - for i in range(len(first)): - result.append(overlap(first[i], second[i], M)) - return result - - -@pytest.mark.sg -@pytest.mark.parametrize("graph_file", UNDIRECTED_DATASETS) -def test_woverlap(gpubenchmark, graph_file): - dataset_path = graph_file.get_path() - Mnx = utils.read_csv_for_nx(dataset_path) - N = max(max(Mnx["0"]), max(Mnx["1"])) + 1 - M = scipy.sparse.csr_matrix((Mnx.weight, (Mnx["0"], Mnx["1"])), shape=(N, N)) - - G = graph_file.get_graph(ignore_weights=True) - pairs = ( - G.get_two_hop_neighbors() - .sort_values(["first", "second"]) - .reset_index(drop=True) - ) - - cu_coeff = cugraph_call(gpubenchmark, graph_file, pairs) - cpu_coeff = cpu_call(M, pairs["first"], pairs["second"]) - assert len(cu_coeff) == len(cpu_coeff) - for i in range(len(cu_coeff)): - if np.isnan(cpu_coeff[i]): - assert np.isnan(cu_coeff[i]) - elif np.isnan(cu_coeff[i]): - assert cpu_coeff[i] == cu_coeff[i] - else: - diff = abs(cpu_coeff[i] - cu_coeff[i]) - assert diff < 1.0e-6 - - -@pytest.mark.sg -@pytest.mark.parametrize("graph_file", UNDIRECTED_DATASETS) -def test_woverlap_multi_column(graph_file): - dataset_path = graph_file.get_path() - M = utils.read_csv_for_nx(dataset_path) - - cu_M = cudf.DataFrame() - cu_M["src_0"] = cudf.Series(M["0"]) - cu_M["dst_0"] = cudf.Series(M["1"]) - cu_M["src_1"] = cu_M["src_0"] + 1000 - cu_M["dst_1"] = cu_M["dst_0"] + 1000 - G1 = cugraph.Graph() - G1.from_cudf_edgelist( - cu_M, source=["src_0", "src_1"], destination=["dst_0", "dst_1"] - ) - - G2 = cugraph.Graph() - G2.from_cudf_edgelist(cu_M, source="src_0", destination="dst_0") - - vertex_pair = cu_M[["src_0", "src_1", "dst_0", "dst_1"]] - vertex_pair = vertex_pair[:5] - - weight_arr = cudf.Series(np.ones(G2.number_of_vertices(), dtype=np.float32)) - - weights = cudf.DataFrame() - weights["vertex"] = G2.nodes() - weights["vertex_"] = weights["vertex"] + 1000 - weights["weight"] = weight_arr - - df_res = cugraph.overlap_w(G1, weights, vertex_pair) - - weights = weights[["vertex", "weight"]] - df_exp = cugraph.overlap_w(G2, weights, vertex_pair[["src_0", "dst_0"]]) - - # Calculating mismatch - actual = df_res.sort_values("0_first").reset_index() - expected = df_exp.sort_values("first").reset_index() - assert_series_equal(actual["overlap_coeff"], expected["overlap_coeff"]) - - -@pytest.mark.sg -def test_invalid_datasets_overlap_w(): - karate = UNDIRECTED_DATASETS[0] - df = karate.get_edgelist() - df = df.add(1) - G = cugraph.Graph(directed=False) - G.from_cudf_edgelist(df, source="src", destination="dst") - with pytest.raises(ValueError): - cugraph.overlap_w(G, None) diff --git a/python/cugraph/cugraph/tests/link_prediction/test_wsorensen.py b/python/cugraph/cugraph/tests/link_prediction/test_wsorensen.py deleted file mode 100644 index 8d09b3e25b3..00000000000 --- a/python/cugraph/cugraph/tests/link_prediction/test_wsorensen.py +++ /dev/null @@ -1,181 +0,0 @@ -# Copyright (c) 2021-2023, NVIDIA CORPORATION. -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import gc - -import pytest -import numpy as np -import networkx as nx - -import cudf -import cugraph -from cudf.testing import assert_series_equal -from cugraph.testing import utils, UNDIRECTED_DATASETS - - -print("Networkx version : {} ".format(nx.__version__)) - - -# ============================================================================= -# Pytest Setup / Teardown - called for each test function -# ============================================================================= -def setup_function(): - gc.collect() - - -def cugraph_call(benchmark_callable, graph_file): - # Device data - cu_M = graph_file.get_edgelist() - weight_arr = cudf.Series( - np.ones(max(cu_M["src"].max(), cu_M["dst"].max()) + 1, dtype=np.float32) - ) - weights = cudf.DataFrame() - weights["vertex"] = np.arange(len(weight_arr), dtype=np.int32) - weights["weight"] = weight_arr - - G = graph_file.get_graph(ignore_weights=True) - - # cugraph Sorensen Call - df = benchmark_callable(cugraph.sorensen_w, G, weights) - - df = df.sort_values(["first", "second"]).reset_index(drop=True) - - return df["sorensen_coeff"] - - -def networkx_call(M, benchmark_callable=None): - - sources = M["0"] - destinations = M["1"] - edges = [] - for i in range(len(sources)): - edges.append((sources[i], destinations[i])) - edges.append((destinations[i], sources[i])) - edges = list(dict.fromkeys(edges)) - edges = sorted(edges) - # in NVGRAPH tests we read as CSR and feed as CSC, so here we doing this - # explicitly - print("Format conversion ... ") - - # NetworkX graph - Gnx = nx.from_pandas_edgelist(M, source="0", target="1", create_using=nx.Graph()) - # Networkx Jaccard Call - print("Solving... ") - if benchmark_callable is not None: - preds = benchmark_callable(nx.jaccard_coefficient, Gnx, edges) - else: - preds = nx.jaccard_coefficient(Gnx, edges) - coeff = [] - for u, v, p in preds: - # FIXME: Use known correct values of WSorensen for few graphs, - # hardcode it and compare to Cugraph WSorensen - # to get a more robust test - - # Conversion from Networkx Jaccard to Sorensen - coeff.append((2 * p) / (1 + p)) - return coeff - - -# ============================================================================= -# Pytest Fixtures -# ============================================================================= -@pytest.fixture(scope="module", params=UNDIRECTED_DATASETS) -def read_csv(request): - """ - Read csv file for both networkx and cugraph - """ - graph_file = request.param - dataset_path = graph_file.get_path() - M = utils.read_csv_for_nx(dataset_path) - - return M, graph_file - - -@pytest.mark.sg -def test_wsorensen(gpubenchmark, read_csv): - - M, graph_file = read_csv - - cu_coeff = cugraph_call(gpubenchmark, graph_file) - nx_coeff = networkx_call(M) - for i in range(len(cu_coeff)): - diff = abs(nx_coeff[i] - cu_coeff[i]) - assert diff < 1.0e-6 - - -@pytest.mark.sg -def test_nx_wsorensen_time(gpubenchmark, read_csv): - - M, _ = read_csv - networkx_call(M, gpubenchmark) - - -@pytest.mark.sg -def test_wsorensen_multi_column_weights(gpubenchmark, read_csv): - - M, cu_M = read_csv - - cu_coeff = cugraph_call(gpubenchmark, cu_M) - nx_coeff = networkx_call(M) - for i in range(len(cu_coeff)): - diff = abs(nx_coeff[i] - cu_coeff[i]) - assert diff < 1.0e-6 - - -@pytest.mark.sg -def test_wsorensen_multi_column(read_csv): - - M, _ = read_csv - - cu_M = cudf.DataFrame() - cu_M["src_0"] = cudf.Series(M["0"]) - cu_M["dst_0"] = cudf.Series(M["1"]) - cu_M["src_1"] = cu_M["src_0"] + 1000 - cu_M["dst_1"] = cu_M["dst_0"] + 1000 - G1 = cugraph.Graph() - G1.from_cudf_edgelist( - cu_M, source=["src_0", "src_1"], destination=["dst_0", "dst_1"] - ) - - G2 = cugraph.Graph() - G2.from_cudf_edgelist(cu_M, source="src_0", destination="dst_0") - - vertex_pair = cu_M[["src_0", "src_1", "dst_0", "dst_1"]] - vertex_pair = vertex_pair[:5] - - weight_arr = cudf.Series(np.ones(G2.number_of_vertices(), dtype=np.float32)) - weights = cudf.DataFrame() - weights["vertex"] = G2.nodes() - weights["vertex_"] = weights["vertex"] + 1000 - weights["weight"] = weight_arr - - df_res = cugraph.sorensen_w(G1, weights, vertex_pair) - - weights = weights[["vertex", "weight"]] - df_exp = cugraph.sorensen_w(G2, weights, vertex_pair[["src_0", "dst_0"]]) - - # Calculating mismatch - actual = df_res.sort_values("0_first").reset_index() - expected = df_exp.sort_values("first").reset_index() - assert_series_equal(actual["sorensen_coeff"], expected["sorensen_coeff"]) - - -@pytest.mark.sg -def test_invalid_datasets_sorensen_w(): - karate = UNDIRECTED_DATASETS[0] - df = karate.get_edgelist() - df = df.add(1) - G = cugraph.Graph(directed=False) - G.from_cudf_edgelist(df, source="src", destination="dst") - with pytest.raises(ValueError): - cugraph.sorensen_w(G, None) diff --git a/python/pylibcugraph/pylibcugraph/__init__.py b/python/pylibcugraph/pylibcugraph/__init__.py index 711652bbae6..45f6de2f663 100644 --- a/python/pylibcugraph/pylibcugraph/__init__.py +++ b/python/pylibcugraph/pylibcugraph/__init__.py @@ -87,6 +87,13 @@ from pylibcugraph.generate_rmat_edgelists import generate_rmat_edgelists +from pylibcugraph.jaccard_coefficients import jaccard_coefficients + +from pylibcugraph.overlap_coefficients import overlap_coefficients + +from pylibcugraph.sorensen_coefficients import sorensen_coefficients + + from pylibcugraph import exceptions __version__ = "23.10.00" diff --git a/python/pylibcugraph/pylibcugraph/experimental/__init__.py b/python/pylibcugraph/pylibcugraph/experimental/__init__.py index 1b93f9322af..6194ace5956 100644 --- a/python/pylibcugraph/pylibcugraph/experimental/__init__.py +++ b/python/pylibcugraph/pylibcugraph/experimental/__init__.py @@ -1,4 +1,4 @@ -# Copyright (c) 2022, NVIDIA CORPORATION. +# Copyright (c) 2022-2023, NVIDIA CORPORATION. # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at @@ -74,18 +74,17 @@ from pylibcugraph.node2vec import node2vec -node2vec = promoted_experimental_warning_wrapper(node2vec) -from pylibcugraph.jaccard_coefficients import EXPERIMENTAL__jaccard_coefficients +# from pylibcugraph.jaccard_coefficients import EXPERIMENTAL__jaccard_coefficients -jaccard_coefficients = experimental_warning_wrapper(EXPERIMENTAL__jaccard_coefficients) +# jaccard_coefficients = experimental_warning_wrapper(EXPERIMENTAL__jaccard_coefficients) -from pylibcugraph.overlap_coefficients import EXPERIMENTAL__overlap_coefficients +# from pylibcugraph.overlap_coefficients import EXPERIMENTAL__overlap_coefficients -overlap_coefficients = experimental_warning_wrapper(EXPERIMENTAL__overlap_coefficients) +# overlap_coefficients = experimental_warning_wrapper(EXPERIMENTAL__overlap_coefficients) -from pylibcugraph.sorensen_coefficients import EXPERIMENTAL__sorensen_coefficients +# from pylibcugraph.sorensen_coefficients import EXPERIMENTAL__sorensen_coefficients -sorensen_coefficients = experimental_warning_wrapper( - EXPERIMENTAL__sorensen_coefficients -) +# sorensen_coefficients = experimental_warning_wrapper( +# EXPERIMENTAL__sorensen_coefficients +# ) diff --git a/python/pylibcugraph/pylibcugraph/jaccard_coefficients.pyx b/python/pylibcugraph/pylibcugraph/jaccard_coefficients.pyx index 805ee821eab..59e94aeb615 100644 --- a/python/pylibcugraph/pylibcugraph/jaccard_coefficients.pyx +++ b/python/pylibcugraph/pylibcugraph/jaccard_coefficients.pyx @@ -1,4 +1,4 @@ -# Copyright (c) 2022, NVIDIA CORPORATION. +# Copyright (c) 2022-2023, NVIDIA CORPORATION. # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at @@ -15,6 +15,8 @@ # cython: language_level = 3 from libc.stdint cimport uintptr_t +from libc.stdio cimport printf +from cython.operator cimport dereference from pylibcugraph._cugraph_c.resource_handle cimport ( bool_t, @@ -57,7 +59,7 @@ from pylibcugraph.utils cimport ( ) -def EXPERIMENTAL__jaccard_coefficients(ResourceHandle resource_handle, +def jaccard_coefficients(ResourceHandle resource_handle, _GPUGraph graph, first, second, @@ -83,8 +85,10 @@ def EXPERIMENTAL__jaccard_coefficients(ResourceHandle resource_handle, second : Destination of the vertex pair. - use_weight : bool, optional (default=False) - Currently not supported + use_weight : bool, optional + If set to True, the compute weighted jaccard_coefficients( + the input graph must be weighted in that case). + Otherwise, computed un-weighted jaccard_coefficients do_expensive_check : bool If True, performs more extensive tests on the inputs to ensure diff --git a/python/pylibcugraph/pylibcugraph/overlap_coefficients.pyx b/python/pylibcugraph/pylibcugraph/overlap_coefficients.pyx index 6af71116469..28360121c64 100644 --- a/python/pylibcugraph/pylibcugraph/overlap_coefficients.pyx +++ b/python/pylibcugraph/pylibcugraph/overlap_coefficients.pyx @@ -1,4 +1,4 @@ -# Copyright (c) 2022, NVIDIA CORPORATION. +# Copyright (c) 2022-2023, NVIDIA CORPORATION. # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at @@ -57,7 +57,7 @@ from pylibcugraph.utils cimport ( ) -def EXPERIMENTAL__overlap_coefficients(ResourceHandle resource_handle, +def overlap_coefficients(ResourceHandle resource_handle, _GPUGraph graph, first, second, @@ -84,8 +84,10 @@ def EXPERIMENTAL__overlap_coefficients(ResourceHandle resource_handle, second : Destination of the vertex pair. - use_weight : bool, optional (default=False) - Currently not supported + use_weight : bool, optional + If set to True, the compute weighted jaccard_coefficients( + the input graph must be weighted in that case). + Otherwise, computed un-weighted jaccard_coefficients do_expensive_check : bool If True, performs more extensive tests on the inputs to ensure diff --git a/python/pylibcugraph/pylibcugraph/sorensen_coefficients.pyx b/python/pylibcugraph/pylibcugraph/sorensen_coefficients.pyx index 12647baccb2..983a635012f 100644 --- a/python/pylibcugraph/pylibcugraph/sorensen_coefficients.pyx +++ b/python/pylibcugraph/pylibcugraph/sorensen_coefficients.pyx @@ -1,4 +1,4 @@ -# Copyright (c) 2022, NVIDIA CORPORATION. +# Copyright (c) 2022-2023, NVIDIA CORPORATION. # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at @@ -57,7 +57,7 @@ from pylibcugraph.utils cimport ( ) -def EXPERIMENTAL__sorensen_coefficients(ResourceHandle resource_handle, +def sorensen_coefficients(ResourceHandle resource_handle, _GPUGraph graph, first, second, @@ -83,8 +83,10 @@ def EXPERIMENTAL__sorensen_coefficients(ResourceHandle resource_handle, second : Destination of the vertex pair. - use_weight : bool, optional (default=False) - Currently not supported + use_weight : bool, optional + If set to True, the compute weighted jaccard_coefficients( + the input graph must be weighted in that case). + Otherwise, computed un-weighted jaccard_coefficients do_expensive_check : bool If True, performs more extensive tests on the inputs to ensure From db5073da6c69ac3ee44d7130d8799177ec69a0ef Mon Sep 17 00:00:00 2001 From: Jake Awe <50372925+AyodeAwe@users.noreply.github.com> Date: Thu, 21 Sep 2023 13:52:57 -0500 Subject: [PATCH 006/111] Update image names (#3867) PR updates `rapidsai/ci` references to `rapidsai/ci-conda` Authors: - Jake Awe (https://github.com/AyodeAwe) Approvers: - AJ Schmidt (https://github.com/ajschmidt8) URL: https://github.com/rapidsai/cugraph/pull/3867 --- .github/workflows/build.yaml | 2 +- .github/workflows/pr.yaml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 2d0d58315a0..02b357c7c88 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -62,7 +62,7 @@ jobs: arch: "amd64" branch: ${{ inputs.branch }} build_type: ${{ inputs.build_type || 'branch' }} - container_image: "rapidsai/ci:cuda11.8.0-ubuntu22.04-py3.10" + container_image: "rapidsai/ci-conda:cuda11.8.0-ubuntu22.04-py3.10" date: ${{ inputs.date }} node_type: "gpu-v100-latest-1" run_script: "ci/build_docs.sh" diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml index 005fe4a0267..d2d24d90fbe 100644 --- a/.github/workflows/pr.yaml +++ b/.github/workflows/pr.yaml @@ -63,7 +63,7 @@ jobs: build_type: pull-request node_type: "gpu-v100-latest-1" arch: "amd64" - container_image: "rapidsai/ci:cuda11.8.0-ubuntu22.04-py3.10" + container_image: "rapidsai/ci-conda:cuda11.8.0-ubuntu22.04-py3.10" run_script: "ci/test_notebooks.sh" docs-build: needs: conda-python-build @@ -73,7 +73,7 @@ jobs: build_type: pull-request node_type: "gpu-v100-latest-1" arch: "amd64" - container_image: "rapidsai/ci:cuda11.8.0-ubuntu22.04-py3.10" + container_image: "rapidsai/ci-conda:cuda11.8.0-ubuntu22.04-py3.10" run_script: "ci/build_docs.sh" wheel-build-pylibcugraph: needs: checks From a7047e3f0049597b4da625138107830ff78405e5 Mon Sep 17 00:00:00 2001 From: Don Acosta <97529984+acostadon@users.noreply.github.com> Date: Thu, 21 Sep 2023 17:19:30 -0400 Subject: [PATCH 007/111] adding dining preference dataset (#3866) This dataset is very small, and uses strings as node names. It will be used to test force atlas, and in a new link prediction/similarity notebook. the licensing is contained here. http://networkdata.ics.uci.edu/netdata/html/Dining-table_partners.html Authors: - Don Acosta (https://github.com/acostadon) - Brad Rees (https://github.com/BradReesWork) Approvers: - Brad Rees (https://github.com/BradReesWork) - ralph (https://github.com/nv-rliu) URL: https://github.com/rapidsai/cugraph/pull/3866 --- python/cugraph/cugraph/datasets/__init__.py | 1 + .../datasets/metadata/dining_prefs.yaml | 23 +++++++++++++++++++ python/cugraph/cugraph/testing/__init__.py | 3 +++ 3 files changed, 27 insertions(+) create mode 100644 python/cugraph/cugraph/datasets/metadata/dining_prefs.yaml diff --git a/python/cugraph/cugraph/datasets/__init__.py b/python/cugraph/cugraph/datasets/__init__.py index 7ba274c5960..65a820f108b 100644 --- a/python/cugraph/cugraph/datasets/__init__.py +++ b/python/cugraph/cugraph/datasets/__init__.py @@ -27,6 +27,7 @@ meta_path = Path(__file__).parent / "metadata" cyber = Dataset(meta_path / "cyber.yaml") +dining_prefs = Dataset(meta_path / "dining_prefs.yaml") dolphins = Dataset(meta_path / "dolphins.yaml") email_Eu_core = Dataset(meta_path / "email_Eu_core.yaml") karate = Dataset(meta_path / "karate.yaml") diff --git a/python/cugraph/cugraph/datasets/metadata/dining_prefs.yaml b/python/cugraph/cugraph/datasets/metadata/dining_prefs.yaml new file mode 100644 index 00000000000..e7ec85d7a1f --- /dev/null +++ b/python/cugraph/cugraph/datasets/metadata/dining_prefs.yaml @@ -0,0 +1,23 @@ +name: dining_prefs +file_type: .csv +description: Classic social networking dataset describes dining preferences for a dormitory in New York state. +author: J.L. Moreno +refs: + J. L. Moreno (1960). The Sociometry Reader. The Free Press, Glencoe, Illinois, pg.35 +delim: " " +header: None +col_names: + - src + - dst + - wgt +col_types: + - string + - string + - int +has_loop: false +is_directed: false +is_multigraph: false +is_symmetric: true +number_of_edges: 42 +number_of_nodes: 26 +url: https://data.rapids.ai/cugraph/datasets/dining_prefs.csv \ No newline at end of file diff --git a/python/cugraph/cugraph/testing/__init__.py b/python/cugraph/cugraph/testing/__init__.py index bde398aadbd..f5f0bcb06eb 100644 --- a/python/cugraph/cugraph/testing/__init__.py +++ b/python/cugraph/cugraph/testing/__init__.py @@ -23,6 +23,7 @@ ) from cugraph.datasets import ( cyber, + dining_prefs, dolphins, karate, karate_disjoint, @@ -42,6 +43,7 @@ UNDIRECTED_DATASETS = [karate, dolphins] SMALL_DATASETS = [karate, dolphins, polbooks] WEIGHTED_DATASETS = [ + dining_prefs, dolphins, karate, karate_disjoint, @@ -51,6 +53,7 @@ small_tree, ] ALL_DATASETS = [ + dining_prefs, dolphins, karate, karate_disjoint, From 367f36cfd4719fb522f12dbb74cec5b8a1e61aa6 Mon Sep 17 00:00:00 2001 From: Ray Douglass <3107146+raydouglass@users.noreply.github.com> Date: Fri, 22 Sep 2023 10:55:52 -0400 Subject: [PATCH 008/111] Add file to update-version.sh [skip ci] (#3870) Add a new file to `update-version.sh`. Tested locally Authors: - Ray Douglass (https://github.com/raydouglass) Approvers: - AJ Schmidt (https://github.com/ajschmidt8) - Jake Awe (https://github.com/AyodeAwe) --- ci/release/update-version.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ci/release/update-version.sh b/ci/release/update-version.sh index 2c8735079f0..bd3aa6bc370 100755 --- a/ci/release/update-version.sh +++ b/ci/release/update-version.sh @@ -126,3 +126,5 @@ for FILE in .github/workflows/*.yaml; do sed_runner "s/dask-cuda.git@branch-[0-9][0-9].[0-9][0-9]/dask-cuda.git@branch-${NEXT_SHORT_TAG}/g" "${FILE}" done sed_runner "s/RAPIDS_VERSION_NUMBER=\".*/RAPIDS_VERSION_NUMBER=\"${NEXT_SHORT_TAG}\"/g" ci/build_docs.sh + +sed_runner "s/branch-.*/branch-${NEXT_SHORT_TAG}/g" python/nx-cugraph/README.md From 81e27decfa736203e966be185105f2353d86a7d1 Mon Sep 17 00:00:00 2001 From: Ray Douglass Date: Fri, 22 Sep 2023 10:58:08 -0400 Subject: [PATCH 009/111] v23.12 Updates [skip ci] --- .github/workflows/build.yaml | 20 ++++----- .github/workflows/pr.yaml | 28 ++++++------- .github/workflows/test.yaml | 8 ++-- ci/build_docs.sh | 2 +- .../all_cuda-118_arch-x86_64.yaml | 26 ++++++------ .../all_cuda-120_arch-x86_64.yaml | 26 ++++++------ .../cugraph-service/conda_build_config.yaml | 2 +- conda/recipes/cugraph/conda_build_config.yaml | 2 +- .../pylibcugraph/conda_build_config.yaml | 2 +- cpp/CMakeLists.txt | 2 +- cpp/doxygen/Doxyfile | 2 +- cpp/libcugraph_etl/CMakeLists.txt | 2 +- dependencies.yaml | 42 +++++++++---------- docs/cugraph/source/conf.py | 4 +- fetch_rapids.cmake | 2 +- .../conda/cugraph_dgl_dev_cuda-118.yaml | 4 +- python/cugraph-dgl/cugraph_dgl/__init__.py | 2 +- python/cugraph-dgl/pyproject.toml | 4 +- .../conda/cugraph_pyg_dev_cuda-118.yaml | 4 +- python/cugraph-pyg/cugraph_pyg/__init__.py | 2 +- python/cugraph-pyg/pyproject.toml | 4 +- .../client/cugraph_service_client/__init__.py | 2 +- python/cugraph-service/client/pyproject.toml | 2 +- .../server/cugraph_service_server/__init__.py | 2 +- python/cugraph-service/server/pyproject.toml | 16 +++---- python/cugraph/CMakeLists.txt | 2 +- python/cugraph/cugraph/__init__.py | 2 +- python/cugraph/pyproject.toml | 22 +++++----- python/nx-cugraph/README.md | 2 +- python/nx-cugraph/nx_cugraph/__init__.py | 2 +- python/nx-cugraph/pyproject.toml | 4 +- python/pylibcugraph/CMakeLists.txt | 2 +- python/pylibcugraph/pylibcugraph/__init__.py | 2 +- python/pylibcugraph/pyproject.toml | 12 +++--- 34 files changed, 131 insertions(+), 131 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 02b357c7c88..6ad499a8ce7 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -28,7 +28,7 @@ concurrency: jobs: cpp-build: secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/conda-cpp-build.yaml@branch-23.10 + uses: rapidsai/shared-action-workflows/.github/workflows/conda-cpp-build.yaml@branch-23.12 with: build_type: ${{ inputs.build_type || 'branch' }} branch: ${{ inputs.branch }} @@ -38,7 +38,7 @@ jobs: python-build: needs: [cpp-build] secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/conda-python-build.yaml@branch-23.10 + uses: rapidsai/shared-action-workflows/.github/workflows/conda-python-build.yaml@branch-23.12 with: build_type: ${{ inputs.build_type || 'branch' }} branch: ${{ inputs.branch }} @@ -47,7 +47,7 @@ jobs: upload-conda: needs: [cpp-build, python-build] secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/conda-upload-packages.yaml@branch-23.10 + uses: rapidsai/shared-action-workflows/.github/workflows/conda-upload-packages.yaml@branch-23.12 with: build_type: ${{ inputs.build_type || 'branch' }} branch: ${{ inputs.branch }} @@ -57,7 +57,7 @@ jobs: if: github.ref_type == 'branch' needs: python-build secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/custom-job.yaml@branch-23.10 + uses: rapidsai/shared-action-workflows/.github/workflows/custom-job.yaml@branch-23.12 with: arch: "amd64" branch: ${{ inputs.branch }} @@ -69,7 +69,7 @@ jobs: sha: ${{ inputs.sha }} wheel-build-pylibcugraph: secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/wheels-build.yaml@branch-23.10 + uses: rapidsai/shared-action-workflows/.github/workflows/wheels-build.yaml@branch-23.12 with: build_type: ${{ inputs.build_type || 'branch' }} branch: ${{ inputs.branch }} @@ -77,12 +77,12 @@ jobs: date: ${{ inputs.date }} script: ci/build_wheel_pylibcugraph.sh extra-repo: rapidsai/cugraph-ops - extra-repo-sha: branch-23.10 + extra-repo-sha: branch-23.12 extra-repo-deploy-key: CUGRAPH_OPS_SSH_PRIVATE_DEPLOY_KEY wheel-publish-pylibcugraph: needs: wheel-build-pylibcugraph secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/wheels-publish.yaml@branch-23.10 + uses: rapidsai/shared-action-workflows/.github/workflows/wheels-publish.yaml@branch-23.12 with: build_type: ${{ inputs.build_type || 'branch' }} branch: ${{ inputs.branch }} @@ -92,7 +92,7 @@ jobs: wheel-build-cugraph: needs: wheel-publish-pylibcugraph secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/wheels-build.yaml@branch-23.10 + uses: rapidsai/shared-action-workflows/.github/workflows/wheels-build.yaml@branch-23.12 with: build_type: ${{ inputs.build_type || 'branch' }} branch: ${{ inputs.branch }} @@ -100,12 +100,12 @@ jobs: date: ${{ inputs.date }} script: ci/build_wheel_cugraph.sh extra-repo: rapidsai/cugraph-ops - extra-repo-sha: branch-23.10 + extra-repo-sha: branch-23.12 extra-repo-deploy-key: CUGRAPH_OPS_SSH_PRIVATE_DEPLOY_KEY wheel-publish-cugraph: needs: wheel-build-cugraph secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/wheels-publish.yaml@branch-23.10 + uses: rapidsai/shared-action-workflows/.github/workflows/wheels-publish.yaml@branch-23.12 with: build_type: ${{ inputs.build_type || 'branch' }} branch: ${{ inputs.branch }} diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml index d2d24d90fbe..71ae82a4e8f 100644 --- a/.github/workflows/pr.yaml +++ b/.github/workflows/pr.yaml @@ -24,41 +24,41 @@ jobs: - wheel-build-cugraph - wheel-tests-cugraph secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/pr-builder.yaml@branch-23.10 + uses: rapidsai/shared-action-workflows/.github/workflows/pr-builder.yaml@branch-23.12 checks: secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/checks.yaml@branch-23.10 + uses: rapidsai/shared-action-workflows/.github/workflows/checks.yaml@branch-23.12 with: enable_check_generated_files: false conda-cpp-build: needs: checks secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/conda-cpp-build.yaml@branch-23.10 + uses: rapidsai/shared-action-workflows/.github/workflows/conda-cpp-build.yaml@branch-23.12 with: build_type: pull-request node_type: cpu32 conda-cpp-tests: needs: conda-cpp-build secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/conda-cpp-tests.yaml@branch-23.10 + uses: rapidsai/shared-action-workflows/.github/workflows/conda-cpp-tests.yaml@branch-23.12 with: build_type: pull-request conda-python-build: needs: conda-cpp-build secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/conda-python-build.yaml@branch-23.10 + uses: rapidsai/shared-action-workflows/.github/workflows/conda-python-build.yaml@branch-23.12 with: build_type: pull-request conda-python-tests: needs: conda-python-build secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/conda-python-tests.yaml@branch-23.10 + uses: rapidsai/shared-action-workflows/.github/workflows/conda-python-tests.yaml@branch-23.12 with: build_type: pull-request conda-notebook-tests: needs: conda-python-build secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/custom-job.yaml@branch-23.10 + uses: rapidsai/shared-action-workflows/.github/workflows/custom-job.yaml@branch-23.12 with: build_type: pull-request node_type: "gpu-v100-latest-1" @@ -68,7 +68,7 @@ jobs: docs-build: needs: conda-python-build secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/custom-job.yaml@branch-23.10 + uses: rapidsai/shared-action-workflows/.github/workflows/custom-job.yaml@branch-23.12 with: build_type: pull-request node_type: "gpu-v100-latest-1" @@ -78,34 +78,34 @@ jobs: wheel-build-pylibcugraph: needs: checks secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/wheels-build.yaml@branch-23.10 + uses: rapidsai/shared-action-workflows/.github/workflows/wheels-build.yaml@branch-23.12 with: build_type: pull-request script: ci/build_wheel_pylibcugraph.sh extra-repo: rapidsai/cugraph-ops - extra-repo-sha: branch-23.10 + extra-repo-sha: branch-23.12 extra-repo-deploy-key: CUGRAPH_OPS_SSH_PRIVATE_DEPLOY_KEY wheel-tests-pylibcugraph: needs: wheel-build-pylibcugraph secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/wheels-test.yaml@branch-23.10 + uses: rapidsai/shared-action-workflows/.github/workflows/wheels-test.yaml@branch-23.12 with: build_type: pull-request script: ci/test_wheel_pylibcugraph.sh wheel-build-cugraph: needs: wheel-tests-pylibcugraph secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/wheels-build.yaml@branch-23.10 + uses: rapidsai/shared-action-workflows/.github/workflows/wheels-build.yaml@branch-23.12 with: build_type: pull-request script: ci/build_wheel_cugraph.sh extra-repo: rapidsai/cugraph-ops - extra-repo-sha: branch-23.10 + extra-repo-sha: branch-23.12 extra-repo-deploy-key: CUGRAPH_OPS_SSH_PRIVATE_DEPLOY_KEY wheel-tests-cugraph: needs: wheel-build-cugraph secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/wheels-test.yaml@branch-23.10 + uses: rapidsai/shared-action-workflows/.github/workflows/wheels-test.yaml@branch-23.12 with: build_type: pull-request script: ci/test_wheel_cugraph.sh diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 97abca71260..fcfd580aa3f 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -16,7 +16,7 @@ on: jobs: conda-cpp-tests: secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/conda-cpp-tests.yaml@branch-23.10 + uses: rapidsai/shared-action-workflows/.github/workflows/conda-cpp-tests.yaml@branch-23.12 with: build_type: nightly branch: ${{ inputs.branch }} @@ -24,7 +24,7 @@ jobs: sha: ${{ inputs.sha }} conda-python-tests: secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/conda-python-tests.yaml@branch-23.10 + uses: rapidsai/shared-action-workflows/.github/workflows/conda-python-tests.yaml@branch-23.12 with: build_type: nightly branch: ${{ inputs.branch }} @@ -32,7 +32,7 @@ jobs: sha: ${{ inputs.sha }} wheel-tests-pylibcugraph: secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/wheels-test.yaml@branch-23.10 + uses: rapidsai/shared-action-workflows/.github/workflows/wheels-test.yaml@branch-23.12 with: build_type: nightly branch: ${{ inputs.branch }} @@ -41,7 +41,7 @@ jobs: script: ci/test_wheel_pylibcugraph.sh wheel-tests-cugraph: secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/wheels-test.yaml@branch-23.10 + uses: rapidsai/shared-action-workflows/.github/workflows/wheels-test.yaml@branch-23.12 with: build_type: nightly branch: ${{ inputs.branch }} diff --git a/ci/build_docs.sh b/ci/build_docs.sh index 2941d062d80..7b4cf152b4a 100755 --- a/ci/build_docs.sh +++ b/ci/build_docs.sh @@ -37,7 +37,7 @@ rapids-mamba-retry install \ rapids-logger "Install cugraph-dgl" rapids-mamba-retry install "${PYTHON_CHANNEL}/linux-64/cugraph-dgl-*.tar.bz2" -export RAPIDS_VERSION_NUMBER="23.10" +export RAPIDS_VERSION_NUMBER="23.12" export RAPIDS_DOCS_DIR="$(mktemp -d)" rapids-logger "Build CPP docs" diff --git a/conda/environments/all_cuda-118_arch-x86_64.yaml b/conda/environments/all_cuda-118_arch-x86_64.yaml index c66890f8ae5..4abac5b2de5 100644 --- a/conda/environments/all_cuda-118_arch-x86_64.yaml +++ b/conda/environments/all_cuda-118_arch-x86_64.yaml @@ -15,13 +15,13 @@ dependencies: - cmake>=3.26.4 - cuda-version=11.8 - cudatoolkit -- cudf==23.10.* +- cudf==23.12.* - cupy>=12.0.0 - cxx-compiler - cython>=3.0.0 - dask-core>=2023.7.1 -- dask-cuda==23.10.* -- dask-cudf==23.10.* +- dask-cuda==23.12.* +- dask-cudf==23.12.* - dask>=2023.7.1 - distributed>=2023.7.1 - doxygen @@ -31,11 +31,11 @@ dependencies: - graphviz - gtest>=1.13.0 - ipython -- libcudf==23.10.* -- libcugraphops==23.10.* -- libraft-headers==23.10.* -- libraft==23.10.* -- librmm==23.10.* +- libcudf==23.12.* +- libcugraphops==23.12.* +- libraft-headers==23.12.* +- libraft==23.12.* +- librmm==23.12.* - nbsphinx - nccl>=2.9.9 - networkx>=2.5.1 @@ -51,18 +51,18 @@ dependencies: - pandas - pre-commit - pydata-sphinx-theme -- pylibcugraphops==23.10.* -- pylibraft==23.10.* +- pylibcugraphops==23.12.* +- pylibraft==23.12.* - pytest - pytest-benchmark - pytest-cov - pytest-mpl - pytest-xdist - python-louvain -- raft-dask==23.10.* +- raft-dask==23.12.* - recommonmark - requests -- rmm==23.10.* +- rmm==23.12.* - scikit-build>=0.13.1 - scikit-learn>=0.23.1 - scipy @@ -71,5 +71,5 @@ dependencies: - sphinx<6 - sphinxcontrib-websupport - ucx-proc=*=gpu -- ucx-py==0.34.* +- ucx-py==0.35.* name: all_cuda-118_arch-x86_64 diff --git a/conda/environments/all_cuda-120_arch-x86_64.yaml b/conda/environments/all_cuda-120_arch-x86_64.yaml index 3afb1415572..51ba25598c8 100644 --- a/conda/environments/all_cuda-120_arch-x86_64.yaml +++ b/conda/environments/all_cuda-120_arch-x86_64.yaml @@ -15,13 +15,13 @@ dependencies: - cmake>=3.26.4 - cuda-nvcc - cuda-version=12.0 -- cudf==23.10.* +- cudf==23.12.* - cupy>=12.0.0 - cxx-compiler - cython>=3.0.0 - dask-core>=2023.7.1 -- dask-cuda==23.10.* -- dask-cudf==23.10.* +- dask-cuda==23.12.* +- dask-cudf==23.12.* - dask>=2023.7.1 - distributed>=2023.7.1 - doxygen @@ -31,11 +31,11 @@ dependencies: - graphviz - gtest>=1.13.0 - ipython -- libcudf==23.10.* -- libcugraphops==23.10.* -- libraft-headers==23.10.* -- libraft==23.10.* -- librmm==23.10.* +- libcudf==23.12.* +- libcugraphops==23.12.* +- libraft-headers==23.12.* +- libraft==23.12.* +- librmm==23.12.* - nbsphinx - nccl>=2.9.9 - networkx>=2.5.1 @@ -50,18 +50,18 @@ dependencies: - pandas - pre-commit - pydata-sphinx-theme -- pylibcugraphops==23.10.* -- pylibraft==23.10.* +- pylibcugraphops==23.12.* +- pylibraft==23.12.* - pytest - pytest-benchmark - pytest-cov - pytest-mpl - pytest-xdist - python-louvain -- raft-dask==23.10.* +- raft-dask==23.12.* - recommonmark - requests -- rmm==23.10.* +- rmm==23.12.* - scikit-build>=0.13.1 - scikit-learn>=0.23.1 - scipy @@ -70,5 +70,5 @@ dependencies: - sphinx<6 - sphinxcontrib-websupport - ucx-proc=*=gpu -- ucx-py==0.34.* +- ucx-py==0.35.* name: all_cuda-120_arch-x86_64 diff --git a/conda/recipes/cugraph-service/conda_build_config.yaml b/conda/recipes/cugraph-service/conda_build_config.yaml index 5fe8d372eba..b971a73fd39 100644 --- a/conda/recipes/cugraph-service/conda_build_config.yaml +++ b/conda/recipes/cugraph-service/conda_build_config.yaml @@ -1,2 +1,2 @@ ucx_py_version: - - "0.34.*" + - "0.35.*" diff --git a/conda/recipes/cugraph/conda_build_config.yaml b/conda/recipes/cugraph/conda_build_config.yaml index ba5b46dda1f..c03d515b9f6 100644 --- a/conda/recipes/cugraph/conda_build_config.yaml +++ b/conda/recipes/cugraph/conda_build_config.yaml @@ -17,4 +17,4 @@ sysroot_version: - "2.17" ucx_py_version: - - "0.34.*" + - "0.35.*" diff --git a/conda/recipes/pylibcugraph/conda_build_config.yaml b/conda/recipes/pylibcugraph/conda_build_config.yaml index ba5b46dda1f..c03d515b9f6 100644 --- a/conda/recipes/pylibcugraph/conda_build_config.yaml +++ b/conda/recipes/pylibcugraph/conda_build_config.yaml @@ -17,4 +17,4 @@ sysroot_version: - "2.17" ucx_py_version: - - "0.34.*" + - "0.35.*" diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index 0d7bd86075d..cb9dca6349f 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -25,7 +25,7 @@ include(rapids-find) rapids_cuda_init_architectures(CUGRAPH) -project(CUGRAPH VERSION 23.10.00 LANGUAGES C CXX CUDA) +project(CUGRAPH VERSION 23.12.00 LANGUAGES C CXX CUDA) if(CMAKE_CUDA_COMPILER_ID STREQUAL "NVIDIA" AND CMAKE_CUDA_COMPILER_VERSION VERSION_LESS 11.0) diff --git a/cpp/doxygen/Doxyfile b/cpp/doxygen/Doxyfile index eb414925388..482ff988098 100644 --- a/cpp/doxygen/Doxyfile +++ b/cpp/doxygen/Doxyfile @@ -38,7 +38,7 @@ PROJECT_NAME = "libcugraph" # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER=23.10 +PROJECT_NUMBER=23.12 # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a diff --git a/cpp/libcugraph_etl/CMakeLists.txt b/cpp/libcugraph_etl/CMakeLists.txt index 18271871087..ac0cb6959e8 100644 --- a/cpp/libcugraph_etl/CMakeLists.txt +++ b/cpp/libcugraph_etl/CMakeLists.txt @@ -25,7 +25,7 @@ include(rapids-find) rapids_cuda_init_architectures(CUGRAPH_ETL) -project(CUGRAPH_ETL VERSION 23.10.00 LANGUAGES C CXX CUDA) +project(CUGRAPH_ETL VERSION 23.12.00 LANGUAGES C CXX CUDA) if(CMAKE_CUDA_COMPILER_ID STREQUAL "NVIDIA" AND CMAKE_CUDA_COMPILER_VERSION VERSION_LESS 11.0) diff --git a/dependencies.yaml b/dependencies.yaml index 04ec1b6e957..514c14dceec 100644 --- a/dependencies.yaml +++ b/dependencies.yaml @@ -268,10 +268,10 @@ dependencies: - cxx-compiler - gmock>=1.13.0 - gtest>=1.13.0 - - libcugraphops==23.10.* - - libraft-headers==23.10.* - - libraft==23.10.* - - librmm==23.10.* + - libcugraphops==23.12.* + - libraft-headers==23.12.* + - libraft==23.12.* + - librmm==23.12.* - openmpi # Required for building cpp-mgtests (multi-GPU tests) specific: - output_types: [conda] @@ -316,7 +316,7 @@ dependencies: - sphinx-markdown-tables - sphinx<6 - sphinxcontrib-websupport - - pylibcugraphops==23.10.* + - pylibcugraphops==23.12.* py_version: specific: - output_types: [conda] @@ -343,34 +343,34 @@ dependencies: - output_types: [conda, pyproject] packages: - cython>=3.0.0 - - &pylibraft pylibraft==23.10.* - - &rmm rmm==23.10.* + - &pylibraft pylibraft==23.12.* + - &rmm rmm==23.12.* - scikit-build>=0.13.1 python_build_cugraph: common: - output_types: [conda, pyproject] packages: - - &pylibcugraph pylibcugraph==23.10.* + - &pylibcugraph pylibcugraph==23.12.* python_run_cugraph: common: - output_types: [conda, pyproject] packages: - - &cudf cudf==23.10.* + - &cudf cudf==23.12.* - &dask dask>=2023.7.1 - &distributed distributed>=2023.7.1 - - &dask_cuda dask-cuda==23.10.* - - &dask_cudf dask-cudf==23.10.* + - &dask_cuda dask-cuda==23.12.* + - &dask_cudf dask-cudf==23.12.* - &numba numba>=0.57 - - raft-dask==23.10.* + - raft-dask==23.12.* - *rmm - - &ucx_py ucx-py==0.34.* + - &ucx_py ucx-py==0.35.* - output_types: conda packages: - aiohttp - &cupy cupy>=12.0.0 - &dask-core dask-core>=2023.7.1 - fsspec>=0.6.0 - - libcudf==23.10.* + - libcudf==23.12.* - requests - nccl>=2.9.9 - ucx-proc=*=gpu @@ -407,7 +407,7 @@ dependencies: - &numpy numpy>=1.21 - output_types: [pyproject] packages: - - &cugraph cugraph==23.10.* + - &cugraph cugraph==23.12.* python_run_cugraph_pyg: common: - output_types: [conda, pyproject] @@ -444,7 +444,7 @@ dependencies: packages: - *cupy_pip - *cugraph - - cugraph-service-client==23.10.* + - cugraph-service-client==23.12.* doc: common: - output_types: [conda] @@ -458,7 +458,7 @@ dependencies: - sphinxcontrib-websupport - sphinx-markdown-tables - sphinx-copybutton - - pylibcugraphops==23.10.* + - pylibcugraphops==23.12.* test_cpp: common: - output_types: conda @@ -505,8 +505,8 @@ dependencies: common: - output_types: [conda] packages: - - cugraph==23.10.* - - pylibcugraphops==23.10.* + - cugraph==23.12.* + - pylibcugraphops==23.12.* - pytorch>=2.0 - pytorch-cuda==11.8 - dgl>=1.1.0.cu* @@ -514,8 +514,8 @@ dependencies: common: - output_types: [conda] packages: - - cugraph==23.10.* - - pylibcugraphops==23.10.* + - cugraph==23.12.* + - pylibcugraphops==23.12.* - pytorch==2.0 - pytorch-cuda==11.8 - pyg=2.3.1=*torch_2.0.0*cu118* diff --git a/docs/cugraph/source/conf.py b/docs/cugraph/source/conf.py index a96f0fc1e82..58ecca27150 100644 --- a/docs/cugraph/source/conf.py +++ b/docs/cugraph/source/conf.py @@ -76,9 +76,9 @@ # built documents. # # The short X.Y version. -version = '23.10' +version = '23.12' # The full version, including alpha/beta/rc tags. -release = '23.10.00' +release = '23.12.00' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/fetch_rapids.cmake b/fetch_rapids.cmake index c32dc74da40..2c1dd855cb5 100644 --- a/fetch_rapids.cmake +++ b/fetch_rapids.cmake @@ -12,7 +12,7 @@ # the License. # ============================================================================= if(NOT EXISTS ${CMAKE_CURRENT_BINARY_DIR}/CUGRAPH_RAPIDS.cmake) - file(DOWNLOAD https://raw.githubusercontent.com/rapidsai/rapids-cmake/branch-23.10/RAPIDS.cmake + file(DOWNLOAD https://raw.githubusercontent.com/rapidsai/rapids-cmake/branch-23.12/RAPIDS.cmake ${CMAKE_CURRENT_BINARY_DIR}/CUGRAPH_RAPIDS.cmake ) endif() diff --git a/python/cugraph-dgl/conda/cugraph_dgl_dev_cuda-118.yaml b/python/cugraph-dgl/conda/cugraph_dgl_dev_cuda-118.yaml index 138d384ebcf..b73ccb0cf9a 100644 --- a/python/cugraph-dgl/conda/cugraph_dgl_dev_cuda-118.yaml +++ b/python/cugraph-dgl/conda/cugraph_dgl_dev_cuda-118.yaml @@ -10,11 +10,11 @@ channels: - conda-forge - nvidia dependencies: -- cugraph==23.10.* +- cugraph==23.12.* - dgl>=1.1.0.cu* - pandas - pre-commit -- pylibcugraphops==23.10.* +- pylibcugraphops==23.12.* - pytest - pytest-benchmark - pytest-cov diff --git a/python/cugraph-dgl/cugraph_dgl/__init__.py b/python/cugraph-dgl/cugraph_dgl/__init__.py index b30cd6c79d9..74be4fdea3f 100644 --- a/python/cugraph-dgl/cugraph_dgl/__init__.py +++ b/python/cugraph-dgl/cugraph_dgl/__init__.py @@ -20,4 +20,4 @@ import cugraph_dgl.dataloading import cugraph_dgl.nn -__version__ = "23.10.00" +__version__ = "23.12.00" diff --git a/python/cugraph-dgl/pyproject.toml b/python/cugraph-dgl/pyproject.toml index 50354184133..fa9e1c5abe5 100644 --- a/python/cugraph-dgl/pyproject.toml +++ b/python/cugraph-dgl/pyproject.toml @@ -10,7 +10,7 @@ build-backend = "setuptools.build_meta" [project] name = "cugraph-dgl" -version = "23.10.00" +version = "23.12.00" description = "cugraph extensions for DGL" readme = { file = "README.md", content-type = "text/markdown" } authors = [ @@ -19,7 +19,7 @@ authors = [ license = { text = "Apache 2.0" } requires-python = ">=3.9" dependencies = [ - "cugraph==23.10.*", + "cugraph==23.12.*", "numba>=0.57", "numpy>=1.21", ] # This list was generated by `rapids-dependency-file-generator`. To make changes, edit ../../dependencies.yaml and run `rapids-dependency-file-generator`. diff --git a/python/cugraph-pyg/conda/cugraph_pyg_dev_cuda-118.yaml b/python/cugraph-pyg/conda/cugraph_pyg_dev_cuda-118.yaml index 4e5159e6b45..f98eab430ba 100644 --- a/python/cugraph-pyg/conda/cugraph_pyg_dev_cuda-118.yaml +++ b/python/cugraph-pyg/conda/cugraph_pyg_dev_cuda-118.yaml @@ -10,11 +10,11 @@ channels: - conda-forge - nvidia dependencies: -- cugraph==23.10.* +- cugraph==23.12.* - pandas - pre-commit - pyg=2.3.1=*torch_2.0.0*cu118* -- pylibcugraphops==23.10.* +- pylibcugraphops==23.12.* - pytest - pytest-benchmark - pytest-cov diff --git a/python/cugraph-pyg/cugraph_pyg/__init__.py b/python/cugraph-pyg/cugraph_pyg/__init__.py index f8187059b86..ecd2f271a00 100644 --- a/python/cugraph-pyg/cugraph_pyg/__init__.py +++ b/python/cugraph-pyg/cugraph_pyg/__init__.py @@ -11,4 +11,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "23.10.00" +__version__ = "23.12.00" diff --git a/python/cugraph-pyg/pyproject.toml b/python/cugraph-pyg/pyproject.toml index 218c09fbd1d..84d30221d55 100644 --- a/python/cugraph-pyg/pyproject.toml +++ b/python/cugraph-pyg/pyproject.toml @@ -12,7 +12,7 @@ testpaths = ["cugraph_pyg/tests"] [project] name = "cugraph_pyg" -version = "23.10.00" +version = "23.12.00" description = "cugraph_pyg - PyG support for cuGraph massive-scale, ultra-fast GPU graph analytics." authors = [ { name = "NVIDIA Corporation" }, @@ -26,7 +26,7 @@ classifiers = [ "Programming Language :: Python :: 3.10", ] dependencies = [ - "cugraph==23.10.*", + "cugraph==23.12.*", "numba>=0.57", "numpy>=1.21", ] # This list was generated by `rapids-dependency-file-generator`. To make changes, edit ../../dependencies.yaml and run `rapids-dependency-file-generator`. diff --git a/python/cugraph-service/client/cugraph_service_client/__init__.py b/python/cugraph-service/client/cugraph_service_client/__init__.py index 229d07b8bc6..a0361abedd3 100644 --- a/python/cugraph-service/client/cugraph_service_client/__init__.py +++ b/python/cugraph-service/client/cugraph_service_client/__init__.py @@ -35,4 +35,4 @@ from cugraph_service_client.client import CugraphServiceClient from cugraph_service_client.remote_graph import RemoteGraph -__version__ = "23.10.00" +__version__ = "23.12.00" diff --git a/python/cugraph-service/client/pyproject.toml b/python/cugraph-service/client/pyproject.toml index 3b31a5f2e0a..7f702252f02 100644 --- a/python/cugraph-service/client/pyproject.toml +++ b/python/cugraph-service/client/pyproject.toml @@ -10,7 +10,7 @@ build-backend = "setuptools.build_meta" [project] name = "cugraph-service-client" -version = "23.10.00" +version = "23.12.00" description = "cuGraph Service client" readme = { file = "README.md", content-type = "text/markdown" } authors = [ diff --git a/python/cugraph-service/server/cugraph_service_server/__init__.py b/python/cugraph-service/server/cugraph_service_server/__init__.py index 017f0990f89..87d35005195 100644 --- a/python/cugraph-service/server/cugraph_service_server/__init__.py +++ b/python/cugraph-service/server/cugraph_service_server/__init__.py @@ -61,4 +61,4 @@ def start_server_blocking( server.serve() # blocks until Ctrl-C (kill -2) -__version__ = "23.10.00" +__version__ = "23.12.00" diff --git a/python/cugraph-service/server/pyproject.toml b/python/cugraph-service/server/pyproject.toml index f25ea6c46e5..3c77cf01c2c 100644 --- a/python/cugraph-service/server/pyproject.toml +++ b/python/cugraph-service/server/pyproject.toml @@ -10,7 +10,7 @@ build-backend = "setuptools.build_meta" [project] name = "cugraph-service-server" -version = "23.10.00" +version = "23.12.00" description = "cuGraph Service server" readme = { file = "README.md", content-type = "text/markdown" } authors = [ @@ -19,19 +19,19 @@ authors = [ license = { text = "Apache 2.0" } requires-python = ">=3.9" dependencies = [ - "cudf==23.10.*", - "cugraph-service-client==23.10.*", - "cugraph==23.10.*", + "cudf==23.12.*", + "cugraph-service-client==23.12.*", + "cugraph==23.12.*", "cupy-cuda11x>=12.0.0", - "dask-cuda==23.10.*", - "dask-cudf==23.10.*", + "dask-cuda==23.12.*", + "dask-cudf==23.12.*", "dask>=2023.7.1", "distributed>=2023.7.1", "numba>=0.57", "numpy>=1.21", - "rmm==23.10.*", + "rmm==23.12.*", "thriftpy2", - "ucx-py==0.34.*", + "ucx-py==0.35.*", ] # This list was generated by `rapids-dependency-file-generator`. To make changes, edit ../../../dependencies.yaml and run `rapids-dependency-file-generator`. classifiers = [ "Intended Audience :: Developers", diff --git a/python/cugraph/CMakeLists.txt b/python/cugraph/CMakeLists.txt index ecfcb9b219f..4eaf0ce6334 100644 --- a/python/cugraph/CMakeLists.txt +++ b/python/cugraph/CMakeLists.txt @@ -14,7 +14,7 @@ cmake_minimum_required(VERSION 3.26.4 FATAL_ERROR) -set(cugraph_version 23.10.00) +set(cugraph_version 23.12.00) include(../../fetch_rapids.cmake) diff --git a/python/cugraph/cugraph/__init__.py b/python/cugraph/cugraph/__init__.py index 43cb30beceb..f3a335183f3 100644 --- a/python/cugraph/cugraph/__init__.py +++ b/python/cugraph/cugraph/__init__.py @@ -120,4 +120,4 @@ from cugraph import exceptions -__version__ = "23.10.00" +__version__ = "23.12.00" diff --git a/python/cugraph/pyproject.toml b/python/cugraph/pyproject.toml index cadf6879e23..5e17be40e7b 100644 --- a/python/cugraph/pyproject.toml +++ b/python/cugraph/pyproject.toml @@ -6,9 +6,9 @@ requires = [ "cmake>=3.26.4", "cython>=3.0.0", "ninja", - "pylibcugraph==23.10.*", - "pylibraft==23.10.*", - "rmm==23.10.*", + "pylibcugraph==23.12.*", + "pylibraft==23.12.*", + "rmm==23.12.*", "scikit-build>=0.13.1", "setuptools>=61.0.0", "wheel", @@ -20,7 +20,7 @@ testpaths = ["cugraph/tests"] [project] name = "cugraph" -version = "23.10.00" +version = "23.12.00" description = "cuGraph - RAPIDS GPU Graph Analytics" readme = { file = "README.md", content-type = "text/markdown" } authors = [ @@ -29,18 +29,18 @@ authors = [ license = { text = "Apache 2.0" } requires-python = ">=3.9" dependencies = [ - "cudf==23.10.*", + "cudf==23.12.*", "cupy-cuda11x>=12.0.0", - "dask-cuda==23.10.*", - "dask-cudf==23.10.*", + "dask-cuda==23.12.*", + "dask-cudf==23.12.*", "dask>=2023.7.1", "distributed>=2023.7.1", "fsspec[http]>=0.6.0", "numba>=0.57", - "pylibcugraph==23.10.*", - "raft-dask==23.10.*", - "rmm==23.10.*", - "ucx-py==0.34.*", + "pylibcugraph==23.12.*", + "raft-dask==23.12.*", + "rmm==23.12.*", + "ucx-py==0.35.*", ] # This list was generated by `rapids-dependency-file-generator`. To make changes, edit ../../dependencies.yaml and run `rapids-dependency-file-generator`. classifiers = [ "Intended Audience :: Developers", diff --git a/python/nx-cugraph/README.md b/python/nx-cugraph/README.md index e7cd26218e6..ab267e5a756 100644 --- a/python/nx-cugraph/README.md +++ b/python/nx-cugraph/README.md @@ -6,7 +6,7 @@ with minimal dependencies (`networkx`, `cupy`, and `pylibcugraph`) to run graph ### Contribute -Follow instructions for [contributing to cugraph](https://github.com/rapidsai/cugraph/blob/branch-23.10/readme_pages/CONTRIBUTING.md) +Follow instructions for [contributing to cugraph](https://github.com/rapidsai/cugraph/blob/branch-23.12 and [building from source](https://docs.rapids.ai/api/cugraph/stable/installation/source_build/), then build nx-cugraph in develop (i.e., editable) mode: ``` $ ./build.sh nx-cugraph --pydevelop diff --git a/python/nx-cugraph/nx_cugraph/__init__.py b/python/nx-cugraph/nx_cugraph/__init__.py index 28066fe2b02..c23542842d4 100644 --- a/python/nx-cugraph/nx_cugraph/__init__.py +++ b/python/nx-cugraph/nx_cugraph/__init__.py @@ -17,4 +17,4 @@ from .classes import * from .convert import * -__version__ = "23.10.00" +__version__ = "23.12.00" diff --git a/python/nx-cugraph/pyproject.toml b/python/nx-cugraph/pyproject.toml index 95e9c256e5d..532b5225692 100644 --- a/python/nx-cugraph/pyproject.toml +++ b/python/nx-cugraph/pyproject.toml @@ -10,7 +10,7 @@ build-backend = "setuptools.build_meta" [project] name = "nx-cugraph" -version = "23.10.00" +version = "23.12.00" description = "cugraph backend for NetworkX" readme = { file = "README.md", content-type = "text/markdown" } authors = [ @@ -32,7 +32,7 @@ classifiers = [ dependencies = [ "cupy-cuda11x>=12.0.0", "networkx>=3.0", - "pylibcugraph==23.10.*", + "pylibcugraph==23.12.*", ] # This list was generated by `rapids-dependency-file-generator`. To make changes, edit ../../dependencies.yaml and run `rapids-dependency-file-generator`. [project.optional-dependencies] diff --git a/python/pylibcugraph/CMakeLists.txt b/python/pylibcugraph/CMakeLists.txt index b5b564e6881..057f30ef3ad 100644 --- a/python/pylibcugraph/CMakeLists.txt +++ b/python/pylibcugraph/CMakeLists.txt @@ -14,7 +14,7 @@ cmake_minimum_required(VERSION 3.26.4 FATAL_ERROR) -set(pylibcugraph_version 23.10.00) +set(pylibcugraph_version 23.12.00) include(../../fetch_rapids.cmake) diff --git a/python/pylibcugraph/pylibcugraph/__init__.py b/python/pylibcugraph/pylibcugraph/__init__.py index 45f6de2f663..187eabaae0c 100644 --- a/python/pylibcugraph/pylibcugraph/__init__.py +++ b/python/pylibcugraph/pylibcugraph/__init__.py @@ -96,4 +96,4 @@ from pylibcugraph import exceptions -__version__ = "23.10.00" +__version__ = "23.12.00" diff --git a/python/pylibcugraph/pyproject.toml b/python/pylibcugraph/pyproject.toml index 806ea65ac6c..f1b439debd2 100644 --- a/python/pylibcugraph/pyproject.toml +++ b/python/pylibcugraph/pyproject.toml @@ -6,8 +6,8 @@ requires = [ "cmake>=3.26.4", "cython>=3.0.0", "ninja", - "pylibraft==23.10.*", - "rmm==23.10.*", + "pylibraft==23.12.*", + "rmm==23.12.*", "scikit-build>=0.13.1", "setuptools>=61.0.0", "wheel", @@ -19,7 +19,7 @@ testpaths = ["pylibcugraph/tests"] [project] name = "pylibcugraph" -version = "23.10.00" +version = "23.12.00" description = "pylibcugraph - Python bindings for the libcugraph cuGraph C/C++/CUDA library" readme = { file = "README.md", content-type = "text/markdown" } authors = [ @@ -28,8 +28,8 @@ authors = [ license = { text = "Apache 2.0" } requires-python = ">=3.9" dependencies = [ - "pylibraft==23.10.*", - "rmm==23.10.*", + "pylibraft==23.12.*", + "rmm==23.12.*", ] # This list was generated by `rapids-dependency-file-generator`. To make changes, edit ../../dependencies.yaml and run `rapids-dependency-file-generator`. classifiers = [ "Intended Audience :: Developers", @@ -40,7 +40,7 @@ classifiers = [ [project.optional-dependencies] test = [ - "cudf==23.10.*", + "cudf==23.12.*", "numpy>=1.21", "pandas", "pytest", From f53bb56dc3245f64523aeeb997430c8f49de4624 Mon Sep 17 00:00:00 2001 From: Tingyu Wang Date: Fri, 22 Sep 2023 12:44:31 -0400 Subject: [PATCH 010/111] Fix torch seed in `cugraph-dgl` and `-pyg` tests for conv layers (#3869) Fixes https://github.com/rapidsai/graph_dl/issues/325 Recently, a few CI runs (ex. [1](https://github.com/rapidsai/cugraph/actions/runs/6254253684/job/16983164330?pr=3828#step:7:5078), [2](https://github.com/rapidsai/cugraph/actions/runs/6224345348/job/16896416094?pr=3843)) failed when comparing results from cugraph-ops-based conv layers against results from upstream frameworks. The tests pass most of the time, but occasionally fail due to a combination of using a strict tolerance and bad numerics (floating point error). This PR fixes the seed used for generating random feature tensors so that CI behaves consistently across different runs. Authors: - Tingyu Wang (https://github.com/tingyu66) Approvers: - Alex Barghi (https://github.com/alexbarghi-nv) URL: https://github.com/rapidsai/cugraph/pull/3869 --- python/cugraph-dgl/tests/nn/test_gatconv.py | 2 ++ python/cugraph-dgl/tests/nn/test_gatv2conv.py | 2 ++ python/cugraph-dgl/tests/nn/test_relgraphconv.py | 15 +++++++++++---- python/cugraph-dgl/tests/nn/test_sageconv.py | 1 + .../cugraph-dgl/tests/nn/test_transformerconv.py | 1 + .../cugraph_pyg/tests/nn/test_gat_conv.py | 1 + .../cugraph_pyg/tests/nn/test_gatv2_conv.py | 1 + .../cugraph_pyg/tests/nn/test_rgcn_conv.py | 1 + .../cugraph_pyg/tests/nn/test_sage_conv.py | 1 + .../cugraph_pyg/tests/nn/test_transformer_conv.py | 1 + 10 files changed, 22 insertions(+), 4 deletions(-) diff --git a/python/cugraph-dgl/tests/nn/test_gatconv.py b/python/cugraph-dgl/tests/nn/test_gatconv.py index ef3047dc2cd..ce145b2bc87 100644 --- a/python/cugraph-dgl/tests/nn/test_gatconv.py +++ b/python/cugraph-dgl/tests/nn/test_gatconv.py @@ -35,6 +35,7 @@ def test_gatconv_equality( ): from dgl.nn.pytorch import GATConv + torch.manual_seed(12345) g = create_graph1().to("cuda") if idtype_int: @@ -121,6 +122,7 @@ def test_gatconv_equality( def test_gatconv_edge_feats( bias, bipartite, concat, max_in_degree, num_heads, to_block, use_edge_feats ): + torch.manual_seed(12345) g = create_graph1().to("cuda") if to_block: diff --git a/python/cugraph-dgl/tests/nn/test_gatv2conv.py b/python/cugraph-dgl/tests/nn/test_gatv2conv.py index cc46a6e4b39..52003edacca 100644 --- a/python/cugraph-dgl/tests/nn/test_gatv2conv.py +++ b/python/cugraph-dgl/tests/nn/test_gatv2conv.py @@ -35,6 +35,7 @@ def test_gatv2conv_equality( ): from dgl.nn.pytorch import GATv2Conv + torch.manual_seed(12345) g = create_graph1().to("cuda") if idtype_int: @@ -109,6 +110,7 @@ def test_gatv2conv_equality( def test_gatv2conv_edge_feats( bias, bipartite, concat, max_in_degree, num_heads, to_block, use_edge_feats ): + torch.manual_seed(12345) g = create_graph1().to("cuda") if to_block: diff --git a/python/cugraph-dgl/tests/nn/test_relgraphconv.py b/python/cugraph-dgl/tests/nn/test_relgraphconv.py index 901f9ba1433..bdaa89e57f2 100644 --- a/python/cugraph-dgl/tests/nn/test_relgraphconv.py +++ b/python/cugraph-dgl/tests/nn/test_relgraphconv.py @@ -41,6 +41,7 @@ def test_relgraphconv_equality( ): from dgl.nn.pytorch import RelGraphConv + torch.manual_seed(12345) in_feat, out_feat, num_rels = 10, 2, 3 args = (in_feat, out_feat, num_rels) kwargs = { @@ -75,12 +76,18 @@ def test_relgraphconv_equality( size=size, src_ids=indices, cdst_ids=offsets, values=etypes, formats="csc" ) - torch.manual_seed(0) conv1 = RelGraphConv(*args, **kwargs).cuda() + conv2 = CuGraphRelGraphConv(*args, **kwargs, apply_norm=False).cuda() - torch.manual_seed(0) - kwargs["apply_norm"] = False - conv2 = CuGraphRelGraphConv(*args, **kwargs).cuda() + with torch.no_grad(): + if self_loop: + conv2.W.data[:-1] = conv1.linear_r.W.data + conv2.W.data[-1] = conv1.loop_weight.data + else: + conv2.W.data = conv1.linear_r.W.data.detach().clone() + + if regularizer is not None: + conv2.coeff.data = conv1.linear_r.coeff.data.detach().clone() out1 = conv1(g, feat, g.edata[dgl.ETYPE]) diff --git a/python/cugraph-dgl/tests/nn/test_sageconv.py b/python/cugraph-dgl/tests/nn/test_sageconv.py index e2acf9e6596..b5d0a44b868 100644 --- a/python/cugraph-dgl/tests/nn/test_sageconv.py +++ b/python/cugraph-dgl/tests/nn/test_sageconv.py @@ -35,6 +35,7 @@ def test_sageconv_equality( ): from dgl.nn.pytorch import SAGEConv + torch.manual_seed(12345) kwargs = {"aggregator_type": aggr, "bias": bias} g = create_graph1().to("cuda") diff --git a/python/cugraph-dgl/tests/nn/test_transformerconv.py b/python/cugraph-dgl/tests/nn/test_transformerconv.py index b2b69cb35ab..5ac4fd7bea7 100644 --- a/python/cugraph-dgl/tests/nn/test_transformerconv.py +++ b/python/cugraph-dgl/tests/nn/test_transformerconv.py @@ -41,6 +41,7 @@ def test_transformerconv( use_edge_feats, sparse_format, ): + torch.manual_seed(12345) device = "cuda" g = create_graph1().to(device) diff --git a/python/cugraph-pyg/cugraph_pyg/tests/nn/test_gat_conv.py b/python/cugraph-pyg/cugraph_pyg/tests/nn/test_gat_conv.py index 21c43bad38c..62bebb9211d 100644 --- a/python/cugraph-pyg/cugraph_pyg/tests/nn/test_gat_conv.py +++ b/python/cugraph-pyg/cugraph_pyg/tests/nn/test_gat_conv.py @@ -32,6 +32,7 @@ def test_gat_conv_equality( import torch from torch_geometric.nn import GATConv + torch.manual_seed(12345) edge_index, size = request.getfixturevalue(graph) edge_index = edge_index.cuda() diff --git a/python/cugraph-pyg/cugraph_pyg/tests/nn/test_gatv2_conv.py b/python/cugraph-pyg/cugraph_pyg/tests/nn/test_gatv2_conv.py index 6b11e87154a..a4794628410 100644 --- a/python/cugraph-pyg/cugraph_pyg/tests/nn/test_gatv2_conv.py +++ b/python/cugraph-pyg/cugraph_pyg/tests/nn/test_gatv2_conv.py @@ -28,6 +28,7 @@ def test_gatv2_conv_equality(bipartite, concat, heads, use_edge_attr, graph, req import torch from torch_geometric.nn import GATv2Conv + torch.manual_seed(12345) edge_index, size = request.getfixturevalue(graph) edge_index = edge_index.cuda() diff --git a/python/cugraph-pyg/cugraph_pyg/tests/nn/test_rgcn_conv.py b/python/cugraph-pyg/cugraph_pyg/tests/nn/test_rgcn_conv.py index 233c6aa2836..ded4f300c0c 100644 --- a/python/cugraph-pyg/cugraph_pyg/tests/nn/test_rgcn_conv.py +++ b/python/cugraph-pyg/cugraph_pyg/tests/nn/test_rgcn_conv.py @@ -31,6 +31,7 @@ def test_rgcn_conv_equality( import torch from torch_geometric.nn import FastRGCNConv as RGCNConv + torch.manual_seed(12345) in_channels, out_channels, num_relations = (4, 2, 3) kwargs = dict(aggr=aggr, bias=bias, num_bases=num_bases, root_weight=root_weight) diff --git a/python/cugraph-pyg/cugraph_pyg/tests/nn/test_sage_conv.py b/python/cugraph-pyg/cugraph_pyg/tests/nn/test_sage_conv.py index 7f73cddbdbb..b2977d1d175 100644 --- a/python/cugraph-pyg/cugraph_pyg/tests/nn/test_sage_conv.py +++ b/python/cugraph-pyg/cugraph_pyg/tests/nn/test_sage_conv.py @@ -32,6 +32,7 @@ def test_sage_conv_equality( import torch from torch_geometric.nn import SAGEConv + torch.manual_seed(12345) edge_index, size = request.getfixturevalue(graph) edge_index = edge_index.cuda() csc = CuGraphSAGEConv.to_csc(edge_index, size) diff --git a/python/cugraph-pyg/cugraph_pyg/tests/nn/test_transformer_conv.py b/python/cugraph-pyg/cugraph_pyg/tests/nn/test_transformer_conv.py index 7dba1a6d515..fbdb244898b 100644 --- a/python/cugraph-pyg/cugraph_pyg/tests/nn/test_transformer_conv.py +++ b/python/cugraph-pyg/cugraph_pyg/tests/nn/test_transformer_conv.py @@ -27,6 +27,7 @@ def test_transformer_conv_equality(bipartite, concat, heads, graph, request): import torch from torch_geometric.nn import TransformerConv + torch.manual_seed(12345) edge_index, size = request.getfixturevalue(graph) edge_index = edge_index.cuda() csc = CuGraphTransformerConv.to_csc(edge_index, size) From fe17abc6da469d810ea512d1d887407032613405 Mon Sep 17 00:00:00 2001 From: Alex Barghi <105237337+alexbarghi-nv@users.noreply.github.com> Date: Mon, 25 Sep 2023 10:48:40 -0400 Subject: [PATCH 011/111] cuGraph-PyG Loader Improvements (#3795) Consolidates various speed improvements tested while running performance benchmarks. Avoids copying batch data, removes redundant data loading code, simplifies and improves de-offsetting, even though that is now being bypassed entirely for homogeneous graphs. Removes extra host to device copy. Properly flips the src/dst columns in the returned `HeteroData` minibatch objects, avoid exposing this to the end user. I've confirmed this cuts the MFG time by a factor of 4. Closes #3807 Authors: - Alex Barghi (https://github.com/alexbarghi-nv) Approvers: - Vibhu Jawa (https://github.com/VibhuJawa) - Don Acosta (https://github.com/acostadon) - Brad Rees (https://github.com/BradReesWork) URL: https://github.com/rapidsai/cugraph/pull/3795 --- .../cugraph_pyg/data/cugraph_store.py | 218 ++++++++++---- .../cugraph_pyg/loader/cugraph_node_loader.py | 107 +++++-- .../cugraph-pyg/cugraph_pyg/loader/filter.py | 57 ---- .../cugraph_pyg/sampler/cugraph_sampler.py | 281 +++++++++++------- .../tests/mg/test_mg_cugraph_loader.py | 4 +- .../tests/mg/test_mg_cugraph_sampler.py | 28 +- .../tests/mg/test_mg_cugraph_store.py | 6 +- .../cugraph_pyg/tests/test_cugraph_loader.py | 158 ++++++---- .../cugraph_pyg/tests/test_cugraph_sampler.py | 28 +- .../cugraph_pyg/tests/test_cugraph_store.py | 2 +- 10 files changed, 548 insertions(+), 341 deletions(-) delete mode 100644 python/cugraph-pyg/cugraph_pyg/loader/filter.py diff --git a/python/cugraph-pyg/cugraph_pyg/data/cugraph_store.py b/python/cugraph-pyg/cugraph_pyg/data/cugraph_store.py index 8d5d2fd4894..e0d318adbe0 100644 --- a/python/cugraph-pyg/cugraph_pyg/data/cugraph_store.py +++ b/python/cugraph-pyg/cugraph_pyg/data/cugraph_store.py @@ -25,6 +25,7 @@ import pandas import cudf import cugraph +import warnings from cugraph.utilities.utils import import_optional, MissingModule @@ -211,7 +212,9 @@ def __init__( F: cugraph.gnn.FeatureStore, G: Union[Dict[str, Tuple[TensorType]], Dict[str, int]], num_nodes_dict: Dict[str, int], + *, multi_gpu: bool = False, + order: str = "CSC", ): """ Constructs a new CuGraphStore from the provided @@ -256,11 +259,20 @@ def __init__( multi_gpu: bool (Optional, default = False) Whether the store should be backed by a multi-GPU graph. Requires dask to have been set up. + + order: str (Optional ["CSR", "CSC"], default = CSC) + The order to use for sampling. Should nearly always be CSC + unless there is a specific expectation of "reverse" sampling. + It is also not uncommon to use CSR order for correctness + testing, which some cuGraph-PyG tests do. """ if None in G: raise ValueError("Unspecified edge types not allowed in PyG") + if order != "CSR" and order != "CSC": + raise ValueError("invalid valid for order") + self.__vertex_dtype = torch.int64 self._tensor_attr_cls = CuGraphTensorAttr @@ -289,6 +301,7 @@ def __init__( self.__features = F self.__graph = None self.__is_graph_owner = False + self.__order = order if construct_graph: if multi_gpu: @@ -297,7 +310,9 @@ def __init__( ) if self.__graph is None: - self.__graph = self.__construct_graph(G, multi_gpu=multi_gpu) + self.__graph = self.__construct_graph( + G, multi_gpu=multi_gpu, order=order + ) self.__is_graph_owner = True self.__subgraphs = {} @@ -347,6 +362,7 @@ def __construct_graph( self, edge_info: Dict[Tuple[str, str, str], List[TensorType]], multi_gpu: bool = False, + order: str = "CSC", ) -> cugraph.MultiGraph: """ This function takes edge information and uses it to construct @@ -363,6 +379,14 @@ def __construct_graph( multi_gpu: bool (Optional, default=False) Whether to construct a single-GPU or multi-GPU cugraph Graph. Defaults to a single-GPU graph. + + order: str (CSC or CSR) + Essentially whether to reverse edges so that the cuGraph + sampling algorithm operates on the CSC matrix instead of + the CSR matrix. Should nearly always be CSC unless there + is a specific expectation of reverse sampling, or correctness + testing is being performed. + Returns ------- A newly-constructed directed cugraph.MultiGraph object. @@ -371,6 +395,9 @@ def __construct_graph( # Ensure the original dict is not modified. edge_info_cg = {} + if order != "CSR" and order != "CSC": + raise ValueError("Order must be either CSC (default) or CSR!") + # Iterate over the keys in sorted order so that the created # numerical types correspond to the lexicographic order # of the keys, which is critical to converting the numeric @@ -430,20 +457,43 @@ def __construct_graph( df = pandas.DataFrame( { - "src": pandas.Series(na_src), - "dst": pandas.Series(na_dst), + "src": pandas.Series(na_dst) + if order == "CSC" + else pandas.Series(na_src), + "dst": pandas.Series(na_src) + if order == "CSC" + else pandas.Series(na_dst), "etp": pandas.Series(na_etp), } ) + vertex_dtype = df.src.dtype if multi_gpu: nworkers = len(distributed.get_client().scheduler_info()["workers"]) - df = dd.from_pandas(df, npartitions=nworkers).persist() - df = df.map_partitions(cudf.DataFrame.from_pandas) - else: - df = cudf.from_pandas(df) + df = dd.from_pandas(df, npartitions=nworkers if len(df) > 32 else 1) + + # Ensure the dataframe is constructed on each partition + # instead of adding additional synchronization head from potential + # host to device copies. + def get_empty_df(): + return cudf.DataFrame( + { + "src": cudf.Series([], dtype=vertex_dtype), + "dst": cudf.Series([], dtype=vertex_dtype), + "etp": cudf.Series([], dtype="int32"), + } + ) - df = df.reset_index(drop=True) + # Have to check for empty partitions and handle them appropriately + df = df.persist() + df = df.map_partitions( + lambda f: cudf.DataFrame.from_pandas(f) + if len(f) > 0 + else get_empty_df(), + meta=get_empty_df(), + ).reset_index(drop=True) + else: + df = cudf.from_pandas(df).reset_index(drop=True) graph = cugraph.MultiGraph(directed=True) if multi_gpu: @@ -468,6 +518,10 @@ def __construct_graph( def _edge_types_to_attrs(self) -> dict: return dict(self.__edge_types_to_attrs) + @property + def order(self) -> str: + return self.__order + @property def node_types(self) -> List[NodeType]: return list(self.__vertex_type_offsets["type"]) @@ -557,6 +611,7 @@ def _get_edge_index(self, attr: CuGraphEdgeAttr) -> Tuple[TensorType, TensorType raise ValueError("Graph is not in memory, cannot access edge index!") if attr.layout != EdgeLayout.COO: + # TODO support returning CSR/CSC (Issue #3802) raise TypeError("Only COO direct access is supported!") # Currently, graph creation enforces that input vertex ids are always of @@ -566,12 +621,14 @@ def _get_edge_index(self, attr: CuGraphEdgeAttr) -> Tuple[TensorType, TensorType # This may change in the future if/when renumbering or the graph # creation process is refactored. # See Issue #3201 for more details. + # Also note src/dst are flipped so that cuGraph sampling is done in + # CSC format rather than CSR format. if self._is_delayed: - src_col_name = self.__graph.renumber_map.renumbered_src_col_name - dst_col_name = self.__graph.renumber_map.renumbered_dst_col_name + dst_col_name = self.__graph.renumber_map.renumbered_src_col_name + src_col_name = self.__graph.renumber_map.renumbered_dst_col_name else: - src_col_name = self.__graph.srcCol - dst_col_name = self.__graph.dstCol + dst_col_name = self.__graph.srcCol + src_col_name = self.__graph.dstCol # If there is only one edge type (homogeneous graph) then # bypass the edge filters for a significant speed improvement. @@ -785,29 +842,73 @@ def _get_renumbered_edge_groups_from_sample( """ row_dict = {} col_dict = {} - if len(self.__edge_types_to_attrs) == 1: + # If there is only 1 edge type (includes heterogeneous graphs) + if len(self.edge_types) == 1: t_pyg_type = list(self.__edge_types_to_attrs.values())[0].edge_type src_type, _, dst_type = t_pyg_type - dst_id_table = noi_index[dst_type] - dst_id_map = ( - cudf.Series(cupy.asarray(dst_id_table), name="dst") - .reset_index() - .rename(columns={"index": "new_id"}) - .set_index("dst") - ) - dst = dst_id_map["new_id"].loc[sampling_results.destinations] - col_dict[t_pyg_type] = torch.as_tensor(dst.values, device="cuda") - - src_id_table = noi_index[src_type] - src_id_map = ( - cudf.Series(cupy.asarray(src_id_table), name="src") - .reset_index() - .rename(columns={"index": "new_id"}) - .set_index("src") - ) - src = src_id_map["new_id"].loc[sampling_results.sources] - row_dict[t_pyg_type] = torch.as_tensor(src.values, device="cuda") + # If there is only 1 node type (homogeneous) + # This should only occur if the cuGraph loader was + # not used. This logic is deprecated. + if len(self.node_types) == 1: + warnings.warn( + "Renumbering after sampling for homogeneous graphs is deprecated.", + FutureWarning, + ) + + # Create a dataframe mapping old ids to new ids. + vtype = src_type + id_table = noi_index[vtype] + id_map = cudf.Series( + cupy.arange(id_table.shape[0], dtype="int32"), + name="new_id", + index=cupy.asarray(id_table), + ).sort_index() + + # Renumber the sources using binary search + # Step 1: get the index of the new id + ix_r = torch.searchsorted( + torch.as_tensor(id_map.index.values, device="cuda"), + torch.as_tensor(sampling_results.sources.values, device="cuda"), + ) + # Step 2: Go from id indices to actual ids + row_dict[t_pyg_type] = torch.as_tensor(id_map.values, device="cuda")[ + ix_r + ] + + # Renumber the destinations using binary search + # Step 1: get the index of the new id + ix_c = torch.searchsorted( + torch.as_tensor(id_map.index.values, device="cuda"), + torch.as_tensor( + sampling_results.destinations.values, device="cuda" + ), + ) + # Step 2: Go from id indices to actual ids + col_dict[t_pyg_type] = torch.as_tensor(id_map.values, device="cuda")[ + ix_c + ] + else: + # Handle the heterogeneous case where there is only 1 edge type + dst_id_table = noi_index[dst_type] + dst_id_map = cudf.DataFrame( + { + "dst": cupy.asarray(dst_id_table), + "new_id": cupy.arange(dst_id_table.shape[0]), + } + ).set_index("dst") + dst = dst_id_map["new_id"].loc[sampling_results.destinations] + col_dict[t_pyg_type] = torch.as_tensor(dst.values, device="cuda") + + src_id_table = noi_index[src_type] + src_id_map = cudf.DataFrame( + { + "src": cupy.asarray(src_id_table), + "new_id": cupy.arange(src_id_table.shape[0]), + } + ).set_index("src") + src = src_id_map["new_id"].loc[sampling_results.sources] + row_dict[t_pyg_type] = torch.as_tensor(src.values, device="cuda") else: # This will retrieve the single string representation. @@ -822,36 +923,18 @@ def _get_renumbered_edge_groups_from_sample( for pyg_can_edge_type_str, ix in eoi_types.items(): pyg_can_edge_type = tuple(pyg_can_edge_type_str.split("__")) - src_type, _, dst_type = pyg_can_edge_type - - # Get the de-offsetted sources - sources = torch.as_tensor( - sampling_results.sources.iloc[ix].values, device="cuda" - ) - sources_ix = torch.searchsorted( - self.__vertex_type_offsets["stop"], sources - ) - sources -= self.__vertex_type_offsets["start"][sources_ix] - # Create the row entry for this type - src_id_table = noi_index[src_type] - src_id_map = ( - cudf.Series(cupy.asarray(src_id_table), name="src") - .reset_index() - .rename(columns={"index": "new_id"}) - .set_index("src") - ) - src = src_id_map["new_id"].loc[cupy.asarray(sources)] - row_dict[pyg_can_edge_type] = torch.as_tensor(src.values, device="cuda") + if self.__order == "CSR": + src_type, _, dst_type = pyg_can_edge_type + else: # CSC + dst_type, _, src_type = pyg_can_edge_type # Get the de-offsetted destinations + dst_num_type = self._numeric_vertex_type_from_name(dst_type) destinations = torch.as_tensor( sampling_results.destinations.iloc[ix].values, device="cuda" ) - destinations_ix = torch.searchsorted( - self.__vertex_type_offsets["stop"], destinations - ) - destinations -= self.__vertex_type_offsets["start"][destinations_ix] + destinations -= self.__vertex_type_offsets["start"][dst_num_type] # Create the col entry for this type dst_id_table = noi_index[dst_type] @@ -864,6 +947,24 @@ def _get_renumbered_edge_groups_from_sample( dst = dst_id_map["new_id"].loc[cupy.asarray(destinations)] col_dict[pyg_can_edge_type] = torch.as_tensor(dst.values, device="cuda") + # Get the de-offsetted sources + src_num_type = self._numeric_vertex_type_from_name(src_type) + sources = torch.as_tensor( + sampling_results.sources.iloc[ix].values, device="cuda" + ) + sources -= self.__vertex_type_offsets["start"][src_num_type] + + # Create the row entry for this type + src_id_table = noi_index[src_type] + src_id_map = ( + cudf.Series(cupy.asarray(src_id_table), name="src") + .reset_index() + .rename(columns={"index": "new_id"}) + .set_index("src") + ) + src = src_id_map["new_id"].loc[cupy.asarray(sources)] + row_dict[pyg_can_edge_type] = torch.as_tensor(src.values, device="cuda") + return row_dict, col_dict def put_tensor(self, tensor, attr) -> None: @@ -959,9 +1060,7 @@ def _get_tensor(self, attr: CuGraphTensorAttr) -> TensorType: t = t[-1] if isinstance(t, np.ndarray): - t = torch.as_tensor(t, device="cuda") - else: - t = t.cuda() + t = torch.as_tensor(t, device="cpu") return t @@ -979,7 +1078,6 @@ def _get_tensor(self, attr: CuGraphTensorAttr) -> TensorType: t = torch.concatenate([t, u]) - t = t.cuda() return t def _multi_get_tensor(self, attrs: List[CuGraphTensorAttr]) -> List[TensorType]: diff --git a/python/cugraph-pyg/cugraph_pyg/loader/cugraph_node_loader.py b/python/cugraph-pyg/cugraph_pyg/loader/cugraph_node_loader.py index 8d79685965f..cf7eb330d67 100644 --- a/python/cugraph-pyg/cugraph_pyg/loader/cugraph_node_loader.py +++ b/python/cugraph-pyg/cugraph_pyg/loader/cugraph_node_loader.py @@ -23,12 +23,15 @@ from cugraph.utilities.utils import import_optional, MissingModule from cugraph_pyg.data import CuGraphStore -from cugraph_pyg.loader.filter import _filter_cugraph_store -from cugraph_pyg.sampler.cugraph_sampler import _sampler_output_from_sampling_results +from cugraph_pyg.sampler.cugraph_sampler import ( + _sampler_output_from_sampling_results_heterogeneous, + _sampler_output_from_sampling_results_homogeneous, +) from typing import Union, Tuple, Sequence, List, Dict torch_geometric = import_optional("torch_geometric") +torch = import_optional("torch") InputNodes = ( Sequence if isinstance(torch_geometric, MissingModule) @@ -253,55 +256,97 @@ def __next__(self): raw_sample_data = cudf.read_parquet(parquet_path) if "map" in raw_sample_data.columns: - self.__renumber_map = raw_sample_data["map"] + num_batches = end_inclusive - self.__start_inclusive + 1 + + map_end = raw_sample_data["map"].iloc[num_batches] + + map = torch.as_tensor( + raw_sample_data["map"].iloc[0:map_end], device="cuda" + ) raw_sample_data.drop("map", axis=1, inplace=True) + + self.__renumber_map_offsets = map[0 : num_batches + 1] - map[0] + self.__renumber_map = map[num_batches + 1 :] + else: self.__renumber_map = None self.__data = raw_sample_data[list(columns.keys())].astype(columns) self.__data.dropna(inplace=True) + if ( + len(self.__graph_store.edge_types) == 1 + and len(self.__graph_store.node_types) == 1 + ): + group_cols = ["batch_id", "hop_id"] + self.__data_index = self.__data.groupby(group_cols, as_index=True).agg( + {"sources": "max", "destinations": "max"} + ) + self.__data_index.rename( + columns={"sources": "src_max", "destinations": "dst_max"}, + inplace=True, + ) + self.__data_index = self.__data_index.to_dict(orient="index") + # Pull the next set of sampling results out of the dataframe in memory f = self.__data["batch_id"] == self.__next_batch if self.__renumber_map is not None: i = self.__next_batch - self.__start_inclusive - ix = self.__renumber_map.iloc[[i, i + 1]] - ix_start, ix_end = ix.iloc[0], ix.iloc[1] - current_renumber_map = self.__renumber_map.iloc[ix_start:ix_end] - if len(current_renumber_map) != ix_end - ix_start: - raise ValueError("invalid renumber map") - else: - current_renumber_map = None - sampler_output = _sampler_output_from_sampling_results( - self.__data[f], current_renumber_map, self.__graph_store - ) + # this should avoid d2h copy + current_renumber_map = self.__renumber_map[ + self.__renumber_map_offsets[i] : self.__renumber_map_offsets[i + 1] + ] - # Get ready for next iteration - self.__next_batch += 1 + else: + current_renumber_map = None # Get and return the sampled subgraph - if isinstance(torch_geometric, MissingModule): - noi_index, row_dict, col_dict, edge_dict = sampler_output["out"] - return _filter_cugraph_store( - self.__feature_store, + if ( + len(self.__graph_store.edge_types) == 1 + and len(self.__graph_store.node_types) == 1 + ): + sampler_output = _sampler_output_from_sampling_results_homogeneous( + self.__data[f], + current_renumber_map, self.__graph_store, - noi_index, - row_dict, - col_dict, - edge_dict, + self.__data_index, + self.__next_batch, ) else: - out = torch_geometric.loader.utils.filter_custom_store( - self.__feature_store, - self.__graph_store, - sampler_output.node, - sampler_output.row, - sampler_output.col, - sampler_output.edge, + sampler_output = _sampler_output_from_sampling_results_heterogeneous( + self.__data[f], current_renumber_map, self.__graph_store ) - return out + # Get ready for next iteration + self.__next_batch += 1 + + # Create a PyG HeteroData object, loading the required features + out = torch_geometric.loader.utils.filter_custom_store( + self.__feature_store, + self.__graph_store, + sampler_output.node, + sampler_output.row, + sampler_output.col, + sampler_output.edge, + ) + + # Account for CSR format in cuGraph vs. CSC format in PyG + if self.__graph_store.order == "CSC": + for node_type in out.edge_index_dict: + out[node_type].edge_index[0], out[node_type].edge_index[1] = ( + out[node_type].edge_index[1], + out[node_type].edge_index[0], + ) + + out.set_value_dict("num_sampled_nodes", sampler_output.num_sampled_nodes) + out.set_value_dict("num_sampled_edges", sampler_output.num_sampled_edges) + + return out + + @property + def _starting_batch_id(self): + return self.__starting_batch_id def __iter__(self): return self diff --git a/python/cugraph-pyg/cugraph_pyg/loader/filter.py b/python/cugraph-pyg/cugraph_pyg/loader/filter.py deleted file mode 100644 index f519ba7cfc9..00000000000 --- a/python/cugraph-pyg/cugraph_pyg/loader/filter.py +++ /dev/null @@ -1,57 +0,0 @@ -# Copyright (c) 2023, NVIDIA CORPORATION. -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import cupy - -from cugraph_pyg.data import CuGraphStore - -from typing import ( - Dict, - Sequence, -) - - -def _filter_cugraph_store( - feature_store: CuGraphStore, - graph_store: CuGraphStore, - node_dict: Dict[str, Sequence], - row_dict: Dict[str, Sequence], - col_dict: Dict[str, Sequence], - edge_dict: Dict[str, Sequence], -) -> dict: - """ - Primarily for testing without torch and torch_geometric. - Returns a dictionary containing the sampled subgraph. - """ - data = {} - - for attr in graph_store.get_all_edge_attrs(): - key = attr.edge_type - if key in row_dict and key in col_dict: - edge_index = cupy.stack([row_dict[key], col_dict[key]]) - data[attr.edge_type] = {} - data[attr.edge_type]["edge_index"] = edge_index - - # Filter node storage: - required_attrs = [] - for attr in feature_store.get_all_tensor_attrs(): - if attr.group_name in node_dict: - attr.index = node_dict[attr.group_name] - required_attrs.append(attr) - data[attr.group_name] = {} - data["num_nodes"] = attr.index.size - tensors = feature_store.multi_get_tensor(required_attrs) - for i, attr in enumerate(required_attrs): - data[attr.group_name][attr.attr_name] = tensors[i] - - return data diff --git a/python/cugraph-pyg/cugraph_pyg/sampler/cugraph_sampler.py b/python/cugraph-pyg/cugraph_pyg/sampler/cugraph_sampler.py index d4f600006be..6e8c4322418 100644 --- a/python/cugraph-pyg/cugraph_pyg/sampler/cugraph_sampler.py +++ b/python/cugraph-pyg/cugraph_pyg/sampler/cugraph_sampler.py @@ -12,26 +12,21 @@ # limitations under the License. -from typing import Sequence +from typing import Sequence, Dict, Tuple from cugraph_pyg.data import CuGraphStore -from cugraph.utilities.utils import import_optional, MissingModule +from cugraph.utilities.utils import import_optional import cudf dask_cudf = import_optional("dask_cudf") torch_geometric = import_optional("torch_geometric") torch = import_optional("torch") +HeteroSamplerOutput = torch_geometric.sampler.base.HeteroSamplerOutput -HeteroSamplerOutput = ( - None - if isinstance(torch_geometric, MissingModule) - else torch_geometric.sampler.base.HeteroSamplerOutput -) - -def _count_unique_nodes( +def _get_unique_nodes( sampling_results: cudf.DataFrame, graph_store: CuGraphStore, node_type: str, @@ -54,8 +49,8 @@ def _count_unique_nodes( Returns ------- - int - The number of unique nodes of the given node type. + cudf.Series + The unique nodes of the given node type. """ if node_position == "src": edge_index = "sources" @@ -78,12 +73,111 @@ def _count_unique_nodes( sampling_results_node = sampling_results[f] else: - return 0 + return cudf.Series([], dtype="int64") - return sampling_results_node[edge_index].nunique() + return sampling_results_node[edge_index] -def _sampler_output_from_sampling_results( +def _sampler_output_from_sampling_results_homogeneous( + sampling_results: cudf.DataFrame, + renumber_map: torch.Tensor, + graph_store: CuGraphStore, + data_index: Dict[Tuple[int, int], Dict[str, int]], + batch_id: int, + metadata: Sequence = None, +) -> HeteroSamplerOutput: + """ + Parameters + ---------- + sampling_results: cudf.DataFrame + The dataframe containing sampling results. + renumber_map: torch.Tensor + The tensor containing the renumber map, or None if there + is no renumber map. + graph_store: CuGraphStore + The graph store containing the structure of the sampled graph. + data_index: Dict[Tuple[int, int], Dict[str, int]] + Dictionary where keys are the batch id and hop id, + and values are dictionaries containing the max src + and max dst node ids for the batch and hop. + batch_id: int + The current batch id, whose samples are being retrieved + from the sampling results and data index. + metadata: Tensor + The metadata for the sampled batch. + + Returns + ------- + HeteroSamplerOutput + """ + + if len(graph_store.edge_types) > 1 or len(graph_store.node_types) > 1: + raise ValueError("Graph is heterogeneous") + + hops = torch.arange( + sampling_results.hop_id.iloc[len(sampling_results) - 1] + 1, device="cuda" + ) + hops = torch.searchsorted( + torch.as_tensor(sampling_results.hop_id, device="cuda"), hops + ) + + node_type = graph_store.node_types[0] + edge_type = graph_store.edge_types[0] + + num_nodes_per_hop_dict = {node_type: torch.zeros(len(hops) + 1, dtype=torch.int64)} + num_edges_per_hop_dict = {edge_type: torch.zeros(len(hops), dtype=torch.int64)} + + if renumber_map is None: + raise ValueError("Renumbered input is expected for homogeneous graphs") + + noi_index = {node_type: torch.as_tensor(renumber_map, device="cuda")} + + row_dict = { + edge_type: torch.as_tensor(sampling_results.sources, device="cuda"), + } + + col_dict = { + edge_type: torch.as_tensor(sampling_results.destinations, device="cuda"), + } + + num_nodes_per_hop_dict[node_type][0] = data_index[batch_id, 0]["src_max"] + 1 + for hop in range(len(hops)): + hop_ix_start = hops[hop] + hop_ix_end = hops[hop + 1] if hop < len(hops) - 1 else len(sampling_results) + + if num_nodes_per_hop_dict[node_type][hop] > 0: + max_id_hop = data_index[batch_id, hop]["dst_max"] + max_id_prev_hop = ( + data_index[batch_id, hop - 1]["dst_max"] + if hop > 0 + else data_index[batch_id, 0]["src_max"] + ) + + if max_id_hop > max_id_prev_hop: + num_nodes_per_hop_dict[node_type][hop + 1] = ( + max_id_hop - max_id_prev_hop + ) + else: + num_nodes_per_hop_dict[node_type][hop + 1] = 0 + # will default to 0 if the previous hop was 0, since this is a PyG requirement + + num_edges_per_hop_dict[edge_type][hop] = hop_ix_end - hop_ix_start + + if HeteroSamplerOutput is None: + raise ImportError("Error importing from pyg") + + return HeteroSamplerOutput( + node=noi_index, + row=row_dict, + col=col_dict, + edge=None, + num_sampled_nodes=num_nodes_per_hop_dict, + num_sampled_edges=num_edges_per_hop_dict, + metadata=metadata, + ) + + +def _sampler_output_from_sampling_results_heterogeneous( sampling_results: cudf.DataFrame, renumber_map: cudf.Series, graph_store: CuGraphStore, @@ -109,7 +203,7 @@ def _sampler_output_from_sampling_results( hops = torch.arange(sampling_results.hop_id.max() + 1, device="cuda") hops = torch.searchsorted( - torch.as_tensor(sampling_results.hop_id.values, device="cuda"), hops + torch.as_tensor(sampling_results.hop_id, device="cuda"), hops ) num_nodes_per_hop_dict = {} @@ -119,13 +213,11 @@ def _sampler_output_from_sampling_results( sampling_results_hop_0 = sampling_results.iloc[ 0 : (hops[1] if len(hops) > 1 else len(sampling_results)) ] + for node_type in graph_store.node_types: - if len(graph_store.node_types) == 1: - num_unique_nodes = sampling_results_hop_0.sources.nunique() - else: - num_unique_nodes = _count_unique_nodes( - sampling_results_hop_0, graph_store, node_type, "src" - ) + num_unique_nodes = _get_unique_nodes( + sampling_results_hop_0, graph_store, node_type, "src" + ).nunique() if num_unique_nodes > 0: num_nodes_per_hop_dict[node_type] = torch.zeros( @@ -134,112 +226,87 @@ def _sampler_output_from_sampling_results( num_nodes_per_hop_dict[node_type][0] = num_unique_nodes if renumber_map is not None: - if len(graph_store.node_types) > 1 or len(graph_store.edge_types) > 1: - raise ValueError( - "Precomputing the renumber map is currently " - "unsupported for heterogeneous graphs." - ) + raise ValueError( + "Precomputing the renumber map is currently " + "unsupported for heterogeneous graphs." + ) - node_type = graph_store.node_types[0] - if not isinstance(node_type, str): - raise ValueError("Node types must be strings") - noi_index = {node_type: torch.as_tensor(renumber_map.values, device="cuda")} - - edge_type = graph_store.edge_types[0] - if ( - not isinstance(edge_type, tuple) - or not isinstance(edge_type[0], str) - or len(edge_type) != 3 - ): - raise ValueError("Edge types must be 3-tuples of strings") - if edge_type[0] != node_type or edge_type[2] != node_type: - raise ValueError("Edge src/dst type must match for homogeneous graphs") - row_dict = { - edge_type: torch.as_tensor(sampling_results.sources.values, device="cuda"), - } - col_dict = { - edge_type: torch.as_tensor( - sampling_results.destinations.values, device="cuda" + # Calculate nodes of interest based on unique nodes in order of appearance + # Use hop 0 sources since those are the only ones not included in destinations + # Use torch.concat based on benchmark performance (vs. cudf.concat) + + if sampling_results_hop_0 is None: + sampling_results_hop_0 = sampling_results.iloc[ + 0 : (hops[1] if len(hops) > 1 else len(sampling_results)) + ] + + nodes_of_interest = ( + cudf.Series( + torch.concat( + [ + torch.as_tensor(sampling_results_hop_0.sources, device="cuda"), + torch.as_tensor(sampling_results.destinations, device="cuda"), + ] ), - } - else: - # Calculate nodes of interest based on unique nodes in order of appearance - # Use hop 0 sources since those are the only ones not included in destinations - # Use torch.concat based on benchmark performance (vs. cudf.concat) - nodes_of_interest = ( - cudf.Series( - torch.concat( - [ - torch.as_tensor( - sampling_results_hop_0.sources.values, device="cuda" - ), - torch.as_tensor( - sampling_results.destinations.values, device="cuda" - ), - ] - ), - name="nodes_of_interest", - ) - .drop_duplicates() - .sort_index() + name="nodes_of_interest", ) - del sampling_results_hop_0 + .drop_duplicates() + .sort_index() + ) - # Get the grouped node index (for creating the renumbered grouped edge index) - noi_index = graph_store._get_vertex_groups_from_sample( - torch.as_tensor(nodes_of_interest.values, device="cuda") - ) - del nodes_of_interest + # Get the grouped node index (for creating the renumbered grouped edge index) + noi_index = graph_store._get_vertex_groups_from_sample( + torch.as_tensor(nodes_of_interest, device="cuda") + ) + del nodes_of_interest - # Get the new edge index (by type as expected for HeteroData) - # FIXME handle edge ids/types after the C++ updates - row_dict, col_dict = graph_store._get_renumbered_edge_groups_from_sample( - sampling_results, noi_index - ) + # Get the new edge index (by type as expected for HeteroData) + # FIXME handle edge ids/types after the C++ updates + row_dict, col_dict = graph_store._get_renumbered_edge_groups_from_sample( + sampling_results, noi_index + ) for hop in range(len(hops)): hop_ix_start = hops[hop] hop_ix_end = hops[hop + 1] if hop < len(hops) - 1 else len(sampling_results) - sampling_results_hop = sampling_results.iloc[hop_ix_start:hop_ix_end] + sampling_results_to_hop = sampling_results.iloc[0:hop_ix_end] for node_type in graph_store.node_types: - if len(graph_store.node_types) == 1: - num_unique_nodes = sampling_results_hop.destinations.nunique() - else: - num_unique_nodes = _count_unique_nodes( - sampling_results_hop, graph_store, node_type, "dst" - ) + unique_nodes_hop = _get_unique_nodes( + sampling_results_to_hop, graph_store, node_type, "dst" + ) + + unique_nodes_0 = _get_unique_nodes( + sampling_results_hop_0, graph_store, node_type, "src" + ) + + num_unique_nodes = cudf.concat([unique_nodes_0, unique_nodes_hop]).nunique() if num_unique_nodes > 0: if node_type not in num_nodes_per_hop_dict: num_nodes_per_hop_dict[node_type] = torch.zeros( len(hops) + 1, dtype=torch.int64 ) - num_nodes_per_hop_dict[node_type][hop + 1] = num_unique_nodes + num_nodes_per_hop_dict[node_type][hop + 1] = num_unique_nodes - int( + num_nodes_per_hop_dict[node_type][: hop + 1].sum(0) + ) - if len(graph_store.edge_types) == 1: - edge_type = graph_store.edge_types[0] - if edge_type not in num_edges_per_hop_dict: - num_edges_per_hop_dict[edge_type] = torch.zeros( + numeric_etypes, counts = torch.unique( + torch.as_tensor( + sampling_results.iloc[hop_ix_start:hop_ix_end].edge_type, + device="cuda", + ), + return_counts=True, + ) + numeric_etypes = list(numeric_etypes) + counts = list(counts) + for num_etype, count in zip(numeric_etypes, counts): + can_etype = graph_store.numeric_edge_type_to_canonical(num_etype) + if can_etype not in num_edges_per_hop_dict: + num_edges_per_hop_dict[can_etype] = torch.zeros( len(hops), dtype=torch.int64 ) - num_edges_per_hop_dict[graph_store.edge_types[0]][hop] = len( - sampling_results_hop - ) - else: - numeric_etypes, counts = torch.unique( - torch.as_tensor(sampling_results_hop.edge_type.values, device="cuda"), - return_counts=True, - ) - numeric_etypes = list(numeric_etypes) - counts = list(counts) - for num_etype, count in zip(numeric_etypes, counts): - can_etype = graph_store.numeric_edge_type_to_canonical(num_etype) - if can_etype not in num_edges_per_hop_dict: - num_edges_per_hop_dict[can_etype] = torch.zeros( - len(hops), dtype=torch.int64 - ) - num_edges_per_hop_dict[can_etype][hop] = count + num_edges_per_hop_dict[can_etype][hop] = count if HeteroSamplerOutput is None: raise ImportError("Error importing from pyg") diff --git a/python/cugraph-pyg/cugraph_pyg/tests/mg/test_mg_cugraph_loader.py b/python/cugraph-pyg/cugraph_pyg/tests/mg/test_mg_cugraph_loader.py index e29f3aea512..55aebf305da 100644 --- a/python/cugraph-pyg/cugraph_pyg/tests/mg/test_mg_cugraph_loader.py +++ b/python/cugraph-pyg/cugraph_pyg/tests/mg/test_mg_cugraph_loader.py @@ -24,7 +24,7 @@ @pytest.mark.skipif(isinstance(torch, MissingModule), reason="torch not available") def test_cugraph_loader_basic(dask_client, karate_gnn): F, G, N = karate_gnn - cugraph_store = CuGraphStore(F, G, N, multi_gpu=True) + cugraph_store = CuGraphStore(F, G, N, multi_gpu=True, order="CSR") loader = CuGraphNeighborLoader( (cugraph_store, cugraph_store), torch.arange(N["type0"] + N["type1"], dtype=torch.int64), @@ -52,7 +52,7 @@ def test_cugraph_loader_basic(dask_client, karate_gnn): @pytest.mark.skipif(isinstance(torch, MissingModule), reason="torch not available") def test_cugraph_loader_hetero(dask_client, karate_gnn): F, G, N = karate_gnn - cugraph_store = CuGraphStore(F, G, N, multi_gpu=True) + cugraph_store = CuGraphStore(F, G, N, multi_gpu=True, order="CSR") loader = CuGraphNeighborLoader( (cugraph_store, cugraph_store), input_nodes=("type1", torch.tensor([0, 1, 2, 5], device="cuda")), diff --git a/python/cugraph-pyg/cugraph_pyg/tests/mg/test_mg_cugraph_sampler.py b/python/cugraph-pyg/cugraph_pyg/tests/mg/test_mg_cugraph_sampler.py index 550852a3303..a1a72a44d0c 100644 --- a/python/cugraph-pyg/cugraph_pyg/tests/mg/test_mg_cugraph_sampler.py +++ b/python/cugraph-pyg/cugraph_pyg/tests/mg/test_mg_cugraph_sampler.py @@ -17,7 +17,9 @@ import pytest from cugraph_pyg.data import CuGraphStore -from cugraph_pyg.sampler.cugraph_sampler import _sampler_output_from_sampling_results +from cugraph_pyg.sampler.cugraph_sampler import ( + _sampler_output_from_sampling_results_heterogeneous, +) from cugraph.gnn import FeatureStore @@ -31,7 +33,7 @@ @pytest.mark.skipif(isinstance(torch, MissingModule), reason="torch not available") def test_neighbor_sample(dask_client, basic_graph_1): F, G, N = basic_graph_1 - cugraph_store = CuGraphStore(F, G, N, multi_gpu=True) + cugraph_store = CuGraphStore(F, G, N, multi_gpu=True, order="CSR") batches = cudf.DataFrame( { @@ -56,7 +58,7 @@ def test_neighbor_sample(dask_client, basic_graph_1): .sort_values(by=["sources", "destinations"]) ) - out = _sampler_output_from_sampling_results( + out = _sampler_output_from_sampling_results_heterogeneous( sampling_results=sampling_results, renumber_map=None, graph_store=cugraph_store, @@ -84,7 +86,7 @@ def test_neighbor_sample(dask_client, basic_graph_1): # check the hop dictionaries assert len(out.num_sampled_nodes) == 1 - assert out.num_sampled_nodes["vt1"].tolist() == [4, 4] + assert out.num_sampled_nodes["vt1"].tolist() == [4, 1] assert len(out.num_sampled_edges) == 1 assert out.num_sampled_edges[("vt1", "pig", "vt1")].tolist() == [6] @@ -95,7 +97,7 @@ def test_neighbor_sample(dask_client, basic_graph_1): @pytest.mark.skip(reason="broken") def test_neighbor_sample_multi_vertex(dask_client, multi_edge_multi_vertex_graph_1): F, G, N = multi_edge_multi_vertex_graph_1 - cugraph_store = CuGraphStore(F, G, N, multi_gpu=True) + cugraph_store = CuGraphStore(F, G, N, multi_gpu=True, order="CSR") batches = cudf.DataFrame( { @@ -119,7 +121,7 @@ def test_neighbor_sample_multi_vertex(dask_client, multi_edge_multi_vertex_graph .compute() ) - out = _sampler_output_from_sampling_results( + out = _sampler_output_from_sampling_results_heterogeneous( sampling_results=sampling_results, renumber_map=None, graph_store=cugraph_store, @@ -144,8 +146,8 @@ def test_neighbor_sample_multi_vertex(dask_client, multi_edge_multi_vertex_graph # check the hop dictionaries assert len(out.num_sampled_nodes) == 2 - assert out.num_sampled_nodes["black"].tolist() == [2, 2] - assert out.num_sampled_nodes["brown"].tolist() == [3, 2] + assert out.num_sampled_nodes["black"].tolist() == [2, 0] + assert out.num_sampled_nodes["brown"].tolist() == [3, 0] assert len(out.num_sampled_edges) == 5 assert out.num_sampled_edges[("brown", "horse", "brown")].tolist() == [2] @@ -186,7 +188,7 @@ def test_neighbor_sample_mock_sampling_results(dask_client): torch.tensor([3.2, 2.1], dtype=torch.float32), type_name="A", feat_name="prop1" ) - graph_store = CuGraphStore(F, G, N, multi_gpu=True) + graph_store = CuGraphStore(F, G, N, multi_gpu=True, order="CSR") # let 0, 1 be the start vertices, fanout = [2, 1, 2, 3] mock_sampling_results = cudf.DataFrame( @@ -198,7 +200,7 @@ def test_neighbor_sample_mock_sampling_results(dask_client): } ) - out = _sampler_output_from_sampling_results( + out = _sampler_output_from_sampling_results_heterogeneous( mock_sampling_results, None, graph_store, None ) @@ -218,9 +220,9 @@ def test_neighbor_sample_mock_sampling_results(dask_client): assert out.col[("B", "ba", "A")].tolist() == [1, 1] assert len(out.num_sampled_nodes) == 3 - assert out.num_sampled_nodes["A"].tolist() == [2, 0, 1, 0, 1] - assert out.num_sampled_nodes["B"].tolist() == [0, 2, 0, 1, 0] - assert out.num_sampled_nodes["C"].tolist() == [0, 0, 2, 0, 2] + assert out.num_sampled_nodes["A"].tolist() == [2, 0, 0, 0, 0] + assert out.num_sampled_nodes["B"].tolist() == [0, 2, 0, 0, 0] + assert out.num_sampled_nodes["C"].tolist() == [0, 0, 2, 0, 1] assert len(out.num_sampled_edges) == 3 assert out.num_sampled_edges[("A", "ab", "B")].tolist() == [3, 0, 1, 0] diff --git a/python/cugraph-pyg/cugraph_pyg/tests/mg/test_mg_cugraph_store.py b/python/cugraph-pyg/cugraph_pyg/tests/mg/test_mg_cugraph_store.py index a5a59623710..43b1e5da5a0 100644 --- a/python/cugraph-pyg/cugraph_pyg/tests/mg/test_mg_cugraph_store.py +++ b/python/cugraph-pyg/cugraph_pyg/tests/mg/test_mg_cugraph_store.py @@ -117,8 +117,8 @@ def test_get_edge_index(graph, edge_index_type, dask_client): G[et][1] = cudf.Series(G[et][1]) elif edge_index_type == "dask-cudf": for et in list(G.keys()): - G[et][0] = dask_cudf.from_cudf(cudf.Series(G[et][0]), npartitions=2) - G[et][1] = dask_cudf.from_cudf(cudf.Series(G[et][1]), npartitions=2) + G[et][0] = dask_cudf.from_cudf(cudf.Series(G[et][0]), npartitions=1) + G[et][1] = dask_cudf.from_cudf(cudf.Series(G[et][1]), npartitions=1) cugraph_store = CuGraphStore(F, G, N, multi_gpu=True) @@ -215,7 +215,7 @@ def test_renumber_vertices_multi_edge_multi_vertex( def test_renumber_edges(abc_graph, dask_client): F, G, N = abc_graph - graph_store = CuGraphStore(F, G, N, multi_gpu=True) + graph_store = CuGraphStore(F, G, N, multi_gpu=True, order="CSR") # let 0, 1 be the start vertices, fanout = [2, 1, 2, 3] mock_sampling_results = cudf.DataFrame( diff --git a/python/cugraph-pyg/cugraph_pyg/tests/test_cugraph_loader.py b/python/cugraph-pyg/cugraph_pyg/tests/test_cugraph_loader.py index 620f1a5eb85..48a21cb7fd6 100644 --- a/python/cugraph-pyg/cugraph_pyg/tests/test_cugraph_loader.py +++ b/python/cugraph-pyg/cugraph_pyg/tests/test_cugraph_loader.py @@ -26,12 +26,14 @@ from cugraph.utilities.utils import import_optional, MissingModule torch = import_optional("torch") +torch_geometric = import_optional("torch_geometric") +trim_to_layer = import_optional("torch_geometric.utils.trim_to_layer") @pytest.mark.skipif(isinstance(torch, MissingModule), reason="torch not available") def test_cugraph_loader_basic(karate_gnn): F, G, N = karate_gnn - cugraph_store = CuGraphStore(F, G, N) + cugraph_store = CuGraphStore(F, G, N, order="CSR") loader = CuGraphNeighborLoader( (cugraph_store, cugraph_store), torch.arange(N["type0"] + N["type1"], dtype=torch.int64), @@ -57,7 +59,7 @@ def test_cugraph_loader_basic(karate_gnn): @pytest.mark.skipif(isinstance(torch, MissingModule), reason="torch not available") def test_cugraph_loader_hetero(karate_gnn): F, G, N = karate_gnn - cugraph_store = CuGraphStore(F, G, N) + cugraph_store = CuGraphStore(F, G, N, order="CSR") loader = CuGraphNeighborLoader( (cugraph_store, cugraph_store), input_nodes=("type1", torch.tensor([0, 1, 2, 5], device="cuda")), @@ -82,23 +84,29 @@ def test_cugraph_loader_hetero(karate_gnn): @pytest.mark.skipif(isinstance(torch, MissingModule), reason="torch not available") def test_cugraph_loader_from_disk(): + m = [2, 9, 99, 82, 9, 3, 18, 1, 12] + n = torch.arange(1, 1 + len(m), dtype=torch.int32) + x = torch.zeros(256, dtype=torch.int32) + x[torch.tensor(m, dtype=torch.int32)] = n F = FeatureStore() - F.add_data(torch.tensor([1, 2, 3, 4, 5, 6, 7]), "t0", "x") + F.add_data(x, "t0", "x") - G = {("t0", "knows", "t0"): 7} - N = {"t0": 7} + G = {("t0", "knows", "t0"): 9080} + N = {"t0": 256} - cugraph_store = CuGraphStore(F, G, N) + cugraph_store = CuGraphStore(F, G, N, order="CSR") bogus_samples = cudf.DataFrame( { - "sources": [0, 1, 2, 3, 4, 5, 6], - "destinations": [6, 4, 3, 2, 2, 1, 5], - "edge_type": cudf.Series([0, 0, 0, 0, 0, 0, 0], dtype="int32"), - "edge_id": [5, 10, 15, 20, 25, 30, 35], - "hop_id": cudf.Series([0, 0, 0, 1, 1, 2, 2], dtype="int32"), + "sources": [0, 1, 2, 3, 4, 5, 6, 6], + "destinations": [5, 4, 3, 2, 2, 6, 5, 2], + "edge_type": cudf.Series([0, 0, 0, 0, 0, 0, 0, 0], dtype="int32"), + "edge_id": [5, 10, 15, 20, 25, 30, 35, 40], + "hop_id": cudf.Series([0, 0, 0, 1, 1, 1, 2, 2], dtype="int32"), } ) + map = cudf.Series(m, name="map") + bogus_samples = bogus_samples.join(map, how="outer").sort_index() tempdir = tempfile.TemporaryDirectory() for s in range(256): @@ -115,32 +123,49 @@ def test_cugraph_loader_from_disk(): for sample in loader: num_samples += 1 assert sample["t0"]["num_nodes"] == 7 - # correct vertex order is [0, 1, 2, 6, 4, 3, 5]; x = [1, 2, 3, 7, 5, 4, 6] - assert sample["t0"]["x"].tolist() == [1, 2, 3, 7, 5, 4, 6] - assert list(sample[("t0", "knows", "t0")]["edge_index"].shape) == [2, 7] + # correct vertex order is [0, 1, 2, 5, 4, 3, 6]; x = [1, 2, 3, 6, 5, 4, 7] + assert sample["t0"]["x"].tolist() == [3, 4, 5, 6, 7, 8, 9] + + edge_index = sample[("t0", "knows", "t0")]["edge_index"] + assert list(edge_index.shape) == [2, 8] + + assert ( + edge_index[0].tolist() + == bogus_samples.sources.dropna().values_host.tolist() + ) + assert ( + edge_index[1].tolist() + == bogus_samples.destinations.dropna().values_host.tolist() + ) assert num_samples == 256 @pytest.mark.skipif(isinstance(torch, MissingModule), reason="torch not available") def test_cugraph_loader_from_disk_subset(): + m = [2, 9, 99, 82, 9, 3, 18, 1, 12] + n = torch.arange(1, 1 + len(m), dtype=torch.int32) + x = torch.zeros(256, dtype=torch.int32) + x[torch.tensor(m, dtype=torch.int32)] = n F = FeatureStore() - F.add_data(torch.tensor([1, 2, 3, 4, 5, 6, 7]), "t0", "x") + F.add_data(x, "t0", "x") - G = {("t0", "knows", "t0"): 7} - N = {"t0": 7} + G = {("t0", "knows", "t0"): 9080} + N = {"t0": 256} - cugraph_store = CuGraphStore(F, G, N) + cugraph_store = CuGraphStore(F, G, N, order="CSR") bogus_samples = cudf.DataFrame( { - "sources": [0, 1, 2, 3, 4, 5, 6], - "destinations": [6, 4, 3, 2, 2, 1, 5], - "edge_type": cudf.Series([0, 0, 0, 0, 0, 0, 0], dtype="int32"), - "edge_id": [5, 10, 15, 20, 25, 30, 35], - "hop_id": cudf.Series([0, 0, 0, 1, 1, 2, 2], dtype="int32"), + "sources": [0, 1, 2, 3, 4, 5, 6, 6], + "destinations": [5, 4, 3, 2, 2, 6, 5, 2], + "edge_type": cudf.Series([0, 0, 0, 0, 0, 0, 0, 0], dtype="int32"), + "edge_id": [5, 10, 15, 20, 25, 30, 35, 40], + "hop_id": cudf.Series([0, 0, 0, 1, 1, 1, 2, 2], dtype="int32"), } ) + map = cudf.Series(m, name="map") + bogus_samples = bogus_samples.join(map, how="outer").sort_index() tempdir = tempfile.TemporaryDirectory() for s in range(256): @@ -159,33 +184,45 @@ def test_cugraph_loader_from_disk_subset(): num_samples += 1 assert sample["t0"]["num_nodes"] == 7 # correct vertex order is [0, 1, 2, 6, 4, 3, 5]; x = [1, 2, 3, 7, 5, 4, 6] - assert sample["t0"]["x"].tolist() == [1, 2, 3, 7, 5, 4, 6] - assert list(sample[("t0", "knows", "t0")]["edge_index"].shape) == [2, 7] + assert sample["t0"]["x"].tolist() == [3, 4, 5, 6, 7, 8, 9] + + edge_index = sample[("t0", "knows", "t0")]["edge_index"] + assert list(edge_index.shape) == [2, 8] + + assert ( + edge_index[0].tolist() + == bogus_samples.sources.dropna().values_host.tolist() + ) + assert ( + edge_index[1].tolist() + == bogus_samples.destinations.dropna().values_host.tolist() + ) assert num_samples == 100 @pytest.mark.skipif(isinstance(torch, MissingModule), reason="torch not available") -def test_cugraph_loader_from_disk_subset_renumbered(): +def test_cugraph_loader_e2e_coo(): + m = [2, 9, 99, 82, 9, 3, 18, 1, 12] + x = torch.randint(3000, (256, 256)).to(torch.float32) F = FeatureStore() - F.add_data(torch.tensor([1, 2, 3, 4, 5, 6, 7]), "t0", "x") + F.add_data(x, "t0", "x") - G = {("t0", "knows", "t0"): 7} - N = {"t0": 7} + G = {("t0", "knows", "t0"): 9999} + N = {"t0": 256} - cugraph_store = CuGraphStore(F, G, N) + cugraph_store = CuGraphStore(F, G, N, order="CSR") bogus_samples = cudf.DataFrame( { - "sources": [0, 1, 2, 3, 4, 5, 6], - "destinations": [6, 4, 3, 2, 2, 1, 5], - "edge_type": cudf.Series([0, 0, 0, 0, 0, 0, 0], dtype="int32"), - "edge_id": [5, 10, 15, 20, 25, 30, 35], - "hop_id": cudf.Series([0, 0, 0, 1, 1, 2, 2], dtype="int32"), + "sources": [0, 1, 2, 3, 4, 5, 6, 6], + "destinations": [5, 4, 3, 2, 2, 6, 5, 2], + "edge_type": cudf.Series([0, 0, 0, 0, 0, 0, 0, 0], dtype="int32"), + "edge_id": [5, 10, 15, 20, 25, 30, 35, 40], + "hop_id": cudf.Series([0, 0, 0, 1, 1, 1, 2, 2], dtype="int32"), } ) - - map = cudf.Series([2, 9, 0, 2, 1, 3, 4, 6, 5], name="map") + map = cudf.Series(m, name="map") bogus_samples = bogus_samples.join(map, how="outer").sort_index() tempdir = tempfile.TemporaryDirectory() @@ -200,22 +237,35 @@ def test_cugraph_loader_from_disk_subset_renumbered(): input_files=list(os.listdir(tempdir.name))[100:200], ) - num_samples = 0 - for sample in loader: - num_samples += 1 - assert sample["t0"]["num_nodes"] == 7 - # correct vertex order is [0, 2, 1, 3, 4, 6, 5]; x = [1, 3, 2, 4, 5, 7, 6] - assert sample["t0"]["x"].tolist() == [1, 3, 2, 4, 5, 7, 6] + convs = [ + torch_geometric.nn.SAGEConv(256, 64, aggr="mean").cuda(), + torch_geometric.nn.SAGEConv(64, 8, aggr="mean").cuda(), + torch_geometric.nn.SAGEConv(8, 1, aggr="mean").cuda(), + ] - edge_index = sample[("t0", "knows", "t0")]["edge_index"] - assert list(edge_index.shape) == [2, 7] - assert ( - edge_index[0].tolist() - == bogus_samples.sources.dropna().values_host.tolist() - ) - assert ( - edge_index[1].tolist() - == bogus_samples.destinations.dropna().values_host.tolist() - ) + trim = trim_to_layer.TrimToLayer() + relu = torch.nn.functional.relu + dropout = torch.nn.functional.dropout - assert num_samples == 100 + for hetero_data in loader: + ei = hetero_data["t0", "knows", "t0"]["edge_index"] + x = hetero_data["t0"]["x"].cuda() + num_sampled_nodes = hetero_data["t0"]["num_sampled_nodes"] + num_sampled_edges = hetero_data["t0", "knows", "t0"]["num_sampled_edges"] + + print(num_sampled_nodes, num_sampled_edges) + + for i in range(len(convs)): + x, ei, _ = trim(i, num_sampled_nodes, num_sampled_edges, x, ei, None) + + s = x.shape[0] + + x = convs[i](x, ei, size=(s, s)) + x = relu(x) + x = dropout(x, p=0.5) + print(x.shape) + + print(x.shape) + x = x.narrow(dim=0, start=0, length=x.shape[0] - num_sampled_nodes[1]) + + assert list(x.shape) == [3, 1] diff --git a/python/cugraph-pyg/cugraph_pyg/tests/test_cugraph_sampler.py b/python/cugraph-pyg/cugraph_pyg/tests/test_cugraph_sampler.py index 08a8625b33b..84f62e80c9d 100644 --- a/python/cugraph-pyg/cugraph_pyg/tests/test_cugraph_sampler.py +++ b/python/cugraph-pyg/cugraph_pyg/tests/test_cugraph_sampler.py @@ -17,7 +17,9 @@ import pytest from cugraph_pyg.data import CuGraphStore -from cugraph_pyg.sampler.cugraph_sampler import _sampler_output_from_sampling_results +from cugraph_pyg.sampler.cugraph_sampler import ( + _sampler_output_from_sampling_results_heterogeneous, +) from cugraph.utilities.utils import import_optional, MissingModule from cugraph import uniform_neighbor_sample @@ -29,7 +31,7 @@ @pytest.mark.skipif(isinstance(torch, MissingModule), reason="torch not available") def test_neighbor_sample(basic_graph_1): F, G, N = basic_graph_1 - cugraph_store = CuGraphStore(F, G, N) + cugraph_store = CuGraphStore(F, G, N, order="CSR") batches = cudf.DataFrame( { @@ -49,7 +51,7 @@ def test_neighbor_sample(basic_graph_1): return_offsets=False, ).sort_values(by=["sources", "destinations"]) - out = _sampler_output_from_sampling_results( + out = _sampler_output_from_sampling_results_heterogeneous( sampling_results=sampling_results, renumber_map=None, graph_store=cugraph_store, @@ -77,7 +79,7 @@ def test_neighbor_sample(basic_graph_1): # check the hop dictionaries assert len(out.num_sampled_nodes) == 1 - assert out.num_sampled_nodes["vt1"].tolist() == [4, 4] + assert out.num_sampled_nodes["vt1"].tolist() == [4, 1] assert len(out.num_sampled_edges) == 1 assert out.num_sampled_edges[("vt1", "pig", "vt1")].tolist() == [6] @@ -87,7 +89,7 @@ def test_neighbor_sample(basic_graph_1): @pytest.mark.skipif(isinstance(torch, MissingModule), reason="torch not available") def test_neighbor_sample_multi_vertex(multi_edge_multi_vertex_graph_1): F, G, N = multi_edge_multi_vertex_graph_1 - cugraph_store = CuGraphStore(F, G, N) + cugraph_store = CuGraphStore(F, G, N, order="CSR") batches = cudf.DataFrame( { @@ -107,7 +109,7 @@ def test_neighbor_sample_multi_vertex(multi_edge_multi_vertex_graph_1): with_batch_ids=True, ).sort_values(by=["sources", "destinations"]) - out = _sampler_output_from_sampling_results( + out = _sampler_output_from_sampling_results_heterogeneous( sampling_results=sampling_results, renumber_map=None, graph_store=cugraph_store, @@ -132,8 +134,8 @@ def test_neighbor_sample_multi_vertex(multi_edge_multi_vertex_graph_1): # check the hop dictionaries assert len(out.num_sampled_nodes) == 2 - assert out.num_sampled_nodes["black"].tolist() == [2, 2] - assert out.num_sampled_nodes["brown"].tolist() == [3, 2] + assert out.num_sampled_nodes["black"].tolist() == [2, 0] + assert out.num_sampled_nodes["brown"].tolist() == [3, 0] assert len(out.num_sampled_edges) == 5 assert out.num_sampled_edges[("brown", "horse", "brown")].tolist() == [2] @@ -147,7 +149,7 @@ def test_neighbor_sample_multi_vertex(multi_edge_multi_vertex_graph_1): def test_neighbor_sample_mock_sampling_results(abc_graph): F, G, N = abc_graph - graph_store = CuGraphStore(F, G, N) + graph_store = CuGraphStore(F, G, N, order="CSR") # let 0, 1 be the start vertices, fanout = [2, 1, 2, 3] mock_sampling_results = cudf.DataFrame( @@ -159,7 +161,7 @@ def test_neighbor_sample_mock_sampling_results(abc_graph): } ) - out = _sampler_output_from_sampling_results( + out = _sampler_output_from_sampling_results_heterogeneous( mock_sampling_results, None, graph_store, None ) @@ -179,9 +181,9 @@ def test_neighbor_sample_mock_sampling_results(abc_graph): assert out.col[("B", "ba", "A")].tolist() == [1, 1] assert len(out.num_sampled_nodes) == 3 - assert out.num_sampled_nodes["A"].tolist() == [2, 0, 1, 0, 1] - assert out.num_sampled_nodes["B"].tolist() == [0, 2, 0, 1, 0] - assert out.num_sampled_nodes["C"].tolist() == [0, 0, 2, 0, 2] + assert out.num_sampled_nodes["A"].tolist() == [2, 0, 0, 0, 0] + assert out.num_sampled_nodes["B"].tolist() == [0, 2, 0, 0, 0] + assert out.num_sampled_nodes["C"].tolist() == [0, 0, 2, 0, 1] assert len(out.num_sampled_edges) == 3 assert out.num_sampled_edges[("A", "ab", "B")].tolist() == [3, 0, 1, 0] diff --git a/python/cugraph-pyg/cugraph_pyg/tests/test_cugraph_store.py b/python/cugraph-pyg/cugraph_pyg/tests/test_cugraph_store.py index 289dd69a829..e815b813050 100644 --- a/python/cugraph-pyg/cugraph_pyg/tests/test_cugraph_store.py +++ b/python/cugraph-pyg/cugraph_pyg/tests/test_cugraph_store.py @@ -199,7 +199,7 @@ def test_renumber_vertices_multi_edge_multi_vertex(multi_edge_multi_vertex_graph def test_renumber_edges(abc_graph): F, G, N = abc_graph - graph_store = CuGraphStore(F, G, N) + graph_store = CuGraphStore(F, G, N, order="CSR") # let 0, 1 be the start vertices, fanout = [2, 1, 2, 3] mock_sampling_results = cudf.DataFrame( From 9c96b2613f029eb9616c19aefb38a689c1267bae Mon Sep 17 00:00:00 2001 From: Vibhu Jawa Date: Mon, 25 Sep 2023 07:49:19 -0700 Subject: [PATCH 012/111] Update dgl benchmarks (#3775) This PR adds upstream DGL benchmarks , I will expand this to add `cugraph-dgl` soon. image CC: @tingyu66 , @BradReesWork Authors: - Vibhu Jawa (https://github.com/VibhuJawa) Approvers: - Brad Rees (https://github.com/BradReesWork) - Alex Barghi (https://github.com/alexbarghi-nv) URL: https://github.com/rapidsai/cugraph/pull/3775 --- .../cugraph-dgl/scale-benchmarks/__init__.py | 0 .../scale-benchmarks/dgl_benchmark.py | 152 ++++++++++++++++++ .../scale-benchmarks/load_graph_feats.py | 123 ++++++++++++++ .../cugraph-dgl/scale-benchmarks/model.py | 110 +++++++++++++ 4 files changed, 385 insertions(+) create mode 100644 benchmarks/cugraph-dgl/scale-benchmarks/__init__.py create mode 100644 benchmarks/cugraph-dgl/scale-benchmarks/dgl_benchmark.py create mode 100644 benchmarks/cugraph-dgl/scale-benchmarks/load_graph_feats.py create mode 100644 benchmarks/cugraph-dgl/scale-benchmarks/model.py diff --git a/benchmarks/cugraph-dgl/scale-benchmarks/__init__.py b/benchmarks/cugraph-dgl/scale-benchmarks/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/benchmarks/cugraph-dgl/scale-benchmarks/dgl_benchmark.py b/benchmarks/cugraph-dgl/scale-benchmarks/dgl_benchmark.py new file mode 100644 index 00000000000..3762226d570 --- /dev/null +++ b/benchmarks/cugraph-dgl/scale-benchmarks/dgl_benchmark.py @@ -0,0 +1,152 @@ +# Copyright (c) 2018-2023, NVIDIA CORPORATION. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import json +import os +import time +import dgl +from dgl.dataloading import MultiLayerNeighborSampler, DataLoader +import pandas as pd +import torch +from model import run_1_epoch +from argparse import ArgumentParser +from load_graph_feats import load_edges_from_disk, load_node_labels, load_node_features + +class DataLoaderArgs: + def __init__(self, args): + self.dataset_path = args.dataset_path + self.replication_factors = [int(x) for x in args.replication_factors.split(",")] + self.fanouts = [[int(y) for y in x.split("_")] for x in args.fanouts.split(",")] + self.batch_sizes = [int(x) for x in args.batch_sizes.split(",")] + self.use_uva = not args.do_not_use_uva + + + +def create_dataloader(g, train_idx, batch_size, fanouts, use_uva): + print("Creating dataloader", flush=True) + st = time.time() + if use_uva: + train_idx = {k: v.to("cuda") for k, v in train_idx.items()} + sampler = MultiLayerNeighborSampler(fanouts=fanouts) + dataloader = DataLoader( + g, + train_idx, + sampler, + num_workers=0, + batch_size=batch_size, + use_uva=use_uva, + shuffle=False, + drop_last=False, + ) + et = time.time() + print(f"Time to create dataloader = {et - st:.2f} seconds", flush=True) + return dataloader + + + +def create_dgl_graph_from_disk(dataset_path, replication_factor=1): + """ + Create a DGL graph from a dataset on disk. + Args: + dataset_path: Path to the dataset on disk. + replication_factor: Number of times to replicate the edges. + Returns: + DGLGraph: DGLGraph with the loaded dataset. + """ + with open(os.path.join(dataset_path, "meta.json"), "r") as f: + input_meta = json.load(f) + + parquet_path = os.path.join(dataset_path, "parquet") + graph_data = load_edges_from_disk( + parquet_path, replication_factor, input_meta + ) + label_data = load_node_labels(dataset_path, replication_factor, input_meta) + if replication_factor <8 : + feat_data = load_node_features(dataset_path, replication_factor, node_type='paper') + else: + feat_data = None + print("labels and features loaded ", flush=True) + + g = dgl.heterograph(graph_data) + + return g, label_data, feat_data + + +def main(args): + print(f"Running dgl dataloading benchmark with the following parameters:\n" + f"Dataset path = {args.dataset_path}\n" + f"Replication factors = {args.replication_factors}\n" + f"Fanouts = {args.fanouts}\n" + f"Batch sizes = {args.batch_sizes}\n" + f"Use UVA = {args.use_uva}\n" + f"{'=' * 30}") + + time_ls = [] + for replication_factor in args.replication_factors: + start_time = time.time() + g, label_data, feat_data = create_dgl_graph_from_disk(args.dataset_path, replication_factor) + elapsed_time = time.time() - start_time + + print(f"Replication factor = {replication_factor}\n" + f"G has {g.num_edges():,} edges and took {elapsed_time:.2f} seconds to load", flush=True) + + train_idx = {"paper": label_data["paper"]["train_idx"]} + y = label_data["paper"]["y"] + r_time_ls = e2e_benchmark(g, feat_data, y, train_idx, args.fanouts, args.batch_sizes, use_uva=args.use_uva) + [x.update({"replication_factor": replication_factor}) for x in r_time_ls] + [x.update({"num_edges": g.num_edges()}) for x in r_time_ls] + time_ls.extend(r_time_ls) + + print(f"Benchmark completed for replication factor = {replication_factor}\n{'=' * 30}", flush=True) + + df = pd.DataFrame(time_ls) + df.to_csv("dgl_e2e_benchmark.csv", index=False) + print(f"Benchmark completed for all replication factors\n{'=' * 30}", flush=True) + + +def e2e_benchmark(g, feat, y, train_idx, fanouts, batch_sizes, use_uva): + """ + Run the e2e_benchmark + Args: + g: DGLGraph + feat: Tensor containing the features. + y: Tensor containing the labels. + train_idx: Tensor containing the training indices. + fanouts: List of fanouts to use for the dataloader. + batch_sizes: List of batch sizes to use for the dataloader. + use_uva: Whether to use unified virtual address space. + model_backend: Backend of model to use. + """ + time_ls = [] + for fanout in fanouts: + for batch_size in batch_sizes: + dataloader = create_dataloader(g, train_idx, batch_size, fanout, use_uva) + time_d = run_1_epoch(dataloader, feat, y, fanout, batch_size, model_backend='dgl') + time_ls.append(time_d) + print("="*30) + return time_ls + + + +def parse_arguments(): + parser = ArgumentParser() + parser.add_argument("--dataset_path", type=str, default="/raid/vjawa/ogbn_papers100M") + parser.add_argument("--replication_factors", type=str, default="2") + parser.add_argument("--fanouts", type=str, default="10_10_10") + parser.add_argument("--batch_sizes", type=str, default="512,1024,8192,16384") + parser.add_argument("--do_not_use_uva", action="store_true") + return parser.parse_args() + +if __name__ == "__main__": + arguments = parse_arguments() + main(DataLoaderArgs(arguments)) diff --git a/benchmarks/cugraph-dgl/scale-benchmarks/load_graph_feats.py b/benchmarks/cugraph-dgl/scale-benchmarks/load_graph_feats.py new file mode 100644 index 00000000000..4f0f81c70e1 --- /dev/null +++ b/benchmarks/cugraph-dgl/scale-benchmarks/load_graph_feats.py @@ -0,0 +1,123 @@ +# Copyright (c) 2018-2023, NVIDIA CORPORATION. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import numpy as np +import pandas as pd +import torch +import os + + +def load_edges_from_disk(parquet_path, replication_factor, input_meta): + """ + Load the edges from disk into a graph data dictionary. + Args: + parquet_path: Path to the parquet directory. + replication_factor: Number of times to replicate the edges. + input_meta: Input meta data. + Returns: + dict: Dictionary of edge types to a tuple of (src, dst) + """ + graph_data = {} + + for edge_type in input_meta["num_edges"].keys(): + print(f"Loading edge index for edge type {edge_type} for replication factor = {replication_factor}") + + canonical_edge_type = tuple(edge_type.split("__")) + edge_index = pd.read_parquet(os.path.join(parquet_path, edge_type, "edge_index.parquet")) + edge_index = { + "src": torch.from_numpy(edge_index.src.values), + "dst": torch.from_numpy(edge_index.dst.values), + } + + if replication_factor > 1: + src_list, dst_list = replicate_edges(edge_index, canonical_edge_type, replication_factor, input_meta) + edge_index["src"] = torch.cat(src_list).contiguous() + edge_index["dst"] = torch.cat(dst_list).contiguous() + + graph_data[canonical_edge_type] = edge_index["src"], edge_index["dst"] + + print("Read Edge Data") + return graph_data + + +def replicate_edges(edge_index, canonical_edge_type, replication_factor, input_meta): + src_list = [edge_index["src"]] + dst_list = [edge_index["dst"]] + + for r in range(1, replication_factor): + new_src = edge_index["src"] + (r * input_meta["num_nodes"][canonical_edge_type[0]]) + new_dst = edge_index["dst"] + (r * input_meta["num_nodes"][canonical_edge_type[2]]) + src_list.append(new_src) + dst_list.append(new_dst) + + return src_list, dst_list + + + + +def load_node_labels(dataset_path, replication_factor, input_meta): + num_nodes_dict = {node_type: t * replication_factor for node_type, t in input_meta["num_nodes"].items()} + node_data = {} + + for node_type in input_meta["num_nodes"].keys(): + node_data[node_type] = {} + label_path = os.path.join(dataset_path, "parquet", node_type, "node_label.parquet") + + if os.path.exists(label_path): + node_data[node_type] = process_node_label(label_path, node_type, replication_factor, num_nodes_dict, input_meta) + + else: + node_data[node_type]["num_nodes"] = num_nodes_dict[node_type] + + print("Loaded node labels", flush=True) + return node_data + +def process_node_label(label_path, node_type, replication_factor, num_nodes_dict, input_meta): + node_label = pd.read_parquet(label_path) + + if replication_factor > 1: + node_label = replicate_node_label(node_label, node_type, replication_factor, input_meta) + + node_label_tensor = torch.full((num_nodes_dict[node_type],), -1, dtype=torch.float32) + node_label_tensor[torch.as_tensor(node_label.node.values)] = torch.as_tensor(node_label.label.values) + + del node_label + + return { + "train_idx": (node_label_tensor > -1).contiguous().nonzero().view(-1), + "y": node_label_tensor.contiguous().long() + } + + +def replicate_node_label(node_label, node_type, replication_factor, input_meta): + base_num_nodes = input_meta["num_nodes"][node_type] + + replicated_df = pd.DataFrame({ + "node": pd.concat([node_label.node + (r * base_num_nodes) for r in range(1, replication_factor)]), + "label": pd.concat([node_label.label for _ in range(1, replication_factor)]) + }) + + return pd.concat([node_label, replicated_df]).reset_index(drop=True) + + +def load_node_features(dataset_path, replication_factor, node_type): + print("Loading node features", flush=True) + node_type_path = os.path.join(dataset_path, "npy", node_type) + if replication_factor == 1: + fname = os.path.join(node_type_path, "node_feat.npy") + else: + fname = os.path.join(node_type_path, f"node_feat_{replication_factor}x.npy") + + feat = torch.from_numpy(np.load(fname)) + print("Loaded node features", flush=True) + return feat diff --git a/benchmarks/cugraph-dgl/scale-benchmarks/model.py b/benchmarks/cugraph-dgl/scale-benchmarks/model.py new file mode 100644 index 00000000000..506e3bd5227 --- /dev/null +++ b/benchmarks/cugraph-dgl/scale-benchmarks/model.py @@ -0,0 +1,110 @@ +# Copyright (c) 2018-2023, NVIDIA CORPORATION. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import torch +import torch.nn.functional as F +import time + + +class GNN(torch.nn.Module): + def __init__(self, in_channels, hidden_channels, out_channels, num_layers, model_backend='dgl'): + if model_backend == 'dgl': + from dgl.nn import SAGEConv + else: + from cugraph_dgl.nn import SAGEConv + + super(GNN, self).__init__() + self.convs = torch.nn.ModuleList() + for _ in range(num_layers - 1): + self.convs.append(SAGEConv(in_channels, hidden_channels, aggregator_type='mean')) + in_channels = hidden_channels + self.convs.append(SAGEConv(hidden_channels, out_channels, aggregator_type='mean')) + + def forward(self, blocks, x): + for i, conv in enumerate(self.convs): + x = conv(blocks[i], x) + if i != len(self.convs) - 1: + x = F.relu(x) + return x + + +def create_model(feat_size, num_classes, num_layers, model_backend='dgl'): + model = GNN(feat_size, 64, num_classes, num_layers, model_backend=model_backend) + model = model.to('cuda') + model.train() + return model + +def train_model(model, dataloader, opt, feat, y): + times = {key: 0 for key in ['mfg_creation', 'feature', 'm_fwd', 'm_bkwd']} + epoch_st = time.time() + mfg_st = time.time() + for input_nodes, output_nodes, blocks in dataloader: + times['mfg_creation'] += time.time() - mfg_st + if feat is not None: + fst = time.time() + input_nodes = input_nodes.to('cpu') + input_feat = feat[input_nodes] + input_feat = input_feat.to('cuda') + if isinstance(output_nodes, dict): + output_nodes = output_nodes['paper'] + output_nodes = output_nodes.to(y.device) + y_batch = y[output_nodes].to('cuda') + times['feature'] += time.time() - fst + + m_fwd_st = time.time() + y_hat = model(blocks, input_feat) + times['m_fwd'] += time.time() - m_fwd_st + + m_bkwd_st = time.time() + loss = F.cross_entropy(y_hat, y_batch) + opt.zero_grad() + loss.backward() + opt.step() + times['m_bkwd'] += time.time() - m_bkwd_st + mfg_st = time.time() + + print(f"Epoch time = {time.time() - epoch_st:.2f} seconds") + + return times + +def analyze_time(dataloader, times, epoch_time, fanout, batch_size): + num_batches = len(dataloader) + time_d = { + "fanout": fanout, + "batch_size": batch_size, + "epoch_time": epoch_time, + "epoch_time_per_batch": epoch_time / num_batches, + "num_batches": num_batches, + } + for key, value in times.items(): + time_d[f"{key}_time_per_epoch"] = value + time_d[f"{key}_time_per_batch"] = value / num_batches + + print(f"Time analysis for fanout = {fanout}, batch_size = {batch_size}") + for k in time_d.keys(): + if 'time_per_epoch' in str(k): + print(f"{k} = {time_d[k]:.2f} seconds") + return time_d + +def run_1_epoch(dataloader, feat, y, fanout, batch_size, model_backend): + if feat is not None: + model = create_model(feat.shape[1], 172, len(fanout), model_backend=model_backend) + opt = torch.optim.Adam(model.parameters(), lr=0.01) + else: + model = None + opt = None + epoch_st = time.time() + times = train_model(model, dataloader, opt, feat, y) + epoch_time = time.time() - epoch_st + time_d = analyze_time(dataloader, times, epoch_time, fanout, batch_size) + return time_d From c11eff23926dd483d23444a4757629e8ed069683 Mon Sep 17 00:00:00 2001 From: Don Acosta <97529984+acostadon@users.noreply.github.com> Date: Mon, 25 Sep 2023 21:59:29 -0400 Subject: [PATCH 013/111] similarity notebook to compare link prediction algos (#3868) New notebook to compare link prediction Dependent on dining_prefs being added to datasets api in PR #3866 Authors: - Don Acosta (https://github.com/acostadon) Approvers: - Brad Rees (https://github.com/BradReesWork) URL: https://github.com/rapidsai/cugraph/pull/3868 --- .../link_prediction/similarity_combined.ipynb | 217 ++++++++++++++++++ 1 file changed, 217 insertions(+) create mode 100644 notebooks/algorithms/link_prediction/similarity_combined.ipynb diff --git a/notebooks/algorithms/link_prediction/similarity_combined.ipynb b/notebooks/algorithms/link_prediction/similarity_combined.ipynb new file mode 100644 index 00000000000..cd80ee34002 --- /dev/null +++ b/notebooks/algorithms/link_prediction/similarity_combined.ipynb @@ -0,0 +1,217 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Similarity Compared\n", + "----\n", + "\n", + "In this notebook, we will compute vertex similarity scores using the various cuGraph algorithms. We will then compare the similarities scores in tables.\n", + "\n", + "| Author Credit | Date | Update | cuGraph Version | Test Hardware |\n", + "| --------------|------------|------------------|-----------------|-----------------------|\n", + "| Don Acosta | 09/25/2023 | created | 23.10 nightly | AMPERE A6000 CUDA 11.7|\n", + "\n", + "\n", + "**Note: On large graphs these algorithms can take prohibitive time or memory. The notebook will show how to run on defined pairs instead.**\n", + "\n", + "The Similarity algorithms in cuGraph use different methods to compare pairs of vertices. All of them use the intersection of the set of adjacent nodes for the set overlap. However each of the three algorithms differ on the denominator to determine the similarity coefficients. All three are normalized between zero and one. where zero is no overlap at all and one means identical adjacencies.\n", + "\n", + "__Jaccard Similarity__
\n", + "The [Jaccard similarity](https://en.wikipedia.org/wiki/Jaccard_index) measure was developed by botonist, Paul Jaccard who used the measure to compare plant species. His work popularized the measure's use in in other fields as well.\n", + "\n", + "It can be expressed as:
\n", + "$\\text{Jaccard similarity} = \\frac{|A \\cap B|}{|A \\cup B|}$\n", + "\n", + "__Overlap Similarity__
\n", + "The [Overlap Similarity](https://en.wikipedia.org/wiki/Overlap_coefficient) is also known as the Szymkiewicz–Simpson coefficient. It is often used to compare binary and categorical data in the fields of Genome analysis, recommender systems and anomaly detection. It differs from the Jaccard measure above in that it uses the size of the smaller of the two set sizes as the denominator.\n", + "\n", + "It can be expressed as\n", + "\n", + "$oc(A,B)=\\frac{|A|\\cap|B|}{min(|A|,|B|)}$\n", + "\n", + "__Sørensen-Dice Coefficient__
\n", + "The [Sørensen coefficient](https://en.wikipedia.org/wiki/S%C3%B8rensen%E2%80%93Dice_coefficient#) is known as the Sørensen-Dice coefficient. It was independently developed for use by botonists Lee Raymond Dice and Thorvald Sørensen. Although originating in the field of Botony, the coefficient is now used in computer vision, Natural Language Processing(NLP) and Data Mining among other fields.\n", + "It differs from Jaccard and Overlap in that the calculation doubles the intersection size and divides it by the sum of the two set sizes.\n", + "\n", + "It can be expressed as\n", + "\n", + "Sørensen coefficient = $\\left(2 * |A \\cap B| \\right) \\over \\left(|A| + |B| \\right)$\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "---\n", + "# Now for the code !" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Load the required dependencies." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import cugraph\n", + "from cugraph.datasets import dining_prefs\n", + "# only needed to display results in a table \n", + "from IPython.display import display_html " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Function that calls all the cuGraph similarity/link prediction algorithms " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def compute_similarity(G,pairs=None):\n", + " _jdf = cugraph.jaccard(G,pairs)\n", + " _jdf2 = _jdf[ (_jdf['first'] != _jdf['second'] ) ]\n", + " _odf = cugraph.overlap(G,pairs)\n", + " _odf2 = _odf[ (_odf['first'] != _odf['second'] ) ]\n", + " _sdf = cugraph.sorensen_coefficient(G,pairs)\n", + " _sdf2 = _sdf[ (_sdf['first'] != _sdf['second'] ) ]\n", + " return _jdf2, _odf2, _sdf2" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Function to put all the results in a convenient table" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Print function\n", + "def print_similarity(jdf,odf,sdf,num_records=5):\n", + "\n", + " js_top = jdf.sort_values(by='jaccard_coeff', ascending=False).head(num_records).to_pandas()\n", + " os_top = odf.sort_values(by='overlap_coeff', ascending=False).head(num_records).to_pandas()\n", + " ss_top = sdf.sort_values(by='sorensen_coeff', ascending=False).head(num_records).to_pandas()\n", + " \n", + " df1_styler = js_top.style.set_table_attributes(\"style='display:inline'\").set_caption('Jaccard').hide(axis='index')\n", + " df2_styler = os_top.style.set_table_attributes(\"style='display:inline'\").set_caption('Overlap').hide(axis='index')\n", + " df3_styler = ss_top.style.set_table_attributes(\"style='display:inline'\").set_caption('Sørensen').hide(axis='index')\n", + "\n", + " display_html(df1_styler._repr_html_()+df2_styler._repr_html_()+df3_styler._repr_html_(), raw=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Create the graph from the Dining preferences data set." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "G = dining_prefs.get_graph(download=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Run the three similarity Algorithms and print out the five links with the highest scores." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "jdf, odf, sdf = compute_similarity(G)\n", + "print_similarity(jdf,odf,sdf)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now find the the complete set of two-hop neigbors and compare them instead of just using the existing one-hop edges. In a larger graph, this will run considerably faster since the default " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# this cugraph algorithm pulls a set containing every pair of vertices\n", + "# that are within 2-hops of each other\n", + "two_hops_pairs = G.get_two_hop_neighbors()\n", + "\n", + "jdf_hops, odf_hops, sdf_hops = compute_similarity(G,pairs=two_hops_pairs)\n", + "print_similarity(jdf_hops,odf_hops,sdf_hops)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### It's that easy with cuGraph" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "----\n", + "Copyright (c) 2023, NVIDIA CORPORATION.\n", + "\n", + "Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n", + "\n", + "Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "cugraph_0802", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.12" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +} From b199bf01a2dbb3a8bc89198c6d35fd5a0444e213 Mon Sep 17 00:00:00 2001 From: Vibhu Jawa Date: Tue, 26 Sep 2023 05:33:13 -0700 Subject: [PATCH 014/111] [REVIEW] Add Pure DGL Dataloading benchmark (#3660) This PR adds the DGL data loading benchmark: Arguments supported: - dataset_path: path to the dataset - replication_factors: replication factors for number of edges - fanouts: fanouts - batch_sizes: batch sizes ```bash python3 dgl_dataloading.py --dataset_path "/datasets/abarghi/ogbn_papers100M" \ --replication_factors "1,2,4" \ --fanouts "25_25,10_10_10,5_10_20" \ --batch_sizes "512,1024" ``` This produces the following results on a V100: | Fanout | Batch Size | Data Loading Time per Epoch | Data Loading Time per Batch | Number of Edges | Number of Batches | Replication Factor | |--------|------------|-----------------------------|-----------------------------|-----------------|-------------------|--------------------| | [25, 25] | 512 | 9.48 | 0.0031 | 1615685872 | 3022 | 1 | | [25, 25] | 1024 | 6.39 | 0.0042 | 1615685872 | 1511 | 1 | | [10, 10, 10] | 512 | 15.91 | 0.0053 | 1615685872 | 3022 | 1 | | [10, 10, 10] | 1024 | 11.64 | 0.0077 | 1615685872 | 1511 | 1 | | [5, 10, 20] | 512 | 17.73 | 0.0059 | 1615685872 | 3022 | 1 | | [5, 10, 20] | 1024 | 13.52 | 0.0089 | 1615685872 | 1511 | 1 | | [25, 25] | 512 | 19.44 | 0.0032 | 3231371744 | 6043 | 2 | | [25, 25] | 1024 | 12.98 | 0.0043 | 3231371744 | 3022 | 2 | | [10, 10, 10] | 512 | 32.88 | 0.0054 | 3231371744 | 6043 | 2 | | [10, 10, 10] | 1024 | 24.35 | 0.0081 | 3231371744 | 3022 | 2 | | [5, 10, 20] | 512 | 38.35 | 0.0063 | 3231371744 | 6043 | 2 | | [5, 10, 20] | 1024 | 28.93 | 0.0096 | 3231371744 | 3022 | 2 | | [25, 25] | 512 | 37.31 | 0.0031 | 6462743488 | 12085 | 4 | | [25, 25] | 1024 | 25.15 | 0.0042 | 6462743488 | 6043 | 4 | | [10, 10, 10] | 512 | 64.29 | 0.0053 | 6462743488 | 12085 | 4 | | [10, 10, 10] | 1024 | 47.13 | 0.0078 | 6462743488 | 6043 | 4 | | [5, 10, 20] | 512 | 72.90 | 0.0060 | 6462743488 | 12085 | 4 | | [5, 10, 20] | 1024 | 56.70 | 0.0094 | 6462743488 | 6043 | 4 | | [25, 25] | 512 | 80.99 | 0.0034 | 12925486976 | 24169 | 8 | | [25, 25] | 1024 | 50.89 | 0.0042 | 12925486976 | 12085 | 8 | | [10, 10, 10] | 512 | 129.49 | 0.0054 | 12925486976 | 24169 | 8 | | [10, 10, 10] | 1024 | 93.66 | 0.0078 | 12925486976 | 12085 | 8 | | [5, 10, 20] | 512 | 143.45 | 0.0059 | 12925486976 | 24169 | 8 | | [5, 10, 20] | 1024 | 110.22 | 0.0091 | 12925486976 | 12085 | 8 | Authors: - Vibhu Jawa (https://github.com/VibhuJawa) - Alex Barghi (https://github.com/alexbarghi-nv) - Brad Rees (https://github.com/BradReesWork) Approvers: - Alex Barghi (https://github.com/alexbarghi-nv) - Tingyu Wang (https://github.com/tingyu66) URL: https://github.com/rapidsai/cugraph/pull/3660 --- .../dgl_benchmark.py | 291 ++++++++++++++++++ 1 file changed, 291 insertions(+) create mode 100644 benchmarks/cugraph-dgl/python-script/dgl_dataloading_benchmark/dgl_benchmark.py diff --git a/benchmarks/cugraph-dgl/python-script/dgl_dataloading_benchmark/dgl_benchmark.py b/benchmarks/cugraph-dgl/python-script/dgl_dataloading_benchmark/dgl_benchmark.py new file mode 100644 index 00000000000..0a52703c546 --- /dev/null +++ b/benchmarks/cugraph-dgl/python-script/dgl_dataloading_benchmark/dgl_benchmark.py @@ -0,0 +1,291 @@ +# Copyright (c) 2023, NVIDIA CORPORATION. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +import dgl +import torch +import pandas as pd +import os +import time +import json +import random +import numpy as np +from argparse import ArgumentParser + + +def load_edges_from_disk(parquet_path, replication_factor, input_meta): + """ + Load the edges from disk into a graph data dictionary. + Args: + parquet_path: Path to the parquet directory. + replication_factor: Number of times to replicate the edges. + input_meta: Input meta data. + Returns: + dict: Dictionary of edge types to a tuple of (src, dst) + """ + graph_data = {} + for edge_type in input_meta["num_edges"].keys(): + print( + f"Loading edge index for edge type {edge_type}" + f"for replication factor = {replication_factor}" + ) + can_edge_type = tuple(edge_type.split("__")) + # TODO: Rename `edge_index` to a better name + ei = pd.read_parquet( + os.path.join(parquet_path, edge_type, "edge_index.parquet") + ) + ei = { + "src": torch.from_numpy(ei.src.values), + "dst": torch.from_numpy(ei.dst.values), + } + if replication_factor > 1: + src_ls = [ei["src"]] + dst_ls = [ei["dst"]] + for r in range(1, replication_factor): + new_src = ei["src"] + ( + r * input_meta["num_nodes"][can_edge_type[0]] + ) + src_ls.append(new_src) + new_dst = ei["dst"] + ( + r * input_meta["num_nodes"][can_edge_type[2]] + ) + dst_ls.append(new_dst) + + ei["src"] = torch.cat(src_ls).contiguous() + ei["dst"] = torch.cat(dst_ls).contiguous() + graph_data[can_edge_type] = ei["src"], ei["dst"] + print("Graph Data compiled") + return graph_data + + +def load_node_labels(dataset_path, replication_factor, input_meta): + num_nodes_dict = { + node_type: t * replication_factor + for node_type, t in input_meta["num_nodes"].items() + } + node_data = {} + for node_type in input_meta["num_nodes"].keys(): + node_data[node_type] = {} + label_path = os.path.join( + dataset_path, "parquet", node_type, "node_label.parquet" + ) + if os.path.exists(label_path): + node_label = pd.read_parquet(label_path) + if replication_factor > 1: + base_num_nodes = input_meta["num_nodes"][node_type] + dfr = pd.DataFrame( + { + "node": pd.concat( + [ + node_label.node + (r * base_num_nodes) + for r in range(1, replication_factor) + ] + ), + "label": pd.concat( + [ + node_label.label + for r in range(1, replication_factor) + ] + ), + } + ) + node_label = pd.concat([node_label, dfr]).reset_index( + drop=True + ) + + node_label_tensor = torch.full( + (num_nodes_dict[node_type],), -1, dtype=torch.float32 + ) + node_label_tensor[ + torch.as_tensor(node_label.node.values) + ] = torch.as_tensor(node_label.label.values) + + del node_label + node_data[node_type]["train_idx"] = ( + (node_label_tensor > -1).contiguous().nonzero().view(-1) + ) + node_data[node_type]["y"] = node_label_tensor.contiguous() + else: + node_data[node_type]["num_nodes"] = num_nodes_dict[node_type] + return node_data + + +def create_dgl_graph_from_disk(dataset_path, replication_factor=1): + """ + Create a DGL graph from a dataset on disk. + Args: + dataset_path: Path to the dataset on disk. + replication_factor: Number of times to replicate the edges. + Returns: + DGLGraph: DGLGraph with the loaded dataset. + """ + with open(os.path.join(dataset_path, "meta.json"), "r") as f: + input_meta = json.load(f) + + parquet_path = os.path.join(dataset_path, "parquet") + graph_data = load_edges_from_disk( + parquet_path, replication_factor, input_meta + ) + node_data = load_node_labels(dataset_path, replication_factor, input_meta) + g = dgl.heterograph(graph_data) + + return g, node_data + + +def create_dataloader(g, train_idx, batch_size, fanouts, use_uva): + """ + Create a DGL dataloader from a DGL graph. + Args: + g: DGLGraph to create the dataloader from. + train_idx: Tensor containing the training indices. + batch_size: Batch size to use for the dataloader. + fanouts: List of fanouts to use for the dataloader. + use_uva: Whether to use unified virtual address space. + Returns: + DGLGraph: DGLGraph with the loaded dataset. + """ + + print("Creating dataloader", flush=True) + st = time.time() + if use_uva: + train_idx = {k: v.to("cuda") for k, v in train_idx.items()} + sampler = dgl.dataloading.MultiLayerNeighborSampler(fanouts=fanouts) + dataloader = dgl.dataloading.DataLoader( + g, + train_idx, + sampler, + num_workers=0, + batch_size=batch_size, + use_uva=use_uva, + shuffle=False, + drop_last=False, + ) + et = time.time() + print(f"Time to create dataloader = {et - st:.2f} seconds") + return dataloader + + +def dataloading_benchmark(g, train_idx, fanouts, batch_sizes, use_uva): + """ + Run the dataloading benchmark. + Args: + g: DGLGraph + train_idx: Tensor containing the training indices. + fanouts: List of fanouts to use for the dataloader. + batch_sizes: List of batch sizes to use for the dataloader. + use_uva: Whether to use unified virtual address space. + """ + time_ls = [] + for fanout in fanouts: + for batch_size in batch_sizes: + dataloader = create_dataloader( + g, + train_idx, + batch_size=batch_size, + fanouts=fanout, + use_uva=use_uva, + ) + dataloading_st = time.time() + for input_nodes, output_nodes, blocks in dataloader: + pass + dataloading_et = time.time() + dataloading_time = dataloading_et - dataloading_st + time_d = { + "fanout": fanout, + "batch_size": batch_size, + "dataloading_time_per_epoch": dataloading_time, + "dataloading_time_per_batch": dataloading_time / len(dataloader), + "num_edges": g.num_edges(), + "num_batches": len(dataloader), + } + time_ls.append(time_d) + + print("Dataloading completed") + print(f"Fanout = {fanout}, batch_size = {batch_size}") + print( + f"Time taken {dataloading_time:.2f} ", + f"seconds for num batches {len(dataloader)}", + flush=True, + ) + print("==============================================") + return time_ls + +def set_seed(seed): + random.seed(seed) + np.random.seed(seed) + torch.manual_seed(seed) + torch.cuda.manual_seed_all(seed) + +if __name__ == "__main__": + parser = ArgumentParser() + parser.add_argument( + "--dataset_path", type=str, default="/datasets/abarghi/ogbn_papers100M" + ) + parser.add_argument("--replication_factors", type=str, default="1,2,4,8") + parser.add_argument( + "--fanouts", type=str, default="25_25,10_10_10,5_10_20" + ) + parser.add_argument("--batch_sizes", type=str, default="512,1024") + parser.add_argument("--do_not_use_uva", action="store_true") + parser.add_argument("--seed", type=int, default=42) + args = parser.parse_args() + + if args.do_not_use_uva: + use_uva = False + else: + use_uva = True + set_seed(args.seed) + replication_factors = [int(x) for x in args.replication_factors.split(",")] + fanouts = [[int(y) for y in x.split("_")] for x in args.fanouts.split(",")] + batch_sizes = [int(x) for x in args.batch_sizes.split(",")] + + print("Running dgl dataloading benchmark with the following parameters:") + print(f"Dataset path = {args.dataset_path}") + print(f"Replication factors = {replication_factors}") + print(f"Fanouts = {fanouts}") + print(f"Batch sizes = {batch_sizes}") + print(f"Use UVA = {use_uva}") + print("==============================================") + + time_ls = [] + for replication_factor in replication_factors: + st = time.time() + g, node_data = create_dgl_graph_from_disk( + dataset_path=args.dataset_path, + replication_factor=replication_factor, + ) + et = time.time() + print(f"Replication factor = {replication_factor}") + print( + f"G has {g.num_edges()} edges and took", + f" {et - st:.2f} seconds to load" + ) + train_idx = {"paper": node_data["paper"]["train_idx"]} + r_time_ls = dataloading_benchmark( + g, train_idx, fanouts, batch_sizes, use_uva=use_uva + ) + print( + "Benchmark completed for replication factor = ", replication_factor + ) + print("==============================================") + # Add replication factor to the time list + [ + x.update({"replication_factor": replication_factor}) + for x in r_time_ls + ] + time_ls.extend(r_time_ls) + + df = pd.DataFrame(time_ls) + df.to_csv("dgl_dataloading_benchmark.csv", index=False) + print("Benchmark completed for all replication factors") + print("==============================================") From 8b02e241617df8bac33d0fd69b03046d2ddaf2d9 Mon Sep 17 00:00:00 2001 From: Naim <110031745+naimnv@users.noreply.github.com> Date: Tue, 26 Sep 2023 15:43:02 +0200 Subject: [PATCH 015/111] Enable temporarily disabled MG tests (#3837) Enable TEMPORARILY disable single-GPU "MG" tests And Skip deleting copied Dataframe while creating distributed graph from cudf edge-lists. Ideally we would like to merger this PR once the [issue 3790](https://github.com/rapidsai/cugraph/issues/3790) is closed, but me might need to merger it if the issue is not resolved before the next release. Authors: - Naim (https://github.com/naimnv) Approvers: - Rick Ratzel (https://github.com/rlratzel) - AJ Schmidt (https://github.com/ajschmidt8) URL: https://github.com/rapidsai/cugraph/pull/3837 --- ci/test_python.sh | 6 +----- ci/test_wheel.sh | 4 +--- .../graph_implementation/simpleDistributedGraph.py | 5 ++++- python/cugraph/cugraph/tests/traversal/test_bfs_mg.py | 5 ++++- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/ci/test_python.sh b/ci/test_python.sh index 14886909fc9..7b0077991ae 100755 --- a/ci/test_python.sh +++ b/ci/test_python.sh @@ -63,10 +63,6 @@ pytest \ tests popd -# FIXME: TEMPORARILY disable single-GPU "MG" testing until -# https://github.com/rapidsai/cugraph/issues/3790 is closed -# When closed, replace -k "not _mg" with -# -k "not test_property_graph_mg" \ rapids-logger "pytest cugraph" pushd python/cugraph/cugraph export DASK_WORKER_DEVICES="0" @@ -79,7 +75,7 @@ pytest \ --cov=cugraph \ --cov-report=xml:"${RAPIDS_COVERAGE_DIR}/cugraph-coverage.xml" \ --cov-report=term \ - -k "not _mg" \ + -k "not test_property_graph_mg" \ tests popd diff --git a/ci/test_wheel.sh b/ci/test_wheel.sh index b62635d08b4..146186ae2e7 100755 --- a/ci/test_wheel.sh +++ b/ci/test_wheel.sh @@ -18,7 +18,5 @@ arch=$(uname -m) if [[ "${arch}" == "aarch64" && ${RAPIDS_BUILD_TYPE} == "pull-request" ]]; then python ./ci/wheel_smoke_test_${package_name}.py else - # FIXME: TEMPORARILY disable single-GPU "MG" testing until - # https://github.com/rapidsai/cugraph/issues/3790 is closed - RAPIDS_DATASET_ROOT_DIR=`pwd`/datasets python -m pytest -k "not _mg" ./python/${package_name}/${package_name}/tests + RAPIDS_DATASET_ROOT_DIR=`pwd`/datasets python -m pytest ./python/${package_name}/${package_name}/tests fi diff --git a/python/cugraph/cugraph/structure/graph_implementation/simpleDistributedGraph.py b/python/cugraph/cugraph/structure/graph_implementation/simpleDistributedGraph.py index 0586d0d853c..01885c2d1c3 100644 --- a/python/cugraph/cugraph/structure/graph_implementation/simpleDistributedGraph.py +++ b/python/cugraph/cugraph/structure/graph_implementation/simpleDistributedGraph.py @@ -14,6 +14,7 @@ import gc from typing import Union import warnings +import random import cudf import cupy as cp @@ -182,7 +183,9 @@ def __from_edgelist( # Repartition to 2 partitions per GPU for memory efficient process input_ddf = input_ddf.repartition(npartitions=len(workers) * 2) # FIXME: Make a copy of the input ddf before implicitly altering it. - input_ddf = input_ddf.map_partitions(lambda df: df.copy()) + input_ddf = input_ddf.map_partitions( + lambda df: df.copy(), token="custom-" + str(random.random()) + ) # The dataframe will be symmetrized iff the graph is undirected # otherwise, the inital dataframe will be returned if edge_attr is not None: diff --git a/python/cugraph/cugraph/tests/traversal/test_bfs_mg.py b/python/cugraph/cugraph/tests/traversal/test_bfs_mg.py index 8ffbecea4fc..5eafc231141 100644 --- a/python/cugraph/cugraph/tests/traversal/test_bfs_mg.py +++ b/python/cugraph/cugraph/tests/traversal/test_bfs_mg.py @@ -12,6 +12,7 @@ # limitations under the License. import gc +import random import pytest @@ -61,7 +62,9 @@ def modify_dataset(df): return cudf.concat([df, temp_df]) meta = ddf._meta - ddf = ddf.map_partitions(modify_dataset, meta=meta) + ddf = ddf.map_partitions( + modify_dataset, meta=meta, token="custom-" + str(random.random()) + ) df = cudf.read_csv( input_data_path, From a9f4297223593f8df211599277519e206c597630 Mon Sep 17 00:00:00 2001 From: Joseph Nke <76006812+jnke2016@users.noreply.github.com> Date: Tue, 26 Sep 2023 08:57:51 -0500 Subject: [PATCH 016/111] Enable weights for MG similarity algorithms (#3879) This is a follow up PR to #3828 which enabled weighted for the python SG similarity algorithms. This PR also updates the tests, docstrings and remove experimental calls Authors: - Joseph Nke (https://github.com/jnke2016) Approvers: - Alex Barghi (https://github.com/alexbarghi-nv) URL: https://github.com/rapidsai/cugraph/pull/3879 --- .../cugraph/dask/link_prediction/jaccard.py | 10 +--- .../cugraph/dask/link_prediction/overlap.py | 10 +--- .../cugraph/dask/link_prediction/sorensen.py | 10 +--- .../tests/link_prediction/test_jaccard_mg.py | 59 ++++++------------- .../tests/link_prediction/test_overlap_mg.py | 59 ++++++------------- .../tests/link_prediction/test_sorensen_mg.py | 59 ++++++------------- 6 files changed, 60 insertions(+), 147 deletions(-) diff --git a/python/cugraph/cugraph/dask/link_prediction/jaccard.py b/python/cugraph/cugraph/dask/link_prediction/jaccard.py index 218e6206fc3..5362c7a9e1e 100644 --- a/python/cugraph/cugraph/dask/link_prediction/jaccard.py +++ b/python/cugraph/cugraph/dask/link_prediction/jaccard.py @@ -118,7 +118,9 @@ def jaccard(input_graph, vertex_pair=None, use_weight=False): adjacent vertices in the graph. use_weight : bool, optional (default=False) - Currently not supported + Flag to indicate whether to compute weighted jaccard (if use_weight==True) + or un-weighted jaccard (if use_weight==False). + 'input_graph' must be weighted if 'use_weight=True'. Returns ------- @@ -144,12 +146,6 @@ def jaccard(input_graph, vertex_pair=None, use_weight=False): vertex_pair_col_name = vertex_pair.columns - if use_weight: - raise ValueError("'use_weight' is currently not supported.") - - if input_graph.is_weighted(): - raise ValueError("Weighted graphs are currently not supported.") - if isinstance(vertex_pair, (dask_cudf.DataFrame, cudf.DataFrame)): vertex_pair = renumber_vertex_pair(input_graph, vertex_pair) diff --git a/python/cugraph/cugraph/dask/link_prediction/overlap.py b/python/cugraph/cugraph/dask/link_prediction/overlap.py index 5540be28fd1..4bda05e3c95 100644 --- a/python/cugraph/cugraph/dask/link_prediction/overlap.py +++ b/python/cugraph/cugraph/dask/link_prediction/overlap.py @@ -96,7 +96,9 @@ def overlap(input_graph, vertex_pair=None, use_weight=False): adjacent vertices in the graph. use_weight : bool, optional (default=False) - Currently not supported + Flag to indicate whether to compute weighted overlap (if use_weight==True) + or un-weighted overlap (if use_weight==False). + 'input_graph' must be weighted if 'use_weight=True'. Returns ------- @@ -122,12 +124,6 @@ def overlap(input_graph, vertex_pair=None, use_weight=False): vertex_pair_col_name = vertex_pair.columns - if use_weight: - raise ValueError("'use_weight' is currently not supported.") - - if input_graph.is_weighted(): - raise ValueError("Weighted graphs are currently not supported.") - if isinstance(vertex_pair, (dask_cudf.DataFrame, cudf.DataFrame)): vertex_pair = renumber_vertex_pair(input_graph, vertex_pair) diff --git a/python/cugraph/cugraph/dask/link_prediction/sorensen.py b/python/cugraph/cugraph/dask/link_prediction/sorensen.py index 24295ac330c..163b0d0dc16 100644 --- a/python/cugraph/cugraph/dask/link_prediction/sorensen.py +++ b/python/cugraph/cugraph/dask/link_prediction/sorensen.py @@ -92,7 +92,9 @@ def sorensen(input_graph, vertex_pair=None, use_weight=False): adjacent vertices in the graph. use_weight : bool, optional (default=False) - Currently not supported + Flag to indicate whether to compute weighted sorensen (if use_weight==True) + or un-weighted sorensen (if use_weight==False). + 'input_graph' must be weighted if 'use_weight=True'. Returns ------- @@ -118,12 +120,6 @@ def sorensen(input_graph, vertex_pair=None, use_weight=False): vertex_pair_col_name = vertex_pair.columns - if use_weight: - raise ValueError("'use_weight' is currently not supported.") - - if input_graph.is_weighted(): - raise ValueError("Weighted graphs are currently not supported.") - if isinstance(vertex_pair, (dask_cudf.DataFrame, cudf.DataFrame)): vertex_pair = renumber_vertex_pair(input_graph, vertex_pair) diff --git a/python/cugraph/cugraph/tests/link_prediction/test_jaccard_mg.py b/python/cugraph/cugraph/tests/link_prediction/test_jaccard_mg.py index b56a6baae2b..ee739c9f236 100644 --- a/python/cugraph/cugraph/tests/link_prediction/test_jaccard_mg.py +++ b/python/cugraph/cugraph/tests/link_prediction/test_jaccard_mg.py @@ -34,6 +34,7 @@ def setup_function(): IS_DIRECTED = [False] HAS_VERTEX_PAIR = [True, False] +IS_WEIGHTED = [True, False] # ============================================================================= @@ -48,6 +49,7 @@ def setup_function(): (datasets, "graph_file"), (IS_DIRECTED, "directed"), (HAS_VERTEX_PAIR, "has_vertex_pair"), + (IS_WEIGHTED, "is_weighted"), ) @@ -57,7 +59,9 @@ def input_combo(request): Simply return the current combination of params as a dictionary for use in tests or other parameterized fixtures. """ - parameters = dict(zip(("graph_file", "directed", "has_vertex_pair"), request.param)) + parameters = dict( + zip(("graph_file", "directed", "has_vertex_pair", "is_weighted"), request.param) + ) return parameters @@ -72,7 +76,10 @@ def input_expected_output(input_combo): input_data_path = input_combo["graph_file"] directed = input_combo["directed"] has_vertex_pair = input_combo["has_vertex_pair"] - G = utils.generate_cugraph_graph_from_file(input_data_path, directed=directed) + is_weighted = input_combo["is_weighted"] + G = utils.generate_cugraph_graph_from_file( + input_data_path, directed=directed, edgevals=is_weighted + ) if has_vertex_pair: # Sample random vertices from the graph and compute the two_hop_neighbors # with those seeds @@ -84,7 +91,9 @@ def input_expected_output(input_combo): vertex_pair = None input_combo["vertex_pair"] = vertex_pair - sg_cugraph_jaccard = cugraph.experimental.jaccard(G, input_combo["vertex_pair"]) + sg_cugraph_jaccard = cugraph.jaccard( + G, input_combo["vertex_pair"], use_weight=is_weighted + ) # Save the results back to the input_combo dictionary to prevent redundant # cuGraph runs. Other tests using the input_combo fixture will look for # them, and if not present they will have to re-run the same cuGraph call. @@ -104,6 +113,7 @@ def input_expected_output(input_combo): ddf, source="src", destination="dst", + edge_attr="value" if is_weighted else None, renumber=True, store_transposed=True, ) @@ -122,8 +132,11 @@ def input_expected_output(input_combo): def test_dask_mg_jaccard(dask_client, benchmark, input_expected_output): dg = input_expected_output["MGGraph"] + use_weight = input_expected_output["is_weighted"] - result_jaccard = benchmark(dcg.jaccard, dg, input_expected_output["vertex_pair"]) + result_jaccard = benchmark( + dcg.jaccard, dg, input_expected_output["vertex_pair"], use_weight=use_weight + ) result_jaccard = ( result_jaccard.compute() @@ -151,41 +164,3 @@ def test_dask_mg_jaccard(dask_client, benchmark, input_expected_output): assert len(jaccard_coeff_diffs1) == 0 assert len(jaccard_coeff_diffs2) == 0 - - -@pytest.mark.mg -def test_dask_mg_weighted_jaccard(dask_client): - input_data_path = datasets[0] - chunksize = dcg.get_chunksize(input_data_path) - ddf = dask_cudf.read_csv( - input_data_path, - chunksize=chunksize, - delimiter=" ", - names=["src", "dst", "value"], - dtype=["int32", "int32", "float32"], - ) - - dg = cugraph.Graph(directed=False) - dg.from_dask_cudf_edgelist( - ddf, - source="src", - destination="dst", - edge_attr="value", - renumber=True, - store_transposed=True, - ) - with pytest.raises(ValueError): - dcg.jaccard(dg) - - dg = cugraph.Graph(directed=False) - dg.from_dask_cudf_edgelist( - ddf, - source="src", - destination="dst", - edge_attr="value", - store_transposed=True, - ) - - use_weight = True - with pytest.raises(ValueError): - dcg.jaccard(dg, use_weight=use_weight) diff --git a/python/cugraph/cugraph/tests/link_prediction/test_overlap_mg.py b/python/cugraph/cugraph/tests/link_prediction/test_overlap_mg.py index ce4bf619f47..87407d7b59c 100644 --- a/python/cugraph/cugraph/tests/link_prediction/test_overlap_mg.py +++ b/python/cugraph/cugraph/tests/link_prediction/test_overlap_mg.py @@ -34,6 +34,7 @@ def setup_function(): IS_DIRECTED = [False] HAS_VERTEX_PAIR = [True, False] +IS_WEIGHTED = [True, False] # ============================================================================= @@ -48,6 +49,7 @@ def setup_function(): (datasets, "graph_file"), (IS_DIRECTED, "directed"), (HAS_VERTEX_PAIR, "has_vertex_pair"), + (IS_WEIGHTED, "is_weighted"), ) @@ -57,7 +59,9 @@ def input_combo(request): Simply return the current combination of params as a dictionary for use in tests or other parameterized fixtures. """ - parameters = dict(zip(("graph_file", "directed", "has_vertex_pair"), request.param)) + parameters = dict( + zip(("graph_file", "directed", "has_vertex_pair", "is_weighted"), request.param) + ) return parameters @@ -72,7 +76,10 @@ def input_expected_output(input_combo): input_data_path = input_combo["graph_file"] directed = input_combo["directed"] has_vertex_pair = input_combo["has_vertex_pair"] - G = utils.generate_cugraph_graph_from_file(input_data_path, directed=directed) + is_weighted = input_combo["is_weighted"] + G = utils.generate_cugraph_graph_from_file( + input_data_path, directed=directed, edgevals=is_weighted + ) if has_vertex_pair: # Sample random vertices from the graph and compute the two_hop_neighbors # with those seeds @@ -84,7 +91,9 @@ def input_expected_output(input_combo): vertex_pair = None input_combo["vertex_pair"] = vertex_pair - sg_cugraph_overlap = cugraph.experimental.overlap(G, input_combo["vertex_pair"]) + sg_cugraph_overlap = cugraph.overlap( + G, input_combo["vertex_pair"], use_weight=is_weighted + ) # Save the results back to the input_combo dictionary to prevent redundant # cuGraph runs. Other tests using the input_combo fixture will look for # them, and if not present they will have to re-run the same cuGraph call. @@ -104,6 +113,7 @@ def input_expected_output(input_combo): ddf, source="src", destination="dst", + edge_attr="value" if is_weighted else None, renumber=True, store_transposed=True, ) @@ -125,8 +135,11 @@ def input_expected_output(input_combo): def test_dask_mg_overlap(dask_client, benchmark, input_expected_output): dg = input_expected_output["MGGraph"] + use_weight = input_expected_output["is_weighted"] - result_overlap = benchmark(dcg.overlap, dg, input_expected_output["vertex_pair"]) + result_overlap = benchmark( + dcg.overlap, dg, input_expected_output["vertex_pair"], use_weight=use_weight + ) result_overlap = ( result_overlap.compute() @@ -154,41 +167,3 @@ def test_dask_mg_overlap(dask_client, benchmark, input_expected_output): assert len(overlap_coeff_diffs1) == 0 assert len(overlap_coeff_diffs2) == 0 - - -@pytest.mark.mg -def test_dask_mg_weighted_overlap(): - input_data_path = datasets[0] - chunksize = dcg.get_chunksize(input_data_path) - ddf = dask_cudf.read_csv( - input_data_path, - chunksize=chunksize, - delimiter=" ", - names=["src", "dst", "value"], - dtype=["int32", "int32", "float32"], - ) - - dg = cugraph.Graph(directed=False) - dg.from_dask_cudf_edgelist( - ddf, - source="src", - destination="dst", - edge_attr="value", - renumber=True, - store_transposed=True, - ) - with pytest.raises(ValueError): - dcg.overlap(dg) - - dg = cugraph.Graph(directed=False) - dg.from_dask_cudf_edgelist( - ddf, - source="src", - destination="dst", - edge_attr="value", - store_transposed=True, - ) - - use_weight = True - with pytest.raises(ValueError): - dcg.overlap(dg, use_weight=use_weight) diff --git a/python/cugraph/cugraph/tests/link_prediction/test_sorensen_mg.py b/python/cugraph/cugraph/tests/link_prediction/test_sorensen_mg.py index af6b60771a0..66832d08427 100644 --- a/python/cugraph/cugraph/tests/link_prediction/test_sorensen_mg.py +++ b/python/cugraph/cugraph/tests/link_prediction/test_sorensen_mg.py @@ -35,6 +35,7 @@ def setup_function(): IS_DIRECTED = [False] HAS_VERTEX_PAIR = [True, False] +IS_WEIGHTED = [True, False] # ============================================================================= @@ -49,6 +50,7 @@ def setup_function(): (datasets, "graph_file"), (IS_DIRECTED, "directed"), (HAS_VERTEX_PAIR, "has_vertex_pair"), + (IS_WEIGHTED, "is_weighted"), ) @@ -58,7 +60,9 @@ def input_combo(request): Simply return the current combination of params as a dictionary for use in tests or other parameterized fixtures. """ - parameters = dict(zip(("graph_file", "directed", "has_vertex_pair"), request.param)) + parameters = dict( + zip(("graph_file", "directed", "has_vertex_pair", "is_weighted"), request.param) + ) return parameters @@ -73,7 +77,10 @@ def input_expected_output(input_combo): input_data_path = input_combo["graph_file"] directed = input_combo["directed"] has_vertex_pair = input_combo["has_vertex_pair"] - G = utils.generate_cugraph_graph_from_file(input_data_path, directed=directed) + is_weighted = input_combo["is_weighted"] + G = utils.generate_cugraph_graph_from_file( + input_data_path, directed=directed, edgevals=is_weighted + ) if has_vertex_pair: # Sample random vertices from the graph and compute the two_hop_neighbors # with those seeds @@ -85,7 +92,9 @@ def input_expected_output(input_combo): vertex_pair = None input_combo["vertex_pair"] = vertex_pair - sg_cugraph_sorensen = cugraph.experimental.sorensen(G, input_combo["vertex_pair"]) + sg_cugraph_sorensen = cugraph.sorensen( + G, input_combo["vertex_pair"], use_weight=is_weighted + ) # Save the results back to the input_combo dictionary to prevent redundant # cuGraph runs. Other tests using the input_combo fixture will look for # them, and if not present they will have to re-run the same cuGraph call. @@ -105,6 +114,7 @@ def input_expected_output(input_combo): ddf, source="src", destination="dst", + edge_attr="value" if is_weighted else None, renumber=True, store_transposed=True, ) @@ -124,8 +134,11 @@ def input_expected_output(input_combo): def test_dask_mg_sorensen(dask_client, benchmark, input_expected_output): dg = input_expected_output["MGGraph"] + use_weight = input_expected_output["is_weighted"] - result_sorensen = benchmark(dcg.sorensen, dg, input_expected_output["vertex_pair"]) + result_sorensen = benchmark( + dcg.sorensen, dg, input_expected_output["vertex_pair"], use_weight=use_weight + ) result_sorensen = ( result_sorensen.compute() @@ -153,41 +166,3 @@ def test_dask_mg_sorensen(dask_client, benchmark, input_expected_output): assert len(sorensen_coeff_diffs1) == 0 assert len(sorensen_coeff_diffs2) == 0 - - -@pytest.mark.mg -def test_dask_mg_weighted_sorensen(dask_client): - input_data_path = datasets[0] - chunksize = dcg.get_chunksize(input_data_path) - ddf = dask_cudf.read_csv( - input_data_path, - chunksize=chunksize, - delimiter=" ", - names=["src", "dst", "value"], - dtype=["int32", "int32", "float32"], - ) - - dg = cugraph.Graph(directed=False) - dg.from_dask_cudf_edgelist( - ddf, - source="src", - destination="dst", - edge_attr="value", - renumber=True, - store_transposed=True, - ) - with pytest.raises(ValueError): - dcg.sorensen(dg) - - dg = cugraph.Graph(directed=False) - dg.from_dask_cudf_edgelist( - ddf, - source="src", - destination="dst", - edge_attr="value", - store_transposed=True, - ) - - use_weight = True - with pytest.raises(ValueError): - dcg.sorensen(dg, use_weight=use_weight) From 5c34d3dd340c76678d8f2667057c6b0ce2f1f480 Mon Sep 17 00:00:00 2001 From: Alex Barghi <105237337+alexbarghi-nv@users.noreply.github.com> Date: Tue, 26 Sep 2023 11:57:30 -0400 Subject: [PATCH 017/111] Update Allocator Selection in cuGraph-DGL Example (#3877) Closes #3847 Authors: - Alex Barghi (https://github.com/alexbarghi-nv) Approvers: - Vibhu Jawa (https://github.com/VibhuJawa) - Brad Rees (https://github.com/BradReesWork) URL: https://github.com/rapidsai/cugraph/pull/3877 --- .../cugraph-dgl/examples/graphsage/node-classification.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/python/cugraph-dgl/examples/graphsage/node-classification.py b/python/cugraph-dgl/examples/graphsage/node-classification.py index 24df73ada75..320890b0312 100644 --- a/python/cugraph-dgl/examples/graphsage/node-classification.py +++ b/python/cugraph-dgl/examples/graphsage/node-classification.py @@ -39,14 +39,16 @@ def set_allocators(): + import rmm import cudf import cupy - import rmm + from rmm.allocators.torch import rmm_torch_allocator + from rmm.allocators.cupy import rmm_cupy_allocator mr = rmm.mr.CudaAsyncMemoryResource() rmm.mr.set_current_device_resource(mr) - torch.cuda.memory.change_current_allocator(rmm.rmm_torch_allocator) - cupy.cuda.set_allocator(rmm.allocators.cupy.rmm_cupy_allocator) + torch.cuda.memory.change_current_allocator(rmm_torch_allocator) + cupy.cuda.set_allocator(rmm_cupy_allocator) cudf.set_option("spill", True) From 5d2f5486899bdd2d71c00b12fb26afbdd60100d1 Mon Sep 17 00:00:00 2001 From: Rick Ratzel <3039903+rlratzel@users.noreply.github.com> Date: Tue, 26 Sep 2023 20:59:55 -0500 Subject: [PATCH 018/111] Updates to build and test `nx-cugraph` wheel as part of CI and nightly workflows (#3852) closes rapidsai/graph_dl#302 * Updates GHA yaml files to build and test a `nx-cugraph` wheel * Adds CI scripts for building and testing the `nx-cugraph` wheel * Adds a smoketest script for `nx-cugraph` * Relevant code cleanup: removes unnecessary dataset download from cugraph wheel testing Authors: - Rick Ratzel (https://github.com/rlratzel) Approvers: - Ray Douglass (https://github.com/raydouglass) - Vyas Ramasubramani (https://github.com/vyasr) - Bradley Dice (https://github.com/bdice) URL: https://github.com/rapidsai/cugraph/pull/3852 --- .github/workflows/build.yaml | 20 ++++++++++++++++ .github/workflows/pr.yaml | 16 +++++++++++++ .github/workflows/test.yaml | 9 ++++++++ ci/build_wheel.sh | 12 ++++++---- ci/build_wheel_nx-cugraph.sh | 6 +++++ ci/test_wheel.sh | 9 +++++--- ci/test_wheel_cugraph.sh | 8 ------- ci/test_wheel_nx-cugraph.sh | 6 +++++ ci/wheel_smoke_test_nx-cugraph.py | 38 +++++++++++++++++++++++++++++++ 9 files changed, 109 insertions(+), 15 deletions(-) create mode 100755 ci/build_wheel_nx-cugraph.sh create mode 100755 ci/test_wheel_nx-cugraph.sh create mode 100644 ci/wheel_smoke_test_nx-cugraph.py diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 02b357c7c88..c01a6fcb94a 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -112,3 +112,23 @@ jobs: sha: ${{ inputs.sha }} date: ${{ inputs.date }} package-name: cugraph + wheel-build-nx-cugraph: + needs: wheel-publish-pylibcugraph + secrets: inherit + uses: rapidsai/shared-action-workflows/.github/workflows/wheels-build.yaml@branch-23.10 + with: + build_type: ${{ inputs.build_type || 'branch' }} + branch: ${{ inputs.branch }} + sha: ${{ inputs.sha }} + date: ${{ inputs.date }} + script: ci/build_wheel_nx-cugraph.sh + wheel-publish-nx-cugraph: + needs: wheel-build-nx-cugraph + secrets: inherit + uses: rapidsai/shared-action-workflows/.github/workflows/wheels-publish.yaml@branch-23.10 + with: + build_type: ${{ inputs.build_type || 'branch' }} + branch: ${{ inputs.branch }} + sha: ${{ inputs.sha }} + date: ${{ inputs.date }} + package-name: nx-cugraph diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml index d2d24d90fbe..d49ae7f8d3d 100644 --- a/.github/workflows/pr.yaml +++ b/.github/workflows/pr.yaml @@ -23,6 +23,8 @@ jobs: - wheel-tests-pylibcugraph - wheel-build-cugraph - wheel-tests-cugraph + - wheel-build-nx-cugraph + - wheel-tests-nx-cugraph secrets: inherit uses: rapidsai/shared-action-workflows/.github/workflows/pr-builder.yaml@branch-23.10 checks: @@ -109,3 +111,17 @@ jobs: with: build_type: pull-request script: ci/test_wheel_cugraph.sh + wheel-build-nx-cugraph: + needs: wheel-tests-pylibcugraph + secrets: inherit + uses: rapidsai/shared-action-workflows/.github/workflows/wheels-build.yaml@branch-23.10 + with: + build_type: pull-request + script: ci/build_wheel_nx-cugraph.sh + wheel-tests-nx-cugraph: + needs: wheel-build-nx-cugraph + secrets: inherit + uses: rapidsai/shared-action-workflows/.github/workflows/wheels-test.yaml@branch-23.10 + with: + build_type: pull-request + script: ci/test_wheel_nx-cugraph.sh diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 97abca71260..dc9ed60b29e 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -48,3 +48,12 @@ jobs: date: ${{ inputs.date }} sha: ${{ inputs.sha }} script: ci/test_wheel_cugraph.sh + wheel-tests-nx-cugraph: + secrets: inherit + uses: rapidsai/shared-action-workflows/.github/workflows/wheels-test.yaml@branch-23.10 + with: + build_type: nightly + branch: ${{ inputs.branch }} + date: ${{ inputs.date }} + sha: ${{ inputs.sha }} + script: ci/test_wheel_nx-cugraph.sh diff --git a/ci/build_wheel.sh b/ci/build_wheel.sh index 3798d561126..821aa25c1b9 100755 --- a/ci/build_wheel.sh +++ b/ci/build_wheel.sh @@ -49,7 +49,11 @@ cd "${package_dir}" python -m pip wheel . -w dist -vvv --no-deps --disable-pip-version-check -mkdir -p final_dist -python -m auditwheel repair -w final_dist dist/* - -RAPIDS_PY_WHEEL_NAME="${package_name}_${RAPIDS_PY_CUDA_SUFFIX}" rapids-upload-wheels-to-s3 final_dist +# pure-python packages should not have auditwheel run on them. +if [[ ${package_name} == "nx-cugraph" ]]; then + RAPIDS_PY_WHEEL_NAME="${package_name}_${RAPIDS_PY_CUDA_SUFFIX}" rapids-upload-wheels-to-s3 dist +else + mkdir -p final_dist + python -m auditwheel repair -w final_dist dist/* + RAPIDS_PY_WHEEL_NAME="${package_name}_${RAPIDS_PY_CUDA_SUFFIX}" rapids-upload-wheels-to-s3 final_dist +fi diff --git a/ci/build_wheel_nx-cugraph.sh b/ci/build_wheel_nx-cugraph.sh new file mode 100755 index 00000000000..4481de1283d --- /dev/null +++ b/ci/build_wheel_nx-cugraph.sh @@ -0,0 +1,6 @@ +#!/bin/bash +# Copyright (c) 2023, NVIDIA CORPORATION. + +set -euo pipefail + +./ci/build_wheel.sh nx-cugraph python/nx-cugraph diff --git a/ci/test_wheel.sh b/ci/test_wheel.sh index 146186ae2e7..3ac3549f143 100755 --- a/ci/test_wheel.sh +++ b/ci/test_wheel.sh @@ -6,17 +6,20 @@ set -eoxu pipefail package_name=$1 package_dir=$2 +python_package_name=$(echo ${package_name}|sed 's/-/_/g') + mkdir -p ./dist RAPIDS_PY_CUDA_SUFFIX="$(rapids-wheel-ctk-name-gen ${RAPIDS_CUDA_VERSION})" -# echo to expand wildcard before adding `[extra]` requires for pip +# use 'ls' to expand wildcard before adding `[extra]` requires for pip RAPIDS_PY_WHEEL_NAME="${package_name}_${RAPIDS_PY_CUDA_SUFFIX}" rapids-download-wheels-from-s3 ./dist -python -m pip install $(echo ./dist/${package_name}*.whl)[test] +# pip creates wheels using python package names +python -m pip install $(ls ./dist/${python_package_name}*.whl)[test] # Run smoke tests for aarch64 pull requests arch=$(uname -m) if [[ "${arch}" == "aarch64" && ${RAPIDS_BUILD_TYPE} == "pull-request" ]]; then python ./ci/wheel_smoke_test_${package_name}.py else - RAPIDS_DATASET_ROOT_DIR=`pwd`/datasets python -m pytest ./python/${package_name}/${package_name}/tests + RAPIDS_DATASET_ROOT_DIR=`pwd`/datasets python -m pytest ./python/${package_name}/${python_package_name}/tests fi diff --git a/ci/test_wheel_cugraph.sh b/ci/test_wheel_cugraph.sh index 4d511ac2a0f..f9e2aa6d8da 100755 --- a/ci/test_wheel_cugraph.sh +++ b/ci/test_wheel_cugraph.sh @@ -11,12 +11,4 @@ python -m pip install --no-deps ./local-pylibcugraph-dep/pylibcugraph*.whl # Always install latest dask for testing python -m pip install git+https://github.com/dask/dask.git@main git+https://github.com/dask/distributed.git@main -# Only download test data for x86 -arch=$(uname -m) -if [[ "${arch}" == "x86_64" ]]; then - pushd ./datasets - bash ./get_test_data.sh - popd -fi - ./ci/test_wheel.sh cugraph python/cugraph diff --git a/ci/test_wheel_nx-cugraph.sh b/ci/test_wheel_nx-cugraph.sh new file mode 100755 index 00000000000..53d40960fc3 --- /dev/null +++ b/ci/test_wheel_nx-cugraph.sh @@ -0,0 +1,6 @@ +#!/bin/bash +# Copyright (c) 2023, NVIDIA CORPORATION. + +set -eoxu pipefail + +./ci/test_wheel.sh nx-cugraph python/nx-cugraph diff --git a/ci/wheel_smoke_test_nx-cugraph.py b/ci/wheel_smoke_test_nx-cugraph.py new file mode 100644 index 00000000000..10d26e3aac7 --- /dev/null +++ b/ci/wheel_smoke_test_nx-cugraph.py @@ -0,0 +1,38 @@ +# Copyright (c) 2023, NVIDIA CORPORATION. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import math + +import networkx as nx +import nx_cugraph as nxcg + + +if __name__ == "__main__": + G = nx.Graph() + G.add_edges_from([(0, 1), (1, 2), (2, 3)]) + + nx_result = nx.betweenness_centrality(G) + # nx_cugraph is intended to be called via the NetworkX dispatcher, like + # this: + # nxcu_result = nx.betweenness_centrality(G, backend="cugraph") + # + # but here it is being called directly since the NetworkX version that + # supports the "backend" kwarg may not be available in the testing env. + nxcu_result = nxcg.betweenness_centrality(G) + + nx_nodes, nxcu_nodes = nx_result.keys(), nxcu_result.keys() + assert nxcu_nodes == nx_nodes + for node_id in nx_nodes: + nx_bc, nxcu_bc = nx_result[node_id], nxcu_result[node_id] + assert math.isclose(nx_bc, nxcu_bc, rel_tol=1e-6), \ + f"bc for {node_id=} exceeds tolerance: {nx_bc=}, {nxcu_bc=}" From 8b87915c8c5e068caef04be4eeeb9cf7ae27b488 Mon Sep 17 00:00:00 2001 From: Paul Taylor <178183+trxcllnt@users.noreply.github.com> Date: Wed, 27 Sep 2023 12:08:12 -0700 Subject: [PATCH 019/111] Add cuGraph devcontainers (#3838) This PR adds some [devcontainers](https://containers.dev/) to help simplify building the cuGraph C++ and Python libraries. It also adds an optional job to the `pr.yaml` to [build the cuGraph libs in each devcontainer](https://github.com/trxcllnt/cugraph/blob/fea/devcontainers/.github/workflows/pr.yaml#L113-L119), so the build caches are populated for devs by CI. A devcontainer can be launched by clicking the "Reopen in Container" button that VSCode shows when opening the repo (or by using the "Rebuild and Reopen in Container" command from the command palette): ![image](https://user-images.githubusercontent.com/178183/221771999-97ab29d5-e718-4e5f-b32f-2cdd51bba25c.png) Clicking this button will cause VSCode to prompt the user to select one of these devcontainer variants: ![image](https://github.com/rapidsai/rmm/assets/178183/68d4b264-4fc2-4008-92b6-cb4bdd19b29f) On startup, the devcontainer creates or updates the conda/pip environment using `cugraph/dependencies.yaml`. The envs/package caches are cached on the host via volume mounts, which are described in more detail in [`.devcontainer/README.md`](https://github.com/trxcllnt/cugraph/blob/fea/devcontainers/.devcontainer/README.md). The container includes convenience functions to clean, configure, and build the various cuGraph components: ```shell $ clean-cugraph-cpp # only cleans the C++ build dir $ clean-cugraph-python # only cleans the Python build dir $ clean-cugraph # cleans both C++ and Python build dirs $ configure-cugraph-cpp # only configures cugraph C++ lib $ build-cugraph-cpp # only builds cugraph C++ lib $ build-cugraph-python # only builds cugraph Python lib $ build-cugraph # builds both C++ and Python libs ``` * The C++ build script is a small wrapper around `cmake -S ~/cugraph/cpp -B ~/cugraph/cpp/build` and `cmake --build ~/cugraph/cpp/build` * The Python build script is a small wrapper around `pip install --editable ~/cugraph/cpp` Unlike `build.sh`, these convenience scripts *don't* install the libraries after building them. Instead, they automatically inject the correct arguments to build the C++ libraries from source and use their build dirs as package roots: ```shell $ cmake -S ~/cugraph/cpp -B ~/cugraph/cpp/build $ CMAKE_ARGS="-Dcugraph_ROOT=~/cugraph/cpp/build" \ # <-- this argument is automatic pip install -e ~/cugraph/cpp ``` Authors: - Paul Taylor (https://github.com/trxcllnt) Approvers: - Chuck Hastings (https://github.com/ChuckHastings) - Jake Awe (https://github.com/AyodeAwe) URL: https://github.com/rapidsai/cugraph/pull/3838 --- .devcontainer/Dockerfile | 33 +++ .devcontainer/README.md | 34 +++ .../cuda11.8-conda/devcontainer.json | 37 +++ .devcontainer/cuda11.8-pip/devcontainer.json | 37 +++ .../cuda12.0-conda/devcontainer.json | 37 +++ .devcontainer/cuda12.0-pip/devcontainer.json | 37 +++ .github/workflows/pr.yaml | 10 + .gitignore | 4 + ci/release/update-version.sh | 7 + .../all_cuda-118_arch-x86_64.yaml | 2 + .../all_cuda-120_arch-x86_64.yaml | 2 + cpp/.clangd | 65 +++++ dependencies.yaml | 263 +++++++++++++++--- 13 files changed, 527 insertions(+), 41 deletions(-) create mode 100644 .devcontainer/Dockerfile create mode 100644 .devcontainer/README.md create mode 100644 .devcontainer/cuda11.8-conda/devcontainer.json create mode 100644 .devcontainer/cuda11.8-pip/devcontainer.json create mode 100644 .devcontainer/cuda12.0-conda/devcontainer.json create mode 100644 .devcontainer/cuda12.0-pip/devcontainer.json create mode 100644 cpp/.clangd diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 00000000000..3d0ac075be3 --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,33 @@ +# syntax=docker/dockerfile:1.5 + +ARG BASE +ARG PYTHON_PACKAGE_MANAGER=conda + +FROM ${BASE} as pip-base + +ENV DEFAULT_VIRTUAL_ENV=rapids + +FROM ${BASE} as conda-base + +ENV DEFAULT_CONDA_ENV=rapids + +FROM ${PYTHON_PACKAGE_MANAGER}-base + +ARG CUDA +ENV CUDAARCHS="RAPIDS" +ENV CUDA_VERSION="${CUDA_VERSION:-${CUDA}}" + +ARG PYTHON_PACKAGE_MANAGER +ENV PYTHON_PACKAGE_MANAGER="${PYTHON_PACKAGE_MANAGER}" + +ENV PYTHONSAFEPATH="1" +ENV PYTHONUNBUFFERED="1" +ENV PYTHONDONTWRITEBYTECODE="1" + +ENV SCCACHE_REGION="us-east-2" +ENV SCCACHE_BUCKET="rapids-sccache-devs" +ENV VAULT_HOST="https://vault.ops.k8s.rapids.ai" +ENV HISTFILE="/home/coder/.cache/._bash_history" + +# cugraph_pyg's setup.py needs this defined when building in a conda env +ENV CUDA_HOME="${CUDA_HOME:-/home/coder/.conda/envs/$DEFAULT_CONDA_ENV}" diff --git a/.devcontainer/README.md b/.devcontainer/README.md new file mode 100644 index 00000000000..e645c51de8b --- /dev/null +++ b/.devcontainer/README.md @@ -0,0 +1,34 @@ +# cuGraph Development Containers + +This directory contains [devcontainer configurations](https://containers.dev/implementors/json_reference/) for using VSCode to [develop in a container](https://code.visualstudio.com/docs/devcontainers/containers) via the `Remote Containers` [extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers) or [GitHub Codespaces](https://github.com/codespaces). + +This container is a turnkey development environment for building and testing the cuGraph C++ and Python libraries. + +## Table of Contents + +* [Prerequisites](#prerequisites) +* [Host bind mounts](#host-bind-mounts) +* [Launch a Dev Container](#launch-a-dev-container) +## Prerequisites + +* [VSCode](https://code.visualstudio.com/download) +* [VSCode Remote Containers extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers) + +## Host bind mounts + +By default, the following directories are bind-mounted into the devcontainer: + +* `${repo}:/home/coder/cugraph` +* `${repo}/../.aws:/home/coder/.aws` +* `${repo}/../.local:/home/coder/.local` +* `${repo}/../.cache:/home/coder/.cache` +* `${repo}/../.conda:/home/coder/.conda` +* `${repo}/../.config:/home/coder/.config` + +This ensures caches, configurations, dependencies, and your commits are persisted on the host across container runs. + +## Launch a Dev Container + +To launch a devcontainer from VSCode, open the cuGraph repo and select the "Reopen in Container" button in the bottom right:
+ +Alternatively, open the VSCode command palette (typically `cmd/ctrl + shift + P`) and run the "Rebuild and Reopen in Container" command. diff --git a/.devcontainer/cuda11.8-conda/devcontainer.json b/.devcontainer/cuda11.8-conda/devcontainer.json new file mode 100644 index 00000000000..cf4ba5aa114 --- /dev/null +++ b/.devcontainer/cuda11.8-conda/devcontainer.json @@ -0,0 +1,37 @@ +{ + "build": { + "context": "${localWorkspaceFolder}/.devcontainer", + "dockerfile": "${localWorkspaceFolder}/.devcontainer/Dockerfile", + "args": { + "CUDA": "11.8", + "PYTHON_PACKAGE_MANAGER": "conda", + "BASE": "rapidsai/devcontainers:23.10-cpp-llvm16-cuda11.8-mambaforge-ubuntu22.04" + } + }, + "hostRequirements": {"gpu": "optional"}, + "features": { + "ghcr.io/rapidsai/devcontainers/features/rapids-build-utils:23.10": {} + }, + "overrideFeatureInstallOrder": [ + "ghcr.io/rapidsai/devcontainers/features/rapids-build-utils" + ], + "initializeCommand": ["/bin/bash", "-c", "mkdir -m 0755 -p ${localWorkspaceFolder}/../.{aws,cache,config,conda/pkgs,conda/${localWorkspaceFolderBasename}-cuda11.8-envs}"], + "postAttachCommand": ["/bin/bash", "-c", "if [ ${CODESPACES:-false} = 'true' ]; then . devcontainer-utils-post-attach-command; . rapids-post-attach-command; fi"], + "workspaceFolder": "/home/coder", + "workspaceMount": "source=${localWorkspaceFolder},target=/home/coder/cugraph,type=bind,consistency=consistent", + "mounts": [ + "source=${localWorkspaceFolder}/../.aws,target=/home/coder/.aws,type=bind,consistency=consistent", + "source=${localWorkspaceFolder}/../.cache,target=/home/coder/.cache,type=bind,consistency=consistent", + "source=${localWorkspaceFolder}/../.config,target=/home/coder/.config,type=bind,consistency=consistent", + "source=${localWorkspaceFolder}/../.conda/pkgs,target=/home/coder/.conda/pkgs,type=bind,consistency=consistent", + "source=${localWorkspaceFolder}/../.conda/${localWorkspaceFolderBasename}-cuda11.8-envs,target=/home/coder/.conda/envs,type=bind,consistency=consistent" + ], + "customizations": { + "vscode": { + "extensions": [ + "ms-python.flake8", + "nvidia.nsight-vscode-edition" + ] + } + } +} diff --git a/.devcontainer/cuda11.8-pip/devcontainer.json b/.devcontainer/cuda11.8-pip/devcontainer.json new file mode 100644 index 00000000000..e86a38abbde --- /dev/null +++ b/.devcontainer/cuda11.8-pip/devcontainer.json @@ -0,0 +1,37 @@ +{ + "build": { + "context": "${localWorkspaceFolder}/.devcontainer", + "dockerfile": "${localWorkspaceFolder}/.devcontainer/Dockerfile", + "args": { + "CUDA": "11.8", + "PYTHON_PACKAGE_MANAGER": "pip", + "BASE": "rapidsai/devcontainers:23.10-cpp-llvm16-cuda11.8-ubuntu22.04" + } + }, + "hostRequirements": {"gpu": "optional"}, + "features": { + "ghcr.io/rapidsai/devcontainers/features/ucx:23.10": {"version": "1.14.1"}, + "ghcr.io/rapidsai/devcontainers/features/rapids-build-utils:23.10": {} + }, + "overrideFeatureInstallOrder": [ + "ghcr.io/rapidsai/devcontainers/features/rapids-build-utils" + ], + "initializeCommand": ["/bin/bash", "-c", "mkdir -m 0755 -p ${localWorkspaceFolder}/../.{aws,cache,config/pip,local/share/${localWorkspaceFolderBasename}-cuda11.8-venvs}"], + "postAttachCommand": ["/bin/bash", "-c", "if [ ${CODESPACES:-false} = 'true' ]; then . devcontainer-utils-post-attach-command; . rapids-post-attach-command; fi"], + "workspaceFolder": "/home/coder", + "workspaceMount": "source=${localWorkspaceFolder},target=/home/coder/cugraph,type=bind,consistency=consistent", + "mounts": [ + "source=${localWorkspaceFolder}/../.aws,target=/home/coder/.aws,type=bind,consistency=consistent", + "source=${localWorkspaceFolder}/../.cache,target=/home/coder/.cache,type=bind,consistency=consistent", + "source=${localWorkspaceFolder}/../.config,target=/home/coder/.config,type=bind,consistency=consistent", + "source=${localWorkspaceFolder}/../.local/share/${localWorkspaceFolderBasename}-cuda11.8-venvs,target=/home/coder/.local/share/venvs,type=bind,consistency=consistent" + ], + "customizations": { + "vscode": { + "extensions": [ + "ms-python.flake8", + "nvidia.nsight-vscode-edition" + ] + } + } +} diff --git a/.devcontainer/cuda12.0-conda/devcontainer.json b/.devcontainer/cuda12.0-conda/devcontainer.json new file mode 100644 index 00000000000..863eeea48ff --- /dev/null +++ b/.devcontainer/cuda12.0-conda/devcontainer.json @@ -0,0 +1,37 @@ +{ + "build": { + "context": "${localWorkspaceFolder}/.devcontainer", + "dockerfile": "${localWorkspaceFolder}/.devcontainer/Dockerfile", + "args": { + "CUDA": "12.0", + "PYTHON_PACKAGE_MANAGER": "conda", + "BASE": "rapidsai/devcontainers:23.10-cpp-mambaforge-ubuntu22.04" + } + }, + "hostRequirements": {"gpu": "optional"}, + "features": { + "ghcr.io/rapidsai/devcontainers/features/rapids-build-utils:23.10": {} + }, + "overrideFeatureInstallOrder": [ + "ghcr.io/rapidsai/devcontainers/features/rapids-build-utils" + ], + "initializeCommand": ["/bin/bash", "-c", "mkdir -m 0755 -p ${localWorkspaceFolder}/../.{aws,cache,config,conda/pkgs,conda/${localWorkspaceFolderBasename}-cuda12.0-envs}"], + "postAttachCommand": ["/bin/bash", "-c", "if [ ${CODESPACES:-false} = 'true' ]; then . devcontainer-utils-post-attach-command; . rapids-post-attach-command; fi"], + "workspaceFolder": "/home/coder", + "workspaceMount": "source=${localWorkspaceFolder},target=/home/coder/cugraph,type=bind,consistency=consistent", + "mounts": [ + "source=${localWorkspaceFolder}/../.aws,target=/home/coder/.aws,type=bind,consistency=consistent", + "source=${localWorkspaceFolder}/../.cache,target=/home/coder/.cache,type=bind,consistency=consistent", + "source=${localWorkspaceFolder}/../.config,target=/home/coder/.config,type=bind,consistency=consistent", + "source=${localWorkspaceFolder}/../.conda/pkgs,target=/home/coder/.conda/pkgs,type=bind,consistency=consistent", + "source=${localWorkspaceFolder}/../.conda/${localWorkspaceFolderBasename}-cuda12.0-envs,target=/home/coder/.conda/envs,type=bind,consistency=consistent" + ], + "customizations": { + "vscode": { + "extensions": [ + "ms-python.flake8", + "nvidia.nsight-vscode-edition" + ] + } + } +} diff --git a/.devcontainer/cuda12.0-pip/devcontainer.json b/.devcontainer/cuda12.0-pip/devcontainer.json new file mode 100644 index 00000000000..c7612771fd3 --- /dev/null +++ b/.devcontainer/cuda12.0-pip/devcontainer.json @@ -0,0 +1,37 @@ +{ + "build": { + "context": "${localWorkspaceFolder}/.devcontainer", + "dockerfile": "${localWorkspaceFolder}/.devcontainer/Dockerfile", + "args": { + "CUDA": "12.0", + "PYTHON_PACKAGE_MANAGER": "pip", + "BASE": "rapidsai/devcontainers:23.10-cpp-llvm16-cuda12.0-ubuntu22.04" + } + }, + "hostRequirements": {"gpu": "optional"}, + "features": { + "ghcr.io/rapidsai/devcontainers/features/ucx:23.10": {"version": "1.14.1"}, + "ghcr.io/rapidsai/devcontainers/features/rapids-build-utils:23.10": {} + }, + "overrideFeatureInstallOrder": [ + "ghcr.io/rapidsai/devcontainers/features/rapids-build-utils" + ], + "initializeCommand": ["/bin/bash", "-c", "mkdir -m 0755 -p ${localWorkspaceFolder}/../.{aws,cache,config/pip,local/share/${localWorkspaceFolderBasename}-cuda12.0-venvs}"], + "postAttachCommand": ["/bin/bash", "-c", "if [ ${CODESPACES:-false} = 'true' ]; then . devcontainer-utils-post-attach-command; . rapids-post-attach-command; fi"], + "workspaceFolder": "/home/coder", + "workspaceMount": "source=${localWorkspaceFolder},target=/home/coder/cugraph,type=bind,consistency=consistent", + "mounts": [ + "source=${localWorkspaceFolder}/../.aws,target=/home/coder/.aws,type=bind,consistency=consistent", + "source=${localWorkspaceFolder}/../.cache,target=/home/coder/.cache,type=bind,consistency=consistent", + "source=${localWorkspaceFolder}/../.config,target=/home/coder/.config,type=bind,consistency=consistent", + "source=${localWorkspaceFolder}/../.local/share/${localWorkspaceFolderBasename}-cuda12.0-venvs,target=/home/coder/.local/share/venvs,type=bind,consistency=consistent" + ], + "customizations": { + "vscode": { + "extensions": [ + "ms-python.flake8", + "nvidia.nsight-vscode-edition" + ] + } + } +} diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml index d49ae7f8d3d..7b267d7edf3 100644 --- a/.github/workflows/pr.yaml +++ b/.github/workflows/pr.yaml @@ -25,6 +25,7 @@ jobs: - wheel-tests-cugraph - wheel-build-nx-cugraph - wheel-tests-nx-cugraph + - devcontainer secrets: inherit uses: rapidsai/shared-action-workflows/.github/workflows/pr-builder.yaml@branch-23.10 checks: @@ -125,3 +126,12 @@ jobs: with: build_type: pull-request script: ci/test_wheel_nx-cugraph.sh + devcontainer: + secrets: inherit + uses: rapidsai/shared-action-workflows/.github/workflows/build-in-devcontainer.yaml@branch-23.10 + with: + extra-repo-deploy-key: CUGRAPH_OPS_SSH_PRIVATE_DEPLOY_KEY + build_command: | + sccache -z; + build-all --verbose; + sccache -s; diff --git a/.gitignore b/.gitignore index 3fda9f8a037..c6bcf6965d7 100644 --- a/.gitignore +++ b/.gitignore @@ -97,3 +97,7 @@ python/cugraph/cugraph/tests/dask-worker-space docs/cugraph/source/api_docs/api/* _html _text + +# clang tooling +compile_commands.json +.clangd/ diff --git a/ci/release/update-version.sh b/ci/release/update-version.sh index bd3aa6bc370..f3892fbd3c4 100755 --- a/ci/release/update-version.sh +++ b/ci/release/update-version.sh @@ -128,3 +128,10 @@ done sed_runner "s/RAPIDS_VERSION_NUMBER=\".*/RAPIDS_VERSION_NUMBER=\"${NEXT_SHORT_TAG}\"/g" ci/build_docs.sh sed_runner "s/branch-.*/branch-${NEXT_SHORT_TAG}/g" python/nx-cugraph/README.md + +# .devcontainer files +find .devcontainer/ -type f -name devcontainer.json -print0 | while IFS= read -r -d '' filename; do + sed_runner "s@rapidsai/devcontainers:[0-9.]*@rapidsai/devcontainers:${NEXT_SHORT_TAG}@g" "${filename}" + sed_runner "s@rapidsai/devcontainers/features/ucx:[0-9.]*@rapidsai/devcontainers/features/ucx:${NEXT_SHORT_TAG_PEP440}@" "${filename}" + sed_runner "s@rapidsai/devcontainers/features/rapids-build-utils:[0-9.]*@rapidsai/devcontainers/features/rapids-build-utils:${NEXT_SHORT_TAG_PEP440}@" "${filename}" +done diff --git a/conda/environments/all_cuda-118_arch-x86_64.yaml b/conda/environments/all_cuda-118_arch-x86_64.yaml index c66890f8ae5..86de24c991d 100644 --- a/conda/environments/all_cuda-118_arch-x86_64.yaml +++ b/conda/environments/all_cuda-118_arch-x86_64.yaml @@ -66,10 +66,12 @@ dependencies: - scikit-build>=0.13.1 - scikit-learn>=0.23.1 - scipy +- setuptools>=61.0.0 - sphinx-copybutton - sphinx-markdown-tables - sphinx<6 - sphinxcontrib-websupport - ucx-proc=*=gpu - ucx-py==0.34.* +- wheel name: all_cuda-118_arch-x86_64 diff --git a/conda/environments/all_cuda-120_arch-x86_64.yaml b/conda/environments/all_cuda-120_arch-x86_64.yaml index 3afb1415572..1054f75ba54 100644 --- a/conda/environments/all_cuda-120_arch-x86_64.yaml +++ b/conda/environments/all_cuda-120_arch-x86_64.yaml @@ -65,10 +65,12 @@ dependencies: - scikit-build>=0.13.1 - scikit-learn>=0.23.1 - scipy +- setuptools>=61.0.0 - sphinx-copybutton - sphinx-markdown-tables - sphinx<6 - sphinxcontrib-websupport - ucx-proc=*=gpu - ucx-py==0.34.* +- wheel name: all_cuda-120_arch-x86_64 diff --git a/cpp/.clangd b/cpp/.clangd new file mode 100644 index 00000000000..7c4fe036ddf --- /dev/null +++ b/cpp/.clangd @@ -0,0 +1,65 @@ +# https://clangd.llvm.org/config + +# Apply a config conditionally to all C files +If: + PathMatch: .*\.(c|h)$ + +--- + +# Apply a config conditionally to all C++ files +If: + PathMatch: .*\.(c|h)pp + +--- + +# Apply a config conditionally to all CUDA files +If: + PathMatch: .*\.cuh? +CompileFlags: + Add: + - "-x" + - "cuda" + # No error on unknown CUDA versions + - "-Wno-unknown-cuda-version" + # Allow variadic CUDA functions + - "-Xclang=-fcuda-allow-variadic-functions" +Diagnostics: + Suppress: + - "variadic_device_fn" + - "attributes_not_allowed" + +--- + +# Tweak the clangd parse settings for all files +CompileFlags: + Add: + # report all errors + - "-ferror-limit=0" + - "-fmacro-backtrace-limit=0" + - "-ftemplate-backtrace-limit=0" + # Skip the CUDA version check + - "--no-cuda-version-check" + Remove: + # remove gcc's -fcoroutines + - -fcoroutines + # remove nvc++ flags unknown to clang + - "-gpu=*" + - "-stdpar*" + # remove nvcc flags unknown to clang + - "-arch*" + - "-gencode*" + - "--generate-code*" + - "-ccbin*" + - "-t=*" + - "--threads*" + - "-Xptxas*" + - "-Xcudafe*" + - "-Xfatbin*" + - "-Xcompiler*" + - "--diag-suppress*" + - "--diag_suppress*" + - "--compiler-options*" + - "--expt-extended-lambda" + - "--expt-relaxed-constexpr" + - "-forward-unknown-to-host-compiler" + - "-Werror=cross-execution-space-call" diff --git a/dependencies.yaml b/dependencies.yaml index 04ec1b6e957..a162ac01354 100644 --- a/dependencies.yaml +++ b/dependencies.yaml @@ -11,9 +11,15 @@ files: - cpp_build - cudatoolkit - docs + - python_build_wheel - python_build_cythonize + - depends_on_rmm + - depends_on_cudf + - depends_on_dask_cudf + - depends_on_pylibraft + - depends_on_raft_dask + - depends_on_cupy - python_run_cugraph - - python_run_pylibcugraph - python_run_nx_cugraph - python_run_cugraph_dgl - python_run_cugraph_pyg @@ -50,6 +56,7 @@ files: output: none includes: - cudatoolkit + - depends_on_cudf - py_version - test_python_common - test_python_cugraph @@ -62,14 +69,22 @@ files: includes: - common_build - python_build_wheel + - depends_on_rmm + - depends_on_pylibraft + - depends_on_pylibcugraph - python_build_cythonize - - python_build_cugraph py_run_cugraph: output: pyproject pyproject_dir: python/cugraph extras: table: project includes: + - depends_on_rmm + - depends_on_cudf + - depends_on_dask_cudf + - depends_on_raft_dask + - depends_on_pylibcugraph + - depends_on_cupy - python_run_cugraph py_test_cugraph: output: pyproject @@ -88,6 +103,8 @@ files: includes: - common_build - python_build_wheel + - depends_on_rmm + - depends_on_pylibraft - python_build_cythonize py_run_pylibcugraph: output: pyproject @@ -95,7 +112,8 @@ files: extras: table: project includes: - - python_run_pylibcugraph + - depends_on_rmm + - depends_on_pylibraft py_test_pylibcugraph: output: pyproject pyproject_dir: python/pylibcugraph @@ -103,6 +121,7 @@ files: table: project.optional-dependencies key: test includes: + - depends_on_cudf - test_python_common - test_python_pylibcugraph py_build_nx_cugraph: @@ -118,6 +137,8 @@ files: extras: table: project includes: + - depends_on_pylibcugraph + - depends_on_cupy - python_run_nx_cugraph py_test_nx_cugraph: output: pyproject @@ -183,6 +204,10 @@ files: extras: table: project includes: + - depends_on_rmm + - depends_on_cudf + - depends_on_dask_cudf + - depends_on_cupy - python_run_cugraph_service_server py_test_cugraph_service_server: output: pyproject @@ -334,41 +359,29 @@ dependencies: - python>=3.9,<3.11 python_build_wheel: common: - - output_types: [conda, pyproject] + - output_types: [conda, pyproject, requirements] packages: - - wheel - setuptools>=61.0.0 + - wheel python_build_cythonize: common: - - output_types: [conda, pyproject] + - output_types: [conda, pyproject, requirements] packages: - cython>=3.0.0 - - &pylibraft pylibraft==23.10.* - - &rmm rmm==23.10.* - scikit-build>=0.13.1 - python_build_cugraph: - common: - - output_types: [conda, pyproject] - packages: - - &pylibcugraph pylibcugraph==23.10.* python_run_cugraph: common: - output_types: [conda, pyproject] packages: - - &cudf cudf==23.10.* - &dask dask>=2023.7.1 - &distributed distributed>=2023.7.1 - &dask_cuda dask-cuda==23.10.* - - &dask_cudf dask-cudf==23.10.* - &numba numba>=0.57 - - raft-dask==23.10.* - - *rmm - &ucx_py ucx-py==0.34.* - output_types: conda packages: - aiohttp - - &cupy cupy>=12.0.0 - - &dask-core dask-core>=2023.7.1 + - &dask-core_conda dask-core>=2023.7.1 - fsspec>=0.6.0 - libcudf==23.10.* - requests @@ -376,29 +389,14 @@ dependencies: - ucx-proc=*=gpu - output_types: pyproject packages: - - &cupy_pip cupy-cuda11x>=12.0.0 # cudf uses fsspec but is protocol independent. cugraph # dataset APIs require [http] extras for use with cudf. - fsspec[http]>=0.6.0 - - *pylibcugraph - python_run_pylibcugraph: - common: - - output_types: [conda, pyproject] - packages: - - *pylibraft - - *rmm python_run_nx_cugraph: common: - output_types: [conda, pyproject] packages: - networkx>=3.0 - - output_types: conda - packages: - - *cupy - - output_types: pyproject - packages: - - *cupy_pip - - *pylibcugraph python_run_cugraph_dgl: common: - output_types: [conda, pyproject] @@ -426,23 +424,18 @@ dependencies: common: - output_types: [conda, pyproject] packages: - - *cudf - *dask - *dask_cuda - - *dask_cudf - *distributed - *numba - *numpy - - *rmm - *thrift - *ucx_py - output_types: conda packages: - - *cupy - - *dask-core + - *dask-core_conda - output_types: pyproject packages: - - *cupy_pip - *cugraph - cugraph-service-client==23.10.* doc: @@ -492,7 +485,6 @@ dependencies: common: - output_types: [conda, pyproject] packages: - - *cudf - *numpy test_python_nx_cugraph: common: @@ -519,3 +511,192 @@ dependencies: - pytorch==2.0 - pytorch-cuda==11.8 - pyg=2.3.1=*torch_2.0.0*cu118* + + depends_on_rmm: + common: + - output_types: conda + packages: + - &rmm_conda rmm==23.10.* + - output_types: requirements + packages: + # pip recognizes the index as a global option for the requirements.txt file + - --extra-index-url=https://pypi.nvidia.com + specific: + - output_types: [requirements, pyproject] + matrices: + - matrix: {cuda: "12.2"} + packages: &rmm_packages_pip_cu12 + - rmm-cu12==23.10.* + - {matrix: {cuda: "12.1"}, packages: *rmm_packages_pip_cu12} + - {matrix: {cuda: "12.0"}, packages: *rmm_packages_pip_cu12} + - matrix: {cuda: "11.8"} + packages: &rmm_packages_pip_cu11 + - rmm-cu11==23.10.* + - {matrix: {cuda: "11.5"}, packages: *rmm_packages_pip_cu11} + - {matrix: {cuda: "11.4"}, packages: *rmm_packages_pip_cu11} + - {matrix: {cuda: "11.2"}, packages: *rmm_packages_pip_cu11} + - {matrix: null, packages: [*rmm_conda]} + + depends_on_cudf: + common: + - output_types: conda + packages: + - &cudf_conda cudf==23.10.* + - output_types: requirements + packages: + # pip recognizes the index as a global option for the requirements.txt file + - --extra-index-url=https://pypi.nvidia.com + specific: + - output_types: [requirements, pyproject] + matrices: + - matrix: {cuda: "12.2"} + packages: &cudf_packages_pip_cu12 + - cudf-cu12==23.10.* + - {matrix: {cuda: "12.1"}, packages: *cudf_packages_pip_cu12} + - {matrix: {cuda: "12.0"}, packages: *cudf_packages_pip_cu12} + - matrix: {cuda: "11.8"} + packages: &cudf_packages_pip_cu11 + - cudf-cu11==23.10.* + - {matrix: {cuda: "11.5"}, packages: *cudf_packages_pip_cu11} + - {matrix: {cuda: "11.4"}, packages: *cudf_packages_pip_cu11} + - {matrix: {cuda: "11.2"}, packages: *cudf_packages_pip_cu11} + - {matrix: null, packages: [*cudf_conda]} + + depends_on_dask_cudf: + common: + - output_types: conda + packages: + - &dask_cudf_conda dask-cudf==23.10.* + - output_types: requirements + packages: + # pip recognizes the index as a global option for the requirements.txt file + - --extra-index-url=https://pypi.nvidia.com + specific: + - output_types: [requirements, pyproject] + matrices: + - matrix: {cuda: "12.2"} + packages: &dask_cudf_packages_pip_cu12 + - dask-cudf-cu12==23.10.* + - {matrix: {cuda: "12.1"}, packages: *dask_cudf_packages_pip_cu12} + - {matrix: {cuda: "12.0"}, packages: *dask_cudf_packages_pip_cu12} + - matrix: {cuda: "11.8"} + packages: &dask_cudf_packages_pip_cu11 + - dask-cudf-cu11==23.10.* + - {matrix: {cuda: "11.5"}, packages: *dask_cudf_packages_pip_cu11} + - {matrix: {cuda: "11.4"}, packages: *dask_cudf_packages_pip_cu11} + - {matrix: {cuda: "11.2"}, packages: *dask_cudf_packages_pip_cu11} + - {matrix: null, packages: [*dask_cudf_conda]} + + depends_on_pylibraft: + common: + - output_types: conda + packages: + - &pylibraft_conda pylibraft==23.10.* + - output_types: requirements + packages: + # pip recognizes the index as a global option for the requirements.txt file + - --extra-index-url=https://pypi.nvidia.com + specific: + - output_types: [requirements, pyproject] + matrices: + - matrix: {cuda: "12.2"} + packages: &pylibraft_packages_pip_cu12 + - pylibraft-cu12==23.10.* + - {matrix: {cuda: "12.1"}, packages: *pylibraft_packages_pip_cu12} + - {matrix: {cuda: "12.0"}, packages: *pylibraft_packages_pip_cu12} + - matrix: {cuda: "11.8"} + packages: &pylibraft_packages_pip_cu11 + - pylibraft-cu11==23.10.* + - {matrix: {cuda: "11.5"}, packages: *pylibraft_packages_pip_cu11} + - {matrix: {cuda: "11.4"}, packages: *pylibraft_packages_pip_cu11} + - {matrix: {cuda: "11.2"}, packages: *pylibraft_packages_pip_cu11} + - {matrix: null, packages: [*pylibraft_conda]} + + depends_on_raft_dask: + common: + - output_types: conda + packages: + - &raft_dask_conda raft-dask==23.10.* + - output_types: requirements + packages: + # pip recognizes the index as a global option for the requirements.txt file + - --extra-index-url=https://pypi.nvidia.com + specific: + - output_types: [requirements, pyproject] + matrices: + - matrix: {cuda: "12.2"} + packages: &raft_dask_packages_pip_cu12 + - raft-dask-cu12==23.10.* + - {matrix: {cuda: "12.1"}, packages: *raft_dask_packages_pip_cu12} + - {matrix: {cuda: "12.0"}, packages: *raft_dask_packages_pip_cu12} + - matrix: {cuda: "11.8"} + packages: &raft_dask_packages_pip_cu11 + - raft-dask-cu11==23.10.* + - {matrix: {cuda: "11.5"}, packages: *raft_dask_packages_pip_cu11} + - {matrix: {cuda: "11.4"}, packages: *raft_dask_packages_pip_cu11} + - {matrix: {cuda: "11.2"}, packages: *raft_dask_packages_pip_cu11} + - {matrix: null, packages: [*raft_dask_conda]} + + depends_on_pylibcugraph: + common: + - output_types: conda + packages: + - &pylibcugraph_conda pylibcugraph==23.10.* + - output_types: requirements + packages: + # pip recognizes the index as a global option for the requirements.txt file + - --extra-index-url=https://pypi.nvidia.com + specific: + - output_types: [requirements, pyproject] + matrices: + - matrix: {cuda: "12.2"} + packages: &pylibcugraph_packages_pip_cu12 + - pylibcugraph-cu12==23.10.* + - {matrix: {cuda: "12.1"}, packages: *pylibcugraph_packages_pip_cu12} + - {matrix: {cuda: "12.0"}, packages: *pylibcugraph_packages_pip_cu12} + - matrix: {cuda: "11.8"} + packages: &pylibcugraph_packages_pip_cu11 + - pylibcugraph-cu11==23.10.* + - {matrix: {cuda: "11.5"}, packages: *pylibcugraph_packages_pip_cu11} + - {matrix: {cuda: "11.4"}, packages: *pylibcugraph_packages_pip_cu11} + - {matrix: {cuda: "11.2"}, packages: *pylibcugraph_packages_pip_cu11} + - {matrix: null, packages: [*pylibcugraph_conda]} + + depends_on_cupy: + common: + - output_types: conda + packages: + - cupy>=12.0.0 + specific: + - output_types: [requirements, pyproject] + matrices: + # All CUDA 12 + x86_64 versions + - matrix: {cuda: "12.2", arch: x86_64} + packages: &cupy_packages_cu12_x86_64 + - cupy-cuda12x>=12.0.0 + - {matrix: {cuda: "12.1", arch: x86_64}, packages: *cupy_packages_cu12_x86_64} + - {matrix: {cuda: "12.0", arch: x86_64}, packages: *cupy_packages_cu12_x86_64} + + # All CUDA 12 + aarch64 versions + - matrix: {cuda: "12.2", arch: aarch64} + packages: &cupy_packages_cu12_aarch64 + - cupy-cuda12x -f https://pip.cupy.dev/aarch64 # TODO: Verify that this works. + - {matrix: {cuda: "12.1", arch: aarch64}, packages: *cupy_packages_cu12_aarch64} + - {matrix: {cuda: "12.0", arch: aarch64}, packages: *cupy_packages_cu12_aarch64} + + # All CUDA 11 + x86_64 versions + - matrix: {cuda: "11.8", arch: x86_64} + packages: &cupy_packages_cu11_x86_64 + - cupy-cuda11x>=12.0.0 + - {matrix: {cuda: "11.5", arch: x86_64}, packages: *cupy_packages_cu11_x86_64} + - {matrix: {cuda: "11.4", arch: x86_64}, packages: *cupy_packages_cu11_x86_64} + - {matrix: {cuda: "11.2", arch: x86_64}, packages: *cupy_packages_cu11_x86_64} + + # All CUDA 11 + aarch64 versions + - matrix: {cuda: "11.8", arch: aarch64} + packages: &cupy_packages_cu11_aarch64 + - cupy-cuda11x -f https://pip.cupy.dev/aarch64 # TODO: Verify that this works. + - {matrix: {cuda: "11.5", arch: aarch64}, packages: *cupy_packages_cu11_aarch64} + - {matrix: {cuda: "11.4", arch: aarch64}, packages: *cupy_packages_cu11_aarch64} + - {matrix: {cuda: "11.2", arch: aarch64}, packages: *cupy_packages_cu11_aarch64} + - {matrix: null, packages: [cupy-cuda11x>=12.0.0]} From 84207c34ee3a5a02853762f85b40cdaeb5afdee9 Mon Sep 17 00:00:00 2001 From: Alex Barghi <105237337+alexbarghi-nv@users.noreply.github.com> Date: Thu, 28 Sep 2023 10:38:29 -0400 Subject: [PATCH 020/111] Integrate C++ Renumbering and Compression (#3841) - [x] C API - [x] PLC - [x] Python API - [x] Bulk Sampling API - [x] Documentation for Python SG - [x] Documentation for Python MG - [x] Documentation for Bulk Sampler - [x] Resolve the C++ empty batch issue with new check - [x] Add FutureWarnings for all temporary flags - [x] Remove all print statements and pytest tags - [x] Verify cuGraph-PyG and cuGraph-DGL tests Authors: - Alex Barghi (https://github.com/alexbarghi-nv) - Seunghwa Kang (https://github.com/seunghwak) Approvers: - Chuck Hastings (https://github.com/ChuckHastings) - Tingyu Wang (https://github.com/tingyu66) - Seunghwa Kang (https://github.com/seunghwak) - Joseph Nke (https://github.com/jnke2016) - Brad Rees (https://github.com/BradReesWork) URL: https://github.com/rapidsai/cugraph/pull/3841 --- cpp/include/cugraph/sampling_functions.hpp | 2 +- cpp/include/cugraph_c/sampling_algorithms.h | 127 ++++---- cpp/src/c_api/uniform_neighbor_sampling.cpp | 291 ++++++++++++----- .../sampling_post_processing_impl.cuh | 4 +- cpp/tests/c_api/create_graph_test.c | 26 +- .../c_api/mg_uniform_neighbor_sample_test.c | 193 +++++++---- .../c_api/uniform_neighbor_sample_test.c | 267 ++++------------ .../dask/sampling/uniform_neighbor_sample.py | 300 +++++++++--------- .../cugraph/gnn/data_loading/bulk_sampler.py | 1 + .../gnn/data_loading/bulk_sampler_io.py | 219 ++++++++++++- .../cugraph/sampling/sampling_utilities.py | 198 ++++++++++++ .../sampling/uniform_neighbor_sample.py | 197 ++++++------ .../tests/sampling/test_bulk_sampler.py | 52 ++- .../tests/sampling/test_bulk_sampler_io.py | 69 +++- .../tests/sampling/test_bulk_sampler_io_mg.py | 14 +- .../tests/sampling/test_bulk_sampler_mg.py | 58 +++- .../sampling/test_uniform_neighbor_sample.py | 207 +++++++++++- .../test_uniform_neighbor_sample_mg.py | 244 +++++++++++++- .../pylibcugraph/_cugraph_c/algorithms.pxd | 48 ++- .../_cugraph_c/sampling_algorithms.pxd | 17 - .../internal_types/sampling_result.pyx | 91 +++++- .../tests/test_uniform_neighbor_sample.py | 4 +- .../pylibcugraph/uniform_neighbor_sample.pyx | 112 ++++++- 23 files changed, 2021 insertions(+), 720 deletions(-) create mode 100644 python/cugraph/cugraph/sampling/sampling_utilities.py diff --git a/cpp/include/cugraph/sampling_functions.hpp b/cpp/include/cugraph/sampling_functions.hpp index e42ef9bfcf3..75cf8f91f92 100644 --- a/cpp/include/cugraph/sampling_functions.hpp +++ b/cpp/include/cugraph/sampling_functions.hpp @@ -103,7 +103,7 @@ namespace cugraph { * std::get<1>(*edgelist_label_offsets) if @p edgelist_label_offsets.has_value() is true and 1 * otherwise and # hops = std::get<1>(*edgelist_hops) if edgelist_hops.has_value() is true and 1 * otherwise, valid only if at least one of @p edgelist_label_offsets.has_value() or @p - * edgelist_hops.has_value() is rue), renumber_map to query original vertices (size = # unique + * edgelist_hops.has_value() is true), renumber_map to query original vertices (size = # unique * vertices or aggregate # unique vertices for every label), and label offsets to the renumber_map * (size = std::get<1>(*edgelist_label_offsets) + 1, valid only if @p * edgelist_label_offsets.has_value() is true). diff --git a/cpp/include/cugraph_c/sampling_algorithms.h b/cpp/include/cugraph_c/sampling_algorithms.h index 37124d100dd..92fe50ef622 100644 --- a/cpp/include/cugraph_c/sampling_algorithms.h +++ b/cpp/include/cugraph_c/sampling_algorithms.h @@ -205,6 +205,21 @@ typedef enum cugraph_prior_sources_behavior_t { but exclude any vertex that has already been used as a source */ } cugraph_prior_sources_behavior_t; +/** + * @brief Selects the type of compression to use for the output samples. + */ +typedef enum cugraph_compression_type_t { + COO = 0, /** Outputs in COO format. Default. */ + CSR, /** Compresses in CSR format. This means the row (src) column + is compressed into a row pointer. */ + CSC, /** Compresses in CSC format. This means the col (dst) column + is compressed into a column pointer. */ + DCSR, /** Compresses in DCSR format. This outputs an additional index + that avoids empty entries in the row pointer. */ + DCSC /** Compresses in DCSC format. This outputs an additional index + that avoid empty entries in the col pointer. */ +} cugraph_compression_type_t; + /** * @brief Create sampling options object * @@ -225,6 +240,14 @@ cugraph_error_code_t cugraph_sampling_options_create(cugraph_sampling_options_t* */ void cugraph_sampling_set_renumber_results(cugraph_sampling_options_t* options, bool_t value); +/** + * @brief Set whether to compress per-hop (True) or globally (False) + * + * @param options - opaque pointer to the sampling options + * @param value - Boolean value to assign to the option + */ +void cugraph_sampling_set_compress_per_hop(cugraph_sampling_options_t* options, bool_t value); + /** * @brief Set flag to sample with_replacement * @@ -241,6 +264,15 @@ void cugraph_sampling_set_with_replacement(cugraph_sampling_options_t* options, */ void cugraph_sampling_set_return_hops(cugraph_sampling_options_t* options, bool_t value); +/** + * @brief Set compression type + * + * @param options - opaque pointer to the sampling options + * @param value - Enum defining the compresion type + */ +void cugraph_sampling_set_compression_type(cugraph_sampling_options_t* options, + cugraph_compression_type_t value); + /** * @brief Set prior sources behavior * @@ -265,62 +297,6 @@ void cugraph_sampling_set_dedupe_sources(cugraph_sampling_options_t* options, bo */ void cugraph_sampling_options_free(cugraph_sampling_options_t* options); -/** - * @brief Uniform Neighborhood Sampling - * @deprecated This call should be replaced with cugraph_uniform_neighbor_sample - * - * Returns a sample of the neighborhood around specified start vertices. Optionally, each - * start vertex can be associated with a label, allowing the caller to specify multiple batches - * of sampling requests in the same function call - which should improve GPU utilization. - * - * If label is NULL then all start vertices will be considered part of the same batch and the - * return value will not have a label column. - * - * @param [in] handle Handle for accessing resources - * @param [in] graph Pointer to graph. NOTE: Graph might be modified if the storage - * needs to be transposed - * @param [in] start_vertices Device array of start vertices for the sampling - * @param [in] start_vertex_labels Device array of start vertex labels for the sampling. The - * labels associated with each start vertex will be included in the output associated with results - * that were derived from that start vertex. We only support label of type INT32. If label is - * NULL, the return data will not be labeled. - * @param [in] label_list Device array of the labels included in @p start_vertex_labels. If - * @p label_to_comm_rank is not specified this parameter is ignored. If specified, label_list - * must be sorted in ascending order. - * @param [in] label_to_comm_rank Device array identifying which comm rank the output for a - * particular label should be shuffled in the output. If not specifed the data is not organized in - * output. If specified then the all data from @p label_list[i] will be shuffled to rank @p - * label_to_comm_rank[i]. If not specified then the output data will not be shuffled between ranks. - * @param [in] fanout Host array defining the fan out at each step in the sampling algorithm. - * We only support fanout values of type INT32 - * @param [in/out] rng_state State of the random number generator, updated with each call - * @param [in] with_replacement - * Boolean value. If true selection of edges is done with - * replacement. If false selection is done without replacement. - * @param [in] return_hops Boolean value. If true include the hop number in the result, - * If false the hop number will not be included in result. - * @param [in] do_expensive_check - * A flag to run expensive checks for input arguments (if set to true) - * @param [in] result Output from the uniform_neighbor_sample call - * @param [out] error Pointer to an error object storing details of any error. Will - * be populated if error code is not CUGRAPH_SUCCESS - * @return error code - */ -cugraph_error_code_t cugraph_uniform_neighbor_sample_with_edge_properties( - const cugraph_resource_handle_t* handle, - cugraph_graph_t* graph, - const cugraph_type_erased_device_array_view_t* start_vertices, - const cugraph_type_erased_device_array_view_t* start_vertex_labels, - const cugraph_type_erased_device_array_view_t* label_list, - const cugraph_type_erased_device_array_view_t* label_to_comm_rank, - const cugraph_type_erased_host_array_view_t* fan_out, - cugraph_rng_state_t* rng_state, - bool_t with_replacement, - bool_t return_hops, - bool_t do_expensive_check, - cugraph_sample_result_t** result, - cugraph_error_t** error); - /** * @brief Uniform Neighborhood Sampling * @@ -374,6 +350,7 @@ cugraph_error_code_t cugraph_uniform_neighbor_sample( cugraph_error_t** error); /** + * @deprecated This call should be replaced with cugraph_sample_result_get_majors * @brief Get the source vertices from the sampling algorithm result * * @param [in] result The result from a sampling algorithm @@ -383,6 +360,7 @@ cugraph_type_erased_device_array_view_t* cugraph_sample_result_get_sources( const cugraph_sample_result_t* result); /** + * @deprecated This call should be replaced with cugraph_sample_result_get_minors * @brief Get the destination vertices from the sampling algorithm result * * @param [in] result The result from a sampling algorithm @@ -391,6 +369,33 @@ cugraph_type_erased_device_array_view_t* cugraph_sample_result_get_sources( cugraph_type_erased_device_array_view_t* cugraph_sample_result_get_destinations( const cugraph_sample_result_t* result); +/** + * @brief Get the major vertices from the sampling algorithm result + * + * @param [in] result The result from a sampling algorithm + * @return type erased array pointing to the major vertices in device memory + */ +cugraph_type_erased_device_array_view_t* cugraph_sample_result_get_majors( + const cugraph_sample_result_t* result); + +/** + * @brief Get the minor vertices from the sampling algorithm result + * + * @param [in] result The result from a sampling algorithm + * @return type erased array pointing to the minor vertices in device memory + */ +cugraph_type_erased_device_array_view_t* cugraph_sample_result_get_minors( + const cugraph_sample_result_t* result); + +/** + * @brief Get the major offsets from the sampling algorithm result + * + * @param [in] result The result from a sampling algorithm + * @return type erased array pointing to the major offsets in device memory + */ +cugraph_type_erased_device_array_view_t* cugraph_sample_result_get_major_offsets( + const cugraph_sample_result_t* result); + /** * @brief Get the start labels from the sampling algorithm result * @@ -436,6 +441,15 @@ cugraph_type_erased_device_array_view_t* cugraph_sample_result_get_edge_weight( cugraph_type_erased_device_array_view_t* cugraph_sample_result_get_hop( const cugraph_sample_result_t* result); +/** + * @brief Get the label-hop offsets from the sampling algorithm result + * + * @param [in] result The result from a sampling algorithm + * @return type erased array pointing to the label-hop offsets + */ +cugraph_type_erased_device_array_view_t* cugraph_sample_result_get_label_hop_offsets( + const cugraph_sample_result_t* result); + /** * @brief Get the index from the sampling algorithm result * @@ -446,6 +460,7 @@ cugraph_type_erased_device_array_view_t* cugraph_sample_result_get_index( const cugraph_sample_result_t* result); /** + * @deprecated This call should be replaced with cugraph_sample_get_get_label_hop_offsets * @brief Get the result offsets from the sampling algorithm result * * @param [in] result The result from a sampling algorithm diff --git a/cpp/src/c_api/uniform_neighbor_sampling.cpp b/cpp/src/c_api/uniform_neighbor_sampling.cpp index f146c331d8c..1a53c899109 100644 --- a/cpp/src/c_api/uniform_neighbor_sampling.cpp +++ b/cpp/src/c_api/uniform_neighbor_sampling.cpp @@ -38,17 +38,20 @@ struct cugraph_sampling_options_t { prior_sources_behavior_t prior_sources_behavior_{prior_sources_behavior_t::DEFAULT}; bool_t dedupe_sources_{FALSE}; bool_t renumber_results_{FALSE}; + cugraph_compression_type_t compression_type_{cugraph_compression_type_t::COO}; + bool_t compress_per_hop_{FALSE}; }; struct cugraph_sample_result_t { - cugraph_type_erased_device_array_t* src_{nullptr}; - cugraph_type_erased_device_array_t* dst_{nullptr}; + cugraph_type_erased_device_array_t* major_offsets_{nullptr}; + cugraph_type_erased_device_array_t* majors_{nullptr}; + cugraph_type_erased_device_array_t* minors_{nullptr}; cugraph_type_erased_device_array_t* edge_id_{nullptr}; cugraph_type_erased_device_array_t* edge_type_{nullptr}; cugraph_type_erased_device_array_t* wgt_{nullptr}; cugraph_type_erased_device_array_t* hop_{nullptr}; + cugraph_type_erased_device_array_t* label_hop_offsets_{nullptr}; cugraph_type_erased_device_array_t* label_{nullptr}; - cugraph_type_erased_device_array_t* offsets_{nullptr}; cugraph_type_erased_device_array_t* renumber_map_{nullptr}; cugraph_type_erased_device_array_t* renumber_map_offsets_{nullptr}; }; @@ -186,6 +189,8 @@ struct uniform_neighbor_sampling_functor : public cugraph::c_api::abstract_funct graph_view.local_vertex_partition_range_last(), do_expensive_check_); + bool has_labels = start_vertex_labels_ != nullptr; + auto&& [src, dst, wgt, edge_id, edge_type, hop, edge_label, offsets] = cugraph::uniform_neighbor_sample( handle_, @@ -229,25 +234,130 @@ struct uniform_neighbor_sampling_functor : public cugraph::c_api::abstract_funct vertex_partition_lasts, do_expensive_check_); + std::optional> majors{std::nullopt}; + rmm::device_uvector minors(0, handle_.get_stream()); + std::optional> major_offsets{std::nullopt}; + + std::optional> label_hop_offsets{std::nullopt}; + std::optional> renumber_map{std::nullopt}; std::optional> renumber_map_offsets{std::nullopt}; + bool src_is_major = (options_.compression_type_ == cugraph_compression_type_t::CSR) || + (options_.compression_type_ == cugraph_compression_type_t::DCSR) || + (options_.compression_type_ == cugraph_compression_type_t::COO); + if (options_.renumber_results_) { - std::tie(src, dst, renumber_map, renumber_map_offsets) = cugraph::renumber_sampled_edgelist( - handle_, - std::move(src), - std::move(dst), - hop ? std::make_optional(raft::device_span{hop->data(), hop->size()}) - : std::nullopt, - std::make_optional(std::make_tuple( - raft::device_span{edge_label->data(), edge_label->size()}, - raft::device_span{offsets->data(), offsets->size()})), - do_expensive_check_); + if (options_.compression_type_ == cugraph_compression_type_t::COO) { + // COO + + rmm::device_uvector output_majors(0, handle_.get_stream()); + rmm::device_uvector output_renumber_map(0, handle_.get_stream()); + std::tie(output_majors, + minors, + wgt, + edge_id, + edge_type, + label_hop_offsets, + output_renumber_map, + renumber_map_offsets) = + cugraph::renumber_and_sort_sampled_edgelist( + handle_, + std::move(src), + std::move(dst), + wgt ? std::move(wgt) : std::nullopt, + edge_id ? std::move(edge_id) : std::nullopt, + edge_type ? std::move(edge_type) : std::nullopt, + hop ? std::make_optional(std::make_tuple(std::move(*hop), fan_out_->size_)) + : std::nullopt, + offsets ? std::make_optional(std::make_tuple( + raft::device_span{offsets->data(), offsets->size()}, + edge_label->size())) + : std::nullopt, + src_is_major, + do_expensive_check_); + + majors.emplace(std::move(output_majors)); + renumber_map.emplace(std::move(output_renumber_map)); + } else { + // (D)CSC, (D)CSR + + bool doubly_compress = (options_.compression_type_ == cugraph_compression_type_t::DCSR) || + (options_.compression_type_ == cugraph_compression_type_t::DCSC); + + rmm::device_uvector output_major_offsets(0, handle_.get_stream()); + rmm::device_uvector output_renumber_map(0, handle_.get_stream()); + std::tie(majors, + output_major_offsets, + minors, + wgt, + edge_id, + edge_type, + label_hop_offsets, + output_renumber_map, + renumber_map_offsets) = + cugraph::renumber_and_compress_sampled_edgelist( + handle_, + std::move(src), + std::move(dst), + wgt ? std::move(wgt) : std::nullopt, + edge_id ? std::move(edge_id) : std::nullopt, + edge_type ? std::move(edge_type) : std::nullopt, + hop ? std::make_optional(std::make_tuple(std::move(*hop), fan_out_->size_)) + : std::nullopt, + offsets ? std::make_optional(std::make_tuple( + raft::device_span{offsets->data(), offsets->size()}, + edge_label->size())) + : std::nullopt, + src_is_major, + options_.compress_per_hop_, + doubly_compress, + do_expensive_check_); + + renumber_map.emplace(std::move(output_renumber_map)); + major_offsets.emplace(std::move(output_major_offsets)); + } + + // These are now represented by label_hop_offsets + hop.reset(); + offsets.reset(); + } else { + if (options_.compression_type_ != cugraph_compression_type_t::COO) { + CUGRAPH_FAIL("Can only use COO format if not renumbering"); + } + + std::tie(src, dst, wgt, edge_id, edge_type, label_hop_offsets) = + cugraph::sort_sampled_edgelist( + handle_, + std::move(src), + std::move(dst), + wgt ? std::move(wgt) : std::nullopt, + edge_id ? std::move(edge_id) : std::nullopt, + edge_type ? std::move(edge_type) : std::nullopt, + hop ? std::make_optional(std::make_tuple(std::move(*hop), fan_out_->size_)) + : std::nullopt, + offsets ? std::make_optional(std::make_tuple( + raft::device_span{offsets->data(), offsets->size()}, + edge_label->size())) + : std::nullopt, + src_is_major, + do_expensive_check_); + + majors.emplace(std::move(src)); + minors = std::move(dst); + + hop.reset(); + offsets.reset(); } result_ = new cugraph::c_api::cugraph_sample_result_t{ - new cugraph::c_api::cugraph_type_erased_device_array_t(src, graph_->vertex_type_), - new cugraph::c_api::cugraph_type_erased_device_array_t(dst, graph_->vertex_type_), + (major_offsets) + ? new cugraph::c_api::cugraph_type_erased_device_array_t(*major_offsets, SIZE_T) + : nullptr, + (majors) + ? new cugraph::c_api::cugraph_type_erased_device_array_t(*majors, graph_->vertex_type_) + : nullptr, + new cugraph::c_api::cugraph_type_erased_device_array_t(minors, graph_->vertex_type_), (edge_id) ? new cugraph::c_api::cugraph_type_erased_device_array_t(*edge_id, graph_->edge_type_) : nullptr, @@ -256,12 +366,14 @@ struct uniform_neighbor_sampling_functor : public cugraph::c_api::abstract_funct : nullptr, (wgt) ? new cugraph::c_api::cugraph_type_erased_device_array_t(*wgt, graph_->weight_type_) : nullptr, - (hop) ? new cugraph::c_api::cugraph_type_erased_device_array_t(*hop, INT32) : nullptr, + (hop) ? new cugraph::c_api::cugraph_type_erased_device_array_t(*hop, INT32) + : nullptr, // FIXME get rid of this + (label_hop_offsets) + ? new cugraph::c_api::cugraph_type_erased_device_array_t(*label_hop_offsets, SIZE_T) + : nullptr, (edge_label) ? new cugraph::c_api::cugraph_type_erased_device_array_t(edge_label.value(), INT32) : nullptr, - (offsets) ? new cugraph::c_api::cugraph_type_erased_device_array_t(offsets.value(), SIZE_T) - : nullptr, (renumber_map) ? new cugraph::c_api::cugraph_type_erased_device_array_t( renumber_map.value(), graph_->vertex_type_) : nullptr, @@ -295,6 +407,13 @@ extern "C" void cugraph_sampling_set_renumber_results(cugraph_sampling_options_t internal_pointer->renumber_results_ = value; } +extern "C" void cugraph_sampling_set_compress_per_hop(cugraph_sampling_options_t* options, + bool_t value) +{ + auto internal_pointer = reinterpret_cast(options); + internal_pointer->compress_per_hop_ = value; +} + extern "C" void cugraph_sampling_set_with_replacement(cugraph_sampling_options_t* options, bool_t value) { @@ -308,6 +427,20 @@ extern "C" void cugraph_sampling_set_return_hops(cugraph_sampling_options_t* opt internal_pointer->return_hops_ = value; } +extern "C" void cugraph_sampling_set_compression_type(cugraph_sampling_options_t* options, + cugraph_compression_type_t value) +{ + auto internal_pointer = reinterpret_cast(options); + switch (value) { + case COO: internal_pointer->compression_type_ = cugraph_compression_type_t::COO; break; + case CSR: internal_pointer->compression_type_ = cugraph_compression_type_t::CSR; break; + case CSC: internal_pointer->compression_type_ = cugraph_compression_type_t::CSC; break; + case DCSR: internal_pointer->compression_type_ = cugraph_compression_type_t::DCSR; break; + case DCSC: internal_pointer->compression_type_ = cugraph_compression_type_t::DCSC; break; + default: CUGRAPH_FAIL("Invalid compression type"); + } +} + extern "C" void cugraph_sampling_set_prior_sources_behavior(cugraph_sampling_options_t* options, cugraph_prior_sources_behavior_t value) { @@ -341,15 +474,45 @@ extern "C" void cugraph_sampling_options_free(cugraph_sampling_options_t* option extern "C" cugraph_type_erased_device_array_view_t* cugraph_sample_result_get_sources( const cugraph_sample_result_t* result) { - auto internal_pointer = reinterpret_cast(result); - return reinterpret_cast(internal_pointer->src_->view()); + // Deprecated. + return cugraph_sample_result_get_majors(result); } extern "C" cugraph_type_erased_device_array_view_t* cugraph_sample_result_get_destinations( const cugraph_sample_result_t* result) +{ + // Deprecated. + return cugraph_sample_result_get_minors(result); +} + +extern "C" cugraph_type_erased_device_array_view_t* cugraph_sample_result_get_majors( + const cugraph_sample_result_t* result) +{ + auto internal_pointer = reinterpret_cast(result); + return (internal_pointer->majors_ != nullptr) + ? reinterpret_cast( + internal_pointer->majors_->view()) + + : NULL; +} + +extern "C" cugraph_type_erased_device_array_view_t* cugraph_sample_result_get_major_offsets( + const cugraph_sample_result_t* result) +{ + auto internal_pointer = reinterpret_cast(result); + return (internal_pointer->major_offsets_ != nullptr) + ? reinterpret_cast( + internal_pointer->major_offsets_->view()) + + : NULL; +} + +extern "C" cugraph_type_erased_device_array_view_t* cugraph_sample_result_get_minors( + const cugraph_sample_result_t* result) { auto internal_pointer = reinterpret_cast(result); - return reinterpret_cast(internal_pointer->dst_->view()); + return reinterpret_cast( + internal_pointer->minors_->view()); } extern "C" cugraph_type_erased_device_array_view_t* cugraph_sample_result_get_start_labels( @@ -402,6 +565,16 @@ extern "C" cugraph_type_erased_device_array_view_t* cugraph_sample_result_get_ho : NULL; } +extern "C" cugraph_type_erased_device_array_view_t* cugraph_sample_result_get_label_hop_offsets( + const cugraph_sample_result_t* result) +{ + auto internal_pointer = reinterpret_cast(result); + return internal_pointer->label_hop_offsets_ != nullptr + ? reinterpret_cast( + internal_pointer->label_hop_offsets_->view()) + : NULL; +} + extern "C" cugraph_type_erased_device_array_view_t* cugraph_sample_result_get_index( const cugraph_sample_result_t* result) { @@ -413,9 +586,8 @@ extern "C" cugraph_type_erased_device_array_view_t* cugraph_sample_result_get_in extern "C" cugraph_type_erased_device_array_view_t* cugraph_sample_result_get_offsets( const cugraph_sample_result_t* result) { - auto internal_pointer = reinterpret_cast(result); - return reinterpret_cast( - internal_pointer->offsets_->view()); + // Deprecated. + return cugraph_sample_result_get_label_hop_offsets(result); } extern "C" cugraph_type_erased_device_array_view_t* cugraph_sample_result_get_renumber_map( @@ -532,6 +704,7 @@ extern "C" cugraph_error_code_t cugraph_test_uniform_neighborhood_sample_result_ // create new cugraph_sample_result_t *result = reinterpret_cast(new cugraph::c_api::cugraph_sample_result_t{ + nullptr, reinterpret_cast( new_device_srcs.release()), reinterpret_cast( @@ -675,78 +848,20 @@ extern "C" cugraph_error_code_t cugraph_test_sample_result_create( extern "C" void cugraph_sample_result_free(cugraph_sample_result_t* result) { auto internal_pointer = reinterpret_cast(result); - delete internal_pointer->src_; - delete internal_pointer->dst_; + delete internal_pointer->major_offsets_; + delete internal_pointer->majors_; + delete internal_pointer->minors_; delete internal_pointer->edge_id_; delete internal_pointer->edge_type_; delete internal_pointer->wgt_; delete internal_pointer->hop_; + delete internal_pointer->label_hop_offsets_; delete internal_pointer->label_; + delete internal_pointer->renumber_map_; + delete internal_pointer->renumber_map_offsets_; delete internal_pointer; } -extern "C" cugraph_error_code_t cugraph_uniform_neighbor_sample_with_edge_properties( - const cugraph_resource_handle_t* handle, - cugraph_graph_t* graph, - const cugraph_type_erased_device_array_view_t* start_vertices, - const cugraph_type_erased_device_array_view_t* start_vertex_labels, - const cugraph_type_erased_device_array_view_t* label_list, - const cugraph_type_erased_device_array_view_t* label_to_comm_rank, - const cugraph_type_erased_host_array_view_t* fan_out, - cugraph_rng_state_t* rng_state, - bool_t with_replacement, - bool_t return_hops, - bool_t do_expensive_check, - cugraph_sample_result_t** result, - cugraph_error_t** error) -{ - CAPI_EXPECTS((start_vertex_labels == nullptr) || - (reinterpret_cast( - start_vertex_labels) - ->type_ == INT32), - CUGRAPH_INVALID_INPUT, - "start_vertex_labels should be of type int", - *error); - - CAPI_EXPECTS((label_to_comm_rank == nullptr) || (start_vertex_labels != nullptr), - CUGRAPH_INVALID_INPUT, - "cannot specify label_to_comm_rank unless start_vertex_labels is also specified", - *error); - - CAPI_EXPECTS((label_to_comm_rank == nullptr) || (label_list != nullptr), - CUGRAPH_INVALID_INPUT, - "cannot specify label_to_comm_rank unless label_list is also specified", - *error); - - CAPI_EXPECTS(reinterpret_cast(graph)->vertex_type_ == - reinterpret_cast( - start_vertices) - ->type_, - CUGRAPH_INVALID_INPUT, - "vertex type of graph and start_vertices must match", - *error); - - CAPI_EXPECTS( - reinterpret_cast(fan_out) - ->type_ == INT32, - CUGRAPH_INVALID_INPUT, - "fan_out should be of type int", - *error); - - uniform_neighbor_sampling_functor functor{ - handle, - graph, - start_vertices, - start_vertex_labels, - label_list, - label_to_comm_rank, - fan_out, - rng_state, - cugraph::c_api::cugraph_sampling_options_t{with_replacement, return_hops}, - do_expensive_check}; - return cugraph::c_api::run_algorithm(graph, functor, result, error); -} - cugraph_error_code_t cugraph_uniform_neighbor_sample( const cugraph_resource_handle_t* handle, cugraph_graph_t* graph, diff --git a/cpp/src/sampling/sampling_post_processing_impl.cuh b/cpp/src/sampling/sampling_post_processing_impl.cuh index 0c397d91b20..77d4f2d865f 100644 --- a/cpp/src/sampling/sampling_post_processing_impl.cuh +++ b/cpp/src/sampling/sampling_post_processing_impl.cuh @@ -166,9 +166,7 @@ void check_input_edges( std::numeric_limits::max()), "Invalid input arguments: current implementation assumes that the number of " "unique labels is no larger than std::numeric_limits::max()."); - CUGRAPH_EXPECTS(!edgelist_label_offsets || std::get<1>(*edgelist_label_offsets) > 0, - "Invlaid input arguments: there should be 1 or more labels if " - "edgelist_label_offsets.has_value() is true."); + CUGRAPH_EXPECTS( !edgelist_label_offsets.has_value() || (std::get<0>(*edgelist_label_offsets).size() == std::get<1>(*edgelist_label_offsets) + 1), diff --git a/cpp/tests/c_api/create_graph_test.c b/cpp/tests/c_api/create_graph_test.c index eef49458f2b..736db761ebd 100644 --- a/cpp/tests/c_api/create_graph_test.c +++ b/cpp/tests/c_api/create_graph_test.c @@ -142,6 +142,14 @@ int test_create_sg_graph_csr() vertex_t h_start[] = {0, 1, 2, 3, 4, 5}; weight_t h_wgt[] = {0.1f, 2.1f, 1.1f, 5.1f, 3.1f, 4.1f, 7.2f, 3.2f}; + bool_t with_replacement = FALSE; + bool_t return_hops = TRUE; + cugraph_prior_sources_behavior_t prior_sources_behavior = DEFAULT; + bool_t dedupe_sources = FALSE; + bool_t renumber_results = FALSE; + cugraph_compression_type_t compression = COO; + bool_t compress_per_hop = FALSE; + cugraph_resource_handle_t* handle = NULL; cugraph_graph_t* graph = NULL; cugraph_graph_properties_t properties; @@ -238,8 +246,21 @@ int test_create_sg_graph_csr() ret_code = cugraph_rng_state_create(handle, 0, &rng_state, &ret_error); TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "rng_state create failed."); - ret_code = cugraph_uniform_neighbor_sample_with_edge_properties( - handle, graph, d_start_view, NULL, NULL, NULL, h_fan_out_view, rng_state, FALSE, FALSE, FALSE, &result, &ret_error); + cugraph_sampling_options_t *sampling_options; + + ret_code = cugraph_sampling_options_create(&sampling_options, &ret_error); + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "sampling_options create failed."); + + cugraph_sampling_set_with_replacement(sampling_options, with_replacement); + cugraph_sampling_set_return_hops(sampling_options, return_hops); + cugraph_sampling_set_prior_sources_behavior(sampling_options, prior_sources_behavior); + cugraph_sampling_set_dedupe_sources(sampling_options, dedupe_sources); + cugraph_sampling_set_renumber_results(sampling_options, renumber_results); + cugraph_sampling_set_compression_type(sampling_options, compression); + cugraph_sampling_set_compress_per_hop(sampling_options, compress_per_hop); + + ret_code = cugraph_uniform_neighbor_sample( + handle, graph, d_start_view, NULL, NULL, NULL, h_fan_out_view, rng_state, sampling_options, FALSE, &result, &ret_error); TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, cugraph_error_message(ret_error)); TEST_ALWAYS_ASSERT(ret_code == CUGRAPH_SUCCESS, "uniform_neighbor_sample failed."); @@ -289,6 +310,7 @@ int test_create_sg_graph_csr() cugraph_free_resource_handle(handle); cugraph_error_free(ret_error); + cugraph_sampling_options_free(sampling_options); return test_ret_value; } diff --git a/cpp/tests/c_api/mg_uniform_neighbor_sample_test.c b/cpp/tests/c_api/mg_uniform_neighbor_sample_test.c index f8241bd8a5f..86a0a92eb01 100644 --- a/cpp/tests/c_api/mg_uniform_neighbor_sample_test.c +++ b/cpp/tests/c_api/mg_uniform_neighbor_sample_test.c @@ -213,11 +213,6 @@ int generic_uniform_neighbor_sample_test(const cugraph_resource_handle_t* handle TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "gatherv_fill failed."); } - if (return_hops) { - ret_code = cugraph_test_device_gatherv_fill(handle, result_hops, h_result_hops); - TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "gatherv_fill failed."); - } - if (d_start_labels != NULL) { size_t sz = cugraph_type_erased_device_array_view_size(result_offsets); @@ -452,6 +447,7 @@ int test_uniform_neighbor_from_alex(const cugraph_resource_handle_t* handle) size_t num_vertices = 5; size_t fan_out_size = 2; size_t num_starts = 2; + size_t num_start_labels = 2; vertex_t src[] = {0, 1, 2, 3, 4, 3, 4, 2, 0, 1, 0, 2}; vertex_t dst[] = {1, 2, 4, 2, 3, 4, 1, 1, 2, 3, 4, 4}; @@ -462,7 +458,6 @@ int test_uniform_neighbor_from_alex(const cugraph_resource_handle_t* handle) int32_t batch[] = {0, 1}; int fan_out[] = {2, 2}; - bool_t with_replacement = TRUE; bool_t store_transposed = FALSE; int test_ret_value = 0; @@ -472,6 +467,14 @@ int test_uniform_neighbor_from_alex(const cugraph_resource_handle_t* handle) cugraph_graph_t* graph = NULL; cugraph_sample_result_t* result = NULL; + bool_t with_replacement = FALSE; + bool_t return_hops = TRUE; + cugraph_prior_sources_behavior_t prior_sources_behavior = DEFAULT; + bool_t dedupe_sources = FALSE; + bool_t renumber_results = FALSE; + cugraph_compression_type_t compression = COO; + bool_t compress_per_hop = FALSE; + cugraph_type_erased_device_array_t* d_start = NULL; cugraph_type_erased_device_array_t* d_label = NULL; cugraph_type_erased_device_array_view_t* d_start_view = NULL; @@ -512,19 +515,31 @@ int test_uniform_neighbor_from_alex(const cugraph_resource_handle_t* handle) h_fan_out_view = cugraph_type_erased_host_array_view_create(fan_out, fan_out_size, INT32); - ret_code = cugraph_uniform_neighbor_sample_with_edge_properties(handle, - graph, - d_start_view, - d_label_view, - NULL, - NULL, - h_fan_out_view, - rng_state, - with_replacement, - TRUE, - FALSE, - &result, - &ret_error); + cugraph_sampling_options_t *sampling_options; + + ret_code = cugraph_sampling_options_create(&sampling_options, &ret_error); + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "sampling_options create failed."); + + cugraph_sampling_set_with_replacement(sampling_options, with_replacement); + cugraph_sampling_set_return_hops(sampling_options, return_hops); + cugraph_sampling_set_prior_sources_behavior(sampling_options, prior_sources_behavior); + cugraph_sampling_set_dedupe_sources(sampling_options, dedupe_sources); + cugraph_sampling_set_renumber_results(sampling_options, renumber_results); + cugraph_sampling_set_compression_type(sampling_options, compression); + cugraph_sampling_set_compress_per_hop(sampling_options, compress_per_hop); + + ret_code = cugraph_uniform_neighbor_sample(handle, + graph, + d_start_view, + d_label_view, + NULL, + NULL, + h_fan_out_view, + rng_state, + sampling_options, + FALSE, + &result, + &ret_error); #ifdef NO_CUGRAPH_OPS TEST_ASSERT( @@ -540,6 +555,7 @@ int test_uniform_neighbor_from_alex(const cugraph_resource_handle_t* handle) cugraph_type_erased_device_array_view_t* result_weight; cugraph_type_erased_device_array_view_t* result_labels; cugraph_type_erased_device_array_view_t* result_hops; + cugraph_type_erased_device_array_view_t* result_offsets; result_src = cugraph_sample_result_get_sources(result); result_dst = cugraph_sample_result_get_destinations(result); @@ -548,8 +564,10 @@ int test_uniform_neighbor_from_alex(const cugraph_resource_handle_t* handle) result_weight = cugraph_sample_result_get_edge_weight(result); result_labels = cugraph_sample_result_get_start_labels(result); result_hops = cugraph_sample_result_get_hop(result); + result_offsets = cugraph_sample_result_get_offsets(result); size_t result_size = cugraph_type_erased_device_array_view_size(result_src); + size_t offsets_size = cugraph_type_erased_device_array_view_size(result_offsets); vertex_t h_srcs[result_size]; vertex_t h_dsts[result_size]; @@ -558,6 +576,7 @@ int test_uniform_neighbor_from_alex(const cugraph_resource_handle_t* handle) weight_t h_wgt[result_size]; int h_labels[result_size]; int h_hop[result_size]; + int h_offsets[offsets_size]; ret_code = cugraph_type_erased_device_array_view_copy_to_host( handle, (byte_t*)h_srcs, result_src, &ret_error); @@ -584,9 +603,24 @@ int test_uniform_neighbor_from_alex(const cugraph_resource_handle_t* handle) TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "copy_to_host failed."); ret_code = cugraph_type_erased_device_array_view_copy_to_host( - handle, (byte_t*)h_hop, result_hops, &ret_error); + handle, (byte_t*)h_offsets, result_offsets, &ret_error); TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "copy_to_host failed."); + for(int k = 0; k < offsets_size-1; k += fan_out_size) { + for(int h = 0; h < fan_out_size; ++h) { + int hop_start = h_offsets[k+h]; + int hop_end = h_offsets[k+h+1]; + for(int i = hop_start; i < hop_end; ++i) { + h_hop[i] = h; + } + } + } + + for(int k = 0; k < num_start_labels+1; ++k) { + h_offsets[k] = h_offsets[k*fan_out_size]; + } + offsets_size = num_start_labels + 1; + // NOTE: The C++ tester does a more thorough validation. For our purposes // here we will do a simpler validation, merely checking that all edges // are actually part of the graph @@ -611,6 +645,7 @@ int test_uniform_neighbor_from_alex(const cugraph_resource_handle_t* handle) cugraph_type_erased_host_array_view_free(h_fan_out_view); cugraph_mg_graph_free(graph); cugraph_error_free(ret_error); + cugraph_sampling_options_free(sampling_options); return test_ret_value; } @@ -661,6 +696,15 @@ int test_uniform_neighbor_sample_alex_bug(const cugraph_resource_handle_t* handl size_t expected_size[] = { 3, 2, 1, 1, 1, 1, 1, 1 }; + + bool_t with_replacement = FALSE; + bool_t return_hops = TRUE; + cugraph_prior_sources_behavior_t prior_sources_behavior = CARRY_OVER; + bool_t dedupe_sources = TRUE; + bool_t renumber_results = FALSE; + cugraph_compression_type_t compression = COO; + bool_t compress_per_hop = FALSE; + // Create graph int test_ret_value = 0; cugraph_error_code_t ret_code = CUGRAPH_SUCCESS; @@ -747,19 +791,30 @@ int test_uniform_neighbor_sample_alex_bug(const cugraph_resource_handle_t* handl h_fan_out_view = cugraph_type_erased_host_array_view_create(fan_out, fan_out_size, INT32); - ret_code = cugraph_uniform_neighbor_sample_with_edge_properties(handle, - graph, - d_start_view, - d_start_labels_view, - d_label_list_view, - d_label_to_output_comm_rank_view, - h_fan_out_view, - rng_state, - FALSE, - TRUE, - FALSE, - &result, - &ret_error); + cugraph_sampling_options_t* sampling_options; + ret_code = cugraph_sampling_options_create(&sampling_options, &ret_error); + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "sampling_options create failed."); + + cugraph_sampling_set_with_replacement(sampling_options, with_replacement); + cugraph_sampling_set_return_hops(sampling_options, return_hops); + cugraph_sampling_set_prior_sources_behavior(sampling_options, prior_sources_behavior); + cugraph_sampling_set_dedupe_sources(sampling_options, dedupe_sources); + cugraph_sampling_set_renumber_results(sampling_options, renumber_results); + cugraph_sampling_set_compression_type(sampling_options, compression); + cugraph_sampling_set_compress_per_hop(sampling_options, compress_per_hop); + + ret_code = cugraph_uniform_neighbor_sample(handle, + graph, + d_start_view, + d_start_labels_view, + d_label_list_view, + d_label_to_output_comm_rank_view, + h_fan_out_view, + rng_state, + sampling_options, + FALSE, + &result, + &ret_error); #ifdef NO_CUGRAPH_OPS TEST_ASSERT( @@ -900,6 +955,14 @@ int test_uniform_neighbor_sample_sort_by_hop(const cugraph_resource_handle_t* ha size_t expected_size[] = { 3, 2, 1, 1, 1, 1, 1, 1 }; + bool_t with_replacement = FALSE; + bool_t return_hops = TRUE; + cugraph_prior_sources_behavior_t prior_sources_behavior = CARRY_OVER; + bool_t dedupe_sources = TRUE; + bool_t renumber_results = FALSE; + cugraph_compression_type_t compression = COO; + bool_t compress_per_hop = FALSE; + // Create graph int test_ret_value = 0; cugraph_error_code_t ret_code = CUGRAPH_SUCCESS; @@ -986,19 +1049,30 @@ int test_uniform_neighbor_sample_sort_by_hop(const cugraph_resource_handle_t* ha h_fan_out_view = cugraph_type_erased_host_array_view_create(fan_out, fan_out_size, INT32); - ret_code = cugraph_uniform_neighbor_sample_with_edge_properties(handle, - graph, - d_start_view, - d_start_labels_view, - d_label_list_view, - d_label_to_output_comm_rank_view, - h_fan_out_view, - rng_state, - FALSE, - TRUE, - FALSE, - &result, - &ret_error); + cugraph_sampling_options_t* sampling_options; + ret_code = cugraph_sampling_options_create(&sampling_options, &ret_error); + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "sampling_options create failed."); + + cugraph_sampling_set_with_replacement(sampling_options, with_replacement); + cugraph_sampling_set_return_hops(sampling_options, return_hops); + cugraph_sampling_set_prior_sources_behavior(sampling_options, prior_sources_behavior); + cugraph_sampling_set_dedupe_sources(sampling_options, dedupe_sources); + cugraph_sampling_set_renumber_results(sampling_options, renumber_results); + cugraph_sampling_set_compression_type(sampling_options, compression); + cugraph_sampling_set_compress_per_hop(sampling_options, compress_per_hop); + + ret_code = cugraph_uniform_neighbor_sample(handle, + graph, + d_start_view, + d_start_labels_view, + d_label_list_view, + d_label_to_output_comm_rank_view, + h_fan_out_view, + rng_state, + sampling_options, + FALSE, + &result, + &ret_error); #ifdef NO_CUGRAPH_OPS TEST_ASSERT( @@ -1047,14 +1121,27 @@ int test_uniform_neighbor_sample_sort_by_hop(const cugraph_resource_handle_t* ha handle, (byte_t*)h_weight, result_weights, &ret_error); TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "copy_to_host failed."); - ret_code = cugraph_type_erased_device_array_view_copy_to_host( - handle, (byte_t*)h_hops, result_hops, &ret_error); - TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "copy_to_host failed."); - ret_code = cugraph_type_erased_device_array_view_copy_to_host( handle, (byte_t*)h_result_offsets, result_offsets, &ret_error); TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "copy_to_host failed."); + for(int k = 0; k < result_offsets_size-1; k += fan_out_size) { + for(int h = 0; h < fan_out_size; ++h) { + int hop_start = h_result_offsets[k+h]; + int hop_end = h_result_offsets[k+h+1]; + for(int i = hop_start; i < hop_end; ++i) { + h_hops[i] = h; + } + } + } + + size_t num_local_labels = (result_offsets_size - 1) / fan_out_size; + + for(int k = 0; k < num_local_labels+1; ++k) { + h_result_offsets[k] = h_result_offsets[k*fan_out_size]; + } + result_offsets_size = num_local_labels + 1; + // NOTE: The C++ tester does a more thorough validation. For our purposes // here we will do a simpler validation, merely checking that all edges // are actually part of the graph @@ -1223,9 +1310,9 @@ int main(int argc, char** argv) result |= RUN_MG_TEST(test_uniform_neighbor_from_alex, handle); //result |= RUN_MG_TEST(test_uniform_neighbor_sample_alex_bug, handle); result |= RUN_MG_TEST(test_uniform_neighbor_sample_sort_by_hop, handle); - result |= RUN_MG_TEST(test_uniform_neighbor_sample_dedupe_sources, handle); - result |= RUN_MG_TEST(test_uniform_neighbor_sample_unique_sources, handle); - result |= RUN_MG_TEST(test_uniform_neighbor_sample_carry_over_sources, handle); + //result |= RUN_MG_TEST(test_uniform_neighbor_sample_dedupe_sources, handle); + //result |= RUN_MG_TEST(test_uniform_neighbor_sample_unique_sources, handle); + //result |= RUN_MG_TEST(test_uniform_neighbor_sample_carry_over_sources, handle); cugraph_free_resource_handle(handle); free_mg_raft_handle(raft_handle); diff --git a/cpp/tests/c_api/uniform_neighbor_sample_test.c b/cpp/tests/c_api/uniform_neighbor_sample_test.c index a2c1e230485..92f3821e3cc 100644 --- a/cpp/tests/c_api/uniform_neighbor_sample_test.c +++ b/cpp/tests/c_api/uniform_neighbor_sample_test.c @@ -53,6 +53,7 @@ int generic_uniform_neighbor_sample_test(const cugraph_resource_handle_t* handle vertex_t *h_start, int *h_start_labels, size_t num_start_vertices, + size_t num_start_labels, int *fan_out, size_t fan_out_size, bool_t with_replacement, @@ -192,7 +193,7 @@ int generic_uniform_neighbor_sample_test(const cugraph_resource_handle_t* handle int32_t h_result_edge_types[result_size]; int32_t h_result_hops[result_size]; size_t h_result_offsets[result_offsets_size]; - int h_result_labels[result_offsets_size-1]; + int h_result_labels[num_start_labels]; vertex_t h_renumber_map[renumber_map_size]; size_t h_renumber_map_offsets[result_offsets_size]; @@ -216,9 +217,7 @@ int generic_uniform_neighbor_sample_test(const cugraph_resource_handle_t* handle handle, (byte_t*)h_result_edge_types, result_edge_types, &ret_error); TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "copy_to_host failed."); - ret_code = cugraph_type_erased_device_array_view_copy_to_host( - handle, (byte_t*)h_result_hops, result_hops, &ret_error); - TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "copy_to_host failed."); + TEST_ASSERT(test_ret_value, result_hops == NULL, "hops was not empty"); ret_code = cugraph_type_erased_device_array_view_copy_to_host( handle, (byte_t*)h_result_offsets, result_offsets, &ret_error); @@ -228,6 +227,21 @@ int generic_uniform_neighbor_sample_test(const cugraph_resource_handle_t* handle handle, (byte_t*)h_result_labels, result_labels, &ret_error); TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "copy_to_host failed."); + for(int k = 0; k < result_offsets_size-1; k += fan_out_size) { + for(int h = 0; h < fan_out_size; ++h) { + int hop_start = h_result_offsets[k+h]; + int hop_end = h_result_offsets[k+h+1]; + for(int i = hop_start; i < hop_end; ++i) { + h_result_hops[i] = h; + } + } + } + + for(int k = 0; k < num_start_labels+1; ++k) { + h_result_offsets[k] = h_result_offsets[k*fan_out_size]; + } + result_offsets_size = num_start_labels + 1; + if (renumber_results) { ret_code = cugraph_type_erased_device_array_view_copy_to_host( handle, (byte_t*)h_renumber_map, result_renumber_map, &ret_error); @@ -348,6 +362,7 @@ int generic_uniform_neighbor_sample_test(const cugraph_resource_handle_t* handle for (size_t i = h_result_offsets[label_id]; (i < h_result_offsets[label_id+1]) && (test_ret_value == 0) ; ++i) { if (h_result_hops[i] == hop) { + bool found = false; for (size_t j = 0 ; (!found) && (j < sources_size) ; ++j) { found = renumber_results ? (h_renumber_map[h_renumber_map_offsets[label_id] + h_result_srcs[i]] == check_sources[j]) @@ -516,183 +531,6 @@ int create_test_graph_with_edge_ids(const cugraph_resource_handle_t* p_handle, return test_ret_value; } -int test_uniform_neighbor_sample_with_properties(const cugraph_resource_handle_t* handle) -{ - data_type_id_t vertex_tid = INT32; - data_type_id_t edge_tid = INT32; - data_type_id_t weight_tid = FLOAT32; - data_type_id_t edge_id_tid = INT32; - data_type_id_t edge_type_tid = INT32; - - size_t num_edges = 8; - size_t num_vertices = 6; - size_t fan_out_size = 1; - size_t num_starts = 1; - - vertex_t src[] = {0, 1, 1, 2, 2, 2, 3, 4}; - vertex_t dst[] = {1, 3, 4, 0, 1, 3, 5, 5}; - edge_t edge_ids[] = {0, 1, 2, 3, 4, 5, 6, 7}; - weight_t weight[] = {0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8}; - int32_t edge_types[] = {7, 6, 5, 4, 3, 2, 1, 0}; - vertex_t start[] = {2}; - int fan_out[] = {-1}; - - // Create graph - int test_ret_value = 0; - cugraph_error_code_t ret_code = CUGRAPH_SUCCESS; - cugraph_error_t* ret_error = NULL; - cugraph_graph_t* graph = NULL; - cugraph_sample_result_t* result = NULL; - - ret_code = create_sg_test_graph(handle, - vertex_tid, - edge_tid, - src, - dst, - weight_tid, - weight, - edge_type_tid, - edge_types, - edge_id_tid, - edge_ids, - num_edges, - FALSE, - TRUE, - FALSE, - FALSE, - &graph, - &ret_error); - - TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "graph creation failed."); - - cugraph_type_erased_device_array_t* d_start = NULL; - cugraph_type_erased_device_array_view_t* d_start_view = NULL; - cugraph_type_erased_host_array_view_t* h_fan_out_view = NULL; - - ret_code = - cugraph_type_erased_device_array_create(handle, num_starts, INT32, &d_start, &ret_error); - TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "d_start create failed."); - - d_start_view = cugraph_type_erased_device_array_view(d_start); - - ret_code = cugraph_type_erased_device_array_view_copy_from_host( - handle, d_start_view, (byte_t*)start, &ret_error); - - TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "start copy_from_host failed."); - - h_fan_out_view = cugraph_type_erased_host_array_view_create(fan_out, 1, INT32); - - cugraph_rng_state_t *rng_state; - ret_code = cugraph_rng_state_create(handle, 0, &rng_state, &ret_error); - TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "rng_state create failed."); - - ret_code = cugraph_uniform_neighbor_sample_with_edge_properties(handle, - graph, - d_start_view, - NULL, - NULL, - NULL, - h_fan_out_view, - rng_state, - FALSE, - TRUE, - FALSE, - &result, - &ret_error); - -#ifdef NO_CUGRAPH_OPS - TEST_ASSERT( - test_ret_value, ret_code != CUGRAPH_SUCCESS, "uniform_neighbor_sample should have failed") -#else - TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, cugraph_error_message(ret_error)); - TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "uniform_neighbor_sample failed."); - - cugraph_type_erased_device_array_view_t* result_srcs; - cugraph_type_erased_device_array_view_t* result_dsts; - cugraph_type_erased_device_array_view_t* result_edge_id; - cugraph_type_erased_device_array_view_t* result_weights; - cugraph_type_erased_device_array_view_t* result_edge_types; - cugraph_type_erased_device_array_view_t* result_hops; - - result_srcs = cugraph_sample_result_get_sources(result); - result_dsts = cugraph_sample_result_get_destinations(result); - result_edge_id = cugraph_sample_result_get_edge_id(result); - result_weights = cugraph_sample_result_get_edge_weight(result); - result_edge_types = cugraph_sample_result_get_edge_type(result); - result_hops = cugraph_sample_result_get_hop(result); - - size_t result_size = cugraph_type_erased_device_array_view_size(result_srcs); - - vertex_t h_srcs[result_size]; - vertex_t h_dsts[result_size]; - edge_t h_edge_id[result_size]; - weight_t h_weight[result_size]; - int32_t h_edge_types[result_size]; - int32_t h_hops[result_size]; - - ret_code = cugraph_type_erased_device_array_view_copy_to_host( - handle, (byte_t*)h_srcs, result_srcs, &ret_error); - TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "copy_to_host failed."); - - ret_code = cugraph_type_erased_device_array_view_copy_to_host( - handle, (byte_t*)h_dsts, result_dsts, &ret_error); - TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "copy_to_host failed."); - - ret_code = cugraph_type_erased_device_array_view_copy_to_host( - handle, (byte_t*)h_edge_id, result_edge_id, &ret_error); - TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "copy_to_host failed."); - - ret_code = cugraph_type_erased_device_array_view_copy_to_host( - handle, (byte_t*)h_weight, result_weights, &ret_error); - TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "copy_to_host failed."); - - ret_code = cugraph_type_erased_device_array_view_copy_to_host( - handle, (byte_t*)h_edge_types, result_edge_types, &ret_error); - TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "copy_to_host failed."); - - ret_code = cugraph_type_erased_device_array_view_copy_to_host( - handle, (byte_t*)h_hops, result_hops, &ret_error); - TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "copy_to_host failed."); - - // NOTE: The C++ tester does a more thorough validation. For our purposes - // here we will do a simpler validation, merely checking that all edges - // are actually part of the graph - weight_t M_w[num_vertices][num_vertices]; - edge_t M_edge_id[num_vertices][num_vertices]; - int32_t M_edge_type[num_vertices][num_vertices]; - - for (int i = 0; i < num_vertices; ++i) - for (int j = 0; j < num_vertices; ++j) { - M_w[i][j] = 0.0; - M_edge_id[i][j] = -1; - M_edge_type[i][j] = -1; - } - - for (int i = 0; i < num_edges; ++i) { - M_w[src[i]][dst[i]] = weight[i]; - M_edge_id[src[i]][dst[i]] = edge_ids[i]; - M_edge_type[src[i]][dst[i]] = edge_types[i]; - } - - for (int i = 0; (i < result_size) && (test_ret_value == 0); ++i) { - TEST_ASSERT(test_ret_value, - M_w[h_srcs[i]][h_dsts[i]] == h_weight[i], - "uniform_neighbor_sample got edge that doesn't exist"); - TEST_ASSERT(test_ret_value, - M_edge_id[h_srcs[i]][h_dsts[i]] == h_edge_id[i], - "uniform_neighbor_sample got edge that doesn't exist"); - TEST_ASSERT(test_ret_value, - M_edge_type[h_srcs[i]][h_dsts[i]] == h_edge_types[i], - "uniform_neighbor_sample got edge that doesn't exist"); - } - - cugraph_sample_result_free(result); -#endif - - cugraph_sg_graph_free(graph); - cugraph_error_free(ret_error); -} - int test_uniform_neighbor_sample_with_labels(const cugraph_resource_handle_t* handle) { data_type_id_t vertex_tid = INT32; @@ -722,6 +560,14 @@ int test_uniform_neighbor_sample_with_labels(const cugraph_resource_handle_t* ha cugraph_graph_t* graph = NULL; cugraph_sample_result_t* result = NULL; + bool_t with_replacement = TRUE; + bool_t return_hops = TRUE; + cugraph_prior_sources_behavior_t prior_sources_behavior = DEFAULT; + bool_t dedupe_sources = FALSE; + bool_t renumber_results = FALSE; + cugraph_compression_type_t compression = COO; + bool_t compress_per_hop = FALSE; + ret_code = create_sg_test_graph(handle, vertex_tid, edge_tid, @@ -775,19 +621,31 @@ int test_uniform_neighbor_sample_with_labels(const cugraph_resource_handle_t* ha ret_code = cugraph_rng_state_create(handle, 0, &rng_state, &ret_error); TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "rng_state create failed."); - ret_code = cugraph_uniform_neighbor_sample_with_edge_properties(handle, - graph, - d_start_view, - d_start_labels_view, - NULL, - NULL, - h_fan_out_view, - rng_state, - FALSE, - TRUE, - FALSE, - &result, - &ret_error); + cugraph_sampling_options_t *sampling_options; + + ret_code = cugraph_sampling_options_create(&sampling_options, &ret_error); + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "sampling_options create failed."); + + cugraph_sampling_set_with_replacement(sampling_options, with_replacement); + cugraph_sampling_set_return_hops(sampling_options, return_hops); + cugraph_sampling_set_prior_sources_behavior(sampling_options, prior_sources_behavior); + cugraph_sampling_set_dedupe_sources(sampling_options, dedupe_sources); + cugraph_sampling_set_renumber_results(sampling_options, renumber_results); + cugraph_sampling_set_compression_type(sampling_options, compression); + cugraph_sampling_set_compress_per_hop(sampling_options, compress_per_hop); + + ret_code = cugraph_uniform_neighbor_sample(handle, + graph, + d_start_view, + d_start_labels_view, + NULL, + NULL, + h_fan_out_view, + rng_state, + sampling_options, + FALSE, + &result, + &ret_error); #ifdef NO_CUGRAPH_OPS TEST_ASSERT( @@ -843,9 +701,7 @@ int test_uniform_neighbor_sample_with_labels(const cugraph_resource_handle_t* ha handle, (byte_t*)h_edge_types, result_edge_types, &ret_error); TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "copy_to_host failed."); - ret_code = cugraph_type_erased_device_array_view_copy_to_host( - handle, (byte_t*)h_hops, result_hops, &ret_error); - TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "copy_to_host failed."); + TEST_ASSERT(test_ret_value, result_hops == NULL, "hops was not empty"); ret_code = cugraph_type_erased_device_array_view_copy_to_host( handle, (byte_t*)h_result_offsets, result_offsets, &ret_error); @@ -884,6 +740,7 @@ int test_uniform_neighbor_sample_with_labels(const cugraph_resource_handle_t* ha } cugraph_sample_result_free(result); + cugraph_sampling_options_free(sampling_options); #endif cugraph_sg_graph_free(graph); @@ -902,6 +759,7 @@ int test_uniform_neighbor_sample_clean(const cugraph_resource_handle_t* handle) size_t num_vertices = 6; size_t fan_out_size = 3; size_t num_starts = 2; + size_t num_start_labels = 2; vertex_t src[] = {0, 0, 1, 1, 2, 2, 2, 3, 4}; vertex_t dst[] = {1, 3, 3, 4, 0, 1, 3, 5, 5}; @@ -923,7 +781,7 @@ int test_uniform_neighbor_sample_clean(const cugraph_resource_handle_t* handle) bool_t renumber_results = FALSE; return generic_uniform_neighbor_sample_test(handle, src, dst, weight, edge_ids, edge_types, num_vertices, num_edges, - start, start_labels, num_starts, + start, start_labels, num_starts, num_start_labels, fan_out, fan_out_size, with_replacement, return_hops, prior_sources_behavior, dedupe_sources, renumber_results); } @@ -940,6 +798,7 @@ int test_uniform_neighbor_sample_dedupe_sources(const cugraph_resource_handle_t* size_t num_vertices = 6; size_t fan_out_size = 3; size_t num_starts = 2; + size_t num_start_labels = 2; vertex_t src[] = {0, 0, 1, 1, 2, 2, 2, 3, 4}; vertex_t dst[] = {1, 3, 3, 4, 0, 1, 3, 5, 5}; @@ -961,7 +820,7 @@ int test_uniform_neighbor_sample_dedupe_sources(const cugraph_resource_handle_t* bool_t renumber_results = FALSE; return generic_uniform_neighbor_sample_test(handle, src, dst, weight, edge_ids, edge_types, num_vertices, num_edges, - start, start_labels, num_starts, + start, start_labels, num_starts, num_start_labels, fan_out, fan_out_size, with_replacement, return_hops, prior_sources_behavior, dedupe_sources, renumber_results); } @@ -978,6 +837,7 @@ int test_uniform_neighbor_sample_unique_sources(const cugraph_resource_handle_t* size_t num_vertices = 6; size_t fan_out_size = 3; size_t num_starts = 2; + size_t num_start_labels = 2; vertex_t src[] = {0, 0, 1, 1, 2, 2, 2, 3, 4}; vertex_t dst[] = {1, 2, 3, 4, 0, 1, 3, 5, 5}; @@ -999,7 +859,7 @@ int test_uniform_neighbor_sample_unique_sources(const cugraph_resource_handle_t* bool_t renumber_results = FALSE; return generic_uniform_neighbor_sample_test(handle, src, dst, weight, edge_ids, edge_types, num_vertices, num_edges, - start, start_labels, num_starts, + start, start_labels, num_starts, num_start_labels, fan_out, fan_out_size, with_replacement, return_hops, prior_sources_behavior, dedupe_sources, renumber_results); } @@ -1016,6 +876,7 @@ int test_uniform_neighbor_sample_carry_over_sources(const cugraph_resource_handl size_t num_vertices = 6; size_t fan_out_size = 3; size_t num_starts = 2; + size_t num_start_labels = 2; vertex_t src[] = {0, 0, 1, 1, 2, 2, 2, 3, 4}; vertex_t dst[] = {1, 2, 3, 4, 0, 1, 3, 5, 5}; @@ -1037,7 +898,7 @@ int test_uniform_neighbor_sample_carry_over_sources(const cugraph_resource_handl bool_t renumber_results = FALSE; return generic_uniform_neighbor_sample_test(handle, src, dst, weight, edge_ids, edge_types, num_vertices, num_edges, - start, start_labels, num_starts, + start, start_labels, num_starts, num_start_labels, fan_out, fan_out_size, with_replacement, return_hops, prior_sources_behavior, dedupe_sources, renumber_results); } @@ -1054,6 +915,7 @@ int test_uniform_neighbor_sample_renumber_results(const cugraph_resource_handle_ size_t num_vertices = 6; size_t fan_out_size = 3; size_t num_starts = 2; + size_t num_start_labels = 2; vertex_t src[] = {0, 0, 1, 1, 2, 2, 2, 3, 4}; vertex_t dst[] = {1, 2, 3, 4, 0, 1, 3, 5, 5}; @@ -1075,7 +937,7 @@ int test_uniform_neighbor_sample_renumber_results(const cugraph_resource_handle_ bool_t renumber_results = TRUE; return generic_uniform_neighbor_sample_test(handle, src, dst, weight, edge_ids, edge_types, num_vertices, num_edges, - start, start_labels, num_starts, + start, start_labels, num_starts, num_start_labels, fan_out, fan_out_size, with_replacement, return_hops, prior_sources_behavior, dedupe_sources, renumber_results); } @@ -1087,7 +949,6 @@ int main(int argc, char** argv) handle = cugraph_create_resource_handle(NULL); int result = 0; - result |= RUN_TEST_NEW(test_uniform_neighbor_sample_with_properties, handle); result |= RUN_TEST_NEW(test_uniform_neighbor_sample_with_labels, handle); result |= RUN_TEST_NEW(test_uniform_neighbor_sample_clean, handle); result |= RUN_TEST_NEW(test_uniform_neighbor_sample_dedupe_sources, handle); diff --git a/python/cugraph/cugraph/dask/sampling/uniform_neighbor_sample.py b/python/cugraph/cugraph/dask/sampling/uniform_neighbor_sample.py index 9e50169b4a7..03746561817 100644 --- a/python/cugraph/cugraph/dask/sampling/uniform_neighbor_sample.py +++ b/python/cugraph/cugraph/dask/sampling/uniform_neighbor_sample.py @@ -42,6 +42,7 @@ if TYPE_CHECKING: from cugraph import Graph + src_n = "sources" dst_n = "destinations" indices_n = "indices" @@ -71,8 +72,21 @@ def create_empty_df(indices_t, weight_t): def create_empty_df_with_edge_props( - indices_t, weight_t, return_offsets=False, renumber=False + indices_t, + weight_t, + return_offsets=False, + renumber=False, + use_legacy_names=True, + include_hop_column=True, + compression="COO", ): + if compression != "COO": + majors_name = "major_offsets" + else: + majors_name = src_n if use_legacy_names else "majors" + + minors_name = dst_n if use_legacy_names else "minors" + if renumber: empty_df_renumber = cudf.DataFrame( { @@ -84,14 +98,17 @@ def create_empty_df_with_edge_props( if return_offsets: df = cudf.DataFrame( { - src_n: numpy.empty(shape=0, dtype=indices_t), - dst_n: numpy.empty(shape=0, dtype=indices_t), + majors_name: numpy.empty(shape=0, dtype=indices_t), + minors_name: numpy.empty(shape=0, dtype=indices_t), weight_n: numpy.empty(shape=0, dtype=weight_t), edge_id_n: numpy.empty(shape=0, dtype=indices_t), edge_type_n: numpy.empty(shape=0, dtype="int32"), - hop_id_n: numpy.empty(shape=0, dtype="int32"), } ) + + if include_hop_column: + df[hop_id_n] = numpy.empty(shape=0, dtype="int32") + empty_df_offsets = cudf.DataFrame( { offsets_n: numpy.empty(shape=0, dtype="int32"), @@ -106,13 +123,13 @@ def create_empty_df_with_edge_props( else: df = cudf.DataFrame( { - src_n: numpy.empty(shape=0, dtype=indices_t), - dst_n: numpy.empty(shape=0, dtype=indices_t), + majors_name: numpy.empty(shape=0, dtype=indices_t), + minors_name: numpy.empty(shape=0, dtype=indices_t), weight_n: numpy.empty(shape=0, dtype=weight_t), edge_id_n: numpy.empty(shape=0, dtype=indices_t), edge_type_n: numpy.empty(shape=0, dtype="int32"), - hop_id_n: numpy.empty(shape=0, dtype="int32"), batch_id_n: numpy.empty(shape=0, dtype="int32"), + hop_id_n: numpy.empty(shape=0, dtype="int32"), } ) if renumber: @@ -121,102 +138,6 @@ def create_empty_df_with_edge_props( return df -def convert_to_cudf( - cp_arrays, weight_t, with_edge_properties, return_offsets=False, renumber=False -): - """ - Creates a cudf DataFrame from cupy arrays from pylibcugraph wrapper - """ - df = cudf.DataFrame() - - if with_edge_properties: - if renumber: - ( - sources, - destinations, - weights, - edge_ids, - edge_types, - batch_ids, - offsets, - hop_ids, - renumber_map, - renumber_map_offsets, - ) = cp_arrays - else: - ( - sources, - destinations, - weights, - edge_ids, - edge_types, - batch_ids, - offsets, - hop_ids, - ) = cp_arrays - - df[src_n] = sources - df[dst_n] = destinations - df[weight_n] = weights - df[edge_id_n] = edge_ids - df[edge_type_n] = edge_types - df[hop_id_n] = hop_ids - - return_dfs = [df] - - if return_offsets: - offsets_df = cudf.DataFrame( - { - batch_id_n: batch_ids, - offsets_n: offsets[:-1], - } - ) - - if renumber: - offsets_df[map_offsets_n] = renumber_map_offsets[:-1] - - return_dfs.append(offsets_df) - else: - batch_ids_b = batch_ids - if len(batch_ids_b) > 0: - batch_ids_b = cudf.Series(batch_ids_b).repeat(cp.diff(offsets)) - batch_ids_b.reset_index(drop=True, inplace=True) - - df[batch_id_n] = batch_ids_b - - if renumber: - renumber_df = cudf.DataFrame( - { - "map": renumber_map, - } - ) - - if not return_offsets: - batch_ids_r = cudf.Series(batch_ids).repeat( - cp.diff(renumber_map_offsets) - ) - batch_ids_r.reset_index(drop=True, inplace=True) - renumber_df["batch_id"] = batch_ids_r - - return_dfs.append(renumber_df) - - return tuple(return_dfs) - else: - cupy_sources, cupy_destinations, cupy_indices = cp_arrays - - df[src_n] = cupy_sources - df[dst_n] = cupy_destinations - df[indices_n] = cupy_indices - - if cupy_indices is not None: - if weight_t == "int32": - df.indices = df.indices.astype("int32") - elif weight_t == "int64": - df.indices = df.indices.astype("int64") - - return (df,) - - def __get_label_to_output_comm_rank(min_batch_id, max_batch_id, n_workers): num_batches = max_batch_id - min_batch_id + 1 num_batches = int(num_batches) @@ -246,6 +167,10 @@ def _call_plc_uniform_neighbor_sample( prior_sources_behavior=None, deduplicate_sources=False, renumber=False, + use_legacy_names=True, + include_hop_column=True, + compress_per_hop=False, + compression="COO", ): st_x = st_x[0] start_list_x = st_x[start_col_name] @@ -259,7 +184,7 @@ def _call_plc_uniform_neighbor_sample( min_batch_id, max_batch_id, n_workers ) - cp_arrays = pylibcugraph_uniform_neighbor_sample( + cupy_array_dict = pylibcugraph_uniform_neighbor_sample( resource_handle=ResourceHandle(Comms.get_handle(sID).getHandle()), input_graph=mg_graph_x, start_list=start_list_x, @@ -275,13 +200,25 @@ def _call_plc_uniform_neighbor_sample( deduplicate_sources=deduplicate_sources, return_hops=return_hops, renumber=renumber, + compression=compression, + compress_per_hop=compress_per_hop, + return_dict=True, + ) + + # have to import here due to circular import issue + from cugraph.sampling.sampling_utilities import ( + sampling_results_from_cupy_array_dict, ) - return convert_to_cudf( - cp_arrays, + + return sampling_results_from_cupy_array_dict( + cupy_array_dict, weight_t, - with_edge_properties, + len(fanout_vals), + with_edge_properties=with_edge_properties, return_offsets=return_offsets, renumber=renumber, + use_legacy_names=use_legacy_names, + include_hop_column=include_hop_column, ) @@ -304,6 +241,10 @@ def _mg_call_plc_uniform_neighbor_sample( prior_sources_behavior=None, deduplicate_sources=False, renumber=False, + use_legacy_names=True, + include_hop_column=True, + compress_per_hop=False, + compression="COO", ): n_workers = None if keep_batches_together: @@ -335,6 +276,10 @@ def _mg_call_plc_uniform_neighbor_sample( prior_sources_behavior=prior_sources_behavior, deduplicate_sources=deduplicate_sources, renumber=renumber, + use_legacy_names=use_legacy_names, # remove in 23.12 + include_hop_column=include_hop_column, # remove in 23.12 + compress_per_hop=compress_per_hop, + compression=compression, allow_other_workers=False, pure=False, ) @@ -348,6 +293,9 @@ def _mg_call_plc_uniform_neighbor_sample( weight_t, return_offsets=return_offsets, renumber=renumber, + use_legacy_names=use_legacy_names, + compression=compression, + include_hop_column=include_hop_column, ) if with_edge_properties else create_empty_df(indices_t, weight_t) @@ -397,6 +345,7 @@ def uniform_neighbor_sample( input_graph: Graph, start_list: Sequence, fanout_vals: List[int], + *, with_replacement: bool = True, with_edge_properties: bool = False, # deprecated with_batch_ids: bool = False, @@ -406,9 +355,13 @@ def uniform_neighbor_sample( random_state: int = None, return_offsets: bool = False, return_hops: bool = True, + include_hop_column: bool = True, # deprecated prior_sources_behavior: str = None, deduplicate_sources: bool = False, renumber: bool = False, + use_legacy_names=True, # deprecated + compress_per_hop=False, + compression="COO", _multiple_clients: bool = False, ) -> Union[dask_cudf.DataFrame, Tuple[dask_cudf.DataFrame, dask_cudf.DataFrame]]: """ @@ -463,6 +416,12 @@ def uniform_neighbor_sample( corresponding to the hop where the edge appeared. Defaults to True. + include_hop_column: bool, optional (default=True) + Deprecated. Defaults to True. + If True, will include the hop column even if + return_offsets is True. This option will + be removed in release 23.12. + prior_sources_behavior: str (Optional) Options are "carryover", and "exclude". Default will leave the source list as-is. @@ -481,6 +440,21 @@ def uniform_neighbor_sample( will return the renumber map and renumber map offsets as an additional dataframe. + use_legacy_names: bool, optional (default=True) + Whether to use the legacy column names (sources, destinations). + If True, will use "sources" and "destinations" as the column names. + If False, will use "majors" and "minors" as the column names. + Deprecated. Will be removed in release 23.12 in favor of always + using the new names "majors" and "minors". + + compress_per_hop: bool, optional (default=False) + Whether to compress globally (default), or to produce a separate + compressed edgelist per hop. + + compression: str, optional (default=COO) + Sets the compression type for the output minibatches. + Valid options are COO (default), CSR, CSC, DCSR, and DCSC. + _multiple_clients: bool, optional (default=False) internal flag to ensure sampling works with multiple dask clients set to True to prevent hangs in multi-client environment @@ -548,12 +522,46 @@ def uniform_neighbor_sample( Contains the batch offsets for the renumber maps """ + if compression not in ["COO", "CSR", "CSC", "DCSR", "DCSC"]: + raise ValueError("compression must be one of COO, CSR, CSC, DCSR, or DCSC") + if with_edge_properties: warning_msg = ( "The with_edge_properties flag is deprecated" " and will be removed in the next release." ) - warnings.warn(warning_msg, DeprecationWarning) + warnings.warn(warning_msg, FutureWarning) + + if ( + (compression != "COO") + and (not compress_per_hop) + and prior_sources_behavior != "exclude" + ): + raise ValueError( + "hop-agnostic compression is only supported with" + " the exclude prior sources behavior due to limitations " + "of the libcugraph C++ API" + ) + + if compress_per_hop and prior_sources_behavior != "carryover": + raise ValueError( + "Compressing the edgelist per hop is only supported " + "with the carryover prior sources behavior due to limitations" + " of the libcugraph C++ API" + ) + + if include_hop_column: + warning_msg = ( + "The include_hop_column flag is deprecated and will be" + " removed in the next release in favor of always " + "excluding the hop column when return_offsets is True" + ) + warnings.warn(warning_msg, FutureWarning) + + if compression != "COO": + raise ValueError( + "Including the hop id column is only supported with COO compression." + ) if isinstance(start_list, int): start_list = [start_list] @@ -643,6 +651,31 @@ def uniform_neighbor_sample( ddf = persist_dask_df_equal_parts_per_worker(ddf, client) ddf = get_persisted_df_worker_map(ddf, client) + sample_call_kwargs = { + "client": client, + "session_id": session_id, + "input_graph": input_graph, + "ddf": ddf, + "keep_batches_together": keep_batches_together, + "min_batch_id": min_batch_id, + "max_batch_id": max_batch_id, + "fanout_vals": fanout_vals, + "with_replacement": with_replacement, + "weight_t": weight_t, + "indices_t": indices_t, + "with_edge_properties": with_edge_properties, + "random_state": random_state, + "return_offsets": return_offsets, + "return_hops": return_hops, + "prior_sources_behavior": prior_sources_behavior, + "deduplicate_sources": deduplicate_sources, + "renumber": renumber, + "use_legacy_names": use_legacy_names, + "include_hop_column": include_hop_column, + "compress_per_hop": compress_per_hop, + "compression": compression, + } + if _multiple_clients: # Distributed centralized lock to allow # two disconnected processes (clients) to coordinate a lock @@ -650,26 +683,7 @@ def uniform_neighbor_sample( lock = Lock("plc_graph_access") if lock.acquire(timeout=100): try: - ddf = _mg_call_plc_uniform_neighbor_sample( - client=client, - session_id=session_id, - input_graph=input_graph, - ddf=ddf, - keep_batches_together=keep_batches_together, - min_batch_id=min_batch_id, - max_batch_id=max_batch_id, - fanout_vals=fanout_vals, - with_replacement=with_replacement, - weight_t=weight_t, - indices_t=indices_t, - with_edge_properties=with_edge_properties, - random_state=random_state, - return_offsets=return_offsets, - return_hops=return_hops, - prior_sources_behavior=prior_sources_behavior, - deduplicate_sources=deduplicate_sources, - renumber=renumber, - ) + ddf = _mg_call_plc_uniform_neighbor_sample(**sample_call_kwargs) finally: lock.release() else: @@ -677,26 +691,7 @@ def uniform_neighbor_sample( "Failed to acquire lock(plc_graph_access) while trying to sampling" ) else: - ddf = _mg_call_plc_uniform_neighbor_sample( - client=client, - session_id=session_id, - input_graph=input_graph, - ddf=ddf, - keep_batches_together=keep_batches_together, - min_batch_id=min_batch_id, - max_batch_id=max_batch_id, - fanout_vals=fanout_vals, - with_replacement=with_replacement, - weight_t=weight_t, - indices_t=indices_t, - with_edge_properties=with_edge_properties, - random_state=random_state, - return_offsets=return_offsets, - return_hops=return_hops, - prior_sources_behavior=prior_sources_behavior, - deduplicate_sources=deduplicate_sources, - renumber=renumber, - ) + ddf = _mg_call_plc_uniform_neighbor_sample(**sample_call_kwargs) if return_offsets: if renumber: @@ -708,9 +703,12 @@ def uniform_neighbor_sample( ddf, renumber_df = ddf if input_graph.renumbered and not renumber: - ddf = input_graph.unrenumber(ddf, "sources", preserve_order=True) - ddf = input_graph.unrenumber(ddf, "destinations", preserve_order=True) - + if use_legacy_names: + ddf = input_graph.unrenumber(ddf, "sources", preserve_order=True) + ddf = input_graph.unrenumber(ddf, "destinations", preserve_order=True) + else: + ddf = input_graph.unrenumber(ddf, "majors", preserve_order=True) + ddf = input_graph.unrenumber(ddf, "minors", preserve_order=True) if return_offsets: if renumber: return ddf, offsets_df, renumber_df diff --git a/python/cugraph/cugraph/gnn/data_loading/bulk_sampler.py b/python/cugraph/cugraph/gnn/data_loading/bulk_sampler.py index 92caba6dbaf..dbfcb124ce5 100644 --- a/python/cugraph/cugraph/gnn/data_loading/bulk_sampler.py +++ b/python/cugraph/cugraph/gnn/data_loading/bulk_sampler.py @@ -269,6 +269,7 @@ def flush(self) -> None: with_edge_properties=True, return_offsets=True, renumber=self.__renumber, + # use_legacy_names=False, ) if self.__renumber: diff --git a/python/cugraph/cugraph/gnn/data_loading/bulk_sampler_io.py b/python/cugraph/cugraph/gnn/data_loading/bulk_sampler_io.py index e9e5be26fc3..7e67eab83c9 100644 --- a/python/cugraph/cugraph/gnn/data_loading/bulk_sampler_io.py +++ b/python/cugraph/cugraph/gnn/data_loading/bulk_sampler_io.py @@ -15,10 +15,24 @@ import cudf import cupy -from typing import Union, Optional +from math import ceil +from pandas import isna -def _write_samples_to_parquet( +from typing import Union, Optional, List + + +def create_df_from_disjoint_series(series_list: List[cudf.Series]): + series_list.sort(key=lambda s: len(s), reverse=True) + + df = cudf.DataFrame() + for s in series_list: + df[s.name] = s + + return df + + +def _write_samples_to_parquet_csr( results: cudf.DataFrame, offsets: cudf.DataFrame, renumber_map: cudf.DataFrame, @@ -27,7 +41,184 @@ def _write_samples_to_parquet( partition_info: Optional[Union[dict, str]] = None, ) -> cudf.Series: """ - Writes the samples to parquet. + Writes CSR/CSC compressed samples to parquet. + + Batches that are empty are discarded, and the remaining non-empty + batches are renumbered to be contiguous starting from the first + batch id. This means that the output batch ids may not match + the input batch ids. + + results: cudf.DataFrame + The results dataframe containing the sampled minibatches. + offsets: cudf.DataFrame + The offsets dataframe indicating the start/end of each minibatch + in the reuslts dataframe. + renumber_map: cudf.DataFrame + The renumber map containing the mapping of renumbered vertex ids + to original vertex ids. + batches_per_partition: int + The maximum number of minibatches allowed per written parquet partition. + output_path: str + The output path (where parquet files should be written to). + partition_info: Union[dict, str] + Either a dictionary containing partition data from dask, the string 'sg' + indicating that this is a single GPU write, or None indicating that this + function should perform a no-op (required by dask). + + Returns an empty cudf series. + """ + # Required by dask; need to skip dummy partitions. + if partition_info is None or len(results) == 0: + return cudf.Series(dtype="int64") + if partition_info != "sg" and (not isinstance(partition_info, dict)): + raise ValueError("Invalid value of partition_info") + + # Additional check to skip dummy partitions required for CSR format. + if isna(offsets.batch_id.iloc[0]): + return cudf.Series(dtype="int64") + + # Output: + # major_offsets - CSR/CSC row/col pointers + # minors - CSR/CSC col/row indices + # edge id - edge ids (same shape as minors) + # edge type - edge types (same shape as minors) + # weight - edge weight (same shape as minors) + # renumber map - the original vertex ids + # renumber map offsets - start/end of the map for each batch + # (only 1 per batch b/c of framework + # stipulations making this legal) + # label-hop offsets - indicate the start/end of each hop + # for each batch + + batch_ids = offsets.batch_id + label_hop_offsets = offsets.offsets + renumber_map_offsets = offsets.renumber_map_offsets + del offsets + + batch_ids.dropna(inplace=True) + label_hop_offsets.dropna(inplace=True) + renumber_map_offsets.dropna(inplace=True) + + major_offsets_array = results.major_offsets + results.drop(columns="major_offsets", inplace=True) + major_offsets_array.dropna(inplace=True) + major_offsets_array = major_offsets_array.values + + minors_array = results.minors + results.drop(columns="minors", inplace=True) + minors_array.dropna(inplace=True) + minors_array = minors_array.values + + weight_array = results.weight + results.drop(columns="weight", inplace=True) + weight_array.dropna(inplace=True) + weight_array = ( + cupy.array([], dtype="float32") if weight_array.empty else weight_array.values + ) + + edge_id_array = results.edge_id + results.drop(columns="edge_id", inplace=True) + edge_id_array.dropna(inplace=True) + edge_id_array = ( + cupy.array([], dtype="int64") if edge_id_array.empty else edge_id_array.values + ) + + edge_type_array = results.edge_type + results.drop(columns="edge_type", inplace=True) + edge_type_array.dropna(inplace=True) + edge_type_array = ( + cupy.array([], dtype="int32") + if edge_type_array.empty + else edge_type_array.values + ) + + del results + + offsets_length = len(label_hop_offsets) - 1 + if offsets_length % len(batch_ids) != 0: + raise ValueError("Invalid hop offsets") + fanout_length = int(offsets_length / len(batch_ids)) + + for p in range(0, int(ceil(len(batch_ids) / batches_per_partition))): + partition_start = p * (batches_per_partition) + partition_end = (p + 1) * (batches_per_partition) + + label_hop_offsets_current_partition = label_hop_offsets.iloc[ + partition_start * fanout_length : partition_end * fanout_length + 1 + ].reset_index(drop=True) + label_hop_offsets_current_partition.name = "label_hop_offsets" + + batch_ids_current_partition = batch_ids.iloc[partition_start:partition_end] + + ( + major_offsets_start, + major_offsets_end, + ) = label_hop_offsets_current_partition.iloc[ + [0, -1] + ].values # legal since offsets has the 1 extra offset + results_start, results_end = major_offsets_array[ + [major_offsets_start, major_offsets_end] + ] # avoid d2h copy + + # no need to use end batch id, just ensure the batch is labeled correctly + start_batch_id = batch_ids_current_partition.iloc[0] + # end_batch_id = batch_ids_current_partition.iloc[-1] + + # create the renumber map offsets + renumber_map_offsets_current_partition = renumber_map_offsets.iloc[ + partition_start : partition_end + 1 + ].reset_index(drop=True) + renumber_map_offsets_current_partition.name = "renumber_map_offsets" + + ( + renumber_map_start, + renumber_map_end, + ) = renumber_map_offsets_current_partition.iloc[ + [0, -1] + ].values # avoid d2h copy + + results_current_partition = create_df_from_disjoint_series( + [ + cudf.Series(minors_array[results_start:results_end], name="minors"), + cudf.Series( + renumber_map.map.values[renumber_map_start:renumber_map_end], + name="map", + ), + label_hop_offsets_current_partition, + cudf.Series( + major_offsets_array[major_offsets_start : major_offsets_end + 1], + name="major_offsets", + ), + cudf.Series(weight_array[results_start:results_end], name="weight"), + cudf.Series(edge_id_array[results_start:results_end], name="edge_id"), + cudf.Series( + edge_type_array[results_start:results_end], name="edge_type" + ), + renumber_map_offsets_current_partition, + ] + ) + + end_batch_id = start_batch_id + len(batch_ids_current_partition) - 1 + filename = f"batch={start_batch_id}-{end_batch_id}.parquet" + full_output_path = os.path.join(output_path, filename) + + results_current_partition.to_parquet( + full_output_path, compression=None, index=False, force_nullable_schema=True + ) + + return cudf.Series(dtype="int64") + + +def _write_samples_to_parquet_coo( + results: cudf.DataFrame, + offsets: cudf.DataFrame, + renumber_map: cudf.DataFrame, + batches_per_partition: int, + output_path: str, + partition_info: Optional[Union[dict, str]] = None, +) -> cudf.Series: + """ + Writes COO compressed samples to parquet. Batches that are empty are discarded, and the remaining non-empty batches are renumbered to be contiguous starting from the first @@ -60,8 +251,10 @@ def _write_samples_to_parquet( if partition_info != "sg" and (not isinstance(partition_info, dict)): raise ValueError("Invalid value of partition_info") + offsets = offsets[:-1] + # Offsets is always in order, so the last batch id is always the highest - max_batch_id = offsets.batch_id.iloc[len(offsets) - 1] + max_batch_id = offsets.batch_id.iloc[-1] results.dropna(axis=1, how="all", inplace=True) results["hop_id"] = results["hop_id"].astype("uint8") @@ -182,9 +375,23 @@ def write_samples( output_path: str The output path (where parquet files should be written to). """ + + if ("majors" in results.columns) and ("minors" in results.columns): + write_fn = _write_samples_to_parquet_coo + + # TODO these names will be deprecated in release 23.12 + elif ("sources" in results.columns) and ("destinations" in results.columns): + write_fn = _write_samples_to_parquet_coo + + elif "major_offsets" in results.columns and "minors" in results.columns: + write_fn = _write_samples_to_parquet_csr + + else: + raise ValueError("invalid columns") + if hasattr(results, "compute"): results.map_partitions( - _write_samples_to_parquet, + write_fn, offsets, renumber_map, batches_per_partition, @@ -194,7 +401,7 @@ def write_samples( ).compute() else: - _write_samples_to_parquet( + write_fn( results, offsets, renumber_map, diff --git a/python/cugraph/cugraph/sampling/sampling_utilities.py b/python/cugraph/cugraph/sampling/sampling_utilities.py new file mode 100644 index 00000000000..50c315129dc --- /dev/null +++ b/python/cugraph/cugraph/sampling/sampling_utilities.py @@ -0,0 +1,198 @@ +# Copyright (c) 2023, NVIDIA CORPORATION. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import cupy +import cudf + +import warnings + + +def sampling_results_from_cupy_array_dict( + cupy_array_dict, + weight_t, + num_hops, + with_edge_properties=False, + return_offsets=False, + renumber=False, + use_legacy_names=True, + include_hop_column=True, +): + """ + Creates a cudf DataFrame from cupy arrays from pylibcugraph wrapper + """ + results_df = cudf.DataFrame() + + if use_legacy_names: + major_col_name = "sources" + minor_col_name = "destinations" + warning_msg = ( + "The legacy column names (sources, destinations)" + " will no longer be supported for uniform_neighbor_sample" + " in release 23.12. The use_legacy_names=False option will" + " become the only option, and (majors, minors) will be the" + " only supported column names." + ) + warnings.warn(warning_msg, FutureWarning) + else: + major_col_name = "majors" + minor_col_name = "minors" + + if with_edge_properties: + majors = cupy_array_dict["majors"] + if majors is not None: + results_df["majors"] = majors + + results_df_cols = [ + "minors", + "weight", + "edge_id", + "edge_type", + ] + + for col in results_df_cols: + array = cupy_array_dict[col] + # The length of each of these arrays should be the same + results_df[col] = array + + results_df.rename( + columns={"majors": major_col_name, "minors": minor_col_name}, inplace=True + ) + + label_hop_offsets = cupy_array_dict["label_hop_offsets"] + batch_ids = cupy_array_dict["batch_id"] + + if renumber: + renumber_df = cudf.DataFrame( + { + "map": cupy_array_dict["renumber_map"], + } + ) + + if not return_offsets: + if len(batch_ids) > 0: + batch_ids_r = cudf.Series(batch_ids).repeat( + cupy.diff(cupy_array_dict["renumber_map_offsets"]) + ) + batch_ids_r.reset_index(drop=True, inplace=True) + renumber_df["batch_id"] = batch_ids_r + else: + renumber_df["batch_id"] = None + + if return_offsets: + batches_series = cudf.Series( + batch_ids, + name="batch_id", + ) + if include_hop_column: + # TODO remove this logic in release 23.12 + offsets_df = cudf.Series( + label_hop_offsets[cupy.arange(len(batch_ids) + 1) * num_hops], + name="offsets", + ).to_frame() + else: + offsets_df = cudf.Series( + label_hop_offsets, + name="offsets", + ).to_frame() + + if len(batches_series) > len(offsets_df): + # this is extremely rare so the inefficiency is ok + offsets_df = offsets_df.join(batches_series, how="outer").sort_index() + else: + offsets_df["batch_id"] = batches_series + + if renumber: + renumber_offset_series = cudf.Series( + cupy_array_dict["renumber_map_offsets"], name="renumber_map_offsets" + ) + + if len(renumber_offset_series) > len(offsets_df): + # this is extremely rare so the inefficiency is ok + offsets_df = offsets_df.join( + renumber_offset_series, how="outer" + ).sort_index() + else: + offsets_df["renumber_map_offsets"] = renumber_offset_series + + else: + if len(batch_ids) > 0: + batch_ids_r = cudf.Series(cupy.repeat(batch_ids, num_hops)) + batch_ids_r = cudf.Series(batch_ids_r).repeat( + cupy.diff(label_hop_offsets) + ) + batch_ids_r.reset_index(drop=True, inplace=True) + + results_df["batch_id"] = batch_ids_r + else: + results_df["batch_id"] = None + + # TODO remove this logic in release 23.12, hops will always returned as offsets + if include_hop_column: + if len(batch_ids) > 0: + hop_ids_r = cudf.Series(cupy.arange(num_hops)) + hop_ids_r = cudf.concat([hop_ids_r] * len(batch_ids), ignore_index=True) + + # generate the hop column + hop_ids_r = ( + cudf.Series(hop_ids_r, name="hop_id") + .repeat(cupy.diff(label_hop_offsets)) + .reset_index(drop=True) + ) + else: + hop_ids_r = cudf.Series(name="hop_id", dtype="int32") + + results_df = results_df.join(hop_ids_r, how="outer").sort_index() + + if major_col_name not in results_df: + if use_legacy_names: + raise ValueError("Can't use legacy names with major offsets") + + major_offsets_series = cudf.Series( + cupy_array_dict["major_offsets"], name="major_offsets" + ) + if len(major_offsets_series) > len(results_df): + # this is extremely rare so the inefficiency is ok + results_df = results_df.join( + major_offsets_series, how="outer" + ).sort_index() + else: + results_df["major_offsets"] = major_offsets_series + + else: + # TODO this is deprecated, remove it in 23.12 + + results_df[major_col_name] = cupy_array_dict["sources"] + results_df[minor_col_name] = cupy_array_dict["destinations"] + indices = cupy_array_dict["indices"] + + if indices is None: + results_df["indices"] = None + else: + results_df["indices"] = indices + if weight_t == "int32": + results_df["indices"] = indices.astype("int32") + elif weight_t == "int64": + results_df["indices"] = indices.astype("int64") + else: + results_df["indices"] = indices + + if return_offsets: + if renumber: + return results_df, offsets_df, renumber_df + else: + return results_df, offsets_df + + if renumber: + return results_df, renumber_df + + return (results_df,) diff --git a/python/cugraph/cugraph/sampling/uniform_neighbor_sample.py b/python/cugraph/cugraph/sampling/uniform_neighbor_sample.py index 219854bb002..1832585c0ab 100644 --- a/python/cugraph/cugraph/sampling/uniform_neighbor_sample.py +++ b/python/cugraph/cugraph/sampling/uniform_neighbor_sample.py @@ -16,6 +16,8 @@ from pylibcugraph import ResourceHandle from pylibcugraph import uniform_neighbor_sample as pylibcugraph_uniform_neighbor_sample +from cugraph.sampling.sampling_utilities import sampling_results_from_cupy_array_dict + import numpy import cudf @@ -58,15 +60,20 @@ def uniform_neighbor_sample( G: Graph, start_list: Sequence, fanout_vals: List[int], + *, with_replacement: bool = True, with_edge_properties: bool = False, # deprecated with_batch_ids: bool = False, random_state: int = None, return_offsets: bool = False, return_hops: bool = True, + include_hop_column: bool = True, # deprecated prior_sources_behavior: str = None, deduplicate_sources: bool = False, renumber: bool = False, + use_legacy_names: bool = True, # deprecated + compress_per_hop: bool = False, + compression: str = "COO", ) -> Union[cudf.DataFrame, Tuple[cudf.DataFrame, cudf.DataFrame]]: """ Does neighborhood sampling, which samples nodes from a graph based on the @@ -111,6 +118,12 @@ def uniform_neighbor_sample( corresponding to the hop where the edge appeared. Defaults to True. + include_hop_column: bool, optional (default=True) + Deprecated. Defaults to True. + If True, will include the hop column even if + return_offsets is True. This option will + be removed in release 23.12. + prior_sources_behavior: str, optional (default=None) Options are "carryover", and "exclude". Default will leave the source list as-is. @@ -129,6 +142,21 @@ def uniform_neighbor_sample( will return the renumber map and renumber map offsets as an additional dataframe. + use_legacy_names: bool, optional (default=True) + Whether to use the legacy column names (sources, destinations). + If True, will use "sources" and "destinations" as the column names. + If False, will use "majors" and "minors" as the column names. + Deprecated. Will be removed in release 23.12 in favor of always + using the new names "majors" and "minors". + + compress_per_hop: bool, optional (default=False) + Whether to compress globally (default), or to produce a separate + compressed edgelist per hop. + + compression: str, optional (default=COO) + Sets the compression type for the output minibatches. + Valid options are COO (default), CSR, CSC, DCSR, and DCSC. + Returns ------- result : cudf.DataFrame or Tuple[cudf.DataFrame, cudf.DataFrame] @@ -193,12 +221,62 @@ def uniform_neighbor_sample( Contains the batch offsets for the renumber maps """ + if use_legacy_names: + major_col_name = "sources" + minor_col_name = "destinations" + warning_msg = ( + "The legacy column names (sources, destinations)" + " will no longer be supported for uniform_neighbor_sample" + " in release 23.12. The use_legacy_names=False option will" + " become the only option, and (majors, minors) will be the" + " only supported column names." + ) + warnings.warn(warning_msg, FutureWarning) + else: + major_col_name = "majors" + minor_col_name = "minors" + + if compression not in ["COO", "CSR", "CSC", "DCSR", "DCSC"]: + raise ValueError("compression must be one of COO, CSR, CSC, DCSR, or DCSC") + + if ( + (compression != "COO") + and (not compress_per_hop) + and prior_sources_behavior != "exclude" + ): + raise ValueError( + "hop-agnostic compression is only supported with" + " the exclude prior sources behavior due to limitations " + "of the libcugraph C++ API" + ) + + if compress_per_hop and prior_sources_behavior != "carryover": + raise ValueError( + "Compressing the edgelist per hop is only supported " + "with the carryover prior sources behavior due to limitations" + " of the libcugraph C++ API" + ) + + if include_hop_column: + warning_msg = ( + "The include_hop_column flag is deprecated and will be" + " removed in the next release in favor of always " + "excluding the hop column when return_offsets is True" + ) + warnings.warn(warning_msg, FutureWarning) + + if compression != "COO": + raise ValueError( + "Including the hop id column is only supported with COO compression." + ) + if with_edge_properties: warning_msg = ( "The with_edge_properties flag is deprecated" - " and will be removed in the next release." + " and will be removed in the next release in favor" + " of returning all properties in the graph" ) - warnings.warn(warning_msg, DeprecationWarning) + warnings.warn(warning_msg, FutureWarning) if isinstance(start_list, int): start_list = [start_list] @@ -255,7 +333,7 @@ def uniform_neighbor_sample( start_list = G.lookup_internal_vertex_id(start_list, columns) start_list = start_list.rename(columns={columns[0]: start_col_name}) - sampling_result = pylibcugraph_uniform_neighbor_sample( + sampling_result_array_dict = pylibcugraph_uniform_neighbor_sample( resource_handle=ResourceHandle(), input_graph=G._plc_graph, start_list=start_list[start_col_name], @@ -271,104 +349,27 @@ def uniform_neighbor_sample( deduplicate_sources=deduplicate_sources, return_hops=return_hops, renumber=renumber, + compression=compression, + compress_per_hop=compress_per_hop, + return_dict=True, ) - df = cudf.DataFrame() - - if with_edge_properties: - # TODO use a dictionary at PLC w/o breaking users - if renumber: - ( - sources, - destinations, - weights, - edge_ids, - edge_types, - batch_ids, - offsets, - hop_ids, - renumber_map, - renumber_map_offsets, - ) = sampling_result - else: - ( - sources, - destinations, - weights, - edge_ids, - edge_types, - batch_ids, - offsets, - hop_ids, - ) = sampling_result - - df["sources"] = sources - df["destinations"] = destinations - df["weight"] = weights - df["edge_id"] = edge_ids - df["edge_type"] = edge_types - df["hop_id"] = hop_ids - - if renumber: - renumber_df = cudf.DataFrame( - { - "map": renumber_map, - } - ) - - if not return_offsets: - batch_ids_r = cudf.Series(batch_ids).repeat( - cp.diff(renumber_map_offsets) - ) - batch_ids_r.reset_index(drop=True, inplace=True) - renumber_df["batch_id"] = batch_ids_r - - if return_offsets: - offsets_df = cudf.DataFrame( - { - "batch_id": batch_ids, - "offsets": offsets[:-1], - } - ) - - if renumber: - offsets_df["renumber_map_offsets"] = renumber_map_offsets[:-1] - - else: - if len(batch_ids) > 0: - batch_ids = cudf.Series(batch_ids).repeat(cp.diff(offsets)) - batch_ids.reset_index(drop=True, inplace=True) - - df["batch_id"] = batch_ids - - else: - sources, destinations, indices = sampling_result - - df["sources"] = sources - df["destinations"] = destinations - - if indices is None: - df["indices"] = None - else: - df["indices"] = indices - if weight_t == "int32": - df["indices"] = indices.astype("int32") - elif weight_t == "int64": - df["indices"] = indices.astype("int64") - else: - df["indices"] = indices + dfs = sampling_results_from_cupy_array_dict( + sampling_result_array_dict, + weight_t, + len(fanout_vals), + with_edge_properties=with_edge_properties, + return_offsets=return_offsets, + renumber=renumber, + use_legacy_names=use_legacy_names, + include_hop_column=include_hop_column, + ) if G.renumbered and not renumber: - df = G.unrenumber(df, "sources", preserve_order=True) - df = G.unrenumber(df, "destinations", preserve_order=True) - - if return_offsets: - if renumber: - return df, offsets_df, renumber_df - else: - return df, offsets_df + dfs[0] = G.unrenumber(dfs[0], major_col_name, preserve_order=True) + dfs[0] = G.unrenumber(dfs[0], minor_col_name, preserve_order=True) - if renumber: - return df, renumber_df + if len(dfs) > 1: + return dfs - return df + return dfs[0] diff --git a/python/cugraph/cugraph/tests/sampling/test_bulk_sampler.py b/python/cugraph/cugraph/tests/sampling/test_bulk_sampler.py index 5ea79e0893a..a945881394b 100644 --- a/python/cugraph/cugraph/tests/sampling/test_bulk_sampler.py +++ b/python/cugraph/cugraph/tests/sampling/test_bulk_sampler.py @@ -16,7 +16,7 @@ import cudf import cupy import cugraph -from cugraph.datasets import karate +from cugraph.datasets import karate, email_Eu_core from cugraph.experimental.gnn import BulkSampler from cugraph.utilities.utils import create_directory_with_overwrite @@ -297,3 +297,53 @@ def test_bulk_sampler_empty_batches(scratch_dir): assert df.batch_id.max() == 1 shutil.rmtree(samples_path) + + +@pytest.mark.sg +def test_bulk_sampler_csr(scratch_dir): + el = email_Eu_core.get_edgelist() + + G = cugraph.Graph(directed=True) + G.from_cudf_edgelist(el, source="src", destination="dst") + + samples_path = os.path.join(scratch_dir, "test_bulk_sampler_csr") + create_directory_with_overwrite(samples_path) + + bs = BulkSampler( + batch_size=7, + output_path=samples_path, + graph=G, + fanout_vals=[5, 4, 3], + with_replacement=False, + batches_per_partition=7, + renumber=True, + use_legacy_names=False, + compression="CSR", + compress_per_hop=False, + prior_sources_behavior="exclude", + include_hop_column=False, + ) + + seeds = G.select_random_vertices(62, 1000) + batch_ids = cudf.Series( + cupy.repeat(cupy.arange(int(1000 / 7) + 1, dtype="int32"), 7)[:1000] + ).sort_values() + + batch_df = cudf.DataFrame( + { + "seed": seeds, + "batch": batch_ids, + } + ) + + bs.add_batches(batch_df, start_col_name="seed", batch_col_name="batch") + bs.flush() + + assert len(os.listdir(samples_path)) == 21 + + for file in os.listdir(samples_path): + df = cudf.read_parquet(os.path.join(samples_path, file)) + + assert df.major_offsets.dropna().iloc[-1] - df.major_offsets.iloc[0] == len(df) + + shutil.rmtree(samples_path) diff --git a/python/cugraph/cugraph/tests/sampling/test_bulk_sampler_io.py b/python/cugraph/cugraph/tests/sampling/test_bulk_sampler_io.py index f71c16a8368..5eafe89ea83 100644 --- a/python/cugraph/cugraph/tests/sampling/test_bulk_sampler_io.py +++ b/python/cugraph/cugraph/tests/sampling/test_bulk_sampler_io.py @@ -16,6 +16,7 @@ import pytest +import cupy import cudf from cugraph.gnn.data_loading.bulk_sampler_io import write_samples from cugraph.utilities.utils import create_directory_with_overwrite @@ -34,7 +35,9 @@ def test_bulk_sampler_io(scratch_dir): } ) - offsets = cudf.DataFrame({"offsets": [0, 8], "batch_id": [0, 1]}) + assert len(results) == 12 + + offsets = cudf.DataFrame({"offsets": [0, 8, 12], "batch_id": [0, 1, None]}) samples_path = os.path.join(scratch_dir, "test_bulk_sampler_io") create_directory_with_overwrite(samples_path) @@ -138,8 +141,12 @@ def test_bulk_sampler_io_empty_batch(scratch_dir): } ) + assert len(results) == 20 + # some batches are missing - offsets = cudf.DataFrame({"offsets": [0, 8, 12, 16], "batch_id": [0, 3, 4, 10]}) + offsets = cudf.DataFrame( + {"offsets": [0, 8, 12, 16, 20], "batch_id": [0, 3, 4, 10, None]} + ) samples_path = os.path.join(scratch_dir, "test_bulk_sampler_io_empty_batch") create_directory_with_overwrite(samples_path) @@ -157,3 +164,61 @@ def test_bulk_sampler_io_empty_batch(scratch_dir): df1 = cudf.read_parquet(os.path.join(samples_path, "batch=4-5.parquet")) assert df1.batch_id.min() == 4 assert df1.batch_id.max() == 5 + + shutil.rmtree(samples_path) + + +@pytest.mark.sg +def test_bulk_sampler_io_mock_csr(scratch_dir): + major_offsets_array = cudf.Series([0, 5, 10, 15]) + minors_array = cudf.Series([1, 2, 3, 4, 8, 9, 1, 3, 4, 5, 3, 0, 4, 9, 1]) + edge_ids = cudf.Series(cupy.arange(len(minors_array))) + + # 2 hops + label_hop_offsets = cudf.Series([0, 1, 3]) + + # map + renumber_map = cudf.Series(cupy.arange(10)) + renumber_map_offsets = cudf.Series([0, 10]) + + results_df = cudf.DataFrame() + results_df["minors"] = minors_array + results_df["major_offsets"] = major_offsets_array + results_df["edge_id"] = edge_ids + results_df["edge_type"] = None + results_df["weight"] = None + + offsets_df = cudf.DataFrame() + offsets_df["offsets"] = label_hop_offsets + offsets_df["renumber_map_offsets"] = renumber_map_offsets + offsets_df["batch_id"] = cudf.Series([0]) + + renumber_df = cudf.DataFrame() + renumber_df["map"] = renumber_map + + samples_path = os.path.join(scratch_dir, "test_bulk_sampler_io_mock_csr") + create_directory_with_overwrite(samples_path) + + write_samples(results_df, offsets_df, renumber_df, 1, samples_path) + + result = cudf.read_parquet(os.path.join(samples_path, "batch=0-0.parquet")) + + assert ( + result.minors.dropna().values_host.tolist() == minors_array.values_host.tolist() + ) + assert ( + result.major_offsets.dropna().values_host.tolist() + == major_offsets_array.values_host.tolist() + ) + assert result.edge_id.dropna().values_host.tolist() == edge_ids.values_host.tolist() + assert ( + result.renumber_map_offsets.dropna().values_host.tolist() + == renumber_map_offsets.values_host.tolist() + ) + assert result.map.dropna().values_host.tolist() == renumber_map.values_host.tolist() + assert ( + result.label_hop_offsets.dropna().values_host.tolist() + == label_hop_offsets.values_host.tolist() + ) + + shutil.rmtree(samples_path) diff --git a/python/cugraph/cugraph/tests/sampling/test_bulk_sampler_io_mg.py b/python/cugraph/cugraph/tests/sampling/test_bulk_sampler_io_mg.py index 41f68c08e5c..638cccbdcaa 100644 --- a/python/cugraph/cugraph/tests/sampling/test_bulk_sampler_io_mg.py +++ b/python/cugraph/cugraph/tests/sampling/test_bulk_sampler_io_mg.py @@ -38,8 +38,12 @@ def test_bulk_sampler_io(scratch_dir): divisions=[0, 8, 11] ) - offsets = cudf.DataFrame({"offsets": [0, 0], "batch_id": [0, 1]}) - offsets = dask_cudf.from_cudf(offsets, npartitions=2) + assert len(results) == 12 + + offsets = cudf.DataFrame({"offsets": [0, 8, 0, 4], "batch_id": [0, None, 1, None]}) + offsets = dask_cudf.from_cudf(offsets, npartitions=1).repartition( + divisions=[0, 2, 3] + ) samples_path = os.path.join(scratch_dir, "mg_test_bulk_sampler_io") create_directory_with_overwrite(samples_path) @@ -149,9 +153,11 @@ def test_bulk_sampler_io_empty_batch(scratch_dir): ) # some batches are missing - offsets = cudf.DataFrame({"offsets": [0, 8, 0, 4], "batch_id": [0, 3, 4, 10]}) + offsets = cudf.DataFrame( + {"offsets": [0, 8, 12, 0, 4, 8], "batch_id": [0, 3, None, 4, 10, None]} + ) offsets = dask_cudf.from_cudf(offsets, npartitions=1).repartition( - divisions=[0, 2, 3] + divisions=[0, 3, 5] ) samples_path = os.path.join(scratch_dir, "mg_test_bulk_sampler_io_empty_batch") diff --git a/python/cugraph/cugraph/tests/sampling/test_bulk_sampler_mg.py b/python/cugraph/cugraph/tests/sampling/test_bulk_sampler_mg.py index eded435f897..aee81e5ffed 100644 --- a/python/cugraph/cugraph/tests/sampling/test_bulk_sampler_mg.py +++ b/python/cugraph/cugraph/tests/sampling/test_bulk_sampler_mg.py @@ -21,7 +21,7 @@ import cupy import cugraph import dask_cudf -from cugraph.datasets import karate +from cugraph.datasets import karate, email_Eu_core from cugraph.experimental import BulkSampler from cugraph.utilities.utils import create_directory_with_overwrite @@ -247,3 +247,59 @@ def test_bulk_sampler_empty_batches(dask_client, scratch_dir): assert df.batch_id.max() == 1 shutil.rmtree(samples_path) + + +@pytest.mark.mg +@pytest.mark.parametrize("mg_input", [True, False]) +def test_bulk_sampler_csr(dask_client, scratch_dir, mg_input): + nworkers = len(dask_client.scheduler_info()["workers"]) + el = dask_cudf.from_cudf(email_Eu_core.get_edgelist(), npartitions=nworkers * 2) + + G = cugraph.Graph(directed=True) + G.from_dask_cudf_edgelist(el, source="src", destination="dst") + + samples_path = os.path.join(scratch_dir, "mg_test_bulk_sampler_csr") + create_directory_with_overwrite(samples_path) + + bs = BulkSampler( + batch_size=7, + output_path=samples_path, + graph=G, + fanout_vals=[5, 4, 3], + with_replacement=False, + batches_per_partition=7, + renumber=True, + use_legacy_names=False, + compression="CSR", + compress_per_hop=True, + prior_sources_behavior="carryover", + deduplicate_sources=True, + include_hop_column=False, + ) + + seeds = G.select_random_vertices(62, 1000) + batch_ids = cudf.Series( + cupy.repeat(cupy.arange(int(1000 / 7) + 1, dtype="int32"), 7)[:1000] + ).sort_values() + + batch_df = cudf.DataFrame( + { + "seed": seeds.compute().values, + "batch": batch_ids, + } + ) + + if mg_input: + batch_df = dask_cudf.from_cudf(batch_df, npartitions=2) + + bs.add_batches(batch_df, start_col_name="seed", batch_col_name="batch") + bs.flush() + + assert len(os.listdir(samples_path)) == 21 + + for file in os.listdir(samples_path): + df = cudf.read_parquet(os.path.join(samples_path, file)) + + assert df.major_offsets.dropna().iloc[-1] - df.major_offsets.iloc[0] == len(df) + + shutil.rmtree(samples_path) diff --git a/python/cugraph/cugraph/tests/sampling/test_uniform_neighbor_sample.py b/python/cugraph/cugraph/tests/sampling/test_uniform_neighbor_sample.py index 62599291d04..206898088ab 100644 --- a/python/cugraph/cugraph/tests/sampling/test_uniform_neighbor_sample.py +++ b/python/cugraph/cugraph/tests/sampling/test_uniform_neighbor_sample.py @@ -15,6 +15,7 @@ import pytest +import cupy import cudf import cugraph from cugraph import uniform_neighbor_sample @@ -151,7 +152,7 @@ def test_uniform_neighbor_sample_simple(input_combo): G, input_combo["start_list"], input_combo["fanout_vals"], - input_combo["with_replacement"], + with_replacement=input_combo["with_replacement"], ) print(input_df) @@ -254,7 +255,9 @@ def test_uniform_neighbor_sample_tree(directed): start_list = cudf.Series([0, 0], dtype="int32") fanout_vals = [4, 1, 3] with_replacement = True - result_nbr = uniform_neighbor_sample(G, start_list, fanout_vals, with_replacement) + result_nbr = uniform_neighbor_sample( + G, start_list, fanout_vals, with_replacement=with_replacement + ) result_nbr = result_nbr.drop_duplicates() @@ -288,7 +291,7 @@ def test_uniform_neighbor_sample_unweighted(simple_unweighted_input_expected_out test_data["Graph"], test_data["start_list"].astype("int64"), test_data["fanout_vals"], - test_data["with_replacement"], + with_replacement=test_data["with_replacement"], ) actual_src = sampling_results.sources @@ -303,7 +306,8 @@ def test_uniform_neighbor_sample_unweighted(simple_unweighted_input_expected_out @pytest.mark.sg @pytest.mark.cugraph_ops @pytest.mark.parametrize("return_offsets", [True, False]) -def test_uniform_neighbor_sample_edge_properties(return_offsets): +@pytest.mark.parametrize("include_hop_column", [True, False]) +def test_uniform_neighbor_sample_edge_properties(return_offsets, include_hop_column): edgelist_df = cudf.DataFrame( { "src": cudf.Series([0, 1, 2, 3, 4, 3, 4, 2, 0, 1, 0, 2], dtype="int32"), @@ -337,6 +341,7 @@ def test_uniform_neighbor_sample_edge_properties(return_offsets): with_edge_properties=True, with_batch_ids=True, return_offsets=return_offsets, + include_hop_column=include_hop_column, ) if return_offsets: sampling_results, sampling_offsets = sampling_results @@ -359,11 +364,29 @@ def test_uniform_neighbor_sample_edge_properties(return_offsets): == sampling_results["destinations"].values_host.tolist() ) - assert sampling_results["hop_id"].values_host.tolist() == ([0, 0, 1, 1, 1, 1] * 2) + if include_hop_column: + assert sampling_results["hop_id"].values_host.tolist() == ( + [0, 0, 1, 1, 1, 1] * 2 + ) + else: + assert "hop_id" not in sampling_results if return_offsets: - assert sampling_offsets["batch_id"].values_host.tolist() == [0, 1] - assert sampling_offsets["offsets"].values_host.tolist() == [0, 6] + assert sampling_offsets["batch_id"].dropna().values_host.tolist() == [0, 1] + if include_hop_column: + assert sampling_offsets["offsets"].dropna().values_host.tolist() == [ + 0, + 6, + 12, + ] + else: + assert sampling_offsets["offsets"].dropna().values_host.tolist() == [ + 0, + 2, + 6, + 8, + 12, + ] else: assert sampling_results["batch_id"].values_host.tolist() == ([0] * 6 + [1] * 6) @@ -778,6 +801,176 @@ def test_uniform_neighbor_sample_renumber(hops): assert (renumber_map.batch_id == 0).all() +@pytest.mark.sg +@pytest.mark.parametrize("hops", [[5], [5, 5], [5, 5, 5]]) +def test_uniform_neighbor_sample_offset_renumber(hops): + el = email_Eu_core.get_edgelist() + + G = cugraph.Graph(directed=True) + G.from_cudf_edgelist(el, source="src", destination="dst") + + seeds = G.select_random_vertices(62, int(0.0001 * len(el))) + + ( + sampling_results_unrenumbered, + offsets_unrenumbered, + ) = cugraph.uniform_neighbor_sample( + G, + seeds, + hops, + with_replacement=False, + with_edge_properties=True, + with_batch_ids=False, + deduplicate_sources=True, + renumber=False, + return_offsets=True, + random_state=62, + ) + + ( + sampling_results_renumbered, + offsets_renumbered, + renumber_map, + ) = cugraph.uniform_neighbor_sample( + G, + seeds, + hops, + with_replacement=False, + with_edge_properties=True, + with_batch_ids=False, + deduplicate_sources=True, + renumber=True, + return_offsets=True, + random_state=62, + ) + + sources_hop_0 = sampling_results_unrenumbered[ + sampling_results_unrenumbered.hop_id == 0 + ].sources + for hop in range(len(hops)): + destinations_hop = sampling_results_unrenumbered[ + sampling_results_unrenumbered.hop_id <= hop + ].destinations + expected_renumber_map = cudf.concat([sources_hop_0, destinations_hop]).unique() + + assert sorted(expected_renumber_map.values_host.tolist()) == sorted( + renumber_map.map[0 : len(expected_renumber_map)].values_host.tolist() + ) + + renumber_map_offsets = offsets_renumbered.renumber_map_offsets.dropna() + assert len(renumber_map_offsets) == 2 + assert renumber_map_offsets.iloc[0] == 0 + assert renumber_map_offsets.iloc[-1] == len(renumber_map) + + assert len(offsets_renumbered) == 2 + + +@pytest.mark.sg +@pytest.mark.parametrize("hops", [[5], [5, 5], [5, 5, 5]]) +@pytest.mark.parametrize("seed", [62, 66, 68]) +def test_uniform_neighbor_sample_csr_csc_global(hops, seed): + el = email_Eu_core.get_edgelist() + + G = cugraph.Graph(directed=True) + G.from_cudf_edgelist(el, source="src", destination="dst") + + seeds = G.select_random_vertices(seed, int(0.0001 * len(el))) + + sampling_results, offsets, renumber_map = cugraph.uniform_neighbor_sample( + G, + seeds, + hops, + with_replacement=False, + with_edge_properties=True, + with_batch_ids=False, + deduplicate_sources=True, + # carryover not valid because C++ sorts on (hop,src) + prior_sources_behavior="exclude", + renumber=True, + return_offsets=True, + random_state=seed, + use_legacy_names=False, + compress_per_hop=False, + compression="CSR", + include_hop_column=False, + ) + + major_offsets = sampling_results["major_offsets"].dropna().values + majors = cudf.Series(cupy.arange(len(major_offsets) - 1)) + majors = majors.repeat(cupy.diff(major_offsets)) + + minors = sampling_results["minors"].dropna() + assert len(majors) == len(minors) + + majors = renumber_map.map.iloc[majors] + minors = renumber_map.map.iloc[minors] + + for i in range(len(majors)): + assert 1 == len(el[(el.src == majors.iloc[i]) & (el.dst == minors.iloc[i])]) + + +@pytest.mark.sg +@pytest.mark.parametrize("seed", [62, 66, 68]) +@pytest.mark.parametrize("hops", [[5], [5, 5], [5, 5, 5]]) +def test_uniform_neighbor_sample_csr_csc_local(hops, seed): + el = email_Eu_core.get_edgelist(download=True) + + G = cugraph.Graph(directed=True) + G.from_cudf_edgelist(el, source="src", destination="dst") + + seeds = cudf.Series( + [49, 71], dtype="int32" + ) # hardcoded to ensure out-degree is high enough + + sampling_results, offsets, renumber_map = cugraph.uniform_neighbor_sample( + G, + seeds, + hops, + with_replacement=False, + with_edge_properties=True, + with_batch_ids=False, + deduplicate_sources=True, + prior_sources_behavior="carryover", + renumber=True, + return_offsets=True, + random_state=seed, + use_legacy_names=False, + compress_per_hop=True, + compression="CSR", + include_hop_column=False, + ) + + for hop in range(len(hops)): + major_offsets = sampling_results["major_offsets"].iloc[ + offsets.offsets.iloc[hop] : (offsets.offsets.iloc[hop + 1] + 1) + ] + + minors = sampling_results["minors"].iloc[ + major_offsets.iloc[0] : major_offsets.iloc[-1] + ] + + majors = cudf.Series(cupy.arange(len(major_offsets) - 1)) + majors = majors.repeat(cupy.diff(major_offsets)) + + majors = renumber_map.map.iloc[majors] + minors = renumber_map.map.iloc[minors] + + for i in range(len(majors)): + assert 1 == len(el[(el.src == majors.iloc[i]) & (el.dst == minors.iloc[i])]) + + +@pytest.mark.sg +@pytest.mark.skip(reason="needs to be written!") +def test_uniform_neighbor_sample_dcsr_dcsc_global(): + raise NotImplementedError + + +@pytest.mark.sg +@pytest.mark.skip(reason="needs to be written!") +def test_uniform_neighbor_sample_dcsr_dcsc_local(): + raise NotImplementedError + + @pytest.mark.sg @pytest.mark.skip(reason="needs to be written!") def test_multi_client_sampling(): diff --git a/python/cugraph/cugraph/tests/sampling/test_uniform_neighbor_sample_mg.py b/python/cugraph/cugraph/tests/sampling/test_uniform_neighbor_sample_mg.py index 9d87c097287..460a25cbd14 100644 --- a/python/cugraph/cugraph/tests/sampling/test_uniform_neighbor_sample_mg.py +++ b/python/cugraph/cugraph/tests/sampling/test_uniform_neighbor_sample_mg.py @@ -17,6 +17,7 @@ import pytest +import pandas import cupy import cudf import cugraph @@ -138,7 +139,7 @@ def test_mg_uniform_neighbor_sample_simple(dask_client, input_combo): dg, input_combo["start_list"], input_combo["fanout_vals"], - input_combo["with_replacement"], + with_replacement=input_combo["with_replacement"], ) # multi edges are dropped to easily verify that each edge in the @@ -228,7 +229,9 @@ def test_mg_uniform_neighbor_sample_tree(dask_client, directed): start_list = cudf.Series([0, 0], dtype="int32") fanout_vals = [4, 1, 3] with_replacement = True - result_nbr = uniform_neighbor_sample(G, start_list, fanout_vals, with_replacement) + result_nbr = uniform_neighbor_sample( + G, start_list, fanout_vals, with_replacement=with_replacement + ) result_nbr = result_nbr.drop_duplicates() @@ -283,7 +286,7 @@ def test_mg_uniform_neighbor_sample_unweighted(dask_client): with_replacement = True sampling_results = uniform_neighbor_sample( - G, start_list, fanout_vals, with_replacement + G, start_list, fanout_vals, with_replacement=with_replacement ) expected_src = [0, 0] @@ -380,13 +383,17 @@ def test_uniform_neighbor_sample_edge_properties(dask_client, return_offsets): dfp = sampling_results.get_partition(i).compute() if len(dfp) > 0: offsets_p = sampling_offsets.get_partition(i).compute() + print(offsets_p) assert len(offsets_p) > 0 if offsets_p.batch_id.iloc[0] == 1: batches_found[1] += 1 - assert offsets_p.batch_id.values_host.tolist() == [1] - assert offsets_p.offsets.values_host.tolist() == [0] + assert offsets_p.batch_id.dropna().values_host.tolist() == [1] + assert offsets_p.offsets.dropna().values_host.tolist() == [ + 0, + len(dfp), + ] assert sorted(dfp.sources.values_host.tolist()) == ( [1, 1, 3, 3, 4, 4] @@ -397,8 +404,11 @@ def test_uniform_neighbor_sample_edge_properties(dask_client, return_offsets): elif offsets_p.batch_id.iloc[0] == 0: batches_found[0] += 1 - assert offsets_p.batch_id.values_host.tolist() == [0] - assert offsets_p.offsets.values_host.tolist() == [0] + assert offsets_p.batch_id.dropna().values_host.tolist() == [0] + assert offsets_p.offsets.dropna().values_host.tolist() == [ + 0, + len(dfp), + ] assert sorted(dfp.sources.values_host.tolist()) == ( [0, 0, 0, 1, 1, 2, 2, 2, 4, 4] @@ -703,7 +713,6 @@ def test_uniform_neighbor_sample_batched(dask_client, dataset, input_df, max_bat source="src", destination="dst", edge_attr=["wgt", "eid", "etp"], - legacy_renum_only=True, ) input_vertices = dask_cudf.concat([df.src, df.dst]).unique().compute() @@ -960,7 +969,6 @@ def test_uniform_neighbor_sample_deduplicate_sources_email_eu_core(dask_client): @pytest.mark.mg @pytest.mark.parametrize("hops", [[5], [5, 5], [5, 5, 5]]) -@pytest.mark.tags("runme") def test_uniform_neighbor_sample_renumber(dask_client, hops): # FIXME This test is not very good because there is a lot of # non-deterministic behavior that still exists despite passing @@ -1005,6 +1013,224 @@ def test_uniform_neighbor_sample_renumber(dask_client, hops): ) +@pytest.mark.mg +@pytest.mark.parametrize("hops", [[5], [5, 5], [5, 5, 5]]) +def test_uniform_neighbor_sample_offset_renumber(dask_client, hops): + el = dask_cudf.from_cudf(email_Eu_core.get_edgelist(), npartitions=4) + + G = cugraph.Graph(directed=True) + G.from_dask_cudf_edgelist(el, source="src", destination="dst") + + seeds = G.select_random_vertices(62, int(0.0001 * len(el))) + + ( + sampling_results_unrenumbered, + offsets_unrenumbered, + ) = cugraph.dask.uniform_neighbor_sample( + G, + seeds, + hops, + with_replacement=False, + with_edge_properties=True, + with_batch_ids=False, + deduplicate_sources=True, + renumber=False, + return_offsets=True, + random_state=62, + ) + sampling_results_unrenumbered = sampling_results_unrenumbered.compute() + offsets_unrenumbered = offsets_unrenumbered.compute() + + ( + sampling_results_renumbered, + offsets_renumbered, + renumber_map, + ) = cugraph.dask.uniform_neighbor_sample( + G, + seeds, + hops, + with_replacement=False, + with_edge_properties=True, + with_batch_ids=False, + deduplicate_sources=True, + renumber=True, + keep_batches_together=True, + min_batch_id=0, + max_batch_id=0, + return_offsets=True, + random_state=62, + ) + + # can't use compute() since empty batches still get a partition + n_workers = len(dask_client.scheduler_info()["workers"]) + for p in range(n_workers): + partition = offsets_renumbered.get_partition(p).compute() + if not pandas.isna(partition.batch_id.iloc[0]): + break + + sampling_results_renumbered = sampling_results_renumbered.get_partition(p).compute() + offsets_renumbered = offsets_renumbered.get_partition(p).compute() + renumber_map = renumber_map.get_partition(p).compute() + + sources_hop_0 = sampling_results_unrenumbered[ + sampling_results_unrenumbered.hop_id == 0 + ].sources + for hop in range(len(hops)): + destinations_hop = sampling_results_unrenumbered[ + sampling_results_unrenumbered.hop_id <= hop + ].destinations + expected_renumber_map = cudf.concat([sources_hop_0, destinations_hop]).unique() + + assert sorted(expected_renumber_map.values_host.tolist()) == sorted( + renumber_map.map[0 : len(expected_renumber_map)].values_host.tolist() + ) + + renumber_map_offsets = offsets_renumbered.renumber_map_offsets.dropna() + assert len(renumber_map_offsets) == 2 + assert renumber_map_offsets.iloc[0] == 0 + assert renumber_map_offsets.iloc[-1] == len(renumber_map) + + assert len(offsets_renumbered) == 2 + + +@pytest.mark.mg +@pytest.mark.parametrize("hops", [[5], [5, 5], [5, 5, 5]]) +@pytest.mark.parametrize("seed", [62, 66, 68]) +def test_uniform_neighbor_sample_csr_csc_global(dask_client, hops, seed): + el = dask_cudf.from_cudf(email_Eu_core.get_edgelist(), npartitions=4) + + G = cugraph.Graph(directed=True) + G.from_dask_cudf_edgelist(el, source="src", destination="dst") + + seeds = G.select_random_vertices(seed, int(0.0001 * len(el))) + + sampling_results, offsets, renumber_map = cugraph.dask.uniform_neighbor_sample( + G, + seeds, + hops, + with_replacement=False, + with_edge_properties=True, + with_batch_ids=False, + deduplicate_sources=True, + # carryover not valid because C++ sorts on (hop,src) + prior_sources_behavior="exclude", + renumber=True, + return_offsets=True, + random_state=seed, + use_legacy_names=False, + compress_per_hop=False, + compression="CSR", + include_hop_column=False, + keep_batches_together=True, + min_batch_id=0, + max_batch_id=0, + ) + + # can't use compute() since empty batches still get a partition + n_workers = len(dask_client.scheduler_info()["workers"]) + for p in range(n_workers): + partition = offsets.get_partition(p).compute() + if not pandas.isna(partition.batch_id.iloc[0]): + break + + sampling_results = sampling_results.get_partition(p).compute() + offsets = offsets.get_partition(p).compute() + renumber_map = renumber_map.get_partition(p).compute() + + major_offsets = sampling_results["major_offsets"].dropna().values + majors = cudf.Series(cupy.arange(len(major_offsets) - 1)) + majors = majors.repeat(cupy.diff(major_offsets)) + + minors = sampling_results["minors"].dropna() + assert len(majors) == len(minors) + + majors = renumber_map.map.iloc[majors] + minors = renumber_map.map.iloc[minors] + + for i in range(len(majors)): + assert 1 == len(el[(el.src == majors.iloc[i]) & (el.dst == minors.iloc[i])]) + + +@pytest.mark.mg +@pytest.mark.parametrize("seed", [62, 66, 68]) +@pytest.mark.parametrize("hops", [[5], [5, 5], [5, 5, 5]]) +def test_uniform_neighbor_sample_csr_csc_local(dask_client, hops, seed): + el = dask_cudf.from_cudf(email_Eu_core.get_edgelist(), npartitions=4) + + G = cugraph.Graph(directed=True) + G.from_dask_cudf_edgelist(el, source="src", destination="dst") + + seeds = dask_cudf.from_cudf( + cudf.Series([49, 71], dtype="int32"), npartitions=1 + ) # hardcoded to ensure out-degree is high enough + + sampling_results, offsets, renumber_map = cugraph.dask.uniform_neighbor_sample( + G, + seeds, + hops, + with_replacement=False, + with_edge_properties=True, + with_batch_ids=False, + deduplicate_sources=True, + prior_sources_behavior="carryover", + renumber=True, + return_offsets=True, + random_state=seed, + use_legacy_names=False, + compress_per_hop=True, + compression="CSR", + include_hop_column=False, + keep_batches_together=True, + min_batch_id=0, + max_batch_id=0, + ) + + # can't use compute() since empty batches still get a partition + n_workers = len(dask_client.scheduler_info()["workers"]) + for p in range(n_workers): + partition = offsets.get_partition(p).compute() + + if not pandas.isna(partition.batch_id.iloc[0]): + break + + sampling_results = sampling_results.get_partition(p).compute() + offsets = offsets.get_partition(p).compute() + renumber_map = renumber_map.get_partition(p).compute() + + print(sampling_results) + print(offsets) + + for hop in range(len(hops)): + major_offsets = sampling_results["major_offsets"].iloc[ + offsets.offsets.iloc[hop] : (offsets.offsets.iloc[hop + 1] + 1) + ] + + minors = sampling_results["minors"].iloc[ + major_offsets.iloc[0] : major_offsets.iloc[-1] + ] + + majors = cudf.Series(cupy.arange(len(major_offsets) - 1)) + majors = majors.repeat(cupy.diff(major_offsets)) + + majors = renumber_map.map.iloc[majors] + minors = renumber_map.map.iloc[minors] + + for i in range(len(majors)): + assert 1 == len(el[(el.src == majors.iloc[i]) & (el.dst == minors.iloc[i])]) + + +@pytest.mark.mg +@pytest.mark.skip(reason="needs to be written!") +def test_uniform_neighbor_sample_dcsr_dcsc_global(): + raise NotImplementedError + + +@pytest.mark.mg +@pytest.mark.skip(reason="needs to be written!") +def test_uniform_neighbor_sample_dcsr_dcsc_local(): + raise NotImplementedError + + # ============================================================================= # Benchmarks # ============================================================================= diff --git a/python/pylibcugraph/pylibcugraph/_cugraph_c/algorithms.pxd b/python/pylibcugraph/pylibcugraph/_cugraph_c/algorithms.pxd index ffb458b409c..29c6d79e08d 100644 --- a/python/pylibcugraph/pylibcugraph/_cugraph_c/algorithms.pxd +++ b/python/pylibcugraph/pylibcugraph/_cugraph_c/algorithms.pxd @@ -176,15 +176,32 @@ cdef extern from "cugraph_c/algorithms.h": const cugraph_sample_result_t* result ) + # Deprecated, use cugraph_sample_result_get_majors cdef cugraph_type_erased_device_array_view_t* \ cugraph_sample_result_get_sources( const cugraph_sample_result_t* result ) + # Deprecated, use cugraph_sample_result_get_minors cdef cugraph_type_erased_device_array_view_t* \ cugraph_sample_result_get_destinations( const cugraph_sample_result_t* result ) + + cdef cugraph_type_erased_device_array_view_t* \ + cugraph_sample_result_get_majors( + const cugraph_sample_result_t* result + ) + + cdef cugraph_type_erased_device_array_view_t* \ + cugraph_sample_result_get_minors( + const cugraph_sample_result_t* result + ) + + cdef cugraph_type_erased_device_array_view_t* \ + cugraph_sample_result_get_major_offsets( + const cugraph_sample_result_t* result + ) cdef cugraph_type_erased_device_array_view_t* \ cugraph_sample_result_get_index( @@ -211,11 +228,17 @@ cdef extern from "cugraph_c/algorithms.h": const cugraph_sample_result_t* result ) + cdef cugraph_type_erased_device_array_view_t* \ + cugraph_sample_result_get_label_hop_offsets( + const cugraph_sample_result_t* result + ) + cdef cugraph_type_erased_device_array_view_t* \ cugraph_sample_result_get_start_labels( const cugraph_sample_result_t* result ) + # Deprecated cdef cugraph_type_erased_device_array_view_t* \ cugraph_sample_result_get_offsets( const cugraph_sample_result_t* result @@ -246,10 +269,17 @@ cdef extern from "cugraph_c/algorithms.h": pass ctypedef enum cugraph_prior_sources_behavior_t: - DEFAULT + DEFAULT=0 CARRY_OVER EXCLUDE + ctypedef enum cugraph_compression_type_t: + COO=0 + CSR + CSC + DCSR + DCSC + cdef cugraph_error_code_t \ cugraph_sampling_options_create( cugraph_sampling_options_t** options, @@ -277,7 +307,7 @@ cdef extern from "cugraph_c/algorithms.h": cdef void \ cugraph_sampling_set_prior_sources_behavior( cugraph_sampling_options_t* options, - cugraph_prior_sources_behavior_t value + cugraph_prior_sources_behavior_t value, ) cdef void \ @@ -286,10 +316,22 @@ cdef extern from "cugraph_c/algorithms.h": bool_t value, ) + cdef void \ + cugraph_sampling_set_compress_per_hop( + cugraph_sampling_options_t* options, + bool_t value, + ) + + cdef void \ + cugraph_sampling_set_compression_type( + cugraph_sampling_options_t* options, + cugraph_compression_type_t value, + ) + cdef void \ cugraph_sampling_options_free( cugraph_sampling_options_t* options, - ) + ) # uniform random walks cdef cugraph_error_code_t \ diff --git a/python/pylibcugraph/pylibcugraph/_cugraph_c/sampling_algorithms.pxd b/python/pylibcugraph/pylibcugraph/_cugraph_c/sampling_algorithms.pxd index 91cc11d6b1c..c32b57f8621 100644 --- a/python/pylibcugraph/pylibcugraph/_cugraph_c/sampling_algorithms.pxd +++ b/python/pylibcugraph/pylibcugraph/_cugraph_c/sampling_algorithms.pxd @@ -43,23 +43,6 @@ from pylibcugraph._cugraph_c.array cimport ( cdef extern from "cugraph_c/sampling_algorithms.h": ########################################################################### - # deprecated, should migrate to cugraph_uniform_neighbor_sample - cdef cugraph_error_code_t cugraph_uniform_neighbor_sample_with_edge_properties( - const cugraph_resource_handle_t* handle, - cugraph_graph_t* graph, - const cugraph_type_erased_device_array_view_t* start_vertices, - const cugraph_type_erased_device_array_view_t* start_vertex_labels, - const cugraph_type_erased_device_array_view_t* label_list, - const cugraph_type_erased_device_array_view_t* label_to_comm_rank, - const cugraph_type_erased_host_array_view_t* fan_out, - cugraph_rng_state_t* rng_state, - bool_t with_replacement, - bool_t return_hops, - bool_t do_expensive_check, - cugraph_sample_result_t** result, - cugraph_error_t** error - ) - cdef cugraph_error_code_t cugraph_uniform_neighbor_sample( const cugraph_resource_handle_t* handle, cugraph_graph_t* graph, diff --git a/python/pylibcugraph/pylibcugraph/internal_types/sampling_result.pyx b/python/pylibcugraph/pylibcugraph/internal_types/sampling_result.pyx index d11f6994298..9f98b4f37b0 100644 --- a/python/pylibcugraph/pylibcugraph/internal_types/sampling_result.pyx +++ b/python/pylibcugraph/pylibcugraph/internal_types/sampling_result.pyx @@ -20,14 +20,18 @@ from pylibcugraph._cugraph_c.array cimport ( ) from pylibcugraph._cugraph_c.algorithms cimport ( cugraph_sample_result_t, - cugraph_sample_result_get_sources, - cugraph_sample_result_get_destinations, + cugraph_sample_result_get_major_offsets, + cugraph_sample_result_get_majors, + cugraph_sample_result_get_minors, + cugraph_sample_result_get_label_hop_offsets, + cugraph_sample_result_get_sources, # deprecated + cugraph_sample_result_get_destinations, # deprecated cugraph_sample_result_get_edge_weight, cugraph_sample_result_get_edge_id, cugraph_sample_result_get_edge_type, - cugraph_sample_result_get_hop, + cugraph_sample_result_get_hop, # deprecated cugraph_sample_result_get_start_labels, - cugraph_sample_result_get_offsets, + cugraph_sample_result_get_offsets, # deprecated cugraph_sample_result_get_renumber_map, cugraph_sample_result_get_renumber_map_offsets, cugraph_sample_result_free, @@ -60,23 +64,71 @@ cdef class SamplingResult: cdef set_ptr(self, cugraph_sample_result_t* sample_result_ptr): self.c_sample_result_ptr = sample_result_ptr + def get_major_offsets(self): + if self.c_sample_result_ptr is NULL: + raise ValueError("pointer not set, must call set_ptr() with a " + "non-NULL value first.") + + cdef cugraph_type_erased_device_array_view_t* device_array_view_ptr = ( + cugraph_sample_result_get_major_offsets(self.c_sample_result_ptr) + ) + if device_array_view_ptr is NULL: + return None + + return create_cupy_array_view_for_device_ptr(device_array_view_ptr, + self) + + def get_majors(self): + if self.c_sample_result_ptr is NULL: + raise ValueError("pointer not set, must call set_ptr() with a " + "non-NULL value first.") + cdef cugraph_type_erased_device_array_view_t* device_array_view_ptr = ( + cugraph_sample_result_get_majors(self.c_sample_result_ptr) + ) + if device_array_view_ptr is NULL: + return None + + return create_cupy_array_view_for_device_ptr(device_array_view_ptr, + self) + + def get_minors(self): + if self.c_sample_result_ptr is NULL: + raise ValueError("pointer not set, must call set_ptr() with a " + "non-NULL value first.") + cdef cugraph_type_erased_device_array_view_t* device_array_view_ptr = ( + cugraph_sample_result_get_minors(self.c_sample_result_ptr) + ) + if device_array_view_ptr is NULL: + return None + + return create_cupy_array_view_for_device_ptr(device_array_view_ptr, + self) + def get_sources(self): + # Deprecated if self.c_sample_result_ptr is NULL: raise ValueError("pointer not set, must call set_ptr() with a " "non-NULL value first.") cdef cugraph_type_erased_device_array_view_t* device_array_view_ptr = ( cugraph_sample_result_get_sources(self.c_sample_result_ptr) ) + if device_array_view_ptr is NULL: + return None + return create_cupy_array_view_for_device_ptr(device_array_view_ptr, self) def get_destinations(self): + # Deprecated if self.c_sample_result_ptr is NULL: raise ValueError("pointer not set, must call set_ptr() with a " "non-NULL value first.") cdef cugraph_type_erased_device_array_view_t* device_array_view_ptr = ( cugraph_sample_result_get_destinations(self.c_sample_result_ptr) ) + if device_array_view_ptr is NULL: + return None + return create_cupy_array_view_for_device_ptr(device_array_view_ptr, self) @@ -95,6 +147,7 @@ cdef class SamplingResult: self) def get_indices(self): + # Deprecated return self.get_edge_weights() def get_edge_ids(self): @@ -132,9 +185,26 @@ cdef class SamplingResult: cdef cugraph_type_erased_device_array_view_t* device_array_view_ptr = ( cugraph_sample_result_get_start_labels(self.c_sample_result_ptr) ) + if device_array_view_ptr is NULL: + return None + return create_cupy_array_view_for_device_ptr(device_array_view_ptr, self) + def get_label_hop_offsets(self): + if self.c_sample_result_ptr is NULL: + raise ValueError("pointer not set, must call set_ptr() with a " + "non-NULL value first.") + cdef cugraph_type_erased_device_array_view_t* device_array_view_ptr = ( + cugraph_sample_result_get_label_hop_offsets(self.c_sample_result_ptr) + ) + if device_array_view_ptr is NULL: + return None + + return create_cupy_array_view_for_device_ptr(device_array_view_ptr, + self) + + # Deprecated def get_offsets(self): if self.c_sample_result_ptr is NULL: raise ValueError("pointer not set, must call set_ptr() with a " @@ -142,9 +212,13 @@ cdef class SamplingResult: cdef cugraph_type_erased_device_array_view_t* device_array_view_ptr = ( cugraph_sample_result_get_offsets(self.c_sample_result_ptr) ) + if device_array_view_ptr is NULL: + return None + return create_cupy_array_view_for_device_ptr(device_array_view_ptr, self) + # Deprecated def get_hop_ids(self): if self.c_sample_result_ptr is NULL: raise ValueError("pointer not set, must call set_ptr() with a " @@ -152,6 +226,9 @@ cdef class SamplingResult: cdef cugraph_type_erased_device_array_view_t* device_array_view_ptr = ( cugraph_sample_result_get_hop(self.c_sample_result_ptr) ) + if device_array_view_ptr is NULL: + return None + return create_cupy_array_view_for_device_ptr(device_array_view_ptr, self) @@ -162,6 +239,9 @@ cdef class SamplingResult: cdef cugraph_type_erased_device_array_view_t* device_array_view_ptr = ( cugraph_sample_result_get_renumber_map(self.c_sample_result_ptr) ) + if device_array_view_ptr is NULL: + return None + return create_cupy_array_view_for_device_ptr(device_array_view_ptr, self) @@ -172,5 +252,8 @@ cdef class SamplingResult: cdef cugraph_type_erased_device_array_view_t* device_array_view_ptr = ( cugraph_sample_result_get_renumber_map_offsets(self.c_sample_result_ptr) ) + if device_array_view_ptr is NULL: + return None + return create_cupy_array_view_for_device_ptr(device_array_view_ptr, self) \ No newline at end of file diff --git a/python/pylibcugraph/pylibcugraph/tests/test_uniform_neighbor_sample.py b/python/pylibcugraph/pylibcugraph/tests/test_uniform_neighbor_sample.py index 74aa6830d24..ac04635edcf 100644 --- a/python/pylibcugraph/pylibcugraph/tests/test_uniform_neighbor_sample.py +++ b/python/pylibcugraph/pylibcugraph/tests/test_uniform_neighbor_sample.py @@ -266,7 +266,7 @@ def test_neighborhood_sampling_large_sg_graph(gpubenchmark): def test_sample_result(): """ - Ensure the SampleResult class returns zero-opy cupy arrays and properly + Ensure the SampleResult class returns zero-copy cupy arrays and properly frees device memory when all references to it are gone and it's garbage collected. """ @@ -304,6 +304,8 @@ def test_sample_result(): assert isinstance(destinations, cp.ndarray) assert isinstance(indices, cp.ndarray) + print("sources:", destinations) + # Delete the SampleResult instance. This *should not* free the device # memory yet since the variables sources, destinations, and indices are # keeping the refcount >0. diff --git a/python/pylibcugraph/pylibcugraph/uniform_neighbor_sample.pyx b/python/pylibcugraph/pylibcugraph/uniform_neighbor_sample.pyx index bc2aa9205f1..ce6493c38f5 100644 --- a/python/pylibcugraph/pylibcugraph/uniform_neighbor_sample.pyx +++ b/python/pylibcugraph/pylibcugraph/uniform_neighbor_sample.pyx @@ -38,6 +38,7 @@ from pylibcugraph._cugraph_c.graph cimport ( from pylibcugraph._cugraph_c.algorithms cimport ( cugraph_sample_result_t, cugraph_prior_sources_behavior_t, + cugraph_compression_type_t, cugraph_sampling_options_t, cugraph_sampling_options_create, cugraph_sampling_options_free, @@ -46,7 +47,8 @@ from pylibcugraph._cugraph_c.algorithms cimport ( cugraph_sampling_set_prior_sources_behavior, cugraph_sampling_set_dedupe_sources, cugraph_sampling_set_renumber_results, - + cugraph_sampling_set_compress_per_hop, + cugraph_sampling_set_compression_type, ) from pylibcugraph._cugraph_c.sampling_algorithms cimport ( cugraph_uniform_neighbor_sample, @@ -73,6 +75,7 @@ from pylibcugraph._cugraph_c.random cimport ( from pylibcugraph.random cimport ( CuGraphRandomState ) +import warnings # TODO accept cupy/numpy random state in addition to raw seed. def uniform_neighbor_sample(ResourceHandle resource_handle, @@ -90,7 +93,10 @@ def uniform_neighbor_sample(ResourceHandle resource_handle, deduplicate_sources=False, return_hops=False, renumber=False, - random_state=None): + compression='COO', + compress_per_hop=False, + random_state=None, + return_dict=False,): """ Does neighborhood sampling, which samples nodes from a graph based on the current node's neighbors, with a corresponding fanout value at each hop. @@ -153,11 +159,27 @@ def uniform_neighbor_sample(ResourceHandle resource_handle, If True, will renumber the sources and destinations on a per-batch basis and return the renumber map and batch offsets in additional to the standard returns. + + compression: str (Optional) + Options: COO (default), CSR, CSC, DCSR, DCSR + Sets the compression format for the returned samples. + + compress_per_hop: bool (Optional) + If False (default), will create a compressed edgelist for the + entire batch. + If True, will create a separate compressed edgelist per hop within + a batch. random_state: int (Optional) Random state to use when generating samples. Optional argument, defaults to a hash of process id, time, and hostname. (See pylibcugraph.random.CuGraphRandomState) + + return_dict: bool (Optional) + Whether to return a dictionary instead of a tuple. + Optional argument, defaults to False, returning a tuple. + This argument will eventually be deprecated in favor + of always returning a dictionary. Returns ------- @@ -173,13 +195,16 @@ def uniform_neighbor_sample(ResourceHandle resource_handle, the renumber map for each batch starts). """ - cdef cugraph_resource_handle_t* c_resource_handle_ptr = \ + cdef cugraph_resource_handle_t* c_resource_handle_ptr = ( resource_handle.c_resource_handle_ptr + ) + cdef cugraph_graph_t* c_graph_ptr = input_graph.c_graph_ptr cdef bool_t c_deduplicate_sources = deduplicate_sources cdef bool_t c_return_hops = return_hops cdef bool_t c_renumber = renumber + cdef bool_t c_compress_per_hop = compress_per_hop assert_CAI_type(start_list, "start_list") assert_CAI_type(batch_id_list, "batch_id_list", True) @@ -269,6 +294,23 @@ def uniform_neighbor_sample(ResourceHandle resource_handle, f'Invalid option {prior_sources_behavior}' ' for prior sources behavior' ) + + cdef cugraph_compression_type_t compression_behavior_e + if compression is None or compression == 'COO': + compression_behavior_e = cugraph_compression_type_t.COO + elif compression == 'CSR': + compression_behavior_e = cugraph_compression_type_t.CSR + elif compression == 'CSC': + compression_behavior_e = cugraph_compression_type_t.CSC + elif compression == 'DCSR': + compression_behavior_e = cugraph_compression_type_t.DCSR + elif compression == 'DCSC': + compression_behavior_e = cugraph_compression_type_t.DCSC + else: + raise ValueError( + f'Invalid option {compression}' + ' for compression type' + ) cdef cugraph_sampling_options_t* sampling_options error_code = cugraph_sampling_options_create(&sampling_options, &error_ptr) @@ -279,6 +321,8 @@ def uniform_neighbor_sample(ResourceHandle resource_handle, cugraph_sampling_set_dedupe_sources(sampling_options, c_deduplicate_sources) cugraph_sampling_set_prior_sources_behavior(sampling_options, prior_sources_behavior_e) cugraph_sampling_set_renumber_results(sampling_options, c_renumber) + cugraph_sampling_set_compression_type(sampling_options, compression_behavior_e) + cugraph_sampling_set_compress_per_hop(sampling_options, c_compress_per_hop) error_code = cugraph_uniform_neighbor_sample( c_resource_handle_ptr, @@ -311,26 +355,74 @@ def uniform_neighbor_sample(ResourceHandle resource_handle, # Get cupy "views" of the individual arrays to return. These each increment # the refcount on the SamplingResult instance which will keep the data alive # until all references are removed and the GC runs. + # TODO Return everything that isn't null in release 23.12 if with_edge_properties: - cupy_sources = result.get_sources() - cupy_destinations = result.get_destinations() + cupy_majors = result.get_majors() + cupy_major_offsets = result.get_major_offsets() + cupy_minors = result.get_minors() cupy_edge_weights = result.get_edge_weights() cupy_edge_ids = result.get_edge_ids() cupy_edge_types = result.get_edge_types() cupy_batch_ids = result.get_batch_ids() - cupy_offsets = result.get_offsets() - cupy_hop_ids = result.get_hop_ids() + cupy_label_hop_offsets = result.get_label_hop_offsets() if renumber: cupy_renumber_map = result.get_renumber_map() cupy_renumber_map_offsets = result.get_renumber_map_offsets() - return (cupy_sources, cupy_destinations, cupy_edge_weights, cupy_edge_ids, cupy_edge_types, cupy_batch_ids, cupy_offsets, cupy_hop_ids, cupy_renumber_map, cupy_renumber_map_offsets) + # TODO drop the placeholder for hop ids in release 23.12 + if return_dict: + return { + 'major_offsets': cupy_major_offsets, + 'majors': cupy_majors, + 'minors': cupy_minors, + 'weight': cupy_edge_weights, + 'edge_id': cupy_edge_ids, + 'edge_type': cupy_edge_types, + 'batch_id': cupy_batch_ids, + 'label_hop_offsets': cupy_label_hop_offsets, + 'hop_id': None, + 'renumber_map': cupy_renumber_map, + 'renumber_map_offsets': cupy_renumber_map_offsets + } + else: + cupy_majors = cupy_major_offsets if cupy_majors is None else cupy_majors + return (cupy_majors, cupy_minors, cupy_edge_weights, cupy_edge_ids, cupy_edge_types, cupy_batch_ids, cupy_label_hop_offsets, None, cupy_renumber_map, cupy_renumber_map_offsets) else: - return (cupy_sources, cupy_destinations, cupy_edge_weights, cupy_edge_ids, cupy_edge_types, cupy_batch_ids, cupy_offsets, cupy_hop_ids) + cupy_hop_ids = result.get_hop_ids() # FIXME remove this + if return_dict: + return { + 'major_offsets': cupy_major_offsets, + 'majors': cupy_majors, + 'minors': cupy_minors, + 'weight': cupy_edge_weights, + 'edge_id': cupy_edge_ids, + 'edge_type': cupy_edge_types, + 'batch_id': cupy_batch_ids, + 'label_hop_offsets': cupy_label_hop_offsets, + 'hop_id': cupy_hop_ids, + } + else: + cupy_majors = cupy_major_offsets if cupy_majors is None else cupy_majors + return (cupy_majors, cupy_minors, cupy_edge_weights, cupy_edge_ids, cupy_edge_types, cupy_batch_ids, cupy_label_hop_offsets, cupy_hop_ids) else: + # TODO this is deprecated, remove it in release 23.12 + warnings.warn( + "Calling uniform_neighbor_sample with the 'with_edge_properties' argument is deprecated." + " Starting in release 23.12, this argument will be removed in favor of behaving like the " + "with_edge_properties=True option, returning whatever properties are in the graph.", + FutureWarning, + ) + cupy_sources = result.get_sources() cupy_destinations = result.get_destinations() cupy_indices = result.get_indices() - return (cupy_sources, cupy_destinations, cupy_indices) + if return_dict: + return { + 'sources': cupy_sources, + 'destinations': cupy_destinations, + 'indices': cupy_indices + } + else: + return (cupy_sources, cupy_destinations, cupy_indices) From f0d633322e66be5e1521b2c91d94a6c96ed699bc Mon Sep 17 00:00:00 2001 From: Erik Welch Date: Thu, 28 Sep 2023 13:09:17 -0500 Subject: [PATCH 021/111] Add entry point to tell NetworkX about nx-cugraph without importing it. (#3848) This allows NetworkX docstrings to be updated (among other things). This will have a companion PR in NetworkX. We still need to determine (and agree) on the dict returned by this entry point, and NetworkX doesn't need to use everything I have here. We should probably add a string for `"description"` that gives a very short description of the backend, and maybe `"url"` or `"homepage"` or whatever so online docs can have links. Here's how to use the entry point (Python >= 3.10) after installing it: ```python In [1]: from importlib.metadata import entry_points In [2]: items = entry_points(group="networkx.plugin_info") In [3]: [plugin] = items In [4]: plugin.load()() Out[4]: {'backend_name': 'cugraph', 'project': 'nx-cugraph', 'package': 'nx_cugraph', 'functions': {'betweenness_centrality', 'edge_betweenness_centrality', 'louvain_communities'}, 'extra_docstrings': {'betweenness_centrality': '`weight` parameter is not yet supported.', 'edge_betweenness_centrality': '`weight` parameter is not yet supported.', 'louvain_communities': '`threshold` and `seed` parameters are currently ignored.'}, 'extra_parameters': {'louvain_communities': {'max_level': 'Upper limit of the number of macro-iterations.'}}} ``` CC @rlratzel @betochimas Authors: - Erik Welch (https://github.com/eriknw) Approvers: - Rick Ratzel (https://github.com/rlratzel) - Ray Douglass (https://github.com/raydouglass) URL: https://github.com/rapidsai/cugraph/pull/3848 --- ci/release/update-version.sh | 1 + python/nx-cugraph/.flake8 | 1 + python/nx-cugraph/Makefile | 10 +++ python/nx-cugraph/_nx_cugraph/__init__.py | 88 ++++++++++++++++++ python/nx-cugraph/_nx_cugraph/core.py | 90 +++++++++++++++++++ python/nx-cugraph/lint.yaml | 19 ++-- python/nx-cugraph/nx_cugraph/__init__.py | 16 +++- .../algorithms/centrality/betweenness.py | 6 +- .../algorithms/community/louvain.py | 17 ++-- python/nx-cugraph/nx_cugraph/interface.py | 5 +- .../nx_cugraph/tests/test_match_api.py | 5 +- .../nx-cugraph/nx_cugraph/utils/decorators.py | 41 +++++++-- python/nx-cugraph/nx_cugraph/utils/misc.py | 6 +- python/nx-cugraph/pyproject.toml | 10 +++ 14 files changed, 278 insertions(+), 37 deletions(-) create mode 100644 python/nx-cugraph/_nx_cugraph/__init__.py create mode 100644 python/nx-cugraph/_nx_cugraph/core.py diff --git a/ci/release/update-version.sh b/ci/release/update-version.sh index f3892fbd3c4..adf3273e311 100755 --- a/ci/release/update-version.sh +++ b/ci/release/update-version.sh @@ -62,6 +62,7 @@ sed_runner "s/__version__ = .*/__version__ = \"${NEXT_FULL_TAG}\"/g" python/cugr sed_runner "s/__version__ = .*/__version__ = \"${NEXT_FULL_TAG}\"/g" python/cugraph-service/server/cugraph_service_server/__init__.py sed_runner "s/__version__ = .*/__version__ = \"${NEXT_FULL_TAG}\"/g" python/pylibcugraph/pylibcugraph/__init__.py sed_runner "s/__version__ = .*/__version__ = \"${NEXT_FULL_TAG}\"/g" python/nx-cugraph/nx_cugraph/__init__.py +sed_runner "s/__version__ = .*/__version__ = \"${NEXT_FULL_TAG}\"/g" python/nx-cugraph/_nx_cugraph/__init__.py # Python pyproject.toml updates sed_runner "s/^version = .*/version = \"${NEXT_FULL_TAG}\"/g" python/cugraph/pyproject.toml diff --git a/python/nx-cugraph/.flake8 b/python/nx-cugraph/.flake8 index 3a2e3fb8617..c5874e54f7e 100644 --- a/python/nx-cugraph/.flake8 +++ b/python/nx-cugraph/.flake8 @@ -11,3 +11,4 @@ extend-ignore = per-file-ignores = nx_cugraph/tests/*.py:T201, __init__.py:F401,F403, + _nx_cugraph/__init__.py:E501, diff --git a/python/nx-cugraph/Makefile b/python/nx-cugraph/Makefile index c9caf147d53..6e1b98ee6e9 100644 --- a/python/nx-cugraph/Makefile +++ b/python/nx-cugraph/Makefile @@ -1,7 +1,17 @@ # Copyright (c) 2023, NVIDIA CORPORATION. SHELL= /bin/bash +.PHONY: all +all: plugin-info lint + +.PHONY: lint lint: git ls-files | xargs pre-commit run --config lint.yaml --files + +.PHONY: lint-update lint-update: pre-commit autoupdate --config lint.yaml + +.PHONY: plugin-info +plugin-info: + python _nx_cugraph/__init__.py diff --git a/python/nx-cugraph/_nx_cugraph/__init__.py b/python/nx-cugraph/_nx_cugraph/__init__.py new file mode 100644 index 00000000000..9b3332106ec --- /dev/null +++ b/python/nx-cugraph/_nx_cugraph/__init__.py @@ -0,0 +1,88 @@ +# Copyright (c) 2023, NVIDIA CORPORATION. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Tell NetworkX about the cugraph backend. This file can update itself: + +$ make plugin-info # Recommended method for development + +or + +$ python _nx_cugraph/__init__.py +""" + +# Entries between BEGIN and END are automatically generated +_info = { + "backend_name": "cugraph", + "project": "nx-cugraph", + "package": "nx_cugraph", + "url": "https://github.com/rapidsai/cugraph/tree/branch-23.10/python/nx-cugraph", + "short_summary": "GPU-accelerated backend.", + # "description": "TODO", + "functions": { + # BEGIN: functions + "betweenness_centrality", + "edge_betweenness_centrality", + "louvain_communities", + # END: functions + }, + "extra_docstrings": { + # BEGIN: extra_docstrings + "betweenness_centrality": "`weight` parameter is not yet supported.", + "edge_betweenness_centrality": "`weight` parameter is not yet supported.", + "louvain_communities": "`threshold` and `seed` parameters are currently ignored.", + # END: extra_docstrings + }, + "extra_parameters": { + # BEGIN: extra_parameters + "louvain_communities": { + "max_level : int, optional": "Upper limit of the number of macro-iterations.", + }, + # END: extra_parameters + }, +} + + +def get_info(): + """Target of ``networkx.plugin_info`` entry point. + + This tells NetworkX about the cugraph backend without importing nx_cugraph. + """ + # Convert to e.g. `{"functions": {"myfunc": {"extra_docstring": ...}}}` + d = _info.copy() + info_keys = { + "extra_docstrings": "extra_docstring", + "extra_parameters": "extra_parameters", + } + d["functions"] = { + func: { + new_key: vals[func] + for old_key, new_key in info_keys.items() + if func in (vals := d[old_key]) + } + for func in d["functions"] + } + for key in info_keys: + del d[key] + return d + + +__version__ = "23.10.00" + +if __name__ == "__main__": + from pathlib import Path + + from _nx_cugraph.core import main + + filepath = Path(__file__) + text = main(filepath) + with filepath.open("w") as f: + f.write(text) diff --git a/python/nx-cugraph/_nx_cugraph/core.py b/python/nx-cugraph/_nx_cugraph/core.py new file mode 100644 index 00000000000..72f9203897e --- /dev/null +++ b/python/nx-cugraph/_nx_cugraph/core.py @@ -0,0 +1,90 @@ +# Copyright (c) 2023, NVIDIA CORPORATION. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Utilities to help keep _nx_cugraph up to date.""" + + +def get_functions(): + from nx_cugraph.interface import BackendInterface + from nx_cugraph.utils import networkx_algorithm + + return { + key: val + for key, val in vars(BackendInterface).items() + if isinstance(val, networkx_algorithm) + } + + +def get_extra_docstrings(functions=None): + if functions is None: + functions = get_functions() + return {key: val.extra_doc for key, val in functions.items() if val.extra_doc} + + +def get_extra_parameters(functions=None): + if functions is None: + functions = get_functions() + return {key: val.extra_params for key, val in functions.items() if val.extra_params} + + +def update_text(text, lines_to_add, target, indent=" " * 8): + begin = f"# BEGIN: {target}\n" + end = f"# END: {target}\n" + start = text.index(begin) + stop = text.index(end) + to_add = "\n".join([f"{indent}{line}" for line in lines_to_add]) + return f"{text[:start]}{begin}{to_add}\n{indent}{text[stop:]}" + + +def dict_to_lines(d, *, indent=""): + for key in sorted(d): + val = d[key] + if "\n" not in val: + yield f"{indent}{key!r}: {val!r}," + else: + yield f"{indent}{key!r}: (" + *lines, last_line = val.split("\n") + for line in lines: + line += "\n" + yield f" {indent}{line!r}" + yield f" {indent}{last_line!r}" + yield f"{indent})," + + +def main(filepath): + from pathlib import Path + + filepath = Path(filepath) + with filepath.open() as f: + orig_text = f.read() + text = orig_text + + # Update functions + functions = get_functions() + to_add = [f'"{name}",' for name in sorted(functions)] + text = update_text(text, to_add, "functions") + + # Update extra_docstrings + extra_docstrings = get_extra_docstrings(functions) + to_add = list(dict_to_lines(extra_docstrings)) + text = update_text(text, to_add, "extra_docstrings") + + # Update extra_parameters + extra_parameters = get_extra_parameters(functions) + to_add = [] + for name in sorted(extra_parameters): + params = extra_parameters[name] + to_add.append(f"{name!r}: {{") + to_add.extend(dict_to_lines(params, indent=" " * 4)) + to_add.append("},") + text = update_text(text, to_add, "extra_parameters") + return text diff --git a/python/nx-cugraph/lint.yaml b/python/nx-cugraph/lint.yaml index dba061bd6b5..6a462a6af79 100644 --- a/python/nx-cugraph/lint.yaml +++ b/python/nx-cugraph/lint.yaml @@ -31,7 +31,7 @@ repos: - id: validate-pyproject name: Validate pyproject.toml - repo: https://github.com/PyCQA/autoflake - rev: v2.2.0 + rev: v2.2.1 hooks: - id: autoflake args: [--in-place] @@ -40,17 +40,17 @@ repos: hooks: - id: isort - repo: https://github.com/asottile/pyupgrade - rev: v3.10.1 + rev: v3.13.0 hooks: - id: pyupgrade args: [--py39-plus] - repo: https://github.com/psf/black - rev: 23.7.0 + rev: 23.9.1 hooks: - id: black # - id: black-jupyter - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.0.286 + rev: v0.0.291 hooks: - id: ruff args: [--fix-only, --show-fixes] @@ -58,11 +58,12 @@ repos: rev: 6.1.0 hooks: - id: flake8 + args: ['--per-file-ignores=_nx_cugraph/__init__.py:E501'] # Why is this necessary? additional_dependencies: &flake8_dependencies - # These versions need updated manually - - flake8==6.1.0 - - flake8-bugbear==23.7.10 - - flake8-simplify==0.20.0 + # These versions need updated manually + - flake8==6.1.0 + - flake8-bugbear==23.9.16 + - flake8-simplify==0.20.0 - repo: https://github.com/asottile/yesqa rev: v1.5.0 hooks: @@ -76,7 +77,7 @@ repos: additional_dependencies: [tomli] files: ^(nx_cugraph|docs)/ - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.0.286 + rev: v0.0.291 hooks: - id: ruff - repo: https://github.com/pre-commit/pre-commit-hooks diff --git a/python/nx-cugraph/nx_cugraph/__init__.py b/python/nx-cugraph/nx_cugraph/__init__.py index 28066fe2b02..4a0e95a109f 100644 --- a/python/nx-cugraph/nx_cugraph/__init__.py +++ b/python/nx-cugraph/nx_cugraph/__init__.py @@ -12,9 +12,21 @@ # limitations under the License. from networkx.exception import * -from . import algorithms, classes, convert, utils -from .algorithms import * +from . import utils + +from . import classes from .classes import * + +from . import convert from .convert import * +# from . import convert_matrix +# from .convert_matrix import * + +# from . import generators +# from .generators import * + +from . import algorithms +from .algorithms import * + __version__ = "23.10.00" diff --git a/python/nx-cugraph/nx_cugraph/algorithms/centrality/betweenness.py b/python/nx-cugraph/nx_cugraph/algorithms/centrality/betweenness.py index b777919f86f..104ac87414c 100644 --- a/python/nx-cugraph/nx_cugraph/algorithms/centrality/betweenness.py +++ b/python/nx-cugraph/nx_cugraph/algorithms/centrality/betweenness.py @@ -13,7 +13,7 @@ import pylibcugraph as plc from nx_cugraph.convert import _to_graph -from nx_cugraph.utils import _handle_seed, networkx_algorithm +from nx_cugraph.utils import _seed_to_int, networkx_algorithm __all__ = ["betweenness_centrality", "edge_betweenness_centrality"] @@ -22,11 +22,12 @@ def betweenness_centrality( G, k=None, normalized=True, weight=None, endpoints=False, seed=None ): + """`weight` parameter is not yet supported.""" if weight is not None: raise NotImplementedError( "Weighted implementation of betweenness centrality not currently supported" ) - seed = _handle_seed(seed) + seed = _seed_to_int(seed) G = _to_graph(G, weight) node_ids, values = plc.betweenness_centrality( resource_handle=plc.ResourceHandle(), @@ -47,6 +48,7 @@ def _(G, k=None, normalized=True, weight=None, endpoints=False, seed=None): @networkx_algorithm def edge_betweenness_centrality(G, k=None, normalized=True, weight=None, seed=None): + """`weight` parameter is not yet supported.""" if weight is not None: raise NotImplementedError( "Weighted implementation of betweenness centrality not currently supported" diff --git a/python/nx-cugraph/nx_cugraph/algorithms/community/louvain.py b/python/nx-cugraph/nx_cugraph/algorithms/community/louvain.py index ca5f05c2014..a183b59fe1d 100644 --- a/python/nx-cugraph/nx_cugraph/algorithms/community/louvain.py +++ b/python/nx-cugraph/nx_cugraph/algorithms/community/louvain.py @@ -17,7 +17,7 @@ from nx_cugraph.convert import _to_undirected_graph from nx_cugraph.utils import ( _groupby, - _handle_seed, + _seed_to_int, networkx_algorithm, not_implemented_for, ) @@ -26,16 +26,17 @@ @not_implemented_for("directed") -@networkx_algorithm(extra_params="max_level") +@networkx_algorithm( + extra_params={ + "max_level : int, optional": "Upper limit of the number of macro-iterations." + } +) def louvain_communities( G, weight="weight", resolution=1, threshold=0.0000001, seed=None, *, max_level=None ): - """`threshold` and `seed` parameters are currently ignored. - - Extra parameter: `max_level` controls the maximum number of levels of the algorithm. - """ + """`threshold` and `seed` parameters are currently ignored.""" # NetworkX allows both directed and undirected, but cugraph only allows undirected. - seed = _handle_seed(seed) # Unused, but ensure it's valid for future compatibility + seed = _seed_to_int(seed) # Unused, but ensure it's valid for future compatibility G = _to_undirected_graph(G, weight) if G.row_indices.size == 0: # TODO: PLC doesn't handle empty graphs gracefully! @@ -46,8 +47,8 @@ def louvain_communities( resource_handle=plc.ResourceHandle(), graph=G._get_plc_graph(), max_level=max_level, # TODO: add this parameter to NetworkX + threshold=threshold, resolution=resolution, - # threshold=threshold, # TODO: add this parameter to PLC do_expensive_check=False, ) groups = _groupby(clusters, vertices) diff --git a/python/nx-cugraph/nx_cugraph/interface.py b/python/nx-cugraph/nx_cugraph/interface.py index cc750cd2d5b..2ad23acd940 100644 --- a/python/nx-cugraph/nx_cugraph/interface.py +++ b/python/nx-cugraph/nx_cugraph/interface.py @@ -62,9 +62,7 @@ def key(testpath): # Reasons for xfailing no_weights = "weighted implementation not currently supported" no_multigraph = "multigraphs not currently supported" - louvain_different = ( - "Louvain may be different due to RNG or unsupported threshold parameter" - ) + louvain_different = "Louvain may be different due to RNG" xfail = {} @@ -176,7 +174,6 @@ def key(testpath): ): louvain_different, key("test_louvain.py:test_none_weight_param"): louvain_different, key("test_louvain.py:test_multigraph"): louvain_different, - key("test_louvain.py:test_threshold"): louvain_different, } ) diff --git a/python/nx-cugraph/nx_cugraph/tests/test_match_api.py b/python/nx-cugraph/nx_cugraph/tests/test_match_api.py index 64d3704dd65..ecfda1397db 100644 --- a/python/nx-cugraph/nx_cugraph/tests/test_match_api.py +++ b/python/nx-cugraph/nx_cugraph/tests/test_match_api.py @@ -45,11 +45,14 @@ def test_match_signature_and_names(): assert orig_sig == func_sig else: # Ignore extra parameters added to nx-cugraph algorithm + # The key of func.extra_params may be like "max_level : int, optional", + # but we only want "max_level" here. + extra_params = {name.split(" ")[0] for name in func.extra_params} assert orig_sig == func_sig.replace( parameters=[ p for name, p in func_sig.parameters.items() - if name not in func.extra_params + if name not in extra_params ] ) if func.can_run is not nxcg.utils.decorators._default_can_run: diff --git a/python/nx-cugraph/nx_cugraph/utils/decorators.py b/python/nx-cugraph/nx_cugraph/utils/decorators.py index 3dbdb07e87f..0f15d236ecd 100644 --- a/python/nx-cugraph/nx_cugraph/utils/decorators.py +++ b/python/nx-cugraph/nx_cugraph/utils/decorators.py @@ -10,13 +10,21 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + from functools import partial, update_wrapper -from networkx.utils.decorators import not_implemented_for +from networkx.utils.decorators import nodes_or_number, not_implemented_for from nx_cugraph.interface import BackendInterface -__all__ = ["not_implemented_for", "networkx_algorithm"] +try: + from networkx.utils.backends import _registered_algorithms +except ModuleNotFoundError: + from networkx.classes.backends import _registered_algorithms + + +__all__ = ["not_implemented_for", "nodes_or_number", "networkx_algorithm"] def networkx_class(api): @@ -28,7 +36,17 @@ def inner(func): class networkx_algorithm: - def __new__(cls, func=None, *, name=None, extra_params=None): + name: str + extra_doc: str | None + extra_params: dict[str, str] | None + + def __new__( + cls, + func=None, + *, + name: str | None = None, + extra_params: dict[str, str] | str | None = None, + ): if func is None: return partial(networkx_algorithm, name=name, extra_params=extra_params) instance = object.__new__(cls) @@ -37,13 +55,20 @@ def __new__(cls, func=None, *, name=None, extra_params=None): instance.__defaults__ = func.__defaults__ instance.__kwdefaults__ = func.__kwdefaults__ instance.name = func.__name__ if name is None else name - # TODO: should extra_params be a dict[str, str] that describes the parameters? if extra_params is None: - instance.extra_params = None + pass elif isinstance(extra_params, str): - instance.extra_params = {extra_params} - else: - instance.extra_params = set(extra_params) + extra_params = {extra_params: ""} + elif not isinstance(extra_params, dict): + raise TypeError( + f"extra_params must be dict, str, or None; got {type(extra_params)}" + ) + instance.extra_params = extra_params + # The docstring on our function is added to the NetworkX docstring. + instance.extra_doc = func.__doc__ + # Copy __doc__ from NetworkX + if instance.name in _registered_algorithms: + instance.__doc__ = _registered_algorithms[instance.name].__doc__ instance.can_run = _default_can_run setattr(BackendInterface, instance.name, instance) # Set methods so they are in __dict__ diff --git a/python/nx-cugraph/nx_cugraph/utils/misc.py b/python/nx-cugraph/nx_cugraph/utils/misc.py index 64c0be066f2..72e4094b8b7 100644 --- a/python/nx-cugraph/nx_cugraph/utils/misc.py +++ b/python/nx-cugraph/nx_cugraph/utils/misc.py @@ -18,7 +18,7 @@ import cupy as cp -__all__ = ["_groupby", "_handle_seed"] +__all__ = ["_groupby", "_seed_to_int"] def _groupby(groups: cp.ndarray, values: cp.ndarray) -> dict[int, cp.ndarray]: @@ -51,8 +51,8 @@ def _groupby(groups: cp.ndarray, values: cp.ndarray) -> dict[int, cp.ndarray]: return rv -def _handle_seed(seed: int | Random | None) -> int: - """Handle seed argument and ensure it is what pylibcugraph needs: an int.""" +def _seed_to_int(seed: int | Random | None) -> int: + """Handle any valid seed argument and convert it to an int if necessary.""" if seed is None: return if isinstance(seed, Random): diff --git a/python/nx-cugraph/pyproject.toml b/python/nx-cugraph/pyproject.toml index 95e9c256e5d..db3b3a22545 100644 --- a/python/nx-cugraph/pyproject.toml +++ b/python/nx-cugraph/pyproject.toml @@ -54,6 +54,9 @@ Documentation = "https://docs.rapids.ai/api/cugraph/stable/" [project.entry-points."networkx.plugins"] cugraph = "nx_cugraph.interface:BackendInterface" +[project.entry-points."networkx.plugin_info"] +cugraph = "_nx_cugraph:get_info" + [tool.setuptools] license-files = ["LICENSE"] @@ -61,6 +64,8 @@ license-files = ["LICENSE"] include = [ "nx_cugraph*", "nx_cugraph.*", + "_nx_cugraph*", + "_nx_cugraph.*", ] [tool.black] @@ -75,6 +80,7 @@ float_to_top = true default_section = "THIRDPARTY" known_first_party = "nx_cugraph" line_length = 88 +extend_skip_glob = ["nx_cugraph/__init__.py"] [tool.pytest.ini_options] minversion = "6.0" @@ -128,6 +134,9 @@ exclude_lines = [ # https://github.com/charliermarsh/ruff/ line-length = 88 target-version = "py39" +unfixable = [ + "F841", # unused-variable (Note: can leave useless expression) +] select = [ "ALL", ] @@ -203,6 +212,7 @@ ignore = [ "__init__.py" = ["F401"] # Allow unused imports (w/o defining `__all__`) # Allow assert, print, RNG, and no docstring "nx_cugraph/**/tests/*py" = ["S101", "S311", "T201", "D103", "D100"] +"_nx_cugraph/__init__.py" = ["E501"] [tool.ruff.flake8-annotations] mypy-init-return = true From f57119bf8d322a2eba902a5498ae194832d8d732 Mon Sep 17 00:00:00 2001 From: Joseph Nke <76006812+jnke2016@users.noreply.github.com> Date: Thu, 28 Sep 2023 13:55:59 -0500 Subject: [PATCH 022/111] Temporarily disable the deletion of the dask dataframe (#3814) temporarily disable the deletion of the dask dataframe Authors: - Joseph Nke (https://github.com/jnke2016) - Naim (https://github.com/naimnv) Approvers: - Brad Rees (https://github.com/BradReesWork) - Rick Ratzel (https://github.com/rlratzel) URL: https://github.com/rapidsai/cugraph/pull/3814 --- .../graph_implementation/simpleDistributedGraph.py | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/python/cugraph/cugraph/structure/graph_implementation/simpleDistributedGraph.py b/python/cugraph/cugraph/structure/graph_implementation/simpleDistributedGraph.py index 01885c2d1c3..fa94fa67625 100644 --- a/python/cugraph/cugraph/structure/graph_implementation/simpleDistributedGraph.py +++ b/python/cugraph/cugraph/structure/graph_implementation/simpleDistributedGraph.py @@ -14,7 +14,6 @@ import gc from typing import Union import warnings -import random import cudf import cupy as cp @@ -182,10 +181,7 @@ def __from_edgelist( workers = _client.scheduler_info()["workers"] # Repartition to 2 partitions per GPU for memory efficient process input_ddf = input_ddf.repartition(npartitions=len(workers) * 2) - # FIXME: Make a copy of the input ddf before implicitly altering it. - input_ddf = input_ddf.map_partitions( - lambda df: df.copy(), token="custom-" + str(random.random()) - ) + input_ddf = input_ddf.map_partitions(lambda df: df.copy()) # The dataframe will be symmetrized iff the graph is undirected # otherwise, the inital dataframe will be returned if edge_attr is not None: @@ -337,7 +333,7 @@ def __from_edgelist( ) for w, edata in ddf.items() } - del ddf + # FIXME: For now, don't delete the copied dataframe to avoid crash self._plc_graph = { w: _client.compute(delayed_task, workers=w, allow_other_workers=False) for w, delayed_task in delayed_tasks_d.items() @@ -1196,7 +1192,5 @@ def _get_column_from_ls_dfs(lst_df, col_name): if len_df == 0: return lst_df[0][col_name] output_col = cudf.concat([df[col_name] for df in lst_df], ignore_index=True) - for df in lst_df: - df.drop(columns=[col_name], inplace=True) - gc.collect() + # FIXME: For now, don't delete the copied dataframe to avoid cras return output_col From 91fbcca659ea1b29e4658913ca4d7f8381584df7 Mon Sep 17 00:00:00 2001 From: Rick Ratzel <3039903+rlratzel@users.noreply.github.com> Date: Thu, 28 Sep 2023 14:03:26 -0500 Subject: [PATCH 023/111] Updates the source build docs to include libcugraphops as a build prerequisite (#3893) closes #3722 Updates the source build docs to include `libcugraphops` as a build prerequisite. Authors: - Rick Ratzel (https://github.com/rlratzel) Approvers: - Brad Rees (https://github.com/BradReesWork) URL: https://github.com/rapidsai/cugraph/pull/3893 --- docs/cugraph/source/installation/source_build.md | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/docs/cugraph/source/installation/source_build.md b/docs/cugraph/source/installation/source_build.md index 7782591f1ce..f5ee0741da6 100644 --- a/docs/cugraph/source/installation/source_build.md +++ b/docs/cugraph/source/installation/source_build.md @@ -6,10 +6,10 @@ The cuGraph package include both a C/C++ CUDA portion and a python portion. Bot ## Prerequisites -__Compiler__: -* `gcc` version 9.3+ -* `nvcc` version 11.0+ -* `cmake` version 3.20.1+ +__Compiler:__ +* `gcc` version 9.3+ +* `nvcc` version 11.0+ +* `cmake` version 3.20.1+ __CUDA:__ * CUDA 11.0+ @@ -18,6 +18,11 @@ __CUDA:__ You can obtain CUDA from [https://developer.nvidia.com/cuda-downloads](https://developer.nvidia.com/cuda-downloads). +__Packages:__ +* `cmake` version 3.20.1+ +* `libcugraphops` (version matching source branch version, eg. `23.10`) + +You can obtain `libcugraphops` using `conda`/`mamba` from the `nvidia` channel, or using `pip` with the `--extra-index-url=https://pypi.nvidia.com` option. See the [RAPIDS docs](https://docs.rapids.ai/install#environment) for more details. ## Building cuGraph To install cuGraph from source, ensure the dependencies are met. From b24121fc0f76f29cfab878875ff9a953b49cc6cd Mon Sep 17 00:00:00 2001 From: GALI PREM SAGAR Date: Thu, 28 Sep 2023 15:53:49 -0500 Subject: [PATCH 024/111] Pin `dask` and `distributed` for `23.10` release (#3896) This PR pins `dask` and `distributed` to `2023.9.2` for `23.10` release. xref: https://github.com/rapidsai/cudf/pull/14225 Authors: - GALI PREM SAGAR (https://github.com/galipremsagar) Approvers: - Ray Douglass (https://github.com/raydouglass) - Peter Andreas Entschev (https://github.com/pentschev) - Brad Rees (https://github.com/BradReesWork) URL: https://github.com/rapidsai/cugraph/pull/3896 --- ci/test_wheel_cugraph.sh | 2 +- conda/environments/all_cuda-118_arch-x86_64.yaml | 6 +++--- conda/environments/all_cuda-120_arch-x86_64.yaml | 6 +++--- conda/recipes/cugraph-pyg/meta.yaml | 2 +- conda/recipes/cugraph-service/meta.yaml | 2 +- conda/recipes/cugraph/meta.yaml | 6 +++--- dependencies.yaml | 6 +++--- python/cugraph-service/server/pyproject.toml | 4 ++-- python/cugraph/pyproject.toml | 4 ++-- 9 files changed, 19 insertions(+), 19 deletions(-) diff --git a/ci/test_wheel_cugraph.sh b/ci/test_wheel_cugraph.sh index f9e2aa6d8da..ac18459128a 100755 --- a/ci/test_wheel_cugraph.sh +++ b/ci/test_wheel_cugraph.sh @@ -9,6 +9,6 @@ RAPIDS_PY_WHEEL_NAME="pylibcugraph_${RAPIDS_PY_CUDA_SUFFIX}" rapids-download-whe python -m pip install --no-deps ./local-pylibcugraph-dep/pylibcugraph*.whl # Always install latest dask for testing -python -m pip install git+https://github.com/dask/dask.git@main git+https://github.com/dask/distributed.git@main +python -m pip install git+https://github.com/dask/dask.git@2023.9.2 git+https://github.com/dask/distributed.git@2023.9.2 ./ci/test_wheel.sh cugraph python/cugraph diff --git a/conda/environments/all_cuda-118_arch-x86_64.yaml b/conda/environments/all_cuda-118_arch-x86_64.yaml index 86de24c991d..952ec9317e2 100644 --- a/conda/environments/all_cuda-118_arch-x86_64.yaml +++ b/conda/environments/all_cuda-118_arch-x86_64.yaml @@ -19,11 +19,11 @@ dependencies: - cupy>=12.0.0 - cxx-compiler - cython>=3.0.0 -- dask-core>=2023.7.1 +- dask-core==2023.9.2 - dask-cuda==23.10.* - dask-cudf==23.10.* -- dask>=2023.7.1 -- distributed>=2023.7.1 +- dask==2023.9.2 +- distributed==2023.9.2 - doxygen - fsspec>=0.6.0 - gcc_linux-64=11.* diff --git a/conda/environments/all_cuda-120_arch-x86_64.yaml b/conda/environments/all_cuda-120_arch-x86_64.yaml index 1054f75ba54..38936c78c38 100644 --- a/conda/environments/all_cuda-120_arch-x86_64.yaml +++ b/conda/environments/all_cuda-120_arch-x86_64.yaml @@ -19,11 +19,11 @@ dependencies: - cupy>=12.0.0 - cxx-compiler - cython>=3.0.0 -- dask-core>=2023.7.1 +- dask-core==2023.9.2 - dask-cuda==23.10.* - dask-cudf==23.10.* -- dask>=2023.7.1 -- distributed>=2023.7.1 +- dask==2023.9.2 +- distributed==2023.9.2 - doxygen - fsspec>=0.6.0 - gcc_linux-64=11.* diff --git a/conda/recipes/cugraph-pyg/meta.yaml b/conda/recipes/cugraph-pyg/meta.yaml index 2d7ed2f4cda..1dc5a75c41b 100644 --- a/conda/recipes/cugraph-pyg/meta.yaml +++ b/conda/recipes/cugraph-pyg/meta.yaml @@ -26,7 +26,7 @@ requirements: - python - scikit-build >=0.13.1 run: - - distributed >=2023.7.1 + - distributed ==2023.9.2 - numba >=0.57 - numpy >=1.21 - python diff --git a/conda/recipes/cugraph-service/meta.yaml b/conda/recipes/cugraph-service/meta.yaml index f3229c27364..2daf0438351 100644 --- a/conda/recipes/cugraph-service/meta.yaml +++ b/conda/recipes/cugraph-service/meta.yaml @@ -59,7 +59,7 @@ outputs: - cupy >=12.0.0 - dask-cuda ={{ minor_version }} - dask-cudf ={{ minor_version }} - - distributed >=2023.7.1 + - distributed ==2023.9.2 - numba >=0.57 - numpy >=1.21 - python diff --git a/conda/recipes/cugraph/meta.yaml b/conda/recipes/cugraph/meta.yaml index ad5965ad20c..f9bf54a2ef4 100644 --- a/conda/recipes/cugraph/meta.yaml +++ b/conda/recipes/cugraph/meta.yaml @@ -76,9 +76,9 @@ requirements: - cupy >=12.0.0 - dask-cuda ={{ minor_version }} - dask-cudf ={{ minor_version }} - - dask >=2023.7.1 - - dask-core >=2023.7.1 - - distributed >=2023.7.1 + - dask ==2023.9.2 + - dask-core ==2023.9.2 + - distributed ==2023.9.2 - fsspec>=0.6.0 - libcugraph ={{ version }} - pylibcugraph ={{ version }} diff --git a/dependencies.yaml b/dependencies.yaml index a162ac01354..f74ed13115b 100644 --- a/dependencies.yaml +++ b/dependencies.yaml @@ -373,15 +373,15 @@ dependencies: common: - output_types: [conda, pyproject] packages: - - &dask dask>=2023.7.1 - - &distributed distributed>=2023.7.1 + - &dask dask==2023.9.2 + - &distributed distributed==2023.9.2 - &dask_cuda dask-cuda==23.10.* - &numba numba>=0.57 - &ucx_py ucx-py==0.34.* - output_types: conda packages: - aiohttp - - &dask-core_conda dask-core>=2023.7.1 + - &dask-core_conda dask-core==2023.9.2 - fsspec>=0.6.0 - libcudf==23.10.* - requests diff --git a/python/cugraph-service/server/pyproject.toml b/python/cugraph-service/server/pyproject.toml index f25ea6c46e5..8787cb838be 100644 --- a/python/cugraph-service/server/pyproject.toml +++ b/python/cugraph-service/server/pyproject.toml @@ -25,8 +25,8 @@ dependencies = [ "cupy-cuda11x>=12.0.0", "dask-cuda==23.10.*", "dask-cudf==23.10.*", - "dask>=2023.7.1", - "distributed>=2023.7.1", + "dask==2023.9.2", + "distributed==2023.9.2", "numba>=0.57", "numpy>=1.21", "rmm==23.10.*", diff --git a/python/cugraph/pyproject.toml b/python/cugraph/pyproject.toml index cadf6879e23..1835ac8bb49 100644 --- a/python/cugraph/pyproject.toml +++ b/python/cugraph/pyproject.toml @@ -33,8 +33,8 @@ dependencies = [ "cupy-cuda11x>=12.0.0", "dask-cuda==23.10.*", "dask-cudf==23.10.*", - "dask>=2023.7.1", - "distributed>=2023.7.1", + "dask==2023.9.2", + "distributed==2023.9.2", "fsspec[http]>=0.6.0", "numba>=0.57", "pylibcugraph==23.10.*", From 6e5e0667b19106b61c98107b0cc58ff2ef7d4202 Mon Sep 17 00:00:00 2001 From: Naim <110031745+naimnv@users.noreply.github.com> Date: Fri, 29 Sep 2023 03:44:59 +0200 Subject: [PATCH 025/111] Adds logic to handle isolated vertices at python layer (#3886) - Adds logic to handle isolated vertices - Clamp downs max level to a sane number closes #3804 Authors: - Naim (https://github.com/naimnv) Approvers: - Joseph Nke (https://github.com/jnke2016) - Brad Rees (https://github.com/BradReesWork) - Chuck Hastings (https://github.com/ChuckHastings) - Rick Ratzel (https://github.com/rlratzel) URL: https://github.com/rapidsai/cugraph/pull/3886 --- python/cugraph/cugraph/community/louvain.py | 91 +++++++++++++++---- .../cugraph/tests/community/test_louvain.py | 16 ++++ python/cugraph/cugraph/utilities/utils.py | 4 +- 3 files changed, 91 insertions(+), 20 deletions(-) diff --git a/python/cugraph/cugraph/community/louvain.py b/python/cugraph/cugraph/community/louvain.py index 7f9742c8f09..0bedd427824 100644 --- a/python/cugraph/cugraph/community/louvain.py +++ b/python/cugraph/cugraph/community/louvain.py @@ -11,7 +11,10 @@ # See the License for the specific language governing permissions and # limitations under the License. +from typing import Union, Tuple +from cugraph.structure import Graph from cugraph.utilities import ( + is_nx_graph_type, ensure_cugraph_obj_for_nx, df_score_to_dictionary, ) @@ -21,9 +24,26 @@ from pylibcugraph import louvain as pylibcugraph_louvain from pylibcugraph import ResourceHandle +from cugraph.utilities.utils import import_optional + +# FIXME: the networkx.Graph type used in type annotations is specified +# using a string literal to avoid depending on and importing networkx. +# Instead, networkx is imported optionally, which may cause a problem +# for a type checker if run in an environment where networkx is not installed. +networkx = import_optional("networkx") + +VERTEX_COL_NAME = "vertex" +CLUSTER_ID_COL_NAME = "partition" + # FIXME: max_level should default to 100 once max_iter is removed -def louvain(G, max_level=None, max_iter=None, resolution=1.0, threshold=1e-7): +def louvain( + G: Union[Graph, "networkx.Graph"], + max_level: Union[int, None] = None, + max_iter: Union[int, None] = None, + resolution: float = 1.0, + threshold: float = 1e-7, +) -> Tuple[Union[cudf.DataFrame, dict], float]: """ Compute the modularity optimizing partition of the input graph using the Louvain method @@ -48,6 +68,9 @@ def louvain(G, max_level=None, max_iter=None, resolution=1.0, threshold=1e-7): than the specified number of levels. No error occurs when the algorithm terminates early in this manner. + If max_level > 500, it will be set to 500 and a warning is emitted + in order to prevent excessive runtime. + max_iter : integer, optional (default=None) This parameter is deprecated in favor of max_level. Previously it was used to control the maximum number of levels of the Louvain @@ -68,18 +91,21 @@ def louvain(G, max_level=None, max_iter=None, resolution=1.0, threshold=1e-7): Returns ------- - parts : cudf.DataFrame - GPU data frame of size V containing two columns the vertex id and the - partition id it is assigned to. + result: cudf.DataFrame or dict + If input graph G is of type cugraph.Graph, a GPU dataframe + with two columns. + + result[VERTEX_COL_NAME] : cudf.Series + Contains the vertex identifiers + result[CLUSTER_ID_COL_NAME] : cudf.Series + Contains the partition assigned to the vertices - df['vertex'] : cudf.Series - Contains the vertex identifiers - df['partition'] : cudf.Series - Contains the partition assigned to the vertices + If input graph G is of type networkx.Graph, a dict + Dictionary of vertices and their partition ids. modularity_score : float - a floating point number containing the global modularity score of the - partitioning. + A floating point number containing the global modularity score + of the partitioning. Examples -------- @@ -89,6 +115,17 @@ def louvain(G, max_level=None, max_iter=None, resolution=1.0, threshold=1e-7): """ + # FIXME: Onece the graph construction calls support isolated vertices through + # the C API (the C++ interface already supports this) then there will be + # no need to compute isolated vertices here. + + isolated_vertices = list() + if is_nx_graph_type(type(G)): + isolated_vertices = [v for v in range(G.number_of_nodes()) if G.degree[v] == 0] + else: + # FIXME: Gather isolated vertices of G + pass + G, isNx = ensure_cugraph_obj_for_nx(G) if G.is_directed(): @@ -112,7 +149,12 @@ def louvain(G, max_level=None, max_iter=None, resolution=1.0, threshold=1e-7): if max_level is None: max_level = 100 - vertex, partition, mod_score = pylibcugraph_louvain( + if max_level > 500: + w_msg = "max_level is set too high, clamping it down to 500." + warnings.warn(w_msg) + max_level = 500 + + vertex, partition, modularity_score = pylibcugraph_louvain( resource_handle=ResourceHandle(), graph=G._plc_graph, max_level=max_level, @@ -121,14 +163,27 @@ def louvain(G, max_level=None, max_iter=None, resolution=1.0, threshold=1e-7): do_expensive_check=False, ) - df = cudf.DataFrame() - df["vertex"] = vertex - df["partition"] = partition + result = cudf.DataFrame() + result[VERTEX_COL_NAME] = vertex + result[CLUSTER_ID_COL_NAME] = partition + + if len(isolated_vertices) > 0: + unique_cids = result[CLUSTER_ID_COL_NAME].unique() + max_cluster_id = -1 if len(result) == 0 else unique_cids.max() + + isolated_vtx_and_cids = cudf.DataFrame() + isolated_vtx_and_cids[VERTEX_COL_NAME] = isolated_vertices + isolated_vtx_and_cids[CLUSTER_ID_COL_NAME] = [ + (max_cluster_id + i + 1) for i in range(len(isolated_vertices)) + ] + result = cudf.concat( + [result, isolated_vtx_and_cids], ignore_index=True, sort=False + ) - if G.renumbered: - df = G.unrenumber(df, "vertex") + if G.renumbered and len(G.input_df) > 0: + result = G.unrenumber(result, VERTEX_COL_NAME) if isNx is True: - df = df_score_to_dictionary(df, "partition") + result = df_score_to_dictionary(result, CLUSTER_ID_COL_NAME) - return df, mod_score + return result, modularity_score diff --git a/python/cugraph/cugraph/tests/community/test_louvain.py b/python/cugraph/cugraph/tests/community/test_louvain.py index 183be071a44..5441998fb46 100644 --- a/python/cugraph/cugraph/tests/community/test_louvain.py +++ b/python/cugraph/cugraph/tests/community/test_louvain.py @@ -142,3 +142,19 @@ def test_louvain_csr_graph(is_weighted): assert len(parition_diffs) == 0 assert mod_csr == mod_coo + + +@pytest.mark.sg +def test_louvain_nx_graph_with_isolated_nodes(): + # Cluster IDs are expected to unique if all nodes are isolated + G = nx.Graph() + G.add_nodes_from(range(5)) + result, _ = cugraph.louvain(G) + assert set(result.keys()) == set(G.nodes) + assert len(set(result.values())) == G.number_of_nodes() + + # A graph with 5 nodes, where 3 of the nodes are isolated + G.add_edge(1, 2) + result, _ = cugraph.louvain(G) + assert set(result.keys()) == set(G.nodes) + assert len(set(result.values())) == G.number_of_nodes() - 1 diff --git a/python/cugraph/cugraph/utilities/utils.py b/python/cugraph/cugraph/utilities/utils.py index e68b5dd4880..7a54a0bf2cf 100644 --- a/python/cugraph/cugraph/utilities/utils.py +++ b/python/cugraph/cugraph/utilities/utils.py @@ -364,8 +364,8 @@ def is_matrix_type(m): return is_cp_matrix_type(m) or is_sp_matrix_type(m) -def is_nx_graph_type(g): - return g in __nx_graph_types +def is_nx_graph_type(graph_type): + return graph_type in __nx_graph_types def is_cugraph_graph_type(g): From eed12230fb41da701ab9ea302642765d81024bc8 Mon Sep 17 00:00:00 2001 From: Joseph Nke <76006812+jnke2016@users.noreply.github.com> Date: Fri, 29 Sep 2023 11:35:12 -0500 Subject: [PATCH 026/111] Refactor legacy k truss (#3843) This PR refactor legacy k truss by leveraging the `C API` instead of the `legacy:COO` graph. This also stands as a preliminary work to later provide a new implementation of `k_truss` matching the `C API`. Closes #3753 Closes #3754 Authors: - Joseph Nke (https://github.com/jnke2016) - Chuck Hastings (https://github.com/ChuckHastings) Approvers: - Chuck Hastings (https://github.com/ChuckHastings) - Brad Rees (https://github.com/BradReesWork) - Don Acosta (https://github.com/acostadon) URL: https://github.com/rapidsai/cugraph/pull/3843 --- cpp/CMakeLists.txt | 1 + cpp/include/cugraph/algorithms.hpp | 43 ++-- .../cugraph/utilities/graph_traits.hpp | 22 +- cpp/include/cugraph_c/community_algorithms.h | 21 ++ cpp/src/c_api/legacy_k_truss.cpp | 150 +++++++++++ cpp/src/community/legacy/ktruss.cu | 127 ++++++---- cpp/tests/CMakeLists.txt | 1 + cpp/tests/c_api/legacy_k_truss_test.c | 236 ++++++++++++++++++ python/cugraph/CMakeLists.txt | 1 - .../cugraph/cugraph/community/CMakeLists.txt | 25 -- .../cugraph/community/ktruss_subgraph.pxd | 26 -- .../cugraph/community/ktruss_subgraph.py | 54 +++- .../community/ktruss_subgraph_wrapper.pyx | 43 ---- .../pylibcugraph/pylibcugraph/CMakeLists.txt | 1 + python/pylibcugraph/pylibcugraph/__init__.py | 2 + .../_cugraph_c/community_algorithms.pxd | 10 + .../pylibcugraph/k_truss_subgraph.pyx | 163 ++++++++++++ 17 files changed, 759 insertions(+), 167 deletions(-) create mode 100644 cpp/src/c_api/legacy_k_truss.cpp create mode 100644 cpp/tests/c_api/legacy_k_truss_test.c delete mode 100644 python/cugraph/cugraph/community/CMakeLists.txt delete mode 100644 python/cugraph/cugraph/community/ktruss_subgraph.pxd delete mode 100644 python/cugraph/cugraph/community/ktruss_subgraph_wrapper.pyx create mode 100644 python/pylibcugraph/pylibcugraph/k_truss_subgraph.pyx diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index 0d7bd86075d..d9f87d7dd72 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -435,6 +435,7 @@ add_library(cugraph_c src/c_api/labeling_result.cpp src/c_api/weakly_connected_components.cpp src/c_api/strongly_connected_components.cpp + src/c_api/legacy_k_truss.cpp ) add_library(cugraph::cugraph_c ALIAS cugraph_c) diff --git a/cpp/include/cugraph/algorithms.hpp b/cpp/include/cugraph/algorithms.hpp index b624ec5c0e0..78846bc5766 100644 --- a/cpp/include/cugraph/algorithms.hpp +++ b/cpp/include/cugraph/algorithms.hpp @@ -430,34 +430,39 @@ void connected_components(legacy::GraphCSRView const& graph, VT* labels); /** - * @brief Compute k truss for a graph + * @brief Compute k truss for a graph ** temporary * * K Truss is the maximal subgraph of a graph which contains at least three * vertices where every edge is incident to at least k-2 triangles. * - * Note that current implementation does not support a weighted graph. + * This version is a temporary solution to clean up python integration through the C API. * - * @throws cugraph::logic_error with a custom message when an error - * occurs. + * This version is only supported SG. * - * @tparam VT Type of vertex identifiers. Supported value : int (signed, - * 32-bit) - * @tparam ET Type of edge identifiers. Supported value : int (signed, - * 32-bit) - * @tparam WT Type of edge weights. Supported values : float or double. + * @throws cugraph::logic_error with a custom message when an error + * occurs. * - * @param[in] graph cuGraph graph descriptor, should contain the connectivity - * information as a COO - * @param[in] k The order of the truss - * @param[in] mr Memory resource used to allocate the returned graph - * @return Unique pointer to K Truss subgraph in COO format + * @tparam vertex_t Type of vertex identifiers. Supported value : int (signed, 32-bit) + * @tparam weight_t Type of edge weights. Supported values : float or double. * + * @param[in] handle Library handle (RAFT). + * @param[in] src Source vertices from COO + * @param[in] dst Destination vertices from COO + * @param[in] wgt Optional edge weights from COO + * @param[in] k The order of the truss + * @return Tuple containing extracted src, dst and optional weights for the + * subgraph */ -template -std::unique_ptr> k_truss_subgraph( - legacy::GraphCOOView const& graph, - int k, - rmm::mr::device_memory_resource* mr = rmm::mr::get_current_device_resource()); +template +std::tuple, + rmm::device_uvector, + std::optional>> +k_truss_subgraph(raft::handle_t const& handle, + raft::device_span src, + raft::device_span dst, + std::optional> wgt, + size_t number_of_vertices, + int k); // FIXME: Internally distances is of int (signed 32-bit) data type, but current // template uses data from VT, ET, WT from the legacy::GraphCSR View even if weights diff --git a/cpp/include/cugraph/utilities/graph_traits.hpp b/cpp/include/cugraph/utilities/graph_traits.hpp index 7385630c011..e2737305aed 100644 --- a/cpp/include/cugraph/utilities/graph_traits.hpp +++ b/cpp/include/cugraph/utilities/graph_traits.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, NVIDIA CORPORATION. + * Copyright (c) 2021-2023, NVIDIA CORPORATION. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -47,6 +47,16 @@ struct is_vertex_edge_combo { (sizeof(vertex_t) <= sizeof(edge_t)); }; +// meta-function that constrains +// vertex_t and edge_t template param candidates to only int32_t: +// +template +struct is_vertex_edge_combo_legacy { + static constexpr bool value = is_one_of::value && + is_one_of::value && + (sizeof(vertex_t) <= sizeof(edge_t)); +}; + // meta-function that constrains // all 3 template param candidates: // @@ -56,4 +66,14 @@ struct is_candidate { is_vertex_edge_combo::value && is_one_of::value; }; +// meta-function that constrains +// all 3 template param candidates where vertex_t and edge_t +// are restricted to int32_t: +// +template +struct is_candidate_legacy { + static constexpr bool value = is_vertex_edge_combo_legacy::value && + is_one_of::value; +}; + } // namespace cugraph diff --git a/cpp/include/cugraph_c/community_algorithms.h b/cpp/include/cugraph_c/community_algorithms.h index e938c77cccd..8f1015f8632 100644 --- a/cpp/include/cugraph_c/community_algorithms.h +++ b/cpp/include/cugraph_c/community_algorithms.h @@ -227,6 +227,27 @@ cugraph_error_code_t cugraph_extract_ego( cugraph_induced_subgraph_result_t** result, cugraph_error_t** error); +/** + * @brief Extract k truss for a graph + * + * @param [in] handle Handle for accessing resources + * @param [in] graph Pointer to graph. NOTE: Graph might be modified if the storage + * needs to be transposed + * @param [in] k The order of the truss + * @param [in] do_expensive_check + * A flag to run expensive checks for input arguments (if set to true) + * @param [out] result Opaque object containing the extracted subgraph + * @param [out] error Pointer to an error object storing details of any error. Will + * be populated if error code is not CUGRAPH_SUCCESS + * @return error code + */ +cugraph_error_code_t cugraph_k_truss_subgraph(const cugraph_resource_handle_t* handle, + cugraph_graph_t* graph, + size_t k, + bool_t do_expensive_check, + cugraph_induced_subgraph_result_t** result, + cugraph_error_t** error); + /** * @brief Opaque clustering output */ diff --git a/cpp/src/c_api/legacy_k_truss.cpp b/cpp/src/c_api/legacy_k_truss.cpp new file mode 100644 index 00000000000..90e0894783a --- /dev/null +++ b/cpp/src/c_api/legacy_k_truss.cpp @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2023, NVIDIA CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +namespace { + +struct k_truss_functor : public cugraph::c_api::abstract_functor { + raft::handle_t const& handle_; + cugraph::c_api::cugraph_graph_t* graph_; + size_t k_; + bool do_expensive_check_; + cugraph::c_api::cugraph_induced_subgraph_result_t* result_{}; + + k_truss_functor(::cugraph_resource_handle_t const* handle, + ::cugraph_graph_t* graph, + size_t k, + bool do_expensive_check) + : abstract_functor(), + handle_(*reinterpret_cast(handle)->handle_), + graph_(reinterpret_cast(graph)), + k_(k), + do_expensive_check_(do_expensive_check) + { + } + + template + void operator()() + { + if constexpr (!cugraph::is_candidate_legacy::value) { + unsupported(); + } else if constexpr (multi_gpu) { + unsupported(); + } else { + // k_truss expects store_transposed == false + if constexpr (store_transposed) { + error_code_ = cugraph::c_api:: + transpose_storage( + handle_, graph_, error_.get()); + if (error_code_ != CUGRAPH_SUCCESS) return; + } + + auto graph = + reinterpret_cast*>(graph_->graph_); + + auto edge_weights = reinterpret_cast< + cugraph::edge_property_t, + weight_t>*>(graph_->edge_weights_); + + auto number_map = reinterpret_cast*>(graph_->number_map_); + + auto graph_view = graph->view(); + rmm::device_uvector src(0, handle_.get_stream()); + rmm::device_uvector dst(0, handle_.get_stream()); + std::optional> wgt{std::nullopt}; + + std::tie(src, dst, wgt, std::ignore) = cugraph::decompress_to_edgelist( + handle_, + graph_view, + edge_weights ? std::make_optional(edge_weights->view()) : std::nullopt, + std::optional>{std::nullopt}, + std::optional>(std::nullopt), + do_expensive_check_); + + auto [result_src, result_dst, result_wgt] = cugraph::k_truss_subgraph( + handle_, + raft::device_span(src.data(), src.size()), + raft::device_span(dst.data(), dst.size()), + wgt ? std::make_optional(raft::device_span(wgt->data(), wgt->size())) + : std::nullopt, + graph_view.number_of_vertices(), + k_); + + cugraph::unrenumber_int_vertices( + handle_, + result_src.data(), + result_src.size(), + number_map->data(), + graph_view.vertex_partition_range_lasts(), + do_expensive_check_); + + cugraph::unrenumber_int_vertices( + handle_, + result_dst.data(), + result_dst.size(), + number_map->data(), + graph_view.vertex_partition_range_lasts(), + do_expensive_check_); + + rmm::device_uvector edge_offsets(2, handle_.get_stream()); + std::vector h_edge_offsets{{0, result_src.size()}}; + raft::update_device( + edge_offsets.data(), h_edge_offsets.data(), h_edge_offsets.size(), handle_.get_stream()); + + result_ = new cugraph::c_api::cugraph_induced_subgraph_result_t{ + new cugraph::c_api::cugraph_type_erased_device_array_t(result_src, graph_->vertex_type_), + new cugraph::c_api::cugraph_type_erased_device_array_t(result_dst, graph_->vertex_type_), + wgt ? new cugraph::c_api::cugraph_type_erased_device_array_t(*result_wgt, + graph_->weight_type_) + : NULL, + new cugraph::c_api::cugraph_type_erased_device_array_t(edge_offsets, + cugraph_data_type_id_t::SIZE_T)}; + } + } +}; + +} // namespace + +extern "C" cugraph_error_code_t cugraph_k_truss_subgraph(const cugraph_resource_handle_t* handle, + cugraph_graph_t* graph, + size_t k, + bool_t do_expensive_check, + cugraph_induced_subgraph_result_t** result, + cugraph_error_t** error) +{ + k_truss_functor functor(handle, graph, k, do_expensive_check); + + return cugraph::c_api::run_algorithm(graph, functor, result, error); +} diff --git a/cpp/src/community/legacy/ktruss.cu b/cpp/src/community/legacy/ktruss.cu index 74a871adb01..403593128c1 100644 --- a/cpp/src/community/legacy/ktruss.cu +++ b/cpp/src/community/legacy/ktruss.cu @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2021, NVIDIA CORPORATION. + * Copyright (c) 2019-2023, NVIDIA CORPORATION. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -34,18 +34,24 @@ namespace cugraph { namespace detail { -template -std::unique_ptr> ktruss_subgraph_impl( - legacy::GraphCOOView const& graph, int k, rmm::mr::device_memory_resource* mr) +template +std::tuple, rmm::device_uvector> ktruss_subgraph_impl( + raft::handle_t const& handle, + raft::device_span src, + raft::device_span dst, + size_t number_of_vertices, + int k) { - using HornetGraph = hornet::gpu::Hornet; - using UpdatePtr = hornet::BatchUpdatePtr; - using Update = hornet::gpu::BatchUpdate; - cudaStream_t stream{nullptr}; - UpdatePtr ptr(graph.number_of_edges, graph.src_indices, graph.dst_indices); + using HornetGraph = hornet::gpu::Hornet; + using UpdatePtr = hornet::BatchUpdatePtr; + using Update = hornet::gpu::BatchUpdate; + + HornetGraph hnt(number_of_vertices + 1); + + // NOTE: Should a constant pointer be passed for @src and @dst + UpdatePtr ptr(static_cast(src.size()), src.data(), dst.data()); Update batch(ptr); - HornetGraph hnt(graph.number_of_vertices + 1); hnt.insert(batch); CUGRAPH_EXPECTS(cudaPeekAtLastError() == cudaSuccess, "KTruss : Failed to initialize graph"); @@ -67,32 +73,42 @@ std::unique_ptr> ktruss_subgraph_impl( kt.runForK(k); CUGRAPH_EXPECTS(cudaPeekAtLastError() == cudaSuccess, "KTruss : Failed to run"); - auto out_graph = std::make_unique>( - graph.number_of_vertices, kt.getGraphEdgeCount(), graph.has_data(), stream, mr); + rmm::device_uvector result_src(kt.getGraphEdgeCount(), handle.get_stream()); + rmm::device_uvector result_dst(kt.getGraphEdgeCount(), handle.get_stream()); - kt.copyGraph(out_graph->src_indices(), out_graph->dst_indices()); + kt.copyGraph(result_src.data(), result_dst.data()); kt.release(); CUGRAPH_EXPECTS(cudaPeekAtLastError() == cudaSuccess, "KTruss : Failed to release"); - return out_graph; + return std::make_tuple(std::move(result_src), std::move(result_dst)); } -template -std::unique_ptr> weighted_ktruss_subgraph_impl( - legacy::GraphCOOView const& graph, int k, rmm::mr::device_memory_resource* mr) + +template +std::tuple, + rmm::device_uvector, + std::optional>> +weighted_ktruss_subgraph_impl(raft::handle_t const& handle, + raft::device_span src, + raft::device_span dst, + std::optional> wgt, + size_t number_of_vertices, + int k) { - using HornetGraph = hornet::gpu::Hornet>; - using UpdatePtr = hornet::BatchUpdatePtr, hornet::DeviceType::DEVICE>; - using Update = hornet::gpu::BatchUpdate>; - cudaStream_t stream{nullptr}; - UpdatePtr ptr(graph.number_of_edges, graph.src_indices, graph.dst_indices, graph.edge_data); + using HornetGraph = hornet::gpu::Hornet>; + using UpdatePtr = + hornet::BatchUpdatePtr, hornet::DeviceType::DEVICE>; + using Update = hornet::gpu::BatchUpdate>; + + HornetGraph hnt(number_of_vertices + 1); + + UpdatePtr ptr(static_cast(src.size()), src.data(), dst.data(), wgt->data()); Update batch(ptr); - HornetGraph hnt(graph.number_of_vertices + 1); hnt.insert(batch); CUGRAPH_EXPECTS(cudaPeekAtLastError() == cudaSuccess, "KTruss : Failed to initialize graph"); - KTrussWeighted kt(hnt); + KTrussWeighted kt(hnt); kt.init(); kt.reset(); @@ -110,41 +126,60 @@ std::unique_ptr> weighted_ktruss_subgraph_impl( kt.runForK(k); CUGRAPH_EXPECTS(cudaPeekAtLastError() == cudaSuccess, "KTruss : Failed to run"); - auto out_graph = std::make_unique>( - graph.number_of_vertices, kt.getGraphEdgeCount(), graph.has_data(), stream, mr); + rmm::device_uvector result_src(kt.getGraphEdgeCount(), handle.get_stream()); + rmm::device_uvector result_dst(kt.getGraphEdgeCount(), handle.get_stream()); + std::optional> result_wgt{std::nullopt}; - kt.copyGraph(out_graph->src_indices(), out_graph->dst_indices(), out_graph->edge_data()); + result_wgt = rmm::device_uvector(kt.getGraphEdgeCount(), handle.get_stream()); + kt.copyGraph(result_src.data(), result_dst.data(), result_wgt->data()); kt.release(); CUGRAPH_EXPECTS(cudaPeekAtLastError() == cudaSuccess, "KTruss : Failed to release"); - return out_graph; + return std::make_tuple(std::move(result_src), std::move(result_dst), std::move(result_wgt)); } } // namespace detail -template -std::unique_ptr> k_truss_subgraph( - legacy::GraphCOOView const& graph, int k, rmm::mr::device_memory_resource* mr) +template +std::tuple, + rmm::device_uvector, + std::optional>> +k_truss_subgraph(raft::handle_t const& handle, + raft::device_span src, + raft::device_span dst, + std::optional> wgt, + size_t number_of_vertices, + int k) { - CUGRAPH_EXPECTS(graph.src_indices != nullptr, "Graph source indices cannot be a nullptr"); - CUGRAPH_EXPECTS(graph.dst_indices != nullptr, "Graph destination indices cannot be a nullptr"); - - if (graph.edge_data == nullptr) { - return detail::ktruss_subgraph_impl(graph, k, mr); + if (wgt.has_value()) { + return detail::weighted_ktruss_subgraph_impl(handle, src, dst, wgt, number_of_vertices, k); } else { - return detail::weighted_ktruss_subgraph_impl(graph, k, mr); + auto [result_src, result_dst] = + detail::ktruss_subgraph_impl(handle, src, dst, number_of_vertices, k); + std::optional> result_wgt{std::nullopt}; + return std::make_tuple(std::move(result_src), std::move(result_dst), std::move(result_wgt)); } } -template std::unique_ptr> -k_truss_subgraph(legacy::GraphCOOView const&, - int, - rmm::mr::device_memory_resource*); - -template std::unique_ptr> -k_truss_subgraph(legacy::GraphCOOView const&, - int, - rmm::mr::device_memory_resource*); +template std::tuple, + rmm::device_uvector, + std::optional>> +k_truss_subgraph(raft::handle_t const& handle, + raft::device_span src, + raft::device_span dst, + std::optional> wgt, + size_t number_of_vertices, + int k); + +template std::tuple, + rmm::device_uvector, + std::optional>> +k_truss_subgraph(raft::handle_t const& handle, + raft::device_span src, + raft::device_span dst, + std::optional> wgt, + size_t number_of_vertices, + int k); } // namespace cugraph diff --git a/cpp/tests/CMakeLists.txt b/cpp/tests/CMakeLists.txt index f08606df8ea..2a4bb8ab2a5 100644 --- a/cpp/tests/CMakeLists.txt +++ b/cpp/tests/CMakeLists.txt @@ -741,5 +741,6 @@ ConfigureCTest(CAPI_K_CORE_TEST c_api/k_core_test.c) ConfigureCTest(CAPI_INDUCED_SUBGRAPH_TEST c_api/induced_subgraph_test.c) ConfigureCTest(CAPI_EGONET_TEST c_api/egonet_test.c) ConfigureCTest(CAPI_TWO_HOP_NEIGHBORS_TEST c_api/two_hop_neighbors_test.c) +ConfigureCTest(CAPI_LEGACY_K_TRUSS_TEST c_api/legacy_k_truss_test.c) rapids_test_install_relocatable(INSTALL_COMPONENT_SET testing_c DESTINATION bin/gtests/libcugraph_c) diff --git a/cpp/tests/c_api/legacy_k_truss_test.c b/cpp/tests/c_api/legacy_k_truss_test.c new file mode 100644 index 00000000000..bc85f568688 --- /dev/null +++ b/cpp/tests/c_api/legacy_k_truss_test.c @@ -0,0 +1,236 @@ +/* + * Copyright (c) 2023, NVIDIA CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "c_test_utils.h" /* RUN_TEST */ + +#include +#include + +#include + +typedef int32_t vertex_t; +typedef int32_t edge_t; +typedef float weight_t; + +int generic_k_truss_test(vertex_t* h_src, + vertex_t* h_dst, + weight_t* h_wgt, + vertex_t* h_expected_src, + vertex_t* h_expected_dst, + weight_t* h_expected_wgt, + size_t* h_expected_offsets, + size_t num_vertices, + size_t num_edges, + size_t k, + size_t num_expected_offsets, + size_t num_expected_edges, + bool_t store_transposed) +{ + int test_ret_value = 0; + + cugraph_error_code_t ret_code = CUGRAPH_SUCCESS; + cugraph_error_t* ret_error; + + data_type_id_t vertex_tid = INT32; + data_type_id_t edge_tid = INT32; + data_type_id_t weight_tid = FLOAT32; + data_type_id_t edge_id_tid = INT32; + data_type_id_t edge_type_tid = INT32; + + cugraph_resource_handle_t* resource_handle = NULL; + cugraph_graph_t* graph = NULL; + cugraph_type_erased_device_array_t* seeds = NULL; + cugraph_type_erased_device_array_view_t* seeds_view = NULL; + cugraph_induced_subgraph_result_t* result = NULL; + + resource_handle = cugraph_create_resource_handle(NULL); + TEST_ASSERT(test_ret_value, resource_handle != NULL, "resource handle creation failed."); + + ret_code = create_sg_test_graph( + resource_handle, + vertex_tid, + edge_tid, + h_src, + h_dst, + weight_tid, + h_wgt, + edge_type_tid, + NULL, + edge_id_tid, + NULL, + num_edges, + store_transposed, + FALSE, + TRUE, + FALSE, + &graph, + &ret_error); + + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "create_test_graph failed."); + TEST_ALWAYS_ASSERT(ret_code == CUGRAPH_SUCCESS, cugraph_error_message(ret_error)); + + ret_code = + cugraph_k_truss_subgraph(resource_handle, graph, k, FALSE, &result, &ret_error); + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, cugraph_error_message(ret_error)); + TEST_ALWAYS_ASSERT(ret_code == CUGRAPH_SUCCESS, "cugraph_k_truss_subgraph failed."); + + if (test_ret_value == 0) { + cugraph_type_erased_device_array_view_t* src; + cugraph_type_erased_device_array_view_t* dst; + cugraph_type_erased_device_array_view_t* wgt; + cugraph_type_erased_device_array_view_t* offsets; + + src = cugraph_induced_subgraph_get_sources(result); + dst = cugraph_induced_subgraph_get_destinations(result); + wgt = cugraph_induced_subgraph_get_edge_weights(result); + offsets = cugraph_induced_subgraph_get_subgraph_offsets(result); + + size_t num_result_edges = cugraph_type_erased_device_array_view_size(src); + size_t num_result_offsets = cugraph_type_erased_device_array_view_size(offsets); + + vertex_t h_result_src[num_result_edges]; + vertex_t h_result_dst[num_result_edges]; + weight_t h_result_wgt[num_result_edges]; + size_t h_result_offsets[num_result_offsets]; + + ret_code = cugraph_type_erased_device_array_view_copy_to_host( + resource_handle, (byte_t*)h_result_src, src, &ret_error); + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "copy_to_host failed."); + + ret_code = cugraph_type_erased_device_array_view_copy_to_host( + resource_handle, (byte_t*)h_result_dst, dst, &ret_error); + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "copy_to_host failed."); + + if (wgt != NULL){ + ret_code = cugraph_type_erased_device_array_view_copy_to_host( + resource_handle, (byte_t*)h_result_wgt, wgt, &ret_error); + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "copy_to_host failed."); + } + + + ret_code = cugraph_type_erased_device_array_view_copy_to_host( + resource_handle, (byte_t*)h_result_offsets, offsets, &ret_error); + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "copy_to_host failed."); + + TEST_ASSERT(test_ret_value, num_result_edges == num_expected_edges, "results not the same size"); + + for (size_t i = 0; (i < num_expected_offsets) && (test_ret_value == 0); ++i) { + TEST_ASSERT(test_ret_value, + h_expected_offsets[i] == h_result_offsets[i], + "graph offsets should match"); + } + + for (size_t i = 0; (i < num_expected_edges) && (test_ret_value == 0); ++i) { + bool_t found = FALSE; + for (size_t j = 0; (j < num_expected_edges) && !found; ++j) { + if ((h_expected_src[i] == h_result_src[j]) && (h_expected_dst[i] == h_result_dst[j])) + if (wgt != NULL){ + found = (nearlyEqual(h_expected_wgt[i], h_result_wgt[j], 0.001)); + } + else{ + found = TRUE; + } + } + TEST_ASSERT(test_ret_value, found, "extracted an edge that doesn't match"); + } + + cugraph_type_erased_device_array_view_free(src); + cugraph_type_erased_device_array_view_free(dst); + cugraph_type_erased_device_array_view_free(wgt); + cugraph_type_erased_device_array_view_free(offsets); + cugraph_induced_subgraph_result_free(result); + } + + cugraph_sg_graph_free(graph); + cugraph_free_resource_handle(resource_handle); + cugraph_error_free(ret_error); + + return test_ret_value; +} + +int test_k_truss() +{ + size_t num_edges = 16; + size_t num_vertices = 6; + size_t k = 3; + + vertex_t h_src[] = {0, 1, 1, 2, 2, 2, 3, 4, 1, 3, 4, 0, 1, 3, 5, 5}; + vertex_t h_dst[] = {1, 3, 4, 0, 1, 3, 5, 5, 0, 1, 1, 2, 2, 2, 3, 4}; + weight_t h_wgt[] = { + 0.1f, 2.1f, 1.1f, 5.1f, 3.1f, 4.1f, 7.2f, 3.2f, 0.1f, 2.1f, 1.1f, 5.1f, 3.1f, 4.1f, 7.2f, 3.2f}; + + vertex_t h_result_src[] = {0, 0, 1, 1, 1, 2, 2, 2, 3, 3}; + vertex_t h_result_dst[] = {1, 2, 0, 2, 3, 0, 1, 3, 1, 2}; + weight_t h_result_wgt[] = {0.1, 5.1, 0.1, 3.1, 2.1, 5.1, 3.1, 4.1, 2.1, 4.1}; + size_t h_result_offsets[] = {0, 10}; + size_t num_expected_edges = 10; + size_t num_expected_offsets = 2; + + return generic_k_truss_test(h_src, + h_dst, + h_wgt, + h_result_src, + h_result_dst, + h_result_wgt, + h_result_offsets, + num_vertices, + num_edges, + k, + num_expected_offsets, + num_expected_edges, + FALSE); +} + +int test_k_truss_no_weights() +{ + size_t num_edges = 16; + size_t num_vertices = 6; + size_t k = 3; + + vertex_t h_src[] = {0, 1, 1, 2, 2, 2, 3, 4, 1, 3, 4, 0, 1, 3, 5, 5}; + vertex_t h_dst[] = {1, 3, 4, 0, 1, 3, 5, 5, 0, 1, 1, 2, 2, 2, 3, 4}; + + vertex_t h_result_src[] = {0, 0, 1, 1, 1, 2, 2, 2, 3, 3}; + vertex_t h_result_dst[] = {1, 2, 0, 2, 3, 0, 1, 3, 1, 2}; + size_t h_result_offsets[] = {0, 10}; + size_t num_expected_edges = 10; + size_t num_expected_offsets = 2; + + return generic_k_truss_test(h_src, + h_dst, + NULL, + h_result_src, + h_result_dst, + NULL, + h_result_offsets, + num_vertices, + num_edges, + k, + num_expected_offsets, + num_expected_edges, + FALSE); +} + + +/******************************************************************************/ + +int main(int argc, char** argv) +{ + int result = 0; + result |= RUN_TEST(test_k_truss); + result |= RUN_TEST(test_k_truss_no_weights); + return result; +} diff --git a/python/cugraph/CMakeLists.txt b/python/cugraph/CMakeLists.txt index ecfcb9b219f..64db9571dc9 100644 --- a/python/cugraph/CMakeLists.txt +++ b/python/cugraph/CMakeLists.txt @@ -82,7 +82,6 @@ endif() rapids_cython_init() -add_subdirectory(cugraph/community) add_subdirectory(cugraph/components) add_subdirectory(cugraph/dask/comms) add_subdirectory(cugraph/dask/structure) diff --git a/python/cugraph/cugraph/community/CMakeLists.txt b/python/cugraph/cugraph/community/CMakeLists.txt deleted file mode 100644 index 185f6accbab..00000000000 --- a/python/cugraph/cugraph/community/CMakeLists.txt +++ /dev/null @@ -1,25 +0,0 @@ -# ============================================================================= -# Copyright (c) 2022-2023, NVIDIA CORPORATION. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except -# in compliance with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software distributed under the License -# is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express -# or implied. See the License for the specific language governing permissions and limitations under -# the License. -# ============================================================================= - -set(cython_sources - ktruss_subgraph_wrapper.pyx -) - -set(linked_libraries cugraph::cugraph) -rapids_cython_create_modules( - CXX - SOURCE_FILES "${cython_sources}" - LINKED_LIBRARIES "${linked_libraries}" MODULE_PREFIX community_ - ASSOCIATED_TARGETS cugraph -) diff --git a/python/cugraph/cugraph/community/ktruss_subgraph.pxd b/python/cugraph/cugraph/community/ktruss_subgraph.pxd deleted file mode 100644 index d993c31c375..00000000000 --- a/python/cugraph/cugraph/community/ktruss_subgraph.pxd +++ /dev/null @@ -1,26 +0,0 @@ -# Copyright (c) 2019-2021, NVIDIA CORPORATION. -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# cython: profile=False -# distutils: language = c++ -# cython: embedsignature = True -# cython: language_level = 3 - -from cugraph.structure.graph_primtypes cimport * - - -cdef extern from "cugraph/algorithms.hpp" namespace "cugraph": - - cdef unique_ptr[GraphCOO[VT,ET,WT]] k_truss_subgraph[VT,ET,WT]( - const GraphCOOView[VT,ET,WT] &graph, - int k) except + diff --git a/python/cugraph/cugraph/community/ktruss_subgraph.py b/python/cugraph/cugraph/community/ktruss_subgraph.py index 0ebbe633317..15a10007610 100644 --- a/python/cugraph/cugraph/community/ktruss_subgraph.py +++ b/python/cugraph/cugraph/community/ktruss_subgraph.py @@ -11,14 +11,27 @@ # See the License for the specific language governing permissions and # limitations under the License. -from cugraph.community import ktruss_subgraph_wrapper from cugraph.structure.graph_classes import Graph +from typing import Union from cugraph.utilities import ( ensure_cugraph_obj_for_nx, cugraph_to_nx, ) +from pylibcugraph import k_truss_subgraph as pylibcugraph_k_truss_subgraph +from pylibcugraph import ResourceHandle +import warnings + from numba import cuda +import cudf +from cugraph.utilities.utils import import_optional + +# FIXME: the networkx.Graph type used in the type annotation for +# ktruss_subgraph() is specified using a string literal to avoid depending on +# and importing networkx. Instead, networkx is imported optionally, which may +# cause a problem for a type checker if run in an environment where networkx is +# not installed. +networkx = import_optional("networkx") # FIXME: special case for ktruss on CUDA 11.4: an 11.4 bug causes ktruss to @@ -39,7 +52,9 @@ def _ensure_compatible_cuda_version(): ) -def k_truss(G, k): +def k_truss( + G: Union[Graph, "networkx.Graph"], k: int +) -> Union[Graph, "networkx.Graph"]: """ Returns the K-Truss subgraph of a graph for a specific k. @@ -90,7 +105,11 @@ def k_truss(G, k): # FIXME: merge this function with k_truss -def ktruss_subgraph(G, k, use_weights=True): +def ktruss_subgraph( + G: Union[Graph, "networkx.Graph"], + k: int, + use_weights=True, # deprecated +) -> Graph: """ Returns the K-Truss subgraph of a graph for a specific k. @@ -103,7 +122,7 @@ def ktruss_subgraph(G, k, use_weights=True): finding the maximal k-clique is known to be NP-Hard. In contrast, finding a k-truss is computationally tractable as its - key building block, namely triangle counting counting, can be executed + key building block, namely triangle counting, can be executed in polnymomial time.Typically, it takes many iterations of triangle counting to find the k-truss of a graph. Yet these iterations operate on a weakly monotonically shrinking graph. @@ -141,7 +160,10 @@ def ktruss_subgraph(G, k, use_weights=True): The desired k to be used for extracting the k-truss subgraph. use_weights : bool, optional (default=True) - whether the output should contain the edge weights if G has them + Whether the output should contain the edge weights if G has them. + + Deprecated: If 'weights' were passed at the graph creation, they will + be used. Returns ------- @@ -162,7 +184,27 @@ def ktruss_subgraph(G, k, use_weights=True): if G.is_directed(): raise ValueError("input graph must be undirected") - subgraph_df = ktruss_subgraph_wrapper.ktruss_subgraph(G, k, use_weights) + if use_weights: + warning_msg = ( + "The use_weights flag is deprecated " + "and will be removed in the next release. if weights " + "were passed at the graph creation, they will be used." + ) + warnings.warn(warning_msg, FutureWarning) + + sources, destinations, edge_weights, _ = pylibcugraph_k_truss_subgraph( + resource_handle=ResourceHandle(), + graph=G._plc_graph, + k=k, + do_expensive_check=True, + ) + + subgraph_df = cudf.DataFrame() + subgraph_df["src"] = sources + subgraph_df["dst"] = destinations + if edge_weights is not None: + subgraph_df["weight"] = edge_weights + if G.renumbered: subgraph_df = G.unrenumber(subgraph_df, "src") subgraph_df = G.unrenumber(subgraph_df, "dst") diff --git a/python/cugraph/cugraph/community/ktruss_subgraph_wrapper.pyx b/python/cugraph/cugraph/community/ktruss_subgraph_wrapper.pyx deleted file mode 100644 index 8b705e8a7b4..00000000000 --- a/python/cugraph/cugraph/community/ktruss_subgraph_wrapper.pyx +++ /dev/null @@ -1,43 +0,0 @@ -# Copyright (c) 2019-2023, NVIDIA CORPORATION. -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# cython: profile=False -# distutils: language = c++ -# cython: embedsignature = True -# cython: language_level = 3 - -from cugraph.community.ktruss_subgraph cimport * -from cugraph.structure.graph_primtypes cimport * -from cugraph.structure import graph_primtypes_wrapper -import numpy as np - - -def ktruss_subgraph_float(input_graph, k, use_weights): - cdef GraphCOOViewFloat in_graph = get_coo_float_graph_view(input_graph, use_weights) - return coo_to_df(move(k_truss_subgraph[int,int,float](in_graph, k))) - - -def ktruss_subgraph_double(input_graph, k, use_weights): - cdef GraphCOOViewDouble in_graph = get_coo_double_graph_view(input_graph, use_weights) - return coo_to_df(move(k_truss_subgraph[int,int,double](in_graph, k))) - - -def ktruss_subgraph(input_graph, k, use_weights): - [input_graph.edgelist.edgelist_df['src'], - input_graph.edgelist.edgelist_df['dst']] = graph_primtypes_wrapper.datatype_cast([input_graph.edgelist.edgelist_df['src'], - input_graph.edgelist.edgelist_df['dst']], - [np.int32]) - if graph_primtypes_wrapper.weight_type(input_graph) == np.float64 and use_weights: - return ktruss_subgraph_double(input_graph, k, use_weights) - else: - return ktruss_subgraph_float(input_graph, k, use_weights) diff --git a/python/pylibcugraph/pylibcugraph/CMakeLists.txt b/python/pylibcugraph/pylibcugraph/CMakeLists.txt index 2f7e63b5c55..6618c50122c 100644 --- a/python/pylibcugraph/pylibcugraph/CMakeLists.txt +++ b/python/pylibcugraph/pylibcugraph/CMakeLists.txt @@ -35,6 +35,7 @@ set(cython_sources hits.pyx induced_subgraph.pyx k_core.pyx + k_truss_subgraph.pyx jaccard_coefficients.pyx sorensen_coefficients.pyx overlap_coefficients.pyx diff --git a/python/pylibcugraph/pylibcugraph/__init__.py b/python/pylibcugraph/pylibcugraph/__init__.py index 45f6de2f663..bd47c7da184 100644 --- a/python/pylibcugraph/pylibcugraph/__init__.py +++ b/python/pylibcugraph/pylibcugraph/__init__.py @@ -87,6 +87,8 @@ from pylibcugraph.generate_rmat_edgelists import generate_rmat_edgelists +from pylibcugraph.k_truss_subgraph import k_truss_subgraph + from pylibcugraph.jaccard_coefficients import jaccard_coefficients from pylibcugraph.overlap_coefficients import overlap_coefficients diff --git a/python/pylibcugraph/pylibcugraph/_cugraph_c/community_algorithms.pxd b/python/pylibcugraph/pylibcugraph/_cugraph_c/community_algorithms.pxd index 64944e8773f..3c273b7d3fa 100644 --- a/python/pylibcugraph/pylibcugraph/_cugraph_c/community_algorithms.pxd +++ b/python/pylibcugraph/pylibcugraph/_cugraph_c/community_algorithms.pxd @@ -253,3 +253,13 @@ cdef extern from "cugraph_c/community_algorithms.h": cugraph_error_t** error ) + ########################################################################### + # K truss + cdef cugraph_error_code_t \ + cugraph_k_truss_subgraph( + const cugraph_resource_handle_t* handle, + cugraph_graph_t* graph, + size_t k, + bool_t do_expensive_check, + cugraph_induced_subgraph_result_t** result, + cugraph_error_t** error) diff --git a/python/pylibcugraph/pylibcugraph/k_truss_subgraph.pyx b/python/pylibcugraph/pylibcugraph/k_truss_subgraph.pyx new file mode 100644 index 00000000000..cc91e76dd55 --- /dev/null +++ b/python/pylibcugraph/pylibcugraph/k_truss_subgraph.pyx @@ -0,0 +1,163 @@ +# Copyright (c) 2023, NVIDIA CORPORATION. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Have cython use python 3 syntax +# cython: language_level = 3 + + +from pylibcugraph._cugraph_c.resource_handle cimport ( + bool_t, + cugraph_resource_handle_t, +) +from pylibcugraph._cugraph_c.error cimport ( + cugraph_error_code_t, + cugraph_error_t, +) +from pylibcugraph._cugraph_c.array cimport ( + cugraph_type_erased_device_array_view_t, +) +from pylibcugraph._cugraph_c.graph cimport ( + cugraph_graph_t, +) +from pylibcugraph._cugraph_c.graph_functions cimport ( + cugraph_induced_subgraph_result_t, + cugraph_induced_subgraph_get_sources, + cugraph_induced_subgraph_get_destinations, + cugraph_induced_subgraph_get_edge_weights, + cugraph_induced_subgraph_get_subgraph_offsets, + cugraph_induced_subgraph_result_free, +) +from pylibcugraph._cugraph_c.community_algorithms cimport ( + cugraph_k_truss_subgraph, +) +from pylibcugraph.resource_handle cimport ( + ResourceHandle, +) +from pylibcugraph.graphs cimport ( + _GPUGraph, +) +from pylibcugraph.utils cimport ( + assert_success, + copy_to_cupy_array, +) + + +def k_truss_subgraph(ResourceHandle resource_handle, + _GPUGraph graph, + size_t k, + bool_t do_expensive_check): + """ + Extract k truss of a graph for a specific k. + + Parameters + ---------- + resource_handle : ResourceHandle + Handle to the underlying device resources needed for referencing data + and running algorithms. + + graph : SGGraph + The input graph. + + k: size_t + The desired k to be used for extracting the k-truss subgraph. + + do_expensive_check : bool_t + If True, performs more extensive tests on the inputs to ensure + validitity, at the expense of increased run time. + + Returns + ------- + A tuple of device arrays containing the sources, destinations, + edge_weights and edge_offsets. + + Examples + -------- + >>> import pylibcugraph, cupy, numpy + >>> srcs = cupy.asarray([0, 1, 1, 3, 1, 4, 2, 0, 2, 1, 2, + ... 3, 3, 4, 3, 5, 4, 5], dtype=numpy.int32) + >>> dsts = cupy.asarray([1, 0, 3, 1, 4, 1, 0, 2, 1, 2, 3, + ... 2, 4, 3, 5, 3, 5, 4], dtype=numpy.int32) + >>> weights = cupy.asarray( + ... [0.1, 0.1, 2.1, 2.1, 1.1, 1.1, 7.2, 7.2, 2.1, 2.1, + ... 1.1, 1.1, 7.2, 7.2, 3.2, 3.2, 6.1, 6.1] + ... ,dtype=numpy.float32) + >>> k = 2 + >>> resource_handle = pylibcugraph.ResourceHandle() + >>> graph_props = pylibcugraph.GraphProperties( + ... is_symmetric=True, is_multigraph=False) + >>> G = pylibcugraph.SGGraph( + ... resource_handle, graph_props, srcs, dsts, weights, + ... store_transposed=False, renumber=False, do_expensive_check=False) + >>> (sources, destinations, edge_weights, subgraph_offsets) = + ... pylibcugraph.k_truss_subgraph(resource_handle, G, k, False) + >>> sources + [0 0 1 1 1 1 2 2 2 3 3 3 3 4 4 4 5 5] + >>> destinations + [1 2 0 2 3 4 0 1 3 1 2 4 5 1 3 5 3 4] + >>> edge_weights + [0.1 7.2 0.1 2.1 2.1 1.1 7.2 2.1 1.1 2.1 1.1 7.2 3.2 1.1 7.2 6.1 3.2 6.1] + >>> subgraph_offsets + [0 18] + + """ + cdef cugraph_resource_handle_t* c_resource_handle_ptr = \ + resource_handle.c_resource_handle_ptr + cdef cugraph_graph_t* c_graph_ptr = graph.c_graph_ptr + cdef cugraph_induced_subgraph_result_t* result_ptr + cdef cugraph_error_code_t error_code + cdef cugraph_error_t* error_ptr + + error_code = cugraph_k_truss_subgraph(c_resource_handle_ptr, + c_graph_ptr, + k, + do_expensive_check, + &result_ptr, + &error_ptr) + assert_success(error_code, error_ptr, "cugraph_k_truss_subgraph") + + # Extract individual device array pointers from result and copy to cupy + # arrays for returning. + cdef cugraph_type_erased_device_array_view_t* sources_ptr = \ + cugraph_induced_subgraph_get_sources(result_ptr) + cdef cugraph_type_erased_device_array_view_t* destinations_ptr = \ + cugraph_induced_subgraph_get_destinations(result_ptr) + cdef cugraph_type_erased_device_array_view_t* edge_weights_ptr = \ + cugraph_induced_subgraph_get_edge_weights(result_ptr) + cdef cugraph_type_erased_device_array_view_t* subgraph_offsets_ptr = \ + cugraph_induced_subgraph_get_subgraph_offsets(result_ptr) + + + # FIXME: Get ownership of the result data instead of performing a copy + # for perfomance improvement + cupy_sources = copy_to_cupy_array( + c_resource_handle_ptr, sources_ptr) + + cupy_destinations = copy_to_cupy_array( + c_resource_handle_ptr, destinations_ptr) + + if edge_weights_ptr is not NULL: + cupy_edge_weights = copy_to_cupy_array( + c_resource_handle_ptr, edge_weights_ptr) + else: + cupy_edge_weights = None + + # FIXME: Should we keep the offsets array or just drop it from the final + # solution? + cupy_subgraph_offsets = copy_to_cupy_array( + c_resource_handle_ptr, subgraph_offsets_ptr) + + + # Free pointer + cugraph_induced_subgraph_result_free(result_ptr) + + return (cupy_sources, cupy_destinations, cupy_edge_weights, cupy_subgraph_offsets) From 9dd01d82c17b6ada9c6701a9f3238e10053ce8a6 Mon Sep 17 00:00:00 2001 From: Don Acosta <97529984+acostadon@users.noreply.github.com> Date: Fri, 29 Sep 2023 15:17:22 -0400 Subject: [PATCH 027/111] fixes force atlas to allow string as vertex names (#3891) fixes force atlas to allow string as vertex names and removes need for mtx formated datasets. resolves #3610 Authors: - Don Acosta (https://github.com/acostadon) - Brad Rees (https://github.com/BradReesWork) Approvers: - Brad Rees (https://github.com/BradReesWork) - ralph (https://github.com/nv-rliu) URL: https://github.com/rapidsai/cugraph/pull/3891 --- .../cugraph/layout/force_atlas2_wrapper.pyx | 8 +- .../cugraph/tests/layout/test_force_atlas2.py | 145 ++++++++---------- 2 files changed, 67 insertions(+), 86 deletions(-) diff --git a/python/cugraph/cugraph/layout/force_atlas2_wrapper.pyx b/python/cugraph/cugraph/layout/force_atlas2_wrapper.pyx index 4258be3ef71..5a2784e2363 100644 --- a/python/cugraph/cugraph/layout/force_atlas2_wrapper.pyx +++ b/python/cugraph/cugraph/layout/force_atlas2_wrapper.pyx @@ -56,9 +56,11 @@ def force_atlas2(input_graph, if not input_graph.edgelist: input_graph.view_edge_list() - # FIXME: This implementation assumes that the number of vertices - # is the max vertex ID + 1 which is not always the case. - num_verts = input_graph.nodes().max() + 1 + # this code allows handling of renumbered graphs + if input_graph.is_renumbered(): + num_verts = input_graph.renumber_map.df_internal_to_external['id'].max()+1 + else: + num_verts = input_graph.nodes().max() + 1 num_edges = len(input_graph.edgelist.edgelist_df['src']) cdef GraphCOOView[int,int,float] graph_float diff --git a/python/cugraph/cugraph/tests/layout/test_force_atlas2.py b/python/cugraph/cugraph/tests/layout/test_force_atlas2.py index 495a2d945c0..6b1fd6bcc4e 100644 --- a/python/cugraph/cugraph/tests/layout/test_force_atlas2.py +++ b/python/cugraph/cugraph/tests/layout/test_force_atlas2.py @@ -13,13 +13,46 @@ import time import pytest -import scipy.io -from sklearn.manifold import trustworthiness -import cudf import cugraph +from cugraph.structure import number_map from cugraph.internals import GraphBasedDimRedCallback -from cugraph.datasets import karate, polbooks, dolphins, netscience +from sklearn.manifold import trustworthiness +import scipy.io +from cugraph.datasets import ( + karate, + polbooks, + dolphins, + netscience, + dining_prefs, +) + +# FIXME Removed the multi column positional due to it being non-deterministic +# need to replace this coverage. Issue 3890 in cuGraph repo was created. + +# This method renumbers a dataframe so it can be tested using Trustworthiness. +# it converts a dataframe with string vertex ids to a renumbered int one. + + +def renumbered_edgelist(df): + renumbered_df, num_map = number_map.NumberMap.renumber(df, "src", "dst") + new_df = renumbered_df[["renumbered_src", "renumbered_dst", "wgt"]] + column_names = {"renumbered_src": "src", "renumbered_dst": "dst"} + new_df = new_df.rename(columns=column_names) + return new_df + + +# This method converts a dataframe to a sparce matrix that is required by +# scipy Trustworthiness to verify the layout +def get_coo_array(edgelist): + coo = edgelist + x = max(coo["src"].max(), coo["dst"].max()) + 1 + row = coo["src"].to_numpy() + col = coo["dst"].to_numpy() + data = coo["wgt"].to_numpy() + M = scipy.sparse.coo_array((data, (row, col)), shape=(x, x)) + + return M def cugraph_call( @@ -37,11 +70,15 @@ def cugraph_call( strong_gravity_mode, gravity, callback=None, + renumber=False, ): - G = cugraph.Graph() + if cu_M["src"] is not int or cu_M["dst"] is not int: + renumber = True + else: + renumber = False G.from_cudf_edgelist( - cu_M, source="src", destination="dst", edge_attr="wgt", renumber=False + cu_M, source="src", destination="dst", edge_attr="wgt", renumber=renumber ) t1 = time.time() @@ -66,7 +103,19 @@ def cugraph_call( return pos -DATASETS = [(karate, 0.70), (polbooks, 0.75), (dolphins, 0.66), (netscience, 0.66)] +DATASETS = [ + (karate, 0.70), + (polbooks, 0.75), + (dolphins, 0.66), + (netscience, 0.66), + (dining_prefs, 0.50), +] + +DATASETS2 = [ + (polbooks, 0.75), + (dolphins, 0.66), + (netscience, 0.66), +] MAX_ITERATIONS = [500] @@ -95,8 +144,7 @@ def on_train_end(self, positions): @pytest.mark.parametrize("max_iter", MAX_ITERATIONS) @pytest.mark.parametrize("barnes_hut_optimize", BARNES_HUT_OPTIMIZE) def test_force_atlas2(graph_file, score, max_iter, barnes_hut_optimize): - cu_M = graph_file.get_edgelist() - dataset_path = graph_file.get_path() + cu_M = graph_file.get_edgelist(download=True) test_callback = TestCallback() cu_pos = cugraph_call( cu_M, @@ -126,9 +174,11 @@ def test_force_atlas2(graph_file, score, max_iter, barnes_hut_optimize): iterations on a given graph. """ - matrix_file = dataset_path.with_suffix(".mtx") - M = scipy.io.mmread(matrix_file) - M = M.toarray() + if "string" in graph_file.metadata["col_types"]: + df = renumbered_edgelist(graph_file.get_edgelist(download=True)) + M = get_coo_array(df) + else: + M = get_coo_array(graph_file.get_edgelist(download=True)) cu_trust = trustworthiness(M, cu_pos[["x", "y"]].to_pandas()) print(cu_trust, score) assert cu_trust > score @@ -138,74 +188,3 @@ def test_force_atlas2(graph_file, score, max_iter, barnes_hut_optimize): assert test_callback.on_epoch_end_called_count == max_iter # verify `on_train_end` was only called once assert test_callback.on_train_end_called_count == 1 - - -# FIXME: this test occasionally fails - skipping to prevent CI failures but -# need to revisit ASAP -@pytest.mark.sg -@pytest.mark.skip(reason="non-deterministric - needs fixing!") -@pytest.mark.parametrize("graph_file, score", DATASETS[:-1]) -@pytest.mark.parametrize("max_iter", MAX_ITERATIONS) -@pytest.mark.parametrize("barnes_hut_optimize", BARNES_HUT_OPTIMIZE) -def test_force_atlas2_multi_column_pos_list( - graph_file, score, max_iter, barnes_hut_optimize -): - cu_M = graph_file.get_edgelist() - dataset_path = graph_file.get_path() - test_callback = TestCallback() - pos = cugraph_call( - cu_M, - max_iter=max_iter, - pos_list=None, - outbound_attraction_distribution=True, - lin_log_mode=False, - prevent_overlapping=False, - edge_weight_influence=1.0, - jitter_tolerance=1.0, - barnes_hut_optimize=False, - barnes_hut_theta=0.5, - scaling_ratio=2.0, - strong_gravity_mode=False, - gravity=1.0, - callback=test_callback, - ) - - cu_M.rename(columns={"0": "src_0", "1": "dst_0"}, inplace=True) - cu_M["src_1"] = cu_M["src_0"] + 1000 - cu_M["dst_1"] = cu_M["dst_0"] + 1000 - - G = cugraph.Graph() - G.from_cudf_edgelist( - cu_M, source=["src_0", "src_1"], destination=["dst_0", "dst_1"], edge_attr="2" - ) - - pos_list = cudf.DataFrame() - pos_list["vertex_0"] = pos["vertex"] - pos_list["vertex_1"] = pos_list["vertex_0"] + 1000 - pos_list["x"] = pos["x"] - pos_list["y"] = pos["y"] - - cu_pos = cugraph.force_atlas2( - G, - max_iter=max_iter, - pos_list=pos_list, - outbound_attraction_distribution=True, - lin_log_mode=False, - prevent_overlapping=False, - edge_weight_influence=1.0, - jitter_tolerance=1.0, - barnes_hut_optimize=False, - barnes_hut_theta=0.5, - scaling_ratio=2.0, - strong_gravity_mode=False, - gravity=1.0, - callback=test_callback, - ) - - cu_pos = cu_pos.sort_values("0_vertex") - matrix_file = dataset_path.with_suffix(".mtx") - M = scipy.io.mmread(matrix_file) - M = M.todense() - cu_trust = trustworthiness(M, cu_pos[["x", "y"]].to_pandas()) - print(cu_trust, score) - assert cu_trust > score From c00c3f1b9b64dbda3173b8f343de37f62d70ad94 Mon Sep 17 00:00:00 2001 From: Alex Barghi <105237337+alexbarghi-nv@users.noreply.github.com> Date: Sat, 30 Sep 2023 19:35:44 -0400 Subject: [PATCH 028/111] WholeGraph Feature Store for cuGraph-PyG and cuGraph-DGL (#3874) Created based on code from @dongxuy04 Adds support for `WholeGraph` `WholeMemory` in the cuGraph `FeatureStore` class. This enables both DGL and PyG to take advantage of distributed feature store functionality. Adds `pylibwholegraph` as a testing dependency so the feature store can be tested. Adds appropriate SG and MG tests. Authors: - Alex Barghi (https://github.com/alexbarghi-nv) Approvers: - Ray Douglass (https://github.com/raydouglass) - Brad Rees (https://github.com/BradReesWork) - Vibhu Jawa (https://github.com/VibhuJawa) URL: https://github.com/rapidsai/cugraph/pull/3874 --- ci/release/update-version.sh | 4 + .../all_cuda-118_arch-x86_64.yaml | 1 + .../all_cuda-120_arch-x86_64.yaml | 1 + dependencies.yaml | 3 + .../gnn/feature_storage/feat_storage.py | 147 ++++++++++++++++-- .../tests/data_store/test_gnn_feat_storage.py | 1 - .../test_gnn_feat_storage_wholegraph.py | 85 ++++++++++ 7 files changed, 226 insertions(+), 16 deletions(-) create mode 100644 python/cugraph/cugraph/tests/data_store/test_gnn_feat_storage_wholegraph.py diff --git a/ci/release/update-version.sh b/ci/release/update-version.sh index adf3273e311..aaeaa715434 100755 --- a/ci/release/update-version.sh +++ b/ci/release/update-version.sh @@ -82,6 +82,9 @@ NEXT_SHORT_TAG_PEP440=$(python -c "from setuptools.extern import packaging; prin DEPENDENCIES=( cudf cugraph + cugraph-dgl + cugraph-pyg + cugraph-service-server cugraph-service-client cuxfilter dask-cuda @@ -93,6 +96,7 @@ DEPENDENCIES=( librmm pylibcugraph pylibcugraphops + pylibwholegraph pylibraft pyraft raft-dask diff --git a/conda/environments/all_cuda-118_arch-x86_64.yaml b/conda/environments/all_cuda-118_arch-x86_64.yaml index 952ec9317e2..87179ef892e 100644 --- a/conda/environments/all_cuda-118_arch-x86_64.yaml +++ b/conda/environments/all_cuda-118_arch-x86_64.yaml @@ -53,6 +53,7 @@ dependencies: - pydata-sphinx-theme - pylibcugraphops==23.10.* - pylibraft==23.10.* +- pylibwholegraph==23.10.* - pytest - pytest-benchmark - pytest-cov diff --git a/conda/environments/all_cuda-120_arch-x86_64.yaml b/conda/environments/all_cuda-120_arch-x86_64.yaml index 38936c78c38..d54dc0abf51 100644 --- a/conda/environments/all_cuda-120_arch-x86_64.yaml +++ b/conda/environments/all_cuda-120_arch-x86_64.yaml @@ -52,6 +52,7 @@ dependencies: - pydata-sphinx-theme - pylibcugraphops==23.10.* - pylibraft==23.10.* +- pylibwholegraph==23.10.* - pytest - pytest-benchmark - pytest-cov diff --git a/dependencies.yaml b/dependencies.yaml index f74ed13115b..292fcf0baed 100644 --- a/dependencies.yaml +++ b/dependencies.yaml @@ -481,6 +481,9 @@ dependencies: - *numpy - python-louvain - scikit-learn>=0.23.1 + - output_types: [conda] + packages: + - pylibwholegraph==23.10.* test_python_pylibcugraph: common: - output_types: [conda, pyproject] diff --git a/python/cugraph/cugraph/gnn/feature_storage/feat_storage.py b/python/cugraph/cugraph/gnn/feature_storage/feat_storage.py index e3fdeb7f150..77a53882fc4 100644 --- a/python/cugraph/cugraph/gnn/feature_storage/feat_storage.py +++ b/python/cugraph/cugraph/gnn/feature_storage/feat_storage.py @@ -17,23 +17,77 @@ import cupy as cp import numpy as np import pandas as pd -from cugraph.utilities.utils import import_optional +from cugraph.utilities.utils import import_optional, MissingModule torch = import_optional("torch") +wgth = import_optional("pylibwholegraph.torch") class FeatureStore: - """The feature-store class used to store feature data for GNNS""" + """The feature-store class used to store feature data for GNNs""" + + def __init__( + self, + backend: str = "numpy", + wg_comm: object = None, + wg_type: str = None, + wg_location: str = None, + ): + """ + Constructs a new FeatureStore object + + Parameters: + ---------- + backend: str ('numpy', 'torch', 'wholegraph') + Optional (default='numpy') + The name of the backend to use. + + wg_comm: WholeMemoryCommunicator + Optional (default=automatic) + Only used with the 'wholegraph' backend. + The communicator to use to store features in WholeGraph. + + wg_type: str ('distributed', 'continuous', 'chunked') + Optional (default='distributed') + Only used with the 'wholegraph' backend. + The memory format (distributed, continuous, or chunked) of + this FeatureStore. For more information see the WholeGraph + documentation. + + wg_location: str ('cpu', 'cuda') + Optional (default='cuda') + Only used with the 'wholegraph' backend. + Where the data is stored (cpu or cuda). + Defaults to storing on the GPU (cuda). + """ - def __init__(self, backend="numpy"): self.fd = defaultdict(dict) - if backend not in ["numpy", "torch"]: + if backend not in ["numpy", "torch", "wholegraph"]: raise ValueError( - f"backend {backend} not supported. Supported backends are numpy, torch" + f"backend {backend} not supported. " + "Supported backends are numpy, torch, wholegraph" ) self.backend = backend - def add_data(self, feat_obj: Sequence, type_name: str, feat_name: str) -> None: + self.__wg_comm = None + self.__wg_type = None + self.__wg_location = None + + if backend == "wholegraph": + self.__wg_comm = ( + wg_comm if wg_comm is not None else wgth.get_local_node_communicator() + ) + self.__wg_type = wg_type if wg_type is not None else "distributed" + self.__wg_location = wg_location if wg_location is not None else "cuda" + + if self.__wg_type not in ["distributed", "chunked", "continuous"]: + raise ValueError(f"invalid memory format {self.__wg_type}") + if (self.__wg_location != "cuda") and (self.__wg_location != "cpu"): + raise ValueError(f"invalid location {self.__wg_location}") + + def add_data( + self, feat_obj: Sequence, type_name: str, feat_name: str, **kwargs + ) -> None: """ Add the feature data to the feature_storage class Parameters: @@ -49,9 +103,31 @@ def add_data(self, feat_obj: Sequence, type_name: str, feat_name: str) -> None: None """ self.fd[feat_name][type_name] = self._cast_feat_obj_to_backend( - feat_obj, self.backend + feat_obj, + self.backend, + wg_comm=self.__wg_comm, + wg_type=self.__wg_type, + wg_location=self.__wg_location, + **kwargs, ) + def add_data_no_cast(self, feat_obj, type_name: str, feat_name: str) -> None: + """ + Direct add the feature data to the feature_storage class with no cast + Parameters: + ---------- + feat_obj : array_like object + The feature object to save in feature store + type_name : str + The node-type/edge-type of the feature + feat_name: str + The name of the feature being stored + Returns: + ------- + None + """ + self.fd[feat_name][type_name] = feat_obj + def get_data( self, indices: Union[np.ndarray, torch.Tensor], @@ -87,26 +163,67 @@ def get_data( f" feature: {list(self.fd[feat_name].keys())}" ) - return self.fd[feat_name][type_name][indices] + feat = self.fd[feat_name][type_name] + if not isinstance(wgth, MissingModule) and isinstance( + feat, wgth.WholeMemoryEmbedding + ): + indices_tensor = ( + indices + if isinstance(indices, torch.Tensor) + else torch.as_tensor(indices, device="cuda") + ) + return feat.gather(indices_tensor) + else: + return feat[indices] def get_feature_list(self) -> list[str]: return {feat_name: feats.keys() for feat_name, feats in self.fd.items()} @staticmethod - def _cast_feat_obj_to_backend(feat_obj, backend: str): + def _cast_feat_obj_to_backend(feat_obj, backend: str, **kwargs): if backend == "numpy": if isinstance(feat_obj, (cudf.DataFrame, pd.DataFrame)): - return _cast_to_numpy_ar(feat_obj.values) + return _cast_to_numpy_ar(feat_obj.values, **kwargs) else: - return _cast_to_numpy_ar(feat_obj) + return _cast_to_numpy_ar(feat_obj, **kwargs) elif backend == "torch": if isinstance(feat_obj, (cudf.DataFrame, pd.DataFrame)): - return _cast_to_torch_tensor(feat_obj.values) + return _cast_to_torch_tensor(feat_obj.values, **kwargs) else: - return _cast_to_torch_tensor(feat_obj) + return _cast_to_torch_tensor(feat_obj, **kwargs) + elif backend == "wholegraph": + return _get_wg_embedding(feat_obj, **kwargs) + +def _get_wg_embedding(feat_obj, wg_comm=None, wg_type=None, wg_location=None, **kwargs): + wg_comm_obj = wg_comm or wgth.get_local_node_communicator() + wg_type_str = wg_type or "distributed" + wg_location_str = wg_location or "cuda" -def _cast_to_torch_tensor(ar): + if isinstance(feat_obj, (cudf.DataFrame, pd.DataFrame)): + th_tensor = _cast_to_torch_tensor(feat_obj.values) + else: + th_tensor = _cast_to_torch_tensor(feat_obj) + wg_embedding = wgth.create_embedding( + wg_comm_obj, + wg_type_str, + wg_location_str, + th_tensor.dtype, + th_tensor.shape, + ) + ( + local_wg_tensor, + local_ld_offset, + ) = wg_embedding.get_embedding_tensor().get_local_tensor() + local_th_tensor = th_tensor[ + local_ld_offset : local_ld_offset + local_wg_tensor.shape[0] + ] + local_wg_tensor.copy_(local_th_tensor) + wg_comm_obj.barrier() + return wg_embedding + + +def _cast_to_torch_tensor(ar, **kwargs): if isinstance(ar, cp.ndarray): ar = torch.as_tensor(ar, device="cuda") elif isinstance(ar, np.ndarray): @@ -116,7 +233,7 @@ def _cast_to_torch_tensor(ar): return ar -def _cast_to_numpy_ar(ar): +def _cast_to_numpy_ar(ar, **kwargs): if isinstance(ar, cp.ndarray): ar = ar.get() elif type(ar).__name__ == "Tensor": diff --git a/python/cugraph/cugraph/tests/data_store/test_gnn_feat_storage.py b/python/cugraph/cugraph/tests/data_store/test_gnn_feat_storage.py index 2d1537d11e3..2b0ec4b11d0 100644 --- a/python/cugraph/cugraph/tests/data_store/test_gnn_feat_storage.py +++ b/python/cugraph/cugraph/tests/data_store/test_gnn_feat_storage.py @@ -10,7 +10,6 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -# Import FeatureStore class import pytest import numpy as np diff --git a/python/cugraph/cugraph/tests/data_store/test_gnn_feat_storage_wholegraph.py b/python/cugraph/cugraph/tests/data_store/test_gnn_feat_storage_wholegraph.py new file mode 100644 index 00000000000..1892e8a85a6 --- /dev/null +++ b/python/cugraph/cugraph/tests/data_store/test_gnn_feat_storage_wholegraph.py @@ -0,0 +1,85 @@ +# Copyright (c) 2023, NVIDIA CORPORATION. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import pytest +import numpy as np + +from cugraph.gnn import FeatureStore + +from cugraph.utilities.utils import import_optional, MissingModule + +pylibwholegraph = import_optional("pylibwholegraph") +wmb = import_optional("pylibwholegraph.binding.wholememory_binding") +torch = import_optional("torch") + + +def runtest(world_rank: int, world_size: int): + from pylibwholegraph.torch.initialize import init_torch_env_and_create_wm_comm + + wm_comm, _ = init_torch_env_and_create_wm_comm( + world_rank, + world_size, + world_rank, + world_size, + ) + wm_comm = wm_comm.wmb_comm + + generator = np.random.default_rng(62) + arr = ( + generator.integers(low=0, high=100, size=100_000) + .reshape(10_000, -1) + .astype("float64") + ) + + fs = FeatureStore(backend="wholegraph") + fs.add_data(arr, "type2", "feat1") + wm_comm.barrier() + + indices_to_fetch = np.random.randint(low=0, high=len(arr), size=1024) + output_fs = fs.get_data(indices_to_fetch, type_name="type2", feat_name="feat1") + assert isinstance(output_fs, torch.Tensor) + assert output_fs.is_cuda + expected = arr[indices_to_fetch] + np.testing.assert_array_equal(output_fs.cpu().numpy(), expected) + + wmb.finalize() + + +@pytest.mark.sg +@pytest.mark.skipif(isinstance(torch, MissingModule), reason="torch not available") +@pytest.mark.skipif( + isinstance(pylibwholegraph, MissingModule), reason="wholegraph not available" +) +def test_feature_storage_wholegraph_backend(): + from pylibwholegraph.utils.multiprocess import multiprocess_run + + gpu_count = wmb.fork_get_gpu_count() + print("gpu count:", gpu_count) + assert gpu_count > 0 + + multiprocess_run(1, runtest) + + +@pytest.mark.mg +@pytest.mark.skipif(isinstance(torch, MissingModule), reason="torch not available") +@pytest.mark.skipif( + isinstance(pylibwholegraph, MissingModule), reason="wholegraph not available" +) +def test_feature_storage_wholegraph_backend_mg(): + from pylibwholegraph.utils.multiprocess import multiprocess_run + + gpu_count = wmb.fork_get_gpu_count() + print("gpu count:", gpu_count) + assert gpu_count > 0 + + multiprocess_run(gpu_count, runtest) From a8638355d4e74351e58706c4f747dcc63d23bd81 Mon Sep 17 00:00:00 2001 From: Tingyu Wang Date: Tue, 3 Oct 2023 13:45:14 -0400 Subject: [PATCH 029/111] Integrate renumbering and compression to `cugraph-dgl` to accelerate MFG creation (#3887) Allow cugraph-dgl dataloader to consume sampled outputs from BulkSampler in CSC format. Authors: - Tingyu Wang (https://github.com/tingyu66) - Seunghwa Kang (https://github.com/seunghwak) - Alex Barghi (https://github.com/alexbarghi-nv) Approvers: - Seunghwa Kang (https://github.com/seunghwak) - Alex Barghi (https://github.com/alexbarghi-nv) - Vibhu Jawa (https://github.com/VibhuJawa) URL: https://github.com/rapidsai/cugraph/pull/3887 --- .../cugraph_dgl/dataloading/__init__.py | 2 +- .../cugraph_dgl/dataloading/dataloader.py | 49 ++++-- .../cugraph_dgl/dataloading/dataset.py | 37 +++-- .../dataloading/utils/sampling_helpers.py | 155 +++++++++++++++++- .../cugraph-dgl/cugraph_dgl/nn/conv/base.py | 7 + python/cugraph-dgl/tests/test_utils.py | 28 ++++ 6 files changed, 250 insertions(+), 28 deletions(-) diff --git a/python/cugraph-dgl/cugraph_dgl/dataloading/__init__.py b/python/cugraph-dgl/cugraph_dgl/dataloading/__init__.py index 6cabea198f6..2fd7d29bd49 100644 --- a/python/cugraph-dgl/cugraph_dgl/dataloading/__init__.py +++ b/python/cugraph-dgl/cugraph_dgl/dataloading/__init__.py @@ -13,7 +13,7 @@ from cugraph_dgl.dataloading.dataset import ( HomogenousBulkSamplerDataset, - HetrogenousBulkSamplerDataset, + HeterogenousBulkSamplerDataset, ) from cugraph_dgl.dataloading.neighbor_sampler import NeighborSampler from cugraph_dgl.dataloading.dataloader import DataLoader diff --git a/python/cugraph-dgl/cugraph_dgl/dataloading/dataloader.py b/python/cugraph-dgl/cugraph_dgl/dataloading/dataloader.py index 0480f61807a..f154b096256 100644 --- a/python/cugraph-dgl/cugraph_dgl/dataloading/dataloader.py +++ b/python/cugraph-dgl/cugraph_dgl/dataloading/dataloader.py @@ -21,7 +21,7 @@ from dask.distributed import default_client, Event from cugraph_dgl.dataloading import ( HomogenousBulkSamplerDataset, - HetrogenousBulkSamplerDataset, + HeterogenousBulkSamplerDataset, ) from cugraph_dgl.dataloading.utils.extract_graph_helpers import ( create_cugraph_graph_from_edges_dict, @@ -47,19 +47,20 @@ def __init__( graph_sampler: cugraph_dgl.dataloading.NeighborSampler, sampling_output_dir: str, batches_per_partition: int = 50, - seeds_per_call: int = 400_000, + seeds_per_call: int = 200_000, device: torch.device = None, use_ddp: bool = False, ddp_seed: int = 0, batch_size: int = 1024, drop_last: bool = False, shuffle: bool = False, + sparse_format: str = "coo", **kwargs, ): """ Constructor for CuGraphStorage: ------------------------------- - graph : CuGraphStorage + graph : CuGraphStorage The graph. indices : Tensor or dict[ntype, Tensor] The set of indices. It can either be a tensor of @@ -89,7 +90,12 @@ def __init__( The seed for shuffling the dataset in :class:`torch.utils.data.distributed.DistributedSampler`. Only effective when :attr:`use_ddp` is True. - batch_size: int, + batch_size: int + Batch size. + sparse_format: str, default = "coo" + The sparse format of the emitted sampled graphs. Choose between "csc" + and "coo". When using "csc", the graphs are of type + cugraph_dgl.nn.SparseGraph. kwargs : dict Key-word arguments to be passed to the parent PyTorch :py:class:`torch.utils.data.DataLoader` class. Common arguments are: @@ -123,6 +129,12 @@ def __init__( ... for input_nodes, output_nodes, blocks in dataloader: ... """ + if sparse_format not in ["coo", "csc"]: + raise ValueError( + f"sparse_format must be one of 'coo', 'csc', " + f"but got {sparse_format}." + ) + self.sparse_format = sparse_format self.ddp_seed = ddp_seed self.use_ddp = use_ddp @@ -156,11 +168,12 @@ def __init__( self.cugraph_dgl_dataset = HomogenousBulkSamplerDataset( total_number_of_nodes=graph.total_number_of_nodes, edge_dir=self.graph_sampler.edge_dir, + sparse_format=sparse_format, ) else: etype_id_to_etype_str_dict = {v: k for k, v in graph._etype_id_dict.items()} - self.cugraph_dgl_dataset = HetrogenousBulkSamplerDataset( + self.cugraph_dgl_dataset = HeterogenousBulkSamplerDataset( num_nodes_dict=graph.num_nodes_dict, etype_id_dict=etype_id_to_etype_str_dict, etype_offset_dict=graph._etype_offset_d, @@ -210,14 +223,23 @@ def __iter__(self): output_dir = os.path.join( self._sampling_output_dir, "epoch_" + str(self.epoch_number) ) + kwargs = {} if isinstance(self.cugraph_dgl_dataset, HomogenousBulkSamplerDataset): - deduplicate_sources = True - prior_sources_behavior = "carryover" - renumber = True + kwargs["deduplicate_sources"] = True + kwargs["prior_sources_behavior"] = "carryover" + kwargs["renumber"] = True + + if self.sparse_format == "csc": + kwargs["compression"] = "CSR" + kwargs["compress_per_hop"] = True + # The following kwargs will be deprecated in uniform sampler. + kwargs["use_legacy_names"] = False + kwargs["include_hop_column"] = False + else: - deduplicate_sources = False - prior_sources_behavior = None - renumber = False + kwargs["deduplicate_sources"] = False + kwargs["prior_sources_behavior"] = None + kwargs["renumber"] = False bs = BulkSampler( output_path=output_dir, @@ -227,10 +249,9 @@ def __iter__(self): seeds_per_call=self._seeds_per_call, fanout_vals=self.graph_sampler._reversed_fanout_vals, with_replacement=self.graph_sampler.replace, - deduplicate_sources=deduplicate_sources, - prior_sources_behavior=prior_sources_behavior, - renumber=renumber, + **kwargs, ) + if self.shuffle: self.tensorized_indices_ds.shuffle() diff --git a/python/cugraph-dgl/cugraph_dgl/dataloading/dataset.py b/python/cugraph-dgl/cugraph_dgl/dataloading/dataset.py index e0d51bcf4cf..815fd30d8eb 100644 --- a/python/cugraph-dgl/cugraph_dgl/dataloading/dataset.py +++ b/python/cugraph-dgl/cugraph_dgl/dataloading/dataset.py @@ -19,6 +19,7 @@ from cugraph_dgl.dataloading.utils.sampling_helpers import ( create_homogeneous_sampled_graphs_from_dataframe, create_heterogeneous_sampled_graphs_from_dataframe, + create_homogeneous_sampled_graphs_from_dataframe_csc, ) @@ -33,17 +34,19 @@ def __init__( total_number_of_nodes: int, edge_dir: str, return_type: str = "dgl.Block", + sparse_format: str = "coo", ): if return_type not in ["dgl.Block", "cugraph_dgl.nn.SparseGraph"]: raise ValueError( - "return_type must be either 'dgl.Block' or \ - 'cugraph_dgl.nn.SparseGraph' " + "return_type must be either 'dgl.Block' or " + "'cugraph_dgl.nn.SparseGraph'." ) # TODO: Deprecate `total_number_of_nodes` # as it is no longer needed # in the next release self.total_number_of_nodes = total_number_of_nodes self.edge_dir = edge_dir + self.sparse_format = sparse_format self._current_batch_fn = None self._input_files = None self._return_type = return_type @@ -60,10 +63,20 @@ def __getitem__(self, idx: int): fn, batch_offset = self._batch_to_fn_d[idx] if fn != self._current_batch_fn: - df = _load_sampled_file(dataset_obj=self, fn=fn) - self._current_batches = create_homogeneous_sampled_graphs_from_dataframe( - sampled_df=df, edge_dir=self.edge_dir, return_type=self._return_type - ) + if self.sparse_format == "csc": + df = _load_sampled_file(dataset_obj=self, fn=fn, skip_rename=True) + self._current_batches = ( + create_homogeneous_sampled_graphs_from_dataframe_csc(df) + ) + else: + df = _load_sampled_file(dataset_obj=self, fn=fn) + self._current_batches = ( + create_homogeneous_sampled_graphs_from_dataframe( + sampled_df=df, + edge_dir=self.edge_dir, + return_type=self._return_type, + ) + ) current_offset = idx - batch_offset return self._current_batches[current_offset] @@ -87,7 +100,7 @@ def set_input_files( ) -class HetrogenousBulkSamplerDataset(torch.utils.data.Dataset): +class HeterogenousBulkSamplerDataset(torch.utils.data.Dataset): def __init__( self, num_nodes_dict: Dict[str, int], @@ -141,18 +154,18 @@ def set_input_files( ---------- input_directory: str input_directory which contains all the files that will be - loaded by HetrogenousBulkSamplerDataset + loaded by HeterogenousBulkSamplerDataset input_file_paths: List[str] - File names that will be loaded by the HetrogenousBulkSamplerDataset + File names that will be loaded by the HeterogenousBulkSamplerDataset """ _set_input_files( self, input_directory=input_directory, input_file_paths=input_file_paths ) -def _load_sampled_file(dataset_obj, fn): +def _load_sampled_file(dataset_obj, fn, skip_rename=False): df = cudf.read_parquet(os.path.join(fn)) - if dataset_obj.edge_dir == "in": + if dataset_obj.edge_dir == "in" and not skip_rename: df.rename( columns={"sources": "destinations", "destinations": "sources"}, inplace=True, @@ -181,7 +194,7 @@ def get_batch_to_fn_d(files): def _set_input_files( - dataset_obj: Union[HomogenousBulkSamplerDataset, HetrogenousBulkSamplerDataset], + dataset_obj: Union[HomogenousBulkSamplerDataset, HeterogenousBulkSamplerDataset], input_directory: Optional[str] = None, input_file_paths: Optional[List[str]] = None, ) -> None: diff --git a/python/cugraph-dgl/cugraph_dgl/dataloading/utils/sampling_helpers.py b/python/cugraph-dgl/cugraph_dgl/dataloading/utils/sampling_helpers.py index bdac3b1a323..a4f64668348 100644 --- a/python/cugraph-dgl/cugraph_dgl/dataloading/utils/sampling_helpers.py +++ b/python/cugraph-dgl/cugraph_dgl/dataloading/utils/sampling_helpers.py @@ -11,10 +11,12 @@ # See the License for the specific language governing permissions and # limitations under the License. from __future__ import annotations -from typing import Tuple, Dict, Optional +from typing import List, Tuple, Dict, Optional from collections import defaultdict import cudf +import cupy from cugraph.utilities.utils import import_optional +from cugraph_dgl.nn import SparseGraph dgl = import_optional("dgl") torch = import_optional("torch") @@ -401,3 +403,154 @@ def create_heterogenous_dgl_block_from_tensors_dict( block = dgl.to_block(sampled_graph, dst_nodes=seed_nodes, src_nodes=src_d) block.edata[dgl.EID] = sampled_graph.edata[dgl.EID] return block + + +def _process_sampled_df_csc( + df: cudf.DataFrame, + reverse_hop_id: bool = True, +) -> Tuple[ + Dict[int, Dict[int, Dict[str, torch.Tensor]]], + List[torch.Tensor], + List[List[int, int]], +]: + """ + Convert a dataframe generated by BulkSampler to a dictionary of tensors, to + facilitate MFG creation. The sampled graphs in the dataframe use CSC-format. + + Parameters + ---------- + df: cudf.DataFrame + The output from BulkSampler compressed in CSC format. The dataframe + should be generated with `compression="CSR"` in BulkSampler, + since the sampling routine treats seed nodes as sources. + + reverse_hop_id: bool (default=True) + Reverse hop id. + + Returns + ------- + tensors_dict: dict + A nested dictionary keyed by batch id and hop id. + `tensor_dict[batch_id][hop_id]` holds "minors" and "major_offsets" + values for CSC MFGs. + + renumber_map_list: list + List of renumbering maps for looking up global indices of nodes. One + map for each batch. + + mfg_sizes: list + List of the number of nodes in each message passing layer. For the + k-th hop, mfg_sizes[k] and mfg_sizes[k+1] is the number of sources and + destinations, respectively. + """ + # dropna + major_offsets = df.major_offsets.dropna().values + label_hop_offsets = df.label_hop_offsets.dropna().values + renumber_map_offsets = df.renumber_map_offsets.dropna().values + renumber_map = df.map.dropna().values + minors = df.minors.dropna().values + + n_batches = renumber_map_offsets.size - 1 + n_hops = int((label_hop_offsets.size - 1) / n_batches) + + # make global offsets local + major_offsets -= major_offsets[0] + label_hop_offsets -= label_hop_offsets[0] + renumber_map_offsets -= renumber_map_offsets[0] + + # get the sizes of each adjacency matrix (for MFGs) + mfg_sizes = (label_hop_offsets[1:] - label_hop_offsets[:-1]).reshape( + (n_batches, n_hops) + ) + n_nodes = renumber_map_offsets[1:] - renumber_map_offsets[:-1] + mfg_sizes = cupy.hstack((mfg_sizes, n_nodes.reshape(n_batches, -1))) + if reverse_hop_id: + mfg_sizes = mfg_sizes[:, ::-1] + + tensors_dict = {} + renumber_map_list = [] + for batch_id in range(n_batches): + batch_dict = {} + + for hop_id in range(n_hops): + hop_dict = {} + idx = batch_id * n_hops + hop_id # idx in label_hop_offsets + major_offsets_start = label_hop_offsets[idx].item() + major_offsets_end = label_hop_offsets[idx + 1].item() + minors_start = major_offsets[major_offsets_start].item() + minors_end = major_offsets[major_offsets_end].item() + # Note: minors and major_offsets from BulkSampler are of type int32 + # and int64 respectively. Since pylibcugraphops binding code doesn't + # support distinct node and edge index type, we simply casting both + # to int32 for now. + hop_dict["minors"] = torch.as_tensor( + minors[minors_start:minors_end], device="cuda" + ).int() + hop_dict["major_offsets"] = torch.as_tensor( + major_offsets[major_offsets_start : major_offsets_end + 1] + - major_offsets[major_offsets_start], + device="cuda", + ).int() + if reverse_hop_id: + batch_dict[n_hops - 1 - hop_id] = hop_dict + else: + batch_dict[hop_id] = hop_dict + + tensors_dict[batch_id] = batch_dict + + renumber_map_list.append( + torch.as_tensor( + renumber_map[ + renumber_map_offsets[batch_id] : renumber_map_offsets[batch_id + 1] + ], + device="cuda", + ) + ) + + return tensors_dict, renumber_map_list, mfg_sizes.tolist() + + +def _create_homogeneous_sparse_graphs_from_csc( + tensors_dict: Dict[int, Dict[int, Dict[str, torch.Tensor]]], + renumber_map_list: List[torch.Tensor], + mfg_sizes: List[int, int], +) -> List[List[torch.Tensor, torch.Tensor, List[SparseGraph]]]: + """Create mini-batches of MFGs. The input arguments are the outputs of + the function `_process_sampled_df_csc`. + + Returns + ------- + output: list + A list of mini-batches. Each mini-batch is a list that consists of + `input_nodes` tensor, `output_nodes` tensor and a list of MFGs. + """ + n_batches, n_hops = len(mfg_sizes), len(mfg_sizes[0]) - 1 + output = [] + for b_id in range(n_batches): + output_batch = [] + output_batch.append(renumber_map_list[b_id]) + output_batch.append(renumber_map_list[b_id][: mfg_sizes[b_id][-1]]) + mfgs = [ + SparseGraph( + size=(mfg_sizes[b_id][h_id], mfg_sizes[b_id][h_id + 1]), + src_ids=tensors_dict[b_id][h_id]["minors"], + cdst_ids=tensors_dict[b_id][h_id]["major_offsets"], + formats=["csc"], + reduce_memory=True, + ) + for h_id in range(n_hops) + ] + + output_batch.append(mfgs) + + output.append(output_batch) + + return output + + +def create_homogeneous_sampled_graphs_from_dataframe_csc(sampled_df: cudf.DataFrame): + """Public API to create mini-batches of MFGs using a dataframe output by + BulkSampler, where the sampled graph is compressed in CSC format.""" + return _create_homogeneous_sparse_graphs_from_csc( + *(_process_sampled_df_csc(sampled_df)) + ) diff --git a/python/cugraph-dgl/cugraph_dgl/nn/conv/base.py b/python/cugraph-dgl/cugraph_dgl/nn/conv/base.py index 307eb33078e..ddd95a76366 100644 --- a/python/cugraph-dgl/cugraph_dgl/nn/conv/base.py +++ b/python/cugraph-dgl/cugraph_dgl/nn/conv/base.py @@ -248,6 +248,13 @@ def csr(self) -> Tuple[torch.Tensor, torch.Tensor, Optional[torch.Tensor]]: value = value[self._perm_csc2csr] return csrc_ids, dst_ids, value + def __repr__(self) -> str: + return ( + f"{self.__class__.__name__}(num_src_nodes={self._num_src_nodes}, " + f"num_dst_nodes={self._num_dst_nodes}, " + f"num_edges={self._src_ids.size(0)}, formats={self._formats})" + ) + class BaseConv(torch.nn.Module): r"""An abstract base class for cugraph-ops nn module.""" diff --git a/python/cugraph-dgl/tests/test_utils.py b/python/cugraph-dgl/tests/test_utils.py index 740db59ce7f..4be66758b43 100644 --- a/python/cugraph-dgl/tests/test_utils.py +++ b/python/cugraph-dgl/tests/test_utils.py @@ -22,6 +22,7 @@ create_homogeneous_sampled_graphs_from_dataframe, _get_source_destination_range, _create_homogeneous_cugraph_dgl_nn_sparse_graph, + create_homogeneous_sampled_graphs_from_dataframe_csc, ) from cugraph.utilities.utils import import_optional @@ -50,6 +51,23 @@ def get_dummy_sampled_df(): return df +def get_dummy_sampled_df_csc(): + df_dict = dict( + minors=np.array( + [1, 1, 2, 1, 0, 3, 1, 3, 2, 3, 2, 4, 0, 1, 1, 0, 3, 2], dtype=np.int32 + ), + major_offsets=np.arange(19, dtype=np.int64), + map=np.array( + [26, 29, 33, 22, 23, 32, 18, 29, 33, 33, 8, 30, 32], dtype=np.int32 + ), + renumber_map_offsets=np.array([0, 4, 9, 13], dtype=np.int64), + label_hop_offsets=np.array([0, 1, 3, 6, 7, 9, 13, 14, 16, 18], dtype=np.int64), + ) + + # convert values to Series so that NaNs are padded automatically + return cudf.DataFrame({k: cudf.Series(v) for k, v in df_dict.items()}) + + def test_get_renumber_map(): sampled_df = get_dummy_sampled_df() @@ -176,3 +194,13 @@ def test__create_homogeneous_cugraph_dgl_nn_sparse_graph(): assert sparse_graph.num_src_nodes() == 2 assert sparse_graph.num_dst_nodes() == seednodes_range + 1 assert isinstance(sparse_graph, cugraph_dgl.nn.SparseGraph) + + +def test_create_homogeneous_sampled_graphs_from_dataframe_csc(): + df = get_dummy_sampled_df_csc() + batches = create_homogeneous_sampled_graphs_from_dataframe_csc(df) + + assert len(batches) == 3 + assert torch.equal(batches[0][0], torch.IntTensor([26, 29, 33, 22]).cuda()) + assert torch.equal(batches[1][0], torch.IntTensor([23, 32, 18, 29, 33]).cuda()) + assert torch.equal(batches[2][0], torch.IntTensor([33, 8, 30, 32]).cuda()) From 5ce3ee1b11db62647337514a200845bbb392d351 Mon Sep 17 00:00:00 2001 From: Erik Welch Date: Tue, 3 Oct 2023 16:50:10 -0500 Subject: [PATCH 030/111] nx-cugraph: handle louvain with isolated nodes (#3897) This handles isolated nodes in `louvain_communities` similar to what is done in #3886. This is expected to be a temporary fix until pylibcugraph can handle isolated nodes. As a bonus, I added `isolates` algorithm :tada: CC @naimnv @rlratzel Authors: - Erik Welch (https://github.com/eriknw) Approvers: - Rick Ratzel (https://github.com/rlratzel) URL: https://github.com/rapidsai/cugraph/pull/3897 --- python/nx-cugraph/_nx_cugraph/__init__.py | 7 ++- python/nx-cugraph/lint.yaml | 2 +- .../nx_cugraph/algorithms/__init__.py | 1 + .../algorithms/community/louvain.py | 28 +++++++-- .../nx_cugraph/algorithms/isolate.py | 63 +++++++++++++++++++ .../nx-cugraph/nx_cugraph/classes/digraph.py | 2 +- python/nx-cugraph/nx_cugraph/classes/graph.py | 6 +- python/nx-cugraph/nx_cugraph/convert.py | 2 +- .../nx_cugraph/tests/test_community.py | 53 ++++++++++++++++ 9 files changed, 151 insertions(+), 13 deletions(-) create mode 100644 python/nx-cugraph/nx_cugraph/algorithms/isolate.py create mode 100644 python/nx-cugraph/nx_cugraph/tests/test_community.py diff --git a/python/nx-cugraph/_nx_cugraph/__init__.py b/python/nx-cugraph/_nx_cugraph/__init__.py index 9b3332106ec..ebd13daded0 100644 --- a/python/nx-cugraph/_nx_cugraph/__init__.py +++ b/python/nx-cugraph/_nx_cugraph/__init__.py @@ -31,20 +31,23 @@ # BEGIN: functions "betweenness_centrality", "edge_betweenness_centrality", + "is_isolate", + "isolates", "louvain_communities", + "number_of_isolates", # END: functions }, "extra_docstrings": { # BEGIN: extra_docstrings "betweenness_centrality": "`weight` parameter is not yet supported.", "edge_betweenness_centrality": "`weight` parameter is not yet supported.", - "louvain_communities": "`threshold` and `seed` parameters are currently ignored.", + "louvain_communities": "`seed` parameter is currently ignored.", # END: extra_docstrings }, "extra_parameters": { # BEGIN: extra_parameters "louvain_communities": { - "max_level : int, optional": "Upper limit of the number of macro-iterations.", + "max_level : int, optional": "Upper limit of the number of macro-iterations (max: 500).", }, # END: extra_parameters }, diff --git a/python/nx-cugraph/lint.yaml b/python/nx-cugraph/lint.yaml index 6a462a6af79..338ca7b230e 100644 --- a/python/nx-cugraph/lint.yaml +++ b/python/nx-cugraph/lint.yaml @@ -63,7 +63,7 @@ repos: # These versions need updated manually - flake8==6.1.0 - flake8-bugbear==23.9.16 - - flake8-simplify==0.20.0 + - flake8-simplify==0.21.0 - repo: https://github.com/asottile/yesqa rev: v1.5.0 hooks: diff --git a/python/nx-cugraph/nx_cugraph/algorithms/__init__.py b/python/nx-cugraph/nx_cugraph/algorithms/__init__.py index 3a585452d6d..dfd9adfc61a 100644 --- a/python/nx-cugraph/nx_cugraph/algorithms/__init__.py +++ b/python/nx-cugraph/nx_cugraph/algorithms/__init__.py @@ -12,3 +12,4 @@ # limitations under the License. from . import centrality, community from .centrality import * +from .isolate import * diff --git a/python/nx-cugraph/nx_cugraph/algorithms/community/louvain.py b/python/nx-cugraph/nx_cugraph/algorithms/community/louvain.py index a183b59fe1d..dc209870c89 100644 --- a/python/nx-cugraph/nx_cugraph/algorithms/community/louvain.py +++ b/python/nx-cugraph/nx_cugraph/algorithms/community/louvain.py @@ -10,7 +10,7 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -import sys +import warnings import pylibcugraph as plc @@ -22,19 +22,23 @@ not_implemented_for, ) +from ..isolate import _isolates + __all__ = ["louvain_communities"] @not_implemented_for("directed") @networkx_algorithm( extra_params={ - "max_level : int, optional": "Upper limit of the number of macro-iterations." + "max_level : int, optional": ( + "Upper limit of the number of macro-iterations (max: 500)." + ) } ) def louvain_communities( G, weight="weight", resolution=1, threshold=0.0000001, seed=None, *, max_level=None ): - """`threshold` and `seed` parameters are currently ignored.""" + """`seed` parameter is currently ignored.""" # NetworkX allows both directed and undirected, but cugraph only allows undirected. seed = _seed_to_int(seed) # Unused, but ensure it's valid for future compatibility G = _to_undirected_graph(G, weight) @@ -42,7 +46,14 @@ def louvain_communities( # TODO: PLC doesn't handle empty graphs gracefully! return [{key} for key in G._nodeiter_to_iter(range(len(G)))] if max_level is None: - max_level = sys.maxsize + max_level = 500 + elif max_level > 500: + warnings.warn( + f"max_level is set too high (={max_level}), setting it to 500.", + UserWarning, + stacklevel=2, + ) + max_level = 500 vertices, clusters, modularity = plc.louvain( resource_handle=plc.ResourceHandle(), graph=G._get_plc_graph(), @@ -52,7 +63,14 @@ def louvain_communities( do_expensive_check=False, ) groups = _groupby(clusters, vertices) - return [set(G._nodearray_to_list(node_ids)) for node_ids in groups.values()] + rv = [set(G._nodearray_to_list(node_ids)) for node_ids in groups.values()] + # TODO: PLC doesn't handle isolated vertices yet, so this is a temporary fix + isolates = _isolates(G) + if isolates.size > 0: + isolates = isolates[isolates > vertices.max()] + if isolates.size > 0: + rv.extend({node} for node in G._nodearray_to_list(isolates)) + return rv @louvain_communities._can_run diff --git a/python/nx-cugraph/nx_cugraph/algorithms/isolate.py b/python/nx-cugraph/nx_cugraph/algorithms/isolate.py new file mode 100644 index 00000000000..774627e84f6 --- /dev/null +++ b/python/nx-cugraph/nx_cugraph/algorithms/isolate.py @@ -0,0 +1,63 @@ +# Copyright (c) 2023, NVIDIA CORPORATION. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +from __future__ import annotations + +from typing import TYPE_CHECKING + +import cupy as cp + +from nx_cugraph.convert import _to_graph +from nx_cugraph.utils import networkx_algorithm + +if TYPE_CHECKING: # pragma: no cover + from nx_cugraph.typing import IndexValue + +__all__ = ["is_isolate", "isolates", "number_of_isolates"] + + +@networkx_algorithm +def is_isolate(G, n): + G = _to_graph(G) + index = n if G.key_to_id is None else G.key_to_id[n] + return not ( + (G.row_indices == index).any().tolist() + or G.is_directed() + and (G.col_indices == index).any().tolist() + ) + + +def _mark_isolates(G) -> cp.ndarray[bool]: + """Return a boolean mask array indicating indices of isolated nodes.""" + mark_isolates = cp.ones(len(G), bool) + mark_isolates[G.row_indices] = False + if G.is_directed(): + mark_isolates[G.col_indices] = False + return mark_isolates + + +def _isolates(G) -> cp.ndarray[IndexValue]: + """Like isolates, but return an array of indices instead of an iterator of nodes.""" + G = _to_graph(G) + return cp.nonzero(_mark_isolates(G))[0] + + +@networkx_algorithm +def isolates(G): + G = _to_graph(G) + return G._nodeiter_to_iter(iter(_isolates(G).tolist())) + + +@networkx_algorithm +def number_of_isolates(G): + G = _to_graph(G) + return _mark_isolates(G).sum().tolist() diff --git a/python/nx-cugraph/nx_cugraph/classes/digraph.py b/python/nx-cugraph/nx_cugraph/classes/digraph.py index 0aaf88fd793..72a1bff21a9 100644 --- a/python/nx-cugraph/nx_cugraph/classes/digraph.py +++ b/python/nx-cugraph/nx_cugraph/classes/digraph.py @@ -20,7 +20,7 @@ from .graph import Graph -if TYPE_CHECKING: +if TYPE_CHECKING: # pragma: no cover from nx_cugraph.typing import NodeKey __all__ = ["DiGraph"] diff --git a/python/nx-cugraph/nx_cugraph/classes/graph.py b/python/nx-cugraph/nx_cugraph/classes/graph.py index 1432f68c752..ded4cc3943f 100644 --- a/python/nx-cugraph/nx_cugraph/classes/graph.py +++ b/python/nx-cugraph/nx_cugraph/classes/graph.py @@ -23,7 +23,7 @@ import nx_cugraph as nxcg -if TYPE_CHECKING: +if TYPE_CHECKING: # pragma: no cover from collections.abc import Iterable, Iterator from nx_cugraph.typing import ( @@ -245,9 +245,9 @@ def from_dcsc( def __new__(cls, incoming_graph_data=None, **attr) -> Graph: if incoming_graph_data is None: new_graph = cls.from_coo(0, cp.empty(0, np.int32), cp.empty(0, np.int32)) - elif incoming_graph_data.__class__ is new_graph.__class__: + elif incoming_graph_data.__class__ is cls: new_graph = incoming_graph_data.copy() - elif incoming_graph_data.__class__ is new_graph.to_networkx_class(): + elif incoming_graph_data.__class__ is cls.to_networkx_class(): new_graph = nxcg.from_networkx(incoming_graph_data, preserve_all_attrs=True) else: raise NotImplementedError diff --git a/python/nx-cugraph/nx_cugraph/convert.py b/python/nx-cugraph/nx_cugraph/convert.py index 9be8cac7877..1240ea71db7 100644 --- a/python/nx-cugraph/nx_cugraph/convert.py +++ b/python/nx-cugraph/nx_cugraph/convert.py @@ -24,7 +24,7 @@ import nx_cugraph as nxcg -if TYPE_CHECKING: +if TYPE_CHECKING: # pragma: no cover from nx_cugraph.typing import AttrKey, Dtype, EdgeValue, NodeValue __all__ = [ diff --git a/python/nx-cugraph/nx_cugraph/tests/test_community.py b/python/nx-cugraph/nx_cugraph/tests/test_community.py new file mode 100644 index 00000000000..126f45c14ae --- /dev/null +++ b/python/nx-cugraph/nx_cugraph/tests/test_community.py @@ -0,0 +1,53 @@ +# Copyright (c) 2023, NVIDIA CORPORATION. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import networkx as nx +import pytest + +import nx_cugraph as nxcg + + +def test_louvain_isolated_nodes(): + is_nx_30_or_31 = hasattr(nx.classes, "backends") + + def check(left, right): + assert len(left) == len(right) + assert set(map(frozenset, left)) == set(map(frozenset, right)) + + # Empty graph (no nodes) + G = nx.Graph() + if is_nx_30_or_31: + with pytest.raises(ZeroDivisionError): + nx.community.louvain_communities(G) + else: + nx_result = nx.community.louvain_communities(G) + cg_result = nxcg.community.louvain_communities(G) + check(nx_result, cg_result) + # Graph with no edges + G.add_nodes_from(range(5)) + if is_nx_30_or_31: + with pytest.raises(ZeroDivisionError): + nx.community.louvain_communities(G) + else: + nx_result = nx.community.louvain_communities(G) + cg_result = nxcg.community.louvain_communities(G) + check(nx_result, cg_result) + # Graph with isolated nodes + G.add_edge(1, 2) + nx_result = nx.community.louvain_communities(G) + cg_result = nxcg.community.louvain_communities(G) + check(nx_result, cg_result) + # Another one + G.add_edge(4, 4) + nx_result = nx.community.louvain_communities(G) + cg_result = nxcg.community.louvain_communities(G) + check(nx_result, cg_result) From 26af14ebad6a6b1f115779d90d3c0a68f0d380ee Mon Sep 17 00:00:00 2001 From: Alex Barghi <105237337+alexbarghi-nv@users.noreply.github.com> Date: Wed, 4 Oct 2023 11:44:01 -0400 Subject: [PATCH 031/111] cuGraph-PyG MFG Creation and Conversion (#3873) Integrates the new CSR bulk sampler output, allowing reading of batches without having to call CSC conversion or count the numbers of vertices and edges in each batch. Should result in major performance improvements, especially for small batches. Authors: - Alex Barghi (https://github.com/alexbarghi-nv) - Seunghwa Kang (https://github.com/seunghwak) - Brad Rees (https://github.com/BradReesWork) Approvers: - Brad Rees (https://github.com/BradReesWork) - Ray Douglass (https://github.com/raydouglass) - Tingyu Wang (https://github.com/tingyu66) URL: https://github.com/rapidsai/cugraph/pull/3873 --- ci/test_python.sh | 7 +- .../cugraph_pyg/data/cugraph_store.py | 38 ++-- .../cugraph_pyg/loader/cugraph_node_loader.py | 199 +++++++++++++---- .../cugraph_pyg/sampler/cugraph_sampler.py | 130 +++++++++++- .../tests/mg/test_mg_cugraph_sampler.py | 10 +- .../tests/mg/test_mg_cugraph_store.py | 4 +- .../cugraph_pyg/tests/test_cugraph_loader.py | 200 ++++++++++++++++-- .../cugraph_pyg/tests/test_cugraph_sampler.py | 10 +- .../cugraph_pyg/tests/test_cugraph_store.py | 4 +- 9 files changed, 497 insertions(+), 105 deletions(-) diff --git a/ci/test_python.sh b/ci/test_python.sh index 7b0077991ae..825d5b242d5 100755 --- a/ci/test_python.sh +++ b/ci/test_python.sh @@ -200,8 +200,11 @@ if [[ "${RAPIDS_CUDA_VERSION}" == "11.8.0" ]]; then --channel pytorch \ --channel nvidia \ 'pyg=2.3' \ - 'pytorch>=2.0' \ - 'pytorch-cuda>=11.8' + 'pytorch=2.0.0' \ + 'pytorch-cuda=11.8' + + # Install pyg dependencies (which requires pip) + pip install pyg_lib torch_scatter torch_sparse torch_cluster torch_spline_conv -f https://data.pyg.org/whl/torch-2.0.0+cu118.html rapids-mamba-retry install \ --channel "${CPP_CHANNEL}" \ diff --git a/python/cugraph-pyg/cugraph_pyg/data/cugraph_store.py b/python/cugraph-pyg/cugraph_pyg/data/cugraph_store.py index e0d318adbe0..fd2172e6ade 100644 --- a/python/cugraph-pyg/cugraph_pyg/data/cugraph_store.py +++ b/python/cugraph-pyg/cugraph_pyg/data/cugraph_store.py @@ -819,8 +819,8 @@ def _get_renumbered_edge_groups_from_sample( before this one to get the noi_index. Example Input: Series({ - 'sources': [0, 5, 11, 3], - 'destinations': [8, 2, 3, 5]}, + 'majors': [0, 5, 11, 3], + 'minors': [8, 2, 3, 5]}, 'edge_type': [1, 3, 5, 14] }), { @@ -865,24 +865,22 @@ def _get_renumbered_edge_groups_from_sample( index=cupy.asarray(id_table), ).sort_index() - # Renumber the sources using binary search + # Renumber the majors using binary search # Step 1: get the index of the new id ix_r = torch.searchsorted( torch.as_tensor(id_map.index.values, device="cuda"), - torch.as_tensor(sampling_results.sources.values, device="cuda"), + torch.as_tensor(sampling_results.majors.values, device="cuda"), ) # Step 2: Go from id indices to actual ids row_dict[t_pyg_type] = torch.as_tensor(id_map.values, device="cuda")[ ix_r ] - # Renumber the destinations using binary search + # Renumber the minors using binary search # Step 1: get the index of the new id ix_c = torch.searchsorted( torch.as_tensor(id_map.index.values, device="cuda"), - torch.as_tensor( - sampling_results.destinations.values, device="cuda" - ), + torch.as_tensor(sampling_results.minors.values, device="cuda"), ) # Step 2: Go from id indices to actual ids col_dict[t_pyg_type] = torch.as_tensor(id_map.values, device="cuda")[ @@ -897,7 +895,7 @@ def _get_renumbered_edge_groups_from_sample( "new_id": cupy.arange(dst_id_table.shape[0]), } ).set_index("dst") - dst = dst_id_map["new_id"].loc[sampling_results.destinations] + dst = dst_id_map["new_id"].loc[sampling_results.minors] col_dict[t_pyg_type] = torch.as_tensor(dst.values, device="cuda") src_id_table = noi_index[src_type] @@ -907,7 +905,7 @@ def _get_renumbered_edge_groups_from_sample( "new_id": cupy.arange(src_id_table.shape[0]), } ).set_index("src") - src = src_id_map["new_id"].loc[sampling_results.sources] + src = src_id_map["new_id"].loc[sampling_results.majors] row_dict[t_pyg_type] = torch.as_tensor(src.values, device="cuda") else: @@ -929,12 +927,12 @@ def _get_renumbered_edge_groups_from_sample( else: # CSC dst_type, _, src_type = pyg_can_edge_type - # Get the de-offsetted destinations + # Get the de-offsetted minors dst_num_type = self._numeric_vertex_type_from_name(dst_type) - destinations = torch.as_tensor( - sampling_results.destinations.iloc[ix].values, device="cuda" + minors = torch.as_tensor( + sampling_results.minors.iloc[ix].values, device="cuda" ) - destinations -= self.__vertex_type_offsets["start"][dst_num_type] + minors -= self.__vertex_type_offsets["start"][dst_num_type] # Create the col entry for this type dst_id_table = noi_index[dst_type] @@ -944,15 +942,15 @@ def _get_renumbered_edge_groups_from_sample( .rename(columns={"index": "new_id"}) .set_index("dst") ) - dst = dst_id_map["new_id"].loc[cupy.asarray(destinations)] + dst = dst_id_map["new_id"].loc[cupy.asarray(minors)] col_dict[pyg_can_edge_type] = torch.as_tensor(dst.values, device="cuda") - # Get the de-offsetted sources + # Get the de-offsetted majors src_num_type = self._numeric_vertex_type_from_name(src_type) - sources = torch.as_tensor( - sampling_results.sources.iloc[ix].values, device="cuda" + majors = torch.as_tensor( + sampling_results.majors.iloc[ix].values, device="cuda" ) - sources -= self.__vertex_type_offsets["start"][src_num_type] + majors -= self.__vertex_type_offsets["start"][src_num_type] # Create the row entry for this type src_id_table = noi_index[src_type] @@ -962,7 +960,7 @@ def _get_renumbered_edge_groups_from_sample( .rename(columns={"index": "new_id"}) .set_index("src") ) - src = src_id_map["new_id"].loc[cupy.asarray(sources)] + src = src_id_map["new_id"].loc[cupy.asarray(majors)] row_dict[pyg_can_edge_type] = torch.as_tensor(src.values, device="cuda") return row_dict, col_dict diff --git a/python/cugraph-pyg/cugraph_pyg/loader/cugraph_node_loader.py b/python/cugraph-pyg/cugraph_pyg/loader/cugraph_node_loader.py index cf7eb330d67..8552e7412e0 100644 --- a/python/cugraph-pyg/cugraph_pyg/loader/cugraph_node_loader.py +++ b/python/cugraph-pyg/cugraph_pyg/loader/cugraph_node_loader.py @@ -25,7 +25,9 @@ from cugraph_pyg.data import CuGraphStore from cugraph_pyg.sampler.cugraph_sampler import ( _sampler_output_from_sampling_results_heterogeneous, - _sampler_output_from_sampling_results_homogeneous, + _sampler_output_from_sampling_results_homogeneous_csr, + _sampler_output_from_sampling_results_homogeneous_coo, + filter_cugraph_store_csc, ) from typing import Union, Tuple, Sequence, List, Dict @@ -58,6 +60,7 @@ def __init__( # Sampler args num_neighbors: Union[List[int], Dict[Tuple[str, str, str], List[int]]] = None, replace: bool = True, + compression: str = "COO", # Other kwargs for the BulkSampler **kwargs, ): @@ -128,6 +131,10 @@ def __init__( self.__batches_per_partition = batches_per_partition self.__starting_batch_id = starting_batch_id + self._total_read_time = 0.0 + self._total_convert_time = 0.0 + self._total_feature_time = 0.0 + if input_nodes is None: # Will be loading from disk self.__num_batches = input_nodes @@ -174,6 +181,10 @@ def __init__( with_replacement=replace, batches_per_partition=self.__batches_per_partition, renumber=renumber, + use_legacy_names=False, + deduplicate_sources=True, + prior_sources_behavior="exclude", + include_hop_column=(compression == "COO"), **kwargs, ) @@ -211,6 +222,10 @@ def __init__( self.__input_files = iter(os.listdir(self.__directory.name)) def __next__(self): + from time import perf_counter + + start_time_read_data = perf_counter() + # Load the next set of sampling results if necessary if self.__next_batch >= self.__end_exclusive: if self.__directory is None: @@ -245,51 +260,98 @@ def __next__(self): fname, ) - columns = { - "sources": "int64", - "destinations": "int64", - # 'edge_id':'int64', - "edge_type": "int32", - "batch_id": "int32", - "hop_id": "int32", - } - raw_sample_data = cudf.read_parquet(parquet_path) + if "map" in raw_sample_data.columns: - num_batches = end_inclusive - self.__start_inclusive + 1 + if "renumber_map_offsets" not in raw_sample_data.columns: + num_batches = end_inclusive - self.__start_inclusive + 1 - map_end = raw_sample_data["map"].iloc[num_batches] + map_end = raw_sample_data["map"].iloc[num_batches] - map = torch.as_tensor( - raw_sample_data["map"].iloc[0:map_end], device="cuda" - ) - raw_sample_data.drop("map", axis=1, inplace=True) + map = torch.as_tensor( + raw_sample_data["map"].iloc[0:map_end], device="cuda" + ) + raw_sample_data.drop("map", axis=1, inplace=True) - self.__renumber_map_offsets = map[0 : num_batches + 1] - map[0] - self.__renumber_map = map[num_batches + 1 :] + self.__renumber_map_offsets = map[0 : num_batches + 1] - map[0] + self.__renumber_map = map[num_batches + 1 :] + else: + self.__renumber_map = raw_sample_data["map"] + self.__renumber_map_offsets = raw_sample_data[ + "renumber_map_offsets" + ] + raw_sample_data.drop( + columns=["map", "renumber_map_offsets"], inplace=True + ) + + self.__renumber_map.dropna(inplace=True) + self.__renumber_map = torch.as_tensor( + self.__renumber_map, device="cuda" + ) + + self.__renumber_map_offsets.dropna(inplace=True) + self.__renumber_map_offsets = torch.as_tensor( + self.__renumber_map_offsets, device="cuda" + ) else: self.__renumber_map = None - self.__data = raw_sample_data[list(columns.keys())].astype(columns) - self.__data.dropna(inplace=True) + self.__data = raw_sample_data + self.__coo = "majors" in self.__data.columns + if self.__coo: + self.__data.dropna(inplace=True) if ( len(self.__graph_store.edge_types) == 1 and len(self.__graph_store.node_types) == 1 ): - group_cols = ["batch_id", "hop_id"] - self.__data_index = self.__data.groupby(group_cols, as_index=True).agg( - {"sources": "max", "destinations": "max"} - ) - self.__data_index.rename( - columns={"sources": "src_max", "destinations": "dst_max"}, - inplace=True, - ) - self.__data_index = self.__data_index.to_dict(orient="index") + if self.__coo: + group_cols = ["batch_id", "hop_id"] + self.__data_index = self.__data.groupby( + group_cols, as_index=True + ).agg({"majors": "max", "minors": "max"}) + self.__data_index.rename( + columns={"majors": "src_max", "minors": "dst_max"}, + inplace=True, + ) + self.__data_index = self.__data_index.to_dict(orient="index") + else: + self.__data_index = None + + self.__label_hop_offsets = self.__data["label_hop_offsets"] + self.__data.drop(columns=["label_hop_offsets"], inplace=True) + self.__label_hop_offsets.dropna(inplace=True) + self.__label_hop_offsets = torch.as_tensor( + self.__label_hop_offsets, device="cuda" + ) + self.__label_hop_offsets -= self.__label_hop_offsets[0].clone() + + self.__major_offsets = self.__data["major_offsets"] + self.__data.drop(columns="major_offsets", inplace=True) + self.__major_offsets.dropna(inplace=True) + self.__major_offsets = torch.as_tensor( + self.__major_offsets, device="cuda" + ) + self.__major_offsets -= self.__major_offsets[0].clone() + + self.__minors = self.__data["minors"] + self.__data.drop(columns="minors", inplace=True) + self.__minors.dropna(inplace=True) + self.__minors = torch.as_tensor(self.__minors, device="cuda") + + num_batches = self.__end_exclusive - self.__start_inclusive + offsets_len = len(self.__label_hop_offsets) - 1 + if offsets_len % num_batches != 0: + raise ValueError("invalid label-hop offsets") + self.__fanout_length = int(offsets_len / num_batches) + + end_time_read_data = perf_counter() + self._total_read_time += end_time_read_data - start_time_read_data # Pull the next set of sampling results out of the dataframe in memory - f = self.__data["batch_id"] == self.__next_batch + if self.__coo: + f = self.__data["batch_id"] == self.__next_batch if self.__renumber_map is not None: i = self.__next_batch - self.__start_inclusive @@ -301,18 +363,43 @@ def __next__(self): else: current_renumber_map = None + start_time_convert = perf_counter() # Get and return the sampled subgraph if ( len(self.__graph_store.edge_types) == 1 and len(self.__graph_store.node_types) == 1 ): - sampler_output = _sampler_output_from_sampling_results_homogeneous( - self.__data[f], - current_renumber_map, - self.__graph_store, - self.__data_index, - self.__next_batch, - ) + if self.__coo: + sampler_output = _sampler_output_from_sampling_results_homogeneous_coo( + self.__data[f], + current_renumber_map, + self.__graph_store, + self.__data_index, + self.__next_batch, + ) + else: + i = (self.__next_batch - self.__start_inclusive) * self.__fanout_length + current_label_hop_offsets = self.__label_hop_offsets[ + i : i + self.__fanout_length + 1 + ] + + current_major_offsets = self.__major_offsets[ + current_label_hop_offsets[0] : (current_label_hop_offsets[-1] + 1) + ] + + current_minors = self.__minors[ + current_major_offsets[0] : current_major_offsets[-1] + ] + + sampler_output = _sampler_output_from_sampling_results_homogeneous_csr( + current_major_offsets, + current_minors, + current_renumber_map, + self.__graph_store, + current_label_hop_offsets, + self.__data_index, + self.__next_batch, + ) else: sampler_output = _sampler_output_from_sampling_results_heterogeneous( self.__data[f], current_renumber_map, self.__graph_store @@ -321,18 +408,35 @@ def __next__(self): # Get ready for next iteration self.__next_batch += 1 + end_time_convert = perf_counter() + self._total_convert_time += end_time_convert - start_time_convert + + start_time_feature = perf_counter() # Create a PyG HeteroData object, loading the required features - out = torch_geometric.loader.utils.filter_custom_store( - self.__feature_store, - self.__graph_store, - sampler_output.node, - sampler_output.row, - sampler_output.col, - sampler_output.edge, - ) + if self.__coo: + out = torch_geometric.loader.utils.filter_custom_store( + self.__feature_store, + self.__graph_store, + sampler_output.node, + sampler_output.row, + sampler_output.col, + sampler_output.edge, + ) + else: + if self.__graph_store.order == "CSR": + raise ValueError("CSR format incompatible with CSC output") + + out = filter_cugraph_store_csc( + self.__feature_store, + self.__graph_store, + sampler_output.node, + sampler_output.row, + sampler_output.col, + sampler_output.edge, + ) # Account for CSR format in cuGraph vs. CSC format in PyG - if self.__graph_store.order == "CSC": + if self.__coo and self.__graph_store.order == "CSC": for node_type in out.edge_index_dict: out[node_type].edge_index[0], out[node_type].edge_index[1] = ( out[node_type].edge_index[1], @@ -342,6 +446,9 @@ def __next__(self): out.set_value_dict("num_sampled_nodes", sampler_output.num_sampled_nodes) out.set_value_dict("num_sampled_edges", sampler_output.num_sampled_edges) + end_time_feature = perf_counter() + self._total_feature_time = end_time_feature - start_time_feature + return out @property diff --git a/python/cugraph-pyg/cugraph_pyg/sampler/cugraph_sampler.py b/python/cugraph-pyg/cugraph_pyg/sampler/cugraph_sampler.py index 6e8c4322418..300ca9beb5a 100644 --- a/python/cugraph-pyg/cugraph_pyg/sampler/cugraph_sampler.py +++ b/python/cugraph-pyg/cugraph_pyg/sampler/cugraph_sampler.py @@ -53,10 +53,10 @@ def _get_unique_nodes( The unique nodes of the given node type. """ if node_position == "src": - edge_index = "sources" + edge_index = "majors" edge_sel = 0 elif node_position == "dst": - edge_index = "destinations" + edge_index = "minors" edge_sel = -1 else: raise ValueError(f"Illegal value {node_position} for node_position") @@ -78,7 +78,7 @@ def _get_unique_nodes( return sampling_results_node[edge_index] -def _sampler_output_from_sampling_results_homogeneous( +def _sampler_output_from_sampling_results_homogeneous_coo( sampling_results: cudf.DataFrame, renumber_map: torch.Tensor, graph_store: CuGraphStore, @@ -133,11 +133,11 @@ def _sampler_output_from_sampling_results_homogeneous( noi_index = {node_type: torch.as_tensor(renumber_map, device="cuda")} row_dict = { - edge_type: torch.as_tensor(sampling_results.sources, device="cuda"), + edge_type: torch.as_tensor(sampling_results.majors, device="cuda"), } col_dict = { - edge_type: torch.as_tensor(sampling_results.destinations, device="cuda"), + edge_type: torch.as_tensor(sampling_results.minors, device="cuda"), } num_nodes_per_hop_dict[node_type][0] = data_index[batch_id, 0]["src_max"] + 1 @@ -177,6 +177,88 @@ def _sampler_output_from_sampling_results_homogeneous( ) +def _sampler_output_from_sampling_results_homogeneous_csr( + major_offsets: torch.Tensor, + minors: torch.Tensor, + renumber_map: torch.Tensor, + graph_store: CuGraphStore, + label_hop_offsets: torch.Tensor, + batch_id: int, + metadata: Sequence = None, +) -> HeteroSamplerOutput: + """ + Parameters + ---------- + major_offsets: torch.Tensor + The major offsets for the CSC/CSR matrix ("row pointer") + minors: torch.Tensor + The minors for the CSC/CSR matrix ("col index") + renumber_map: torch.Tensor + The tensor containing the renumber map. + Required. + graph_store: CuGraphStore + The graph store containing the structure of the sampled graph. + label_hop_offsets: torch.Tensor + The tensor containing the label-hop offsets. + batch_id: int + The current batch id, whose samples are being retrieved + from the sampling results and data index. + metadata: Tensor + The metadata for the sampled batch. + + Returns + ------- + HeteroSamplerOutput + """ + + if len(graph_store.edge_types) > 1 or len(graph_store.node_types) > 1: + raise ValueError("Graph is heterogeneous") + + if renumber_map is None: + raise ValueError("Renumbered input is expected for homogeneous graphs") + + node_type = graph_store.node_types[0] + edge_type = graph_store.edge_types[0] + + major_offsets = major_offsets.clone() - major_offsets[0] + label_hop_offsets = label_hop_offsets.clone() - label_hop_offsets[0] + + num_edges_per_hop_dict = {edge_type: major_offsets[label_hop_offsets].diff().cpu()} + + label_hop_offsets = label_hop_offsets.cpu() + num_nodes_per_hop_dict = { + node_type: torch.concat( + [ + label_hop_offsets.diff(), + (renumber_map.shape[0] - label_hop_offsets[-1]).reshape((1,)), + ] + ).cpu() + } + + noi_index = {node_type: torch.as_tensor(renumber_map, device="cuda")} + + col_dict = { + edge_type: major_offsets, + } + + row_dict = { + edge_type: minors, + } + + if HeteroSamplerOutput is None: + raise ImportError("Error importing from pyg") + + return HeteroSamplerOutput( + node=noi_index, + row=row_dict, + col=col_dict, + edge=None, + num_sampled_nodes=num_nodes_per_hop_dict, + num_sampled_edges=num_edges_per_hop_dict, + metadata=metadata, + ) + + def _sampler_output_from_sampling_results_heterogeneous( sampling_results: cudf.DataFrame, renumber_map: cudf.Series, @@ -244,8 +326,8 @@ def _sampler_output_from_sampling_results_heterogeneous( cudf.Series( torch.concat( [ - torch.as_tensor(sampling_results_hop_0.sources, device="cuda"), - torch.as_tensor(sampling_results.destinations, device="cuda"), + torch.as_tensor(sampling_results_hop_0.majors, device="cuda"), + torch.as_tensor(sampling_results.minors, device="cuda"), ] ), name="nodes_of_interest", @@ -320,3 +402,37 @@ def _sampler_output_from_sampling_results_heterogeneous( num_sampled_edges=num_edges_per_hop_dict, metadata=metadata, ) + + +def filter_cugraph_store_csc( + feature_store: torch_geometric.data.FeatureStore, + graph_store: torch_geometric.data.GraphStore, + node_dict: Dict[str, torch.Tensor], + row_dict: Dict[str, torch.Tensor], + col_dict: Dict[str, torch.Tensor], + edge_dict: Dict[str, Tuple[torch.Tensor]], +) -> torch_geometric.data.HeteroData: + data = torch_geometric.data.HeteroData() + + for attr in graph_store.get_all_edge_attrs(): + key = attr.edge_type + if key in row_dict and key in col_dict: + data.put_edge_index( + (row_dict[key], col_dict[key]), + edge_type=key, + layout="csc", + is_sorted=True, + ) + + required_attrs = [] + for attr in feature_store.get_all_tensor_attrs(): + if attr.group_name in node_dict: + attr.index = node_dict[attr.group_name] + required_attrs.append(attr) + data[attr.group_name].num_nodes = attr.index.size(0) + + tensors = feature_store.multi_get_tensor(required_attrs) + for i, attr in enumerate(required_attrs): + data[attr.group_name][attr.attr_name] = tensors[i] + + return data diff --git a/python/cugraph-pyg/cugraph_pyg/tests/mg/test_mg_cugraph_sampler.py b/python/cugraph-pyg/cugraph_pyg/tests/mg/test_mg_cugraph_sampler.py index a1a72a44d0c..80a2d0a6c79 100644 --- a/python/cugraph-pyg/cugraph_pyg/tests/mg/test_mg_cugraph_sampler.py +++ b/python/cugraph-pyg/cugraph_pyg/tests/mg/test_mg_cugraph_sampler.py @@ -53,9 +53,10 @@ def test_neighbor_sample(dask_client, basic_graph_1): random_state=62, return_offsets=False, return_hops=True, + use_legacy_names=False, ) .compute() - .sort_values(by=["sources", "destinations"]) + .sort_values(by=["majors", "minors"]) ) out = _sampler_output_from_sampling_results_heterogeneous( @@ -116,8 +117,9 @@ def test_neighbor_sample_multi_vertex(dask_client, multi_edge_multi_vertex_graph random_state=62, return_offsets=False, with_batch_ids=True, + use_legacy_names=False, ) - .sort_values(by=["sources", "destinations"]) + .sort_values(by=["majors", "minors"]) .compute() ) @@ -193,8 +195,8 @@ def test_neighbor_sample_mock_sampling_results(dask_client): # let 0, 1 be the start vertices, fanout = [2, 1, 2, 3] mock_sampling_results = cudf.DataFrame( { - "sources": cudf.Series([0, 0, 1, 2, 3, 3, 1, 3, 3, 3], dtype="int64"), - "destinations": cudf.Series([2, 3, 3, 8, 1, 7, 3, 1, 5, 7], dtype="int64"), + "majors": cudf.Series([0, 0, 1, 2, 3, 3, 1, 3, 3, 3], dtype="int64"), + "minors": cudf.Series([2, 3, 3, 8, 1, 7, 3, 1, 5, 7], dtype="int64"), "hop_id": cudf.Series([0, 0, 0, 1, 1, 1, 2, 3, 3, 3], dtype="int32"), "edge_type": cudf.Series([0, 0, 0, 2, 1, 2, 0, 1, 2, 2], dtype="int32"), } diff --git a/python/cugraph-pyg/cugraph_pyg/tests/mg/test_mg_cugraph_store.py b/python/cugraph-pyg/cugraph_pyg/tests/mg/test_mg_cugraph_store.py index 43b1e5da5a0..ed7f70034e2 100644 --- a/python/cugraph-pyg/cugraph_pyg/tests/mg/test_mg_cugraph_store.py +++ b/python/cugraph-pyg/cugraph_pyg/tests/mg/test_mg_cugraph_store.py @@ -220,8 +220,8 @@ def test_renumber_edges(abc_graph, dask_client): # let 0, 1 be the start vertices, fanout = [2, 1, 2, 3] mock_sampling_results = cudf.DataFrame( { - "sources": cudf.Series([0, 0, 1, 2, 3, 3, 1, 3, 3, 3], dtype="int64"), - "destinations": cudf.Series([2, 3, 3, 8, 1, 7, 3, 1, 5, 7], dtype="int64"), + "majors": cudf.Series([0, 0, 1, 2, 3, 3, 1, 3, 3, 3], dtype="int64"), + "minors": cudf.Series([2, 3, 3, 8, 1, 7, 3, 1, 5, 7], dtype="int64"), "hop_id": cudf.Series([0, 0, 0, 1, 1, 1, 2, 3, 3, 3], dtype="int32"), "edge_type": cudf.Series([0, 0, 0, 2, 1, 2, 0, 1, 2, 2], dtype="int32"), } diff --git a/python/cugraph-pyg/cugraph_pyg/tests/test_cugraph_loader.py b/python/cugraph-pyg/cugraph_pyg/tests/test_cugraph_loader.py index 48a21cb7fd6..03274948158 100644 --- a/python/cugraph-pyg/cugraph_pyg/tests/test_cugraph_loader.py +++ b/python/cugraph-pyg/cugraph_pyg/tests/test_cugraph_loader.py @@ -22,6 +22,8 @@ from cugraph_pyg.loader import CuGraphNeighborLoader from cugraph_pyg.loader import BulkSampleLoader from cugraph_pyg.data import CuGraphStore +from cugraph_pyg.nn import SAGEConv as CuGraphSAGEConv + from cugraph.gnn import FeatureStore from cugraph.utilities.utils import import_optional, MissingModule @@ -98,8 +100,8 @@ def test_cugraph_loader_from_disk(): bogus_samples = cudf.DataFrame( { - "sources": [0, 1, 2, 3, 4, 5, 6, 6], - "destinations": [5, 4, 3, 2, 2, 6, 5, 2], + "majors": [0, 1, 2, 3, 4, 5, 6, 6], + "minors": [5, 4, 3, 2, 2, 6, 5, 2], "edge_type": cudf.Series([0, 0, 0, 0, 0, 0, 0, 0], dtype="int32"), "edge_id": [5, 10, 15, 20, 25, 30, 35, 40], "hop_id": cudf.Series([0, 0, 0, 1, 1, 1, 2, 2], dtype="int32"), @@ -130,12 +132,10 @@ def test_cugraph_loader_from_disk(): assert list(edge_index.shape) == [2, 8] assert ( - edge_index[0].tolist() - == bogus_samples.sources.dropna().values_host.tolist() + edge_index[0].tolist() == bogus_samples.majors.dropna().values_host.tolist() ) assert ( - edge_index[1].tolist() - == bogus_samples.destinations.dropna().values_host.tolist() + edge_index[1].tolist() == bogus_samples.minors.dropna().values_host.tolist() ) assert num_samples == 256 @@ -157,8 +157,8 @@ def test_cugraph_loader_from_disk_subset(): bogus_samples = cudf.DataFrame( { - "sources": [0, 1, 2, 3, 4, 5, 6, 6], - "destinations": [5, 4, 3, 2, 2, 6, 5, 2], + "majors": [0, 1, 2, 3, 4, 5, 6, 6], + "minors": [5, 4, 3, 2, 2, 6, 5, 2], "edge_type": cudf.Series([0, 0, 0, 0, 0, 0, 0, 0], dtype="int32"), "edge_id": [5, 10, 15, 20, 25, 30, 35, 40], "hop_id": cudf.Series([0, 0, 0, 1, 1, 1, 2, 2], dtype="int32"), @@ -190,13 +190,77 @@ def test_cugraph_loader_from_disk_subset(): assert list(edge_index.shape) == [2, 8] assert ( - edge_index[0].tolist() - == bogus_samples.sources.dropna().values_host.tolist() + edge_index[0].tolist() == bogus_samples.majors.dropna().values_host.tolist() + ) + assert ( + edge_index[1].tolist() == bogus_samples.minors.dropna().values_host.tolist() ) + + assert num_samples == 100 + + +@pytest.mark.skipif(isinstance(torch, MissingModule), reason="torch not available") +def test_cugraph_loader_from_disk_subset_csr(): + m = [2, 9, 99, 82, 11, 13] + n = torch.arange(1, 1 + len(m), dtype=torch.int32) + x = torch.zeros(256, dtype=torch.int32) + x[torch.tensor(m, dtype=torch.int32)] = n + F = FeatureStore() + F.add_data(x, "t0", "x") + + G = {("t0", "knows", "t0"): 9080} + N = {"t0": 256} + + cugraph_store = CuGraphStore(F, G, N) + + bogus_samples = cudf.DataFrame( + { + "major_offsets": [0, 3, 5, 7, 8, None, None, None], + "minors": [1, 2, 3, 0, 3, 4, 5, 1], + "edge_type": cudf.Series([0, 0, 0, 0, 0, 0, 0, 0], dtype="int32"), + "edge_id": [5, 10, 15, 20, 25, 30, 35, 40], + "label_hop_offsets": cudf.Series( + [0, 1, 4, None, None, None, None, None], dtype="int32" + ), + "renumber_map_offsets": cudf.Series([0, 6], dtype="int32"), + } + ) + map = cudf.Series(m, name="map") + bogus_samples["map"] = map + + tempdir = tempfile.TemporaryDirectory() + for s in range(256): + # offset the offsets + bogus_samples["batch_id"] = cupy.int32(s) + bogus_samples.to_parquet(os.path.join(tempdir.name, f"batch={s}-{s}.parquet")) + + loader = BulkSampleLoader( + feature_store=cugraph_store, + graph_store=cugraph_store, + directory=tempdir, + input_files=list(os.listdir(tempdir.name))[100:200], + ) + + num_samples = 0 + for sample in loader: + num_samples += 1 + assert sample["t0"]["num_nodes"] == 6 + + assert sample["t0"]["x"].tolist() == [1, 2, 3, 4, 5, 6] + + edge_index = sample[("t0", "knows", "t0")]["adj_t"] + assert edge_index.size(0) == 4 + assert edge_index.size(1) == 6 + + colptr, row, _ = edge_index.csr() + assert ( - edge_index[1].tolist() - == bogus_samples.destinations.dropna().values_host.tolist() + colptr.tolist() == bogus_samples.major_offsets.dropna().values_host.tolist() ) + assert row.tolist() == bogus_samples.minors.dropna().values_host.tolist() + + assert sample["t0"]["num_sampled_nodes"].tolist() == [1, 3, 2] + assert sample["t0", "knows", "t0"]["num_sampled_edges"].tolist() == [3, 5] assert num_samples == 100 @@ -215,8 +279,8 @@ def test_cugraph_loader_e2e_coo(): bogus_samples = cudf.DataFrame( { - "sources": [0, 1, 2, 3, 4, 5, 6, 6], - "destinations": [5, 4, 3, 2, 2, 6, 5, 2], + "majors": [0, 1, 2, 3, 4, 5, 6, 6], + "minors": [5, 4, 3, 2, 2, 6, 5, 2], "edge_type": cudf.Series([0, 0, 0, 0, 0, 0, 0, 0], dtype="int32"), "edge_id": [5, 10, 15, 20, 25, 30, 35, 40], "hop_id": cudf.Series([0, 0, 0, 1, 1, 1, 2, 2], dtype="int32"), @@ -253,8 +317,6 @@ def test_cugraph_loader_e2e_coo(): num_sampled_nodes = hetero_data["t0"]["num_sampled_nodes"] num_sampled_edges = hetero_data["t0", "knows", "t0"]["num_sampled_edges"] - print(num_sampled_nodes, num_sampled_edges) - for i in range(len(convs)): x, ei, _ = trim(i, num_sampled_nodes, num_sampled_edges, x, ei, None) @@ -263,9 +325,111 @@ def test_cugraph_loader_e2e_coo(): x = convs[i](x, ei, size=(s, s)) x = relu(x) x = dropout(x, p=0.5) - print(x.shape) - print(x.shape) x = x.narrow(dim=0, start=0, length=x.shape[0] - num_sampled_nodes[1]) assert list(x.shape) == [3, 1] + + +@pytest.mark.skipif(isinstance(torch, MissingModule), reason="torch not available") +@pytest.mark.parametrize("framework", ["pyg", "cugraph-ops"]) +def test_cugraph_loader_e2e_csc(framework): + m = [2, 9, 99, 82, 9, 3, 18, 1, 12] + x = torch.randint(3000, (256, 256)).to(torch.float32) + F = FeatureStore() + F.add_data(x, "t0", "x") + + G = {("t0", "knows", "t0"): 9999} + N = {"t0": 256} + + cugraph_store = CuGraphStore(F, G, N) + + bogus_samples = cudf.DataFrame( + { + "major_offsets": [0, 3, 5, 7, 8, None, None, None], + "minors": [1, 2, 3, 0, 3, 4, 5, 1], + "edge_type": cudf.Series([0, 0, 0, 0, 0, 0, 0, 0], dtype="int32"), + "edge_id": [5, 10, 15, 20, 25, 30, 35, 40], + "label_hop_offsets": cudf.Series( + [0, 1, 4, None, None, None, None, None], dtype="int32" + ), + "renumber_map_offsets": cudf.Series([0, 6], dtype="int32"), + } + ) + map = cudf.Series(m, name="map") + bogus_samples = bogus_samples.join(map, how="outer").sort_index() + + tempdir = tempfile.TemporaryDirectory() + for s in range(256): + bogus_samples["batch_id"] = cupy.int32(s) + bogus_samples.to_parquet(os.path.join(tempdir.name, f"batch={s}-{s}.parquet")) + + loader = BulkSampleLoader( + feature_store=cugraph_store, + graph_store=cugraph_store, + directory=tempdir, + input_files=list(os.listdir(tempdir.name))[100:200], + ) + + if framework == "pyg": + convs = [ + torch_geometric.nn.SAGEConv(256, 64, aggr="mean").cuda(), + torch_geometric.nn.SAGEConv(64, 1, aggr="mean").cuda(), + ] + else: + convs = [ + CuGraphSAGEConv(256, 64, aggr="mean").cuda(), + CuGraphSAGEConv(64, 1, aggr="mean").cuda(), + ] + + trim = trim_to_layer.TrimToLayer() + relu = torch.nn.functional.relu + dropout = torch.nn.functional.dropout + + for hetero_data in loader: + x = hetero_data["t0"]["x"].cuda() + + if framework == "pyg": + ei = hetero_data["t0", "knows", "t0"]["adj_t"].coo() + ei = torch.stack((ei[0], ei[1])) + else: + ei = hetero_data["t0", "knows", "t0"]["adj_t"].csr() + ei = [ei[1], ei[0], x.shape[0]] + + num_sampled_nodes = hetero_data["t0"]["num_sampled_nodes"] + num_sampled_edges = hetero_data["t0", "knows", "t0"]["num_sampled_edges"] + + s = x.shape[0] + for i in range(len(convs)): + if framework == "pyg": + x, ei, _ = trim(i, num_sampled_nodes, num_sampled_edges, x, ei, None) + else: + if i > 0: + x = x.narrow( + dim=0, + start=0, + length=s - num_sampled_nodes[-i], + ) + + ei[0] = ei[0].narrow( + dim=0, + start=0, + length=ei[0].size(0) - num_sampled_edges[-i], + ) + ei[1] = ei[1].narrow( + dim=0, start=0, length=ei[1].size(0) - num_sampled_nodes[-i] + ) + ei[2] = x.size(0) + + s = x.shape[0] + + if framework == "pyg": + x = convs[i](x, ei, size=(s, s)) + else: + x = convs[i](x, ei) + x = relu(x) + x = dropout(x, p=0.5) + + x = x.narrow(dim=0, start=0, length=s - num_sampled_nodes[1]) + + assert list(x.shape) == [1, 1] diff --git a/python/cugraph-pyg/cugraph_pyg/tests/test_cugraph_sampler.py b/python/cugraph-pyg/cugraph_pyg/tests/test_cugraph_sampler.py index 84f62e80c9d..e703d477b70 100644 --- a/python/cugraph-pyg/cugraph_pyg/tests/test_cugraph_sampler.py +++ b/python/cugraph-pyg/cugraph_pyg/tests/test_cugraph_sampler.py @@ -49,7 +49,8 @@ def test_neighbor_sample(basic_graph_1): with_batch_ids=True, random_state=62, return_offsets=False, - ).sort_values(by=["sources", "destinations"]) + use_legacy_names=False, + ).sort_values(by=["majors", "minors"]) out = _sampler_output_from_sampling_results_heterogeneous( sampling_results=sampling_results, @@ -107,7 +108,8 @@ def test_neighbor_sample_multi_vertex(multi_edge_multi_vertex_graph_1): random_state=62, return_offsets=False, with_batch_ids=True, - ).sort_values(by=["sources", "destinations"]) + use_legacy_names=False, + ).sort_values(by=["majors", "minors"]) out = _sampler_output_from_sampling_results_heterogeneous( sampling_results=sampling_results, @@ -154,8 +156,8 @@ def test_neighbor_sample_mock_sampling_results(abc_graph): # let 0, 1 be the start vertices, fanout = [2, 1, 2, 3] mock_sampling_results = cudf.DataFrame( { - "sources": cudf.Series([0, 0, 1, 2, 3, 3, 1, 3, 3, 3], dtype="int64"), - "destinations": cudf.Series([2, 3, 3, 8, 1, 7, 3, 1, 5, 7], dtype="int64"), + "majors": cudf.Series([0, 0, 1, 2, 3, 3, 1, 3, 3, 3], dtype="int64"), + "minors": cudf.Series([2, 3, 3, 8, 1, 7, 3, 1, 5, 7], dtype="int64"), "hop_id": cudf.Series([0, 0, 0, 1, 1, 1, 2, 3, 3, 3], dtype="int32"), "edge_type": cudf.Series([0, 0, 0, 2, 1, 2, 0, 1, 2, 2], dtype="int32"), } diff --git a/python/cugraph-pyg/cugraph_pyg/tests/test_cugraph_store.py b/python/cugraph-pyg/cugraph_pyg/tests/test_cugraph_store.py index e815b813050..da3043760d4 100644 --- a/python/cugraph-pyg/cugraph_pyg/tests/test_cugraph_store.py +++ b/python/cugraph-pyg/cugraph_pyg/tests/test_cugraph_store.py @@ -204,8 +204,8 @@ def test_renumber_edges(abc_graph): # let 0, 1 be the start vertices, fanout = [2, 1, 2, 3] mock_sampling_results = cudf.DataFrame( { - "sources": cudf.Series([0, 0, 1, 2, 3, 3, 1, 3, 3, 3], dtype="int64"), - "destinations": cudf.Series([2, 3, 3, 8, 1, 7, 3, 1, 5, 7], dtype="int64"), + "majors": cudf.Series([0, 0, 1, 2, 3, 3, 1, 3, 3, 3], dtype="int64"), + "minors": cudf.Series([2, 3, 3, 8, 1, 7, 3, 1, 5, 7], dtype="int64"), "hop_id": cudf.Series([0, 0, 0, 1, 1, 1, 2, 3, 3, 3], dtype="int32"), "edge_type": cudf.Series([0, 0, 0, 2, 1, 2, 0, 1, 2, 2], dtype="int32"), } From 061ada449b7b11fd39789aca59a65a663f631a3e Mon Sep 17 00:00:00 2001 From: Joseph Nke <76006812+jnke2016@users.noreply.github.com> Date: Wed, 4 Oct 2023 14:41:24 -0500 Subject: [PATCH 032/111] Increase dask-related timeouts for CI testing (#3907) This PR increases the minimum timeout when waiting for the workers to complete their tasks. Authors: - Joseph Nke (https://github.com/jnke2016) Approvers: - Brad Rees (https://github.com/BradReesWork) - Vibhu Jawa (https://github.com/VibhuJawa) - Rick Ratzel (https://github.com/rlratzel) - Jake Awe (https://github.com/AyodeAwe) URL: https://github.com/rapidsai/cugraph/pull/3907 --- ci/test_python.sh | 5 ++++- ci/test_wheel.sh | 6 +++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/ci/test_python.sh b/ci/test_python.sh index 825d5b242d5..df0f34377a3 100755 --- a/ci/test_python.sh +++ b/ci/test_python.sh @@ -65,7 +65,10 @@ popd rapids-logger "pytest cugraph" pushd python/cugraph/cugraph -export DASK_WORKER_DEVICES="0" +DASK_WORKER_DEVICES="0" \ +DASK_DISTRIBUTED__SCHEDULER__WORKER_TTL="1000s" \ +DASK_DISTRIBUTED__COMM__TIMEOUTS__CONNECT="1000s" \ +DASK_CUDA_WAIT_WORKERS_MIN_TIMEOUT=20 \ pytest \ -v \ --benchmark-disable \ diff --git a/ci/test_wheel.sh b/ci/test_wheel.sh index 3ac3549f143..d6ec67cd9e9 100755 --- a/ci/test_wheel.sh +++ b/ci/test_wheel.sh @@ -21,5 +21,9 @@ arch=$(uname -m) if [[ "${arch}" == "aarch64" && ${RAPIDS_BUILD_TYPE} == "pull-request" ]]; then python ./ci/wheel_smoke_test_${package_name}.py else - RAPIDS_DATASET_ROOT_DIR=`pwd`/datasets python -m pytest ./python/${package_name}/${python_package_name}/tests + RAPIDS_DATASET_ROOT_DIR=`pwd`/datasets \ + DASK_DISTRIBUTED__SCHEDULER__WORKER_TTL="1000s" \ + DASK_DISTRIBUTED__COMM__TIMEOUTS__CONNECT="1000s" \ + DASK_CUDA_WAIT_WORKERS_MIN_TIMEOUT=20 \ + python -m pytest ./python/${package_name}/${python_package_name}/tests fi From b3d2452071da4983714cbd490d652cc1e13a7381 Mon Sep 17 00:00:00 2001 From: Bradley Dice Date: Wed, 4 Oct 2023 15:53:25 -0500 Subject: [PATCH 033/111] CUDA 12.0 ARM conda support. (#3903) This PR builds conda packages using CUDA 12 on ARM. Closes #3864. Authors: - Bradley Dice (https://github.com/bdice) Approvers: - Ray Douglass (https://github.com/raydouglass) --- .github/workflows/build.yaml | 20 ++++++++++---------- .github/workflows/pr.yaml | 30 +++++++++++++++--------------- .github/workflows/test.yaml | 10 +++++----- 3 files changed, 30 insertions(+), 30 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index fb8fc6bea9d..b02b134b337 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -28,7 +28,7 @@ concurrency: jobs: cpp-build: secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/conda-cpp-build.yaml@branch-23.12 + uses: rapidsai/shared-action-workflows/.github/workflows/conda-cpp-build.yaml@cuda-120-arm with: build_type: ${{ inputs.build_type || 'branch' }} branch: ${{ inputs.branch }} @@ -38,7 +38,7 @@ jobs: python-build: needs: [cpp-build] secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/conda-python-build.yaml@branch-23.12 + uses: rapidsai/shared-action-workflows/.github/workflows/conda-python-build.yaml@cuda-120-arm with: build_type: ${{ inputs.build_type || 'branch' }} branch: ${{ inputs.branch }} @@ -47,7 +47,7 @@ jobs: upload-conda: needs: [cpp-build, python-build] secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/conda-upload-packages.yaml@branch-23.12 + uses: rapidsai/shared-action-workflows/.github/workflows/conda-upload-packages.yaml@cuda-120-arm with: build_type: ${{ inputs.build_type || 'branch' }} branch: ${{ inputs.branch }} @@ -57,7 +57,7 @@ jobs: if: github.ref_type == 'branch' needs: python-build secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/custom-job.yaml@branch-23.12 + uses: rapidsai/shared-action-workflows/.github/workflows/custom-job.yaml@cuda-120-arm with: arch: "amd64" branch: ${{ inputs.branch }} @@ -69,7 +69,7 @@ jobs: sha: ${{ inputs.sha }} wheel-build-pylibcugraph: secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/wheels-build.yaml@branch-23.12 + uses: rapidsai/shared-action-workflows/.github/workflows/wheels-build.yaml@cuda-120-arm with: build_type: ${{ inputs.build_type || 'branch' }} branch: ${{ inputs.branch }} @@ -82,7 +82,7 @@ jobs: wheel-publish-pylibcugraph: needs: wheel-build-pylibcugraph secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/wheels-publish.yaml@branch-23.12 + uses: rapidsai/shared-action-workflows/.github/workflows/wheels-publish.yaml@cuda-120-arm with: build_type: ${{ inputs.build_type || 'branch' }} branch: ${{ inputs.branch }} @@ -92,7 +92,7 @@ jobs: wheel-build-cugraph: needs: wheel-publish-pylibcugraph secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/wheels-build.yaml@branch-23.12 + uses: rapidsai/shared-action-workflows/.github/workflows/wheels-build.yaml@cuda-120-arm with: build_type: ${{ inputs.build_type || 'branch' }} branch: ${{ inputs.branch }} @@ -105,7 +105,7 @@ jobs: wheel-publish-cugraph: needs: wheel-build-cugraph secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/wheels-publish.yaml@branch-23.12 + uses: rapidsai/shared-action-workflows/.github/workflows/wheels-publish.yaml@cuda-120-arm with: build_type: ${{ inputs.build_type || 'branch' }} branch: ${{ inputs.branch }} @@ -115,7 +115,7 @@ jobs: wheel-build-nx-cugraph: needs: wheel-publish-pylibcugraph secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/wheels-build.yaml@branch-23.10 + uses: rapidsai/shared-action-workflows/.github/workflows/wheels-build.yaml@cuda-120-arm with: build_type: ${{ inputs.build_type || 'branch' }} branch: ${{ inputs.branch }} @@ -125,7 +125,7 @@ jobs: wheel-publish-nx-cugraph: needs: wheel-build-nx-cugraph secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/wheels-publish.yaml@branch-23.10 + uses: rapidsai/shared-action-workflows/.github/workflows/wheels-publish.yaml@cuda-120-arm with: build_type: ${{ inputs.build_type || 'branch' }} branch: ${{ inputs.branch }} diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml index 4e9b0ce1774..cd6c5457a93 100644 --- a/.github/workflows/pr.yaml +++ b/.github/workflows/pr.yaml @@ -27,41 +27,41 @@ jobs: - wheel-tests-nx-cugraph - devcontainer secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/pr-builder.yaml@branch-23.12 + uses: rapidsai/shared-action-workflows/.github/workflows/pr-builder.yaml@cuda-120-arm checks: secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/checks.yaml@branch-23.12 + uses: rapidsai/shared-action-workflows/.github/workflows/checks.yaml@cuda-120-arm with: enable_check_generated_files: false conda-cpp-build: needs: checks secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/conda-cpp-build.yaml@branch-23.12 + uses: rapidsai/shared-action-workflows/.github/workflows/conda-cpp-build.yaml@cuda-120-arm with: build_type: pull-request node_type: cpu32 conda-cpp-tests: needs: conda-cpp-build secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/conda-cpp-tests.yaml@branch-23.12 + uses: rapidsai/shared-action-workflows/.github/workflows/conda-cpp-tests.yaml@cuda-120-arm with: build_type: pull-request conda-python-build: needs: conda-cpp-build secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/conda-python-build.yaml@branch-23.12 + uses: rapidsai/shared-action-workflows/.github/workflows/conda-python-build.yaml@cuda-120-arm with: build_type: pull-request conda-python-tests: needs: conda-python-build secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/conda-python-tests.yaml@branch-23.12 + uses: rapidsai/shared-action-workflows/.github/workflows/conda-python-tests.yaml@cuda-120-arm with: build_type: pull-request conda-notebook-tests: needs: conda-python-build secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/custom-job.yaml@branch-23.12 + uses: rapidsai/shared-action-workflows/.github/workflows/custom-job.yaml@cuda-120-arm with: build_type: pull-request node_type: "gpu-v100-latest-1" @@ -71,7 +71,7 @@ jobs: docs-build: needs: conda-python-build secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/custom-job.yaml@branch-23.12 + uses: rapidsai/shared-action-workflows/.github/workflows/custom-job.yaml@cuda-120-arm with: build_type: pull-request node_type: "gpu-v100-latest-1" @@ -81,7 +81,7 @@ jobs: wheel-build-pylibcugraph: needs: checks secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/wheels-build.yaml@branch-23.12 + uses: rapidsai/shared-action-workflows/.github/workflows/wheels-build.yaml@cuda-120-arm with: build_type: pull-request script: ci/build_wheel_pylibcugraph.sh @@ -91,14 +91,14 @@ jobs: wheel-tests-pylibcugraph: needs: wheel-build-pylibcugraph secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/wheels-test.yaml@branch-23.12 + uses: rapidsai/shared-action-workflows/.github/workflows/wheels-test.yaml@cuda-120-arm with: build_type: pull-request script: ci/test_wheel_pylibcugraph.sh wheel-build-cugraph: needs: wheel-tests-pylibcugraph secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/wheels-build.yaml@branch-23.12 + uses: rapidsai/shared-action-workflows/.github/workflows/wheels-build.yaml@cuda-120-arm with: build_type: pull-request script: ci/build_wheel_cugraph.sh @@ -108,27 +108,27 @@ jobs: wheel-tests-cugraph: needs: wheel-build-cugraph secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/wheels-test.yaml@branch-23.12 + uses: rapidsai/shared-action-workflows/.github/workflows/wheels-test.yaml@cuda-120-arm with: build_type: pull-request script: ci/test_wheel_cugraph.sh wheel-build-nx-cugraph: needs: wheel-tests-pylibcugraph secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/wheels-build.yaml@branch-23.10 + uses: rapidsai/shared-action-workflows/.github/workflows/wheels-build.yaml@cuda-120-arm with: build_type: pull-request script: ci/build_wheel_nx-cugraph.sh wheel-tests-nx-cugraph: needs: wheel-build-nx-cugraph secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/wheels-test.yaml@branch-23.10 + uses: rapidsai/shared-action-workflows/.github/workflows/wheels-test.yaml@cuda-120-arm with: build_type: pull-request script: ci/test_wheel_nx-cugraph.sh devcontainer: secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/build-in-devcontainer.yaml@branch-23.10 + uses: rapidsai/shared-action-workflows/.github/workflows/build-in-devcontainer.yaml@cuda-120-arm with: extra-repo-deploy-key: CUGRAPH_OPS_SSH_PRIVATE_DEPLOY_KEY build_command: | diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index ed359d4f4ef..dc40eae5f0d 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -16,7 +16,7 @@ on: jobs: conda-cpp-tests: secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/conda-cpp-tests.yaml@branch-23.12 + uses: rapidsai/shared-action-workflows/.github/workflows/conda-cpp-tests.yaml@cuda-120-arm with: build_type: nightly branch: ${{ inputs.branch }} @@ -24,7 +24,7 @@ jobs: sha: ${{ inputs.sha }} conda-python-tests: secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/conda-python-tests.yaml@branch-23.12 + uses: rapidsai/shared-action-workflows/.github/workflows/conda-python-tests.yaml@cuda-120-arm with: build_type: nightly branch: ${{ inputs.branch }} @@ -32,7 +32,7 @@ jobs: sha: ${{ inputs.sha }} wheel-tests-pylibcugraph: secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/wheels-test.yaml@branch-23.12 + uses: rapidsai/shared-action-workflows/.github/workflows/wheels-test.yaml@cuda-120-arm with: build_type: nightly branch: ${{ inputs.branch }} @@ -41,7 +41,7 @@ jobs: script: ci/test_wheel_pylibcugraph.sh wheel-tests-cugraph: secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/wheels-test.yaml@branch-23.12 + uses: rapidsai/shared-action-workflows/.github/workflows/wheels-test.yaml@cuda-120-arm with: build_type: nightly branch: ${{ inputs.branch }} @@ -50,7 +50,7 @@ jobs: script: ci/test_wheel_cugraph.sh wheel-tests-nx-cugraph: secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/wheels-test.yaml@branch-23.10 + uses: rapidsai/shared-action-workflows/.github/workflows/wheels-test.yaml@cuda-120-arm with: build_type: nightly branch: ${{ inputs.branch }} From d03cb0fd33e072756f6d87fbe009b561ff48bafb Mon Sep 17 00:00:00 2001 From: Vibhu Jawa Date: Thu, 5 Oct 2023 09:31:01 -0700 Subject: [PATCH 034/111] Remove `dask_cudf` dataframe for the `_make_plc_graph` while creating `cugraph.Graph` (#3895) This PR attempts to fix https://github.com/rapidsai/cugraph/issues/3790 Please note that I have not being able to cause failure locally so it is really hard for me to know if it actually fixes anything or not . MRE being used to test locally: https://gist.github.com/VibhuJawa/4b1ec24022b6e2dd7879cd2e8d3fab67 CC: @jnke2016 , @rlratzel , CC: @rjzamora , Please let me know what i can do better here. Authors: - Vibhu Jawa (https://github.com/VibhuJawa) - Brad Rees (https://github.com/BradReesWork) Approvers: - Rick Ratzel (https://github.com/rlratzel) - Joseph Nke (https://github.com/jnke2016) URL: https://github.com/rapidsai/cugraph/pull/3895 --- .../cugraph/cugraph/dask/common/part_utils.py | 62 ++++++++++++++++--- .../simpleDistributedGraph.py | 24 ++++--- 2 files changed, 71 insertions(+), 15 deletions(-) diff --git a/python/cugraph/cugraph/dask/common/part_utils.py b/python/cugraph/cugraph/dask/common/part_utils.py index 7c0aad6c3ee..25311902b29 100644 --- a/python/cugraph/cugraph/dask/common/part_utils.py +++ b/python/cugraph/cugraph/dask/common/part_utils.py @@ -99,19 +99,65 @@ def _chunk_lst(ls, num_parts): return [ls[i::num_parts] for i in range(num_parts)] -def persist_dask_df_equal_parts_per_worker(dask_df, client): +def persist_dask_df_equal_parts_per_worker( + dask_df, client, return_type="dask_cudf.DataFrame" +): + """ + Persist dask_df with equal parts per worker + Args: + dask_df: dask_cudf.DataFrame + client: dask.distributed.Client + return_type: str, "dask_cudf.DataFrame" or "dict" + Returns: + persisted_keys: dict of {worker: [persisted_keys]} + """ + if return_type not in ["dask_cudf.DataFrame", "dict"]: + raise ValueError("return_type must be either 'dask_cudf.DataFrame' or 'dict'") + ddf_keys = dask_df.to_delayed() workers = client.scheduler_info()["workers"].keys() ddf_keys_ls = _chunk_lst(ddf_keys, len(workers)) - persisted_keys = [] + persisted_keys_d = {} for w, ddf_k in zip(workers, ddf_keys_ls): - persisted_keys.extend( - client.persist(ddf_k, workers=w, allow_other_workers=False) + persisted_keys_d[w] = client.compute( + ddf_k, workers=w, allow_other_workers=False, pure=False ) - dask_df = dask_cudf.from_delayed(persisted_keys, meta=dask_df._meta).persist() - wait(dask_df) - client.rebalance(dask_df) - return dask_df + + persisted_keys_ls = [ + item for sublist in persisted_keys_d.values() for item in sublist + ] + wait(persisted_keys_ls) + if return_type == "dask_cudf.DataFrame": + dask_df = dask_cudf.from_delayed( + persisted_keys_ls, meta=dask_df._meta + ).persist() + wait(dask_df) + return dask_df + + return persisted_keys_d + + +def get_length_of_parts(persisted_keys_d, client): + """ + Get the length of each partition + Args: + persisted_keys_d: dict of {worker: [persisted_keys]} + client: dask.distributed.Client + Returns: + length_of_parts: dict of {worker: [length_of_parts]} + """ + length_of_parts = {} + for w, p_keys in persisted_keys_d.items(): + length_of_parts[w] = [ + client.submit( + len, p_key, pure=False, workers=[w], allow_other_workers=False + ) + for p_key in p_keys + ] + + for w, len_futures in length_of_parts.items(): + length_of_parts[w] = client.gather(len_futures) + return length_of_parts async def _extract_partitions( diff --git a/python/cugraph/cugraph/structure/graph_implementation/simpleDistributedGraph.py b/python/cugraph/cugraph/structure/graph_implementation/simpleDistributedGraph.py index fa94fa67625..935d0c597d4 100644 --- a/python/cugraph/cugraph/structure/graph_implementation/simpleDistributedGraph.py +++ b/python/cugraph/cugraph/structure/graph_implementation/simpleDistributedGraph.py @@ -36,6 +36,7 @@ from cugraph.structure.symmetrize import symmetrize from cugraph.dask.common.part_utils import ( get_persisted_df_worker_map, + get_length_of_parts, persist_dask_df_equal_parts_per_worker, ) from cugraph.dask import get_n_workers @@ -318,9 +319,14 @@ def __from_edgelist( is_symmetric=not self.properties.directed, ) ddf = ddf.repartition(npartitions=len(workers) * 2) - ddf = persist_dask_df_equal_parts_per_worker(ddf, _client) - num_edges = len(ddf) - ddf = get_persisted_df_worker_map(ddf, _client) + persisted_keys_d = persist_dask_df_equal_parts_per_worker( + ddf, _client, return_type="dict" + ) + del ddf + length_of_parts = get_length_of_parts(persisted_keys_d, _client) + num_edges = sum( + [item for sublist in length_of_parts.values() for item in sublist] + ) delayed_tasks_d = { w: delayed(simpleDistributedGraphImpl._make_plc_graph)( Comms.get_session_id(), @@ -331,14 +337,16 @@ def __from_edgelist( store_transposed, num_edges, ) - for w, edata in ddf.items() + for w, edata in persisted_keys_d.items() } - # FIXME: For now, don't delete the copied dataframe to avoid crash self._plc_graph = { - w: _client.compute(delayed_task, workers=w, allow_other_workers=False) + w: _client.compute( + delayed_task, workers=w, allow_other_workers=False, pure=False + ) for w, delayed_task in delayed_tasks_d.items() } wait(list(self._plc_graph.values())) + del persisted_keys_d del delayed_tasks_d _client.run(gc.collect) @@ -1192,5 +1200,7 @@ def _get_column_from_ls_dfs(lst_df, col_name): if len_df == 0: return lst_df[0][col_name] output_col = cudf.concat([df[col_name] for df in lst_df], ignore_index=True) - # FIXME: For now, don't delete the copied dataframe to avoid cras + for df in lst_df: + df.drop(columns=[col_name], inplace=True) + gc.collect() return output_col From 2c1626ff84ac0c56fb2e686c3c9e0202ee97e1b9 Mon Sep 17 00:00:00 2001 From: Rick Ratzel <3039903+rlratzel@users.noreply.github.com> Date: Fri, 6 Oct 2023 12:18:24 -0500 Subject: [PATCH 035/111] Adds benchmarks for `nx-cugraph` (#3854) closes rapidsai/graph_dl#299 This PR adds new benchmarks for `nx-cugraph` which can be used to compare performance for NetworkX with and without the `cugraph` backend. These benchmarks depend on `pytest`, `pytest-benchmark`, `networkx>=3.0`, `cugraph` (only for the Dataset APIs), and `nx-cugraph` Some results can be seen here: ![image](https://github.com/rapidsai/cugraph/assets/3039903/d595eb3f-2ae6-4b2e-a0b1-a0f0bcb050a8) ![image](https://github.com/rapidsai/cugraph/assets/3039903/3ca367b4-14b8-4673-91d2-f83a947163e7) ### Other changes: * `black` is now run on all python sources under `benchmarks`, which resulted in several format-only changes. * Several other updates to versions, etc. were applied to various .yaml files in order to resolve CI errors. * Merged in changes from [jnke2016/branch-23.10_increase-timeout](https://github.com/rapidsai/cugraph/pull/3854/commits/cbdbd8a6d22903f251c64b1b0321ba41ec03fcb3) to get changes needed to fix dask CI failure. Authors: - Rick Ratzel (https://github.com/rlratzel) - Joseph Nke (https://github.com/jnke2016) - AJ Schmidt (https://github.com/ajschmidt8) - Naim (https://github.com/naimnv) - Seunghwa Kang (https://github.com/seunghwak) - https://github.com/lmeyerov - Erik Welch (https://github.com/eriknw) - Alex Barghi (https://github.com/alexbarghi-nv) - Vyas Ramasubramani (https://github.com/vyasr) - Divye Gala (https://github.com/divyegala) - Tingyu Wang (https://github.com/tingyu66) - Chuck Hastings (https://github.com/ChuckHastings) - ralph (https://github.com/nv-rliu) - Paul Taylor (https://github.com/trxcllnt) Approvers: - Erik Welch (https://github.com/eriknw) - Ray Douglass (https://github.com/raydouglass) URL: https://github.com/rapidsai/cugraph/pull/3854 --- .../cuda11.8-conda/devcontainer.json | 4 +- .devcontainer/cuda11.8-pip/devcontainer.json | 6 +- .../cuda12.0-conda/devcontainer.json | 4 +- .devcontainer/cuda12.0-pip/devcontainer.json | 6 +- .github/workflows/pr.yaml | 2 +- .pre-commit-config.yaml | 2 +- ...nch_cugraph_dgl_uniform_neighbor_sample.py | 51 +- .../dgl_benchmark.py | 41 +- .../python-script/ogbn_mag_benchmark.py | 89 +-- .../scale-benchmarks/dgl_benchmark.py | 71 ++- .../scale-benchmarks/load_graph_feats.py | 75 ++- .../cugraph-dgl/scale-benchmarks/model.py | 54 +- .../pytest-based/bench_cgs_client_scaling.py | 22 +- .../cugraph/pytest-based/bench_algos.py | 185 +++--- benchmarks/cugraph/pytest-based/conftest.py | 65 ++- benchmarks/cugraph/standalone/benchmark.py | 60 +- .../bulk_sampling/cugraph_bulk_sampling.py | 552 ++++++++++-------- .../cugraph/standalone/cugraph_dask_funcs.py | 67 ++- .../cugraph/standalone/cugraph_funcs.py | 58 +- .../standalone/cugraph_graph_creation.py | 2 +- benchmarks/cugraph/standalone/main.py | 200 ++++--- .../cugraph/standalone/pylibcugraph_bench.py | 47 +- benchmarks/cugraph/standalone/reporting.py | 12 +- benchmarks/dgl/create_dataset.py | 10 +- benchmarks/dgl/pytest-based/dgl_benchmark.py | 114 ++-- .../nx-cugraph/pytest-based/bench_algos.py | 212 +++++++ benchmarks/pytest.ini | 4 +- .../test_client_bandwidth.py | 37 +- .../test_cugraph_sampling.py | 35 +- .../python/cugraph_benchmarking/params.py | 158 +++-- ci/release/update-version.sh | 4 + ci/test_python.sh | 5 +- ci/test_wheel.sh | 6 +- .../all_cuda-118_arch-x86_64.yaml | 1 + .../all_cuda-120_arch-x86_64.yaml | 1 + dependencies.yaml | 84 +-- .../cugraph_dgl/dataloading/__init__.py | 2 +- .../cugraph_dgl/dataloading/dataloader.py | 49 +- .../cugraph_dgl/dataloading/dataset.py | 37 +- .../dataloading/utils/sampling_helpers.py | 155 ++++- .../cugraph-dgl/cugraph_dgl/nn/conv/base.py | 7 + python/cugraph-dgl/tests/test_utils.py | 28 + .../gnn/feature_storage/feat_storage.py | 147 ++++- .../tests/data_store/test_gnn_feat_storage.py | 1 - .../test_gnn_feat_storage_wholegraph.py | 85 +++ python/nx-cugraph/_nx_cugraph/__init__.py | 2 +- 46 files changed, 1955 insertions(+), 904 deletions(-) create mode 100644 benchmarks/nx-cugraph/pytest-based/bench_algos.py create mode 100644 python/cugraph/cugraph/tests/data_store/test_gnn_feat_storage_wholegraph.py diff --git a/.devcontainer/cuda11.8-conda/devcontainer.json b/.devcontainer/cuda11.8-conda/devcontainer.json index cf4ba5aa114..8cfe0e35990 100644 --- a/.devcontainer/cuda11.8-conda/devcontainer.json +++ b/.devcontainer/cuda11.8-conda/devcontainer.json @@ -5,12 +5,12 @@ "args": { "CUDA": "11.8", "PYTHON_PACKAGE_MANAGER": "conda", - "BASE": "rapidsai/devcontainers:23.10-cpp-llvm16-cuda11.8-mambaforge-ubuntu22.04" + "BASE": "rapidsai/devcontainers:23.12-cpp-llvm16-cuda11.8-mambaforge-ubuntu22.04" } }, "hostRequirements": {"gpu": "optional"}, "features": { - "ghcr.io/rapidsai/devcontainers/features/rapids-build-utils:23.10": {} + "ghcr.io/rapidsai/devcontainers/features/rapids-build-utils:23.12": {} }, "overrideFeatureInstallOrder": [ "ghcr.io/rapidsai/devcontainers/features/rapids-build-utils" diff --git a/.devcontainer/cuda11.8-pip/devcontainer.json b/.devcontainer/cuda11.8-pip/devcontainer.json index e86a38abbde..fd3f8d22f89 100644 --- a/.devcontainer/cuda11.8-pip/devcontainer.json +++ b/.devcontainer/cuda11.8-pip/devcontainer.json @@ -5,13 +5,13 @@ "args": { "CUDA": "11.8", "PYTHON_PACKAGE_MANAGER": "pip", - "BASE": "rapidsai/devcontainers:23.10-cpp-llvm16-cuda11.8-ubuntu22.04" + "BASE": "rapidsai/devcontainers:23.12-cpp-llvm16-cuda11.8-ubuntu22.04" } }, "hostRequirements": {"gpu": "optional"}, "features": { - "ghcr.io/rapidsai/devcontainers/features/ucx:23.10": {"version": "1.14.1"}, - "ghcr.io/rapidsai/devcontainers/features/rapids-build-utils:23.10": {} + "ghcr.io/rapidsai/devcontainers/features/ucx:23.12": {"version": "1.14.1"}, + "ghcr.io/rapidsai/devcontainers/features/rapids-build-utils:23.12": {} }, "overrideFeatureInstallOrder": [ "ghcr.io/rapidsai/devcontainers/features/rapids-build-utils" diff --git a/.devcontainer/cuda12.0-conda/devcontainer.json b/.devcontainer/cuda12.0-conda/devcontainer.json index 863eeea48ff..bc4a2cb6fb4 100644 --- a/.devcontainer/cuda12.0-conda/devcontainer.json +++ b/.devcontainer/cuda12.0-conda/devcontainer.json @@ -5,12 +5,12 @@ "args": { "CUDA": "12.0", "PYTHON_PACKAGE_MANAGER": "conda", - "BASE": "rapidsai/devcontainers:23.10-cpp-mambaforge-ubuntu22.04" + "BASE": "rapidsai/devcontainers:23.12-cpp-mambaforge-ubuntu22.04" } }, "hostRequirements": {"gpu": "optional"}, "features": { - "ghcr.io/rapidsai/devcontainers/features/rapids-build-utils:23.10": {} + "ghcr.io/rapidsai/devcontainers/features/rapids-build-utils:23.12": {} }, "overrideFeatureInstallOrder": [ "ghcr.io/rapidsai/devcontainers/features/rapids-build-utils" diff --git a/.devcontainer/cuda12.0-pip/devcontainer.json b/.devcontainer/cuda12.0-pip/devcontainer.json index c7612771fd3..abff840ebdd 100644 --- a/.devcontainer/cuda12.0-pip/devcontainer.json +++ b/.devcontainer/cuda12.0-pip/devcontainer.json @@ -5,13 +5,13 @@ "args": { "CUDA": "12.0", "PYTHON_PACKAGE_MANAGER": "pip", - "BASE": "rapidsai/devcontainers:23.10-cpp-llvm16-cuda12.0-ubuntu22.04" + "BASE": "rapidsai/devcontainers:23.12-cpp-llvm16-cuda12.0-ubuntu22.04" } }, "hostRequirements": {"gpu": "optional"}, "features": { - "ghcr.io/rapidsai/devcontainers/features/ucx:23.10": {"version": "1.14.1"}, - "ghcr.io/rapidsai/devcontainers/features/rapids-build-utils:23.10": {} + "ghcr.io/rapidsai/devcontainers/features/ucx:23.12": {"version": "1.14.1"}, + "ghcr.io/rapidsai/devcontainers/features/rapids-build-utils:23.12": {} }, "overrideFeatureInstallOrder": [ "ghcr.io/rapidsai/devcontainers/features/rapids-build-utils" diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml index cd6c5457a93..e8834e58ace 100644 --- a/.github/workflows/pr.yaml +++ b/.github/workflows/pr.yaml @@ -133,5 +133,5 @@ jobs: extra-repo-deploy-key: CUGRAPH_OPS_SSH_PRIVATE_DEPLOY_KEY build_command: | sccache -z; - build-all --verbose; + build-all --verbose -j$(nproc --ignore=1); sccache -s; diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 865d06b20e4..bab39557c99 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ repos: - id: black language_version: python3 args: [--target-version=py38] - files: ^python/ + files: ^(python/.*|benchmarks/.*)$ - repo: https://github.com/PyCQA/flake8 rev: 6.0.0 hooks: diff --git a/benchmarks/cugraph-dgl/pytest-based/bench_cugraph_dgl_uniform_neighbor_sample.py b/benchmarks/cugraph-dgl/pytest-based/bench_cugraph_dgl_uniform_neighbor_sample.py index eeee163b0af..dc961223cb1 100644 --- a/benchmarks/cugraph-dgl/pytest-based/bench_cugraph_dgl_uniform_neighbor_sample.py +++ b/benchmarks/cugraph-dgl/pytest-based/bench_cugraph_dgl_uniform_neighbor_sample.py @@ -21,6 +21,7 @@ import pytest import numpy as np import cupy as cp + # Facing issues with rapids-pytest-benchmark plugin # pytest-benchmark. import pytest_benchmark @@ -33,6 +34,7 @@ import dgl import torch import rmm + _seed = 42 @@ -71,21 +73,21 @@ def create_graph(graph_data): else: raise TypeError(f"graph_data can only be str or dict, got {type(graph_data)}") - num_nodes = max(edgelist_df['src'].max(), - edgelist_df['dst'].max())+1 + num_nodes = max(edgelist_df["src"].max(), edgelist_df["dst"].max()) + 1 - num_nodes_dict = {'_N':num_nodes} + num_nodes_dict = {"_N": num_nodes} gs = CuGraphStorage(num_nodes_dict=num_nodes_dict, single_gpu=True) - gs.add_edge_data(edgelist_df, - # reverse to make same graph as cugraph - node_col_names=['dst', 'src'], - canonical_etype=['_N', 'connects', '_N']) + gs.add_edge_data( + edgelist_df, + # reverse to make same graph as cugraph + node_col_names=["dst", "src"], + canonical_etype=["_N", "connects", "_N"], + ) return gs - def create_mg_graph(graph_data): """ Create a graph instance based on the data to be loaded/generated. @@ -93,7 +95,9 @@ def create_mg_graph(graph_data): # range starts at 1 to let let 0 be used by benchmark/client process visible_devices = os.getenv("DASK_WORKER_DEVICES", "1,2,3,4") - cluster = LocalCUDACluster(protocol='ucx', rmm_pool_size='25GB', CUDA_VISIBLE_DEVICES=visible_devices) + cluster = LocalCUDACluster( + protocol="ucx", rmm_pool_size="25GB", CUDA_VISIBLE_DEVICES=visible_devices + ) client = Client(cluster) Comms.initialize(p2p=True) rmm.reinitialize(pool_allocator=True) @@ -126,25 +130,23 @@ def create_mg_graph(graph_data): else: raise TypeError(f"graph_data can only be str or dict, got {type(graph_data)}") - num_nodes = max(edgelist_df['src'].max().compute(), - edgelist_df['dst'].max().compute()) + num_nodes = max( + edgelist_df["src"].max().compute(), edgelist_df["dst"].max().compute() + ) # running into issues with smaller partitions - edgelist_df = edgelist_df.repartition(npartitions=edgelist_df.npartitions*2) + edgelist_df = edgelist_df.repartition(npartitions=edgelist_df.npartitions * 2) - num_nodes_dict = {'_N':num_nodes} + num_nodes_dict = {"_N": num_nodes} - gs = CuGraphStorage(num_nodes_dict=num_nodes_dict, single_gpu=False) - gs.add_edge_data(edgelist_df, - node_col_names=['dst', 'src'], - canonical_etype=['_N', 'C', '_N']) + gs = CuGraphStorage(num_nodes_dict=num_nodes_dict, single_gpu=False) + gs.add_edge_data( + edgelist_df, node_col_names=["dst", "src"], canonical_etype=["_N", "C", "_N"] + ) return (gs, client, cluster) - -def get_uniform_neighbor_sample_args( - G, seed, batch_size, fanout, with_replacement -): +def get_uniform_neighbor_sample_args(G, seed, batch_size, fanout, with_replacement): """ Return a dictionary containing the args for uniform_neighbor_sample based on the graph and desired args passed in. For example, if a large start list @@ -165,7 +167,7 @@ def get_uniform_neighbor_sample_args( else: num_start_verts = batch_size - srcs = G.graphstore.gdata.get_edge_data()['_SRC_'] + srcs = G.graphstore.gdata.get_edge_data()["_SRC_"] start_list = srcs.head(num_start_verts) assert len(start_list) == num_start_verts @@ -205,7 +207,6 @@ def graph_objs(request): dask_cluster.close() - ################################################################################ # Benchmarks @pytest.mark.parametrize("batch_size", params.batch_sizes.values()) @@ -223,7 +224,7 @@ def bench_cugraph_dgl_uniform_neighbor_sample( # Reverse to match cugraph # DGL does from dst to src - fanout_val = uns_args['fanout'] + fanout_val = uns_args["fanout"] fanout_val.reverse() sampler = dgl.dataloading.NeighborSampler(uns_args["fanout"]) sampler_f = sampler.sample_blocks @@ -231,7 +232,7 @@ def bench_cugraph_dgl_uniform_neighbor_sample( # Warmup _ = sampler_f(g=G, seed_nodes=uns_args["seed_nodes"]) # print(f"\n{uns_args}") - result_seed_nodes, output_nodes, blocks = benchmark( + result_seed_nodes, output_nodes, blocks = benchmark( sampler_f, g=G, seed_nodes=uns_args["seed_nodes"], diff --git a/benchmarks/cugraph-dgl/python-script/dgl_dataloading_benchmark/dgl_benchmark.py b/benchmarks/cugraph-dgl/python-script/dgl_dataloading_benchmark/dgl_benchmark.py index 0a52703c546..fc7543a91e0 100644 --- a/benchmarks/cugraph-dgl/python-script/dgl_dataloading_benchmark/dgl_benchmark.py +++ b/benchmarks/cugraph-dgl/python-script/dgl_dataloading_benchmark/dgl_benchmark.py @@ -52,13 +52,9 @@ def load_edges_from_disk(parquet_path, replication_factor, input_meta): src_ls = [ei["src"]] dst_ls = [ei["dst"]] for r in range(1, replication_factor): - new_src = ei["src"] + ( - r * input_meta["num_nodes"][can_edge_type[0]] - ) + new_src = ei["src"] + (r * input_meta["num_nodes"][can_edge_type[0]]) src_ls.append(new_src) - new_dst = ei["dst"] + ( - r * input_meta["num_nodes"][can_edge_type[2]] - ) + new_dst = ei["dst"] + (r * input_meta["num_nodes"][can_edge_type[2]]) dst_ls.append(new_dst) ei["src"] = torch.cat(src_ls).contiguous() @@ -92,16 +88,11 @@ def load_node_labels(dataset_path, replication_factor, input_meta): ] ), "label": pd.concat( - [ - node_label.label - for r in range(1, replication_factor) - ] + [node_label.label for r in range(1, replication_factor)] ), } ) - node_label = pd.concat([node_label, dfr]).reset_index( - drop=True - ) + node_label = pd.concat([node_label, dfr]).reset_index(drop=True) node_label_tensor = torch.full( (num_nodes_dict[node_type],), -1, dtype=torch.float32 @@ -133,9 +124,7 @@ def create_dgl_graph_from_disk(dataset_path, replication_factor=1): input_meta = json.load(f) parquet_path = os.path.join(dataset_path, "parquet") - graph_data = load_edges_from_disk( - parquet_path, replication_factor, input_meta - ) + graph_data = load_edges_from_disk(parquet_path, replication_factor, input_meta) node_data = load_node_labels(dataset_path, replication_factor, input_meta) g = dgl.heterograph(graph_data) @@ -154,7 +143,7 @@ def create_dataloader(g, train_idx, batch_size, fanouts, use_uva): Returns: DGLGraph: DGLGraph with the loaded dataset. """ - + print("Creating dataloader", flush=True) st = time.time() if use_uva: @@ -220,21 +209,21 @@ def dataloading_benchmark(g, train_idx, fanouts, batch_sizes, use_uva): print("==============================================") return time_ls + def set_seed(seed): random.seed(seed) np.random.seed(seed) torch.manual_seed(seed) torch.cuda.manual_seed_all(seed) + if __name__ == "__main__": parser = ArgumentParser() parser.add_argument( "--dataset_path", type=str, default="/datasets/abarghi/ogbn_papers100M" ) parser.add_argument("--replication_factors", type=str, default="1,2,4,8") - parser.add_argument( - "--fanouts", type=str, default="25_25,10_10_10,5_10_20" - ) + parser.add_argument("--fanouts", type=str, default="25_25,10_10_10,5_10_20") parser.add_argument("--batch_sizes", type=str, default="512,1024") parser.add_argument("--do_not_use_uva", action="store_true") parser.add_argument("--seed", type=int, default=42) @@ -267,22 +256,16 @@ def set_seed(seed): et = time.time() print(f"Replication factor = {replication_factor}") print( - f"G has {g.num_edges()} edges and took", - f" {et - st:.2f} seconds to load" + f"G has {g.num_edges()} edges and took", f" {et - st:.2f} seconds to load" ) train_idx = {"paper": node_data["paper"]["train_idx"]} r_time_ls = dataloading_benchmark( g, train_idx, fanouts, batch_sizes, use_uva=use_uva ) - print( - "Benchmark completed for replication factor = ", replication_factor - ) + print("Benchmark completed for replication factor = ", replication_factor) print("==============================================") # Add replication factor to the time list - [ - x.update({"replication_factor": replication_factor}) - for x in r_time_ls - ] + [x.update({"replication_factor": replication_factor}) for x in r_time_ls] time_ls.extend(r_time_ls) df = pd.DataFrame(time_ls) diff --git a/benchmarks/cugraph-dgl/python-script/ogbn_mag_benchmark.py b/benchmarks/cugraph-dgl/python-script/ogbn_mag_benchmark.py index 7c8b352d7af..539fe333b1e 100644 --- a/benchmarks/cugraph-dgl/python-script/ogbn_mag_benchmark.py +++ b/benchmarks/cugraph-dgl/python-script/ogbn_mag_benchmark.py @@ -16,15 +16,17 @@ import torch.nn.functional as F import time import argparse + ## DGL Specific Import from cugraph_dgl import c + def load_dgl_graph(): from ogb.nodeproppred import DglNodePropPredDataset - dataset = DglNodePropPredDataset(name="ogbn-mag", root='/datasets/vjawa/gnn/') + dataset = DglNodePropPredDataset(name="ogbn-mag", root="/datasets/vjawa/gnn/") split_idx = dataset.get_idx_split() - g, labels = dataset[0] + g, labels = dataset[0] # Uncomment for obgn-mag labels = labels["paper"].flatten() # labels = labels @@ -33,26 +35,30 @@ def load_dgl_graph(): return g, labels, dataset.num_classes, split_idx -def sampling_func(g, seed_nodes,labels, train_loader): +def sampling_func(g, seed_nodes, labels, train_loader): category = "paper" for input_nodes, seeds, blocks in train_loader: seeds = seeds[category] - feat = blocks[0].srcdata['feat']['paper'] + feat = blocks[0].srcdata["feat"]["paper"] return None if __name__ == "__main__": - parser = argparse.ArgumentParser(description='Benchmark Sampling') - parser.add_argument('--batch_size', type=int, default=100_000) - parser.add_argument('--use_cugraph',dest='use_cugraph', action='store_true', default=True) - parser.add_argument('--use_dgl_upstream', dest='use_cugraph', action='store_false') - parser.add_argument('--single_gpu', dest='single_gpu', action='store_true', default=True) - parser.add_argument('--multi_gpu', dest='single_gpu', action='store_false') - parser.add_argument('--n_gpus', type=int, default=1) - + parser = argparse.ArgumentParser(description="Benchmark Sampling") + parser.add_argument("--batch_size", type=int, default=100_000) + parser.add_argument( + "--use_cugraph", dest="use_cugraph", action="store_true", default=True + ) + parser.add_argument("--use_dgl_upstream", dest="use_cugraph", action="store_false") + parser.add_argument( + "--single_gpu", dest="single_gpu", action="store_true", default=True + ) + parser.add_argument("--multi_gpu", dest="single_gpu", action="store_false") + parser.add_argument("--n_gpus", type=int, default=1) + args = parser.parse_args() print(args, flush=True) - + single_gpu = args.single_gpu use_cugraph = args.use_cugraph batch_size = args.batch_size @@ -60,42 +66,49 @@ def sampling_func(g, seed_nodes,labels, train_loader): if single_gpu: import rmm - rmm.reinitialize(pool_allocator=True,initial_pool_size=5e+9, maximum_pool_size=22e+9) + + rmm.reinitialize( + pool_allocator=True, initial_pool_size=5e9, maximum_pool_size=22e9 + ) else: #### Dask Cluster from dask_cuda import LocalCUDACluster from cugraph.dask.comms import comms as Comms from dask.distributed import Client - #Change according to your GPUS - #Client at GPU-0 - #Workers at specifed GPUS - #UCX seems to be freezing :-0 on DGX - cuda_visible_devices = ','.join([str(i) for i in range(1,n_gpus+1)]) - cluster = LocalCUDACluster(CUDA_VISIBLE_DEVICES=cuda_visible_devices, protocol='tcp', rmm_pool_size='12 GB', - jit_unspill=True) + # Change according to your GPUS + # Client at GPU-0 + # Workers at specifed GPUS + # UCX seems to be freezing :-0 on DGX + cuda_visible_devices = ",".join([str(i) for i in range(1, n_gpus + 1)]) + cluster = LocalCUDACluster( + CUDA_VISIBLE_DEVICES=cuda_visible_devices, + protocol="tcp", + rmm_pool_size="12 GB", + jit_unspill=True, + ) client = Client(cluster) Comms.initialize(p2p=True) - - - device = 'cuda' + + device = "cuda" g, labels, num_classes, split_idx = load_dgl_graph() g = g.to(device) - + if use_cugraph: if not single_gpu: g = g.int() g = cugraph_storage_from_heterograph(g, single_gpu=single_gpu) - - - indx_type = g.idtype - subset_split_idx = {'train': {k: v.to(device).to(g.idtype) for k,v in split_idx['train'].items()}, - 'valid' : {k: v.to(device).to(g.idtype) for k,v in split_idx['valid'].items()}, - 'test' : {k: v.to(device).to(g.idtype) for k,v in split_idx['test'].items()}, - } + indx_type = g.idtype + subset_split_idx = { + "train": {k: v.to(device).to(g.idtype) for k, v in split_idx["train"].items()}, + "valid": {k: v.to(device).to(g.idtype) for k, v in split_idx["valid"].items()}, + "test": {k: v.to(device).to(g.idtype) for k, v in split_idx["test"].items()}, + } - sampler = dgl.dataloading.MultiLayerNeighborSampler([20,25], prefetch_node_feats={'paper':['feat']}) + sampler = dgl.dataloading.MultiLayerNeighborSampler( + [20, 25], prefetch_node_feats={"paper": ["feat"]} + ) train_loader = dgl.dataloading.DataLoader( g, subset_split_idx["train"], @@ -107,10 +120,10 @@ def sampling_func(g, seed_nodes,labels, train_loader): ) ### Warmup RUN - sampling_func(g, subset_split_idx['train'],labels, train_loader) - + sampling_func(g, subset_split_idx["train"], labels, train_loader) + ### Benchmarking RUN st = time.time() - sampling_func(g, subset_split_idx['train'],labels, train_loader) - et = time.time() - print(f"Sampling time taken = {et-st} s") \ No newline at end of file + sampling_func(g, subset_split_idx["train"], labels, train_loader) + et = time.time() + print(f"Sampling time taken = {et-st} s") diff --git a/benchmarks/cugraph-dgl/scale-benchmarks/dgl_benchmark.py b/benchmarks/cugraph-dgl/scale-benchmarks/dgl_benchmark.py index 3762226d570..3e0b8e3e0cc 100644 --- a/benchmarks/cugraph-dgl/scale-benchmarks/dgl_benchmark.py +++ b/benchmarks/cugraph-dgl/scale-benchmarks/dgl_benchmark.py @@ -22,6 +22,7 @@ from argparse import ArgumentParser from load_graph_feats import load_edges_from_disk, load_node_labels, load_node_features + class DataLoaderArgs: def __init__(self, args): self.dataset_path = args.dataset_path @@ -31,7 +32,6 @@ def __init__(self, args): self.use_uva = not args.do_not_use_uva - def create_dataloader(g, train_idx, batch_size, fanouts, use_uva): print("Creating dataloader", flush=True) st = time.time() @@ -53,7 +53,6 @@ def create_dataloader(g, train_idx, batch_size, fanouts, use_uva): return dataloader - def create_dgl_graph_from_disk(dataset_path, replication_factor=1): """ Create a DGL graph from a dataset on disk. @@ -67,14 +66,14 @@ def create_dgl_graph_from_disk(dataset_path, replication_factor=1): input_meta = json.load(f) parquet_path = os.path.join(dataset_path, "parquet") - graph_data = load_edges_from_disk( - parquet_path, replication_factor, input_meta - ) + graph_data = load_edges_from_disk(parquet_path, replication_factor, input_meta) label_data = load_node_labels(dataset_path, replication_factor, input_meta) - if replication_factor <8 : - feat_data = load_node_features(dataset_path, replication_factor, node_type='paper') + if replication_factor < 8: + feat_data = load_node_features( + dataset_path, replication_factor, node_type="paper" + ) else: - feat_data = None + feat_data = None print("labels and features loaded ", flush=True) g = dgl.heterograph(graph_data) @@ -83,31 +82,49 @@ def create_dgl_graph_from_disk(dataset_path, replication_factor=1): def main(args): - print(f"Running dgl dataloading benchmark with the following parameters:\n" - f"Dataset path = {args.dataset_path}\n" - f"Replication factors = {args.replication_factors}\n" - f"Fanouts = {args.fanouts}\n" - f"Batch sizes = {args.batch_sizes}\n" - f"Use UVA = {args.use_uva}\n" - f"{'=' * 30}") + print( + f"Running dgl dataloading benchmark with the following parameters:\n" + f"Dataset path = {args.dataset_path}\n" + f"Replication factors = {args.replication_factors}\n" + f"Fanouts = {args.fanouts}\n" + f"Batch sizes = {args.batch_sizes}\n" + f"Use UVA = {args.use_uva}\n" + f"{'=' * 30}" + ) time_ls = [] for replication_factor in args.replication_factors: start_time = time.time() - g, label_data, feat_data = create_dgl_graph_from_disk(args.dataset_path, replication_factor) + g, label_data, feat_data = create_dgl_graph_from_disk( + args.dataset_path, replication_factor + ) elapsed_time = time.time() - start_time - print(f"Replication factor = {replication_factor}\n" - f"G has {g.num_edges():,} edges and took {elapsed_time:.2f} seconds to load", flush=True) + print( + f"Replication factor = {replication_factor}\n" + f"G has {g.num_edges():,} edges and took {elapsed_time:.2f} seconds to load", + flush=True, + ) train_idx = {"paper": label_data["paper"]["train_idx"]} y = label_data["paper"]["y"] - r_time_ls = e2e_benchmark(g, feat_data, y, train_idx, args.fanouts, args.batch_sizes, use_uva=args.use_uva) + r_time_ls = e2e_benchmark( + g, + feat_data, + y, + train_idx, + args.fanouts, + args.batch_sizes, + use_uva=args.use_uva, + ) [x.update({"replication_factor": replication_factor}) for x in r_time_ls] - [x.update({"num_edges": g.num_edges()}) for x in r_time_ls] + [x.update({"num_edges": g.num_edges()}) for x in r_time_ls] time_ls.extend(r_time_ls) - print(f"Benchmark completed for replication factor = {replication_factor}\n{'=' * 30}", flush=True) + print( + f"Benchmark completed for replication factor = {replication_factor}\n{'=' * 30}", + flush=True, + ) df = pd.DataFrame(time_ls) df.to_csv("dgl_e2e_benchmark.csv", index=False) @@ -131,22 +148,26 @@ def e2e_benchmark(g, feat, y, train_idx, fanouts, batch_sizes, use_uva): for fanout in fanouts: for batch_size in batch_sizes: dataloader = create_dataloader(g, train_idx, batch_size, fanout, use_uva) - time_d = run_1_epoch(dataloader, feat, y, fanout, batch_size, model_backend='dgl') + time_d = run_1_epoch( + dataloader, feat, y, fanout, batch_size, model_backend="dgl" + ) time_ls.append(time_d) - print("="*30) + print("=" * 30) return time_ls - def parse_arguments(): parser = ArgumentParser() - parser.add_argument("--dataset_path", type=str, default="/raid/vjawa/ogbn_papers100M") + parser.add_argument( + "--dataset_path", type=str, default="/raid/vjawa/ogbn_papers100M" + ) parser.add_argument("--replication_factors", type=str, default="2") parser.add_argument("--fanouts", type=str, default="10_10_10") parser.add_argument("--batch_sizes", type=str, default="512,1024,8192,16384") parser.add_argument("--do_not_use_uva", action="store_true") return parser.parse_args() + if __name__ == "__main__": arguments = parse_arguments() main(DataLoaderArgs(arguments)) diff --git a/benchmarks/cugraph-dgl/scale-benchmarks/load_graph_feats.py b/benchmarks/cugraph-dgl/scale-benchmarks/load_graph_feats.py index 4f0f81c70e1..f38d4432d46 100644 --- a/benchmarks/cugraph-dgl/scale-benchmarks/load_graph_feats.py +++ b/benchmarks/cugraph-dgl/scale-benchmarks/load_graph_feats.py @@ -30,17 +30,23 @@ def load_edges_from_disk(parquet_path, replication_factor, input_meta): graph_data = {} for edge_type in input_meta["num_edges"].keys(): - print(f"Loading edge index for edge type {edge_type} for replication factor = {replication_factor}") + print( + f"Loading edge index for edge type {edge_type} for replication factor = {replication_factor}" + ) canonical_edge_type = tuple(edge_type.split("__")) - edge_index = pd.read_parquet(os.path.join(parquet_path, edge_type, "edge_index.parquet")) + edge_index = pd.read_parquet( + os.path.join(parquet_path, edge_type, "edge_index.parquet") + ) edge_index = { "src": torch.from_numpy(edge_index.src.values), "dst": torch.from_numpy(edge_index.dst.values), } if replication_factor > 1: - src_list, dst_list = replicate_edges(edge_index, canonical_edge_type, replication_factor, input_meta) + src_list, dst_list = replicate_edges( + edge_index, canonical_edge_type, replication_factor, input_meta + ) edge_index["src"] = torch.cat(src_list).contiguous() edge_index["dst"] = torch.cat(dst_list).contiguous() @@ -55,26 +61,35 @@ def replicate_edges(edge_index, canonical_edge_type, replication_factor, input_m dst_list = [edge_index["dst"]] for r in range(1, replication_factor): - new_src = edge_index["src"] + (r * input_meta["num_nodes"][canonical_edge_type[0]]) - new_dst = edge_index["dst"] + (r * input_meta["num_nodes"][canonical_edge_type[2]]) + new_src = edge_index["src"] + ( + r * input_meta["num_nodes"][canonical_edge_type[0]] + ) + new_dst = edge_index["dst"] + ( + r * input_meta["num_nodes"][canonical_edge_type[2]] + ) src_list.append(new_src) dst_list.append(new_dst) return src_list, dst_list - - def load_node_labels(dataset_path, replication_factor, input_meta): - num_nodes_dict = {node_type: t * replication_factor for node_type, t in input_meta["num_nodes"].items()} + num_nodes_dict = { + node_type: t * replication_factor + for node_type, t in input_meta["num_nodes"].items() + } node_data = {} for node_type in input_meta["num_nodes"].keys(): node_data[node_type] = {} - label_path = os.path.join(dataset_path, "parquet", node_type, "node_label.parquet") + label_path = os.path.join( + dataset_path, "parquet", node_type, "node_label.parquet" + ) if os.path.exists(label_path): - node_data[node_type] = process_node_label(label_path, node_type, replication_factor, num_nodes_dict, input_meta) + node_data[node_type] = process_node_label( + label_path, node_type, replication_factor, num_nodes_dict, input_meta + ) else: node_data[node_type]["num_nodes"] = num_nodes_dict[node_type] @@ -82,30 +97,48 @@ def load_node_labels(dataset_path, replication_factor, input_meta): print("Loaded node labels", flush=True) return node_data -def process_node_label(label_path, node_type, replication_factor, num_nodes_dict, input_meta): + +def process_node_label( + label_path, node_type, replication_factor, num_nodes_dict, input_meta +): node_label = pd.read_parquet(label_path) if replication_factor > 1: - node_label = replicate_node_label(node_label, node_type, replication_factor, input_meta) + node_label = replicate_node_label( + node_label, node_type, replication_factor, input_meta + ) - node_label_tensor = torch.full((num_nodes_dict[node_type],), -1, dtype=torch.float32) - node_label_tensor[torch.as_tensor(node_label.node.values)] = torch.as_tensor(node_label.label.values) + node_label_tensor = torch.full( + (num_nodes_dict[node_type],), -1, dtype=torch.float32 + ) + node_label_tensor[torch.as_tensor(node_label.node.values)] = torch.as_tensor( + node_label.label.values + ) del node_label return { "train_idx": (node_label_tensor > -1).contiguous().nonzero().view(-1), - "y": node_label_tensor.contiguous().long() + "y": node_label_tensor.contiguous().long(), } def replicate_node_label(node_label, node_type, replication_factor, input_meta): base_num_nodes = input_meta["num_nodes"][node_type] - replicated_df = pd.DataFrame({ - "node": pd.concat([node_label.node + (r * base_num_nodes) for r in range(1, replication_factor)]), - "label": pd.concat([node_label.label for _ in range(1, replication_factor)]) - }) + replicated_df = pd.DataFrame( + { + "node": pd.concat( + [ + node_label.node + (r * base_num_nodes) + for r in range(1, replication_factor) + ] + ), + "label": pd.concat( + [node_label.label for _ in range(1, replication_factor)] + ), + } + ) return pd.concat([node_label, replicated_df]).reset_index(drop=True) @@ -114,10 +147,10 @@ def load_node_features(dataset_path, replication_factor, node_type): print("Loading node features", flush=True) node_type_path = os.path.join(dataset_path, "npy", node_type) if replication_factor == 1: - fname = os.path.join(node_type_path, "node_feat.npy") + fname = os.path.join(node_type_path, "node_feat.npy") else: fname = os.path.join(node_type_path, f"node_feat_{replication_factor}x.npy") - + feat = torch.from_numpy(np.load(fname)) print("Loaded node features", flush=True) return feat diff --git a/benchmarks/cugraph-dgl/scale-benchmarks/model.py b/benchmarks/cugraph-dgl/scale-benchmarks/model.py index 506e3bd5227..08ae0e8b1ee 100644 --- a/benchmarks/cugraph-dgl/scale-benchmarks/model.py +++ b/benchmarks/cugraph-dgl/scale-benchmarks/model.py @@ -17,8 +17,15 @@ class GNN(torch.nn.Module): - def __init__(self, in_channels, hidden_channels, out_channels, num_layers, model_backend='dgl'): - if model_backend == 'dgl': + def __init__( + self, + in_channels, + hidden_channels, + out_channels, + num_layers, + model_backend="dgl", + ): + if model_backend == "dgl": from dgl.nn import SAGEConv else: from cugraph_dgl.nn import SAGEConv @@ -26,9 +33,13 @@ def __init__(self, in_channels, hidden_channels, out_channels, num_layers, model super(GNN, self).__init__() self.convs = torch.nn.ModuleList() for _ in range(num_layers - 1): - self.convs.append(SAGEConv(in_channels, hidden_channels, aggregator_type='mean')) + self.convs.append( + SAGEConv(in_channels, hidden_channels, aggregator_type="mean") + ) in_channels = hidden_channels - self.convs.append(SAGEConv(hidden_channels, out_channels, aggregator_type='mean')) + self.convs.append( + SAGEConv(hidden_channels, out_channels, aggregator_type="mean") + ) def forward(self, blocks, x): for i, conv in enumerate(self.convs): @@ -38,45 +49,47 @@ def forward(self, blocks, x): return x -def create_model(feat_size, num_classes, num_layers, model_backend='dgl'): +def create_model(feat_size, num_classes, num_layers, model_backend="dgl"): model = GNN(feat_size, 64, num_classes, num_layers, model_backend=model_backend) - model = model.to('cuda') + model = model.to("cuda") model.train() return model + def train_model(model, dataloader, opt, feat, y): - times = {key: 0 for key in ['mfg_creation', 'feature', 'm_fwd', 'm_bkwd']} + times = {key: 0 for key in ["mfg_creation", "feature", "m_fwd", "m_bkwd"]} epoch_st = time.time() mfg_st = time.time() for input_nodes, output_nodes, blocks in dataloader: - times['mfg_creation'] += time.time() - mfg_st + times["mfg_creation"] += time.time() - mfg_st if feat is not None: fst = time.time() - input_nodes = input_nodes.to('cpu') + input_nodes = input_nodes.to("cpu") input_feat = feat[input_nodes] - input_feat = input_feat.to('cuda') + input_feat = input_feat.to("cuda") if isinstance(output_nodes, dict): - output_nodes = output_nodes['paper'] + output_nodes = output_nodes["paper"] output_nodes = output_nodes.to(y.device) - y_batch = y[output_nodes].to('cuda') - times['feature'] += time.time() - fst + y_batch = y[output_nodes].to("cuda") + times["feature"] += time.time() - fst m_fwd_st = time.time() y_hat = model(blocks, input_feat) - times['m_fwd'] += time.time() - m_fwd_st - + times["m_fwd"] += time.time() - m_fwd_st + m_bkwd_st = time.time() loss = F.cross_entropy(y_hat, y_batch) opt.zero_grad() loss.backward() opt.step() - times['m_bkwd'] += time.time() - m_bkwd_st + times["m_bkwd"] += time.time() - m_bkwd_st mfg_st = time.time() print(f"Epoch time = {time.time() - epoch_st:.2f} seconds") - + return times + def analyze_time(dataloader, times, epoch_time, fanout, batch_size): num_batches = len(dataloader) time_d = { @@ -92,13 +105,16 @@ def analyze_time(dataloader, times, epoch_time, fanout, batch_size): print(f"Time analysis for fanout = {fanout}, batch_size = {batch_size}") for k in time_d.keys(): - if 'time_per_epoch' in str(k): + if "time_per_epoch" in str(k): print(f"{k} = {time_d[k]:.2f} seconds") return time_d + def run_1_epoch(dataloader, feat, y, fanout, batch_size, model_backend): if feat is not None: - model = create_model(feat.shape[1], 172, len(fanout), model_backend=model_backend) + model = create_model( + feat.shape[1], 172, len(fanout), model_backend=model_backend + ) opt = torch.optim.Adam(model.parameters(), lr=0.01) else: model = None diff --git a/benchmarks/cugraph-service/pytest-based/bench_cgs_client_scaling.py b/benchmarks/cugraph-service/pytest-based/bench_cgs_client_scaling.py index 9871ce64a31..541b3e23490 100644 --- a/benchmarks/cugraph-service/pytest-based/bench_cgs_client_scaling.py +++ b/benchmarks/cugraph-service/pytest-based/bench_cgs_client_scaling.py @@ -46,11 +46,8 @@ def running_server_for_sampling_with_graph(request): control returns to this fixture, the graph is deleted. If the fixture started a server subprocess, then it is terminated as well. """ - (client, server_process) = ( - utils.ensure_running_server_for_sampling(host=_host, - port=_port, - dask_scheduler_file=None, - start_local_cuda_cluster=True) + (client, server_process) = utils.ensure_running_server_for_sampling( + host=_host, port=_port, dask_scheduler_file=None, start_local_cuda_cluster=True ) num_edges = (2**_graph_scale) * _edge_factor gid = client.call_graph_creation_extension( @@ -80,9 +77,7 @@ def create_sampling_client(host, port, graph_id): """ client = CugraphServiceClient(host, port) fanout_vals = [10, 25] - start_list = client.call_extension( - "gen_vertex_list", graph_id, _batch_size - ) + start_list = client.call_extension("gen_vertex_list", graph_id, _batch_size) def sampling_function(result_device): return client.uniform_neighbor_sample( @@ -108,9 +103,11 @@ def sampling_function(result_device): # Use the benchmark fixture once it can be run in a way where each run is timed # with other running clients. + @pytest.mark.parametrize("num_clients", params.num_clients.values()) -def bench_cgs_client_scaling_individual_time(running_server_for_sampling_with_graph, - num_clients): +def bench_cgs_client_scaling_individual_time( + running_server_for_sampling_with_graph, num_clients +): graph_id = running_server_for_sampling_with_graph @@ -133,8 +130,9 @@ def bench_cgs_client_scaling_individual_time(running_server_for_sampling_with_gr @pytest.mark.parametrize("num_clients", params.num_clients.values()) -def bench_cgs_client_scaling_total_time(running_server_for_sampling_with_graph, - num_clients): +def bench_cgs_client_scaling_total_time( + running_server_for_sampling_with_graph, num_clients +): graph_id = running_server_for_sampling_with_graph diff --git a/benchmarks/cugraph/pytest-based/bench_algos.py b/benchmarks/cugraph/pytest-based/bench_algos.py index d7fcb7812e4..69d6700ca3d 100644 --- a/benchmarks/cugraph/pytest-based/bench_algos.py +++ b/benchmarks/cugraph/pytest-based/bench_algos.py @@ -14,13 +14,16 @@ import pytest import numpy as np import pytest_benchmark + # FIXME: Remove this when rapids_pytest_benchmark.gpubenchmark is available # everywhere try: from rapids_pytest_benchmark import setFixtureParamNames except ImportError: - print("\n\nWARNING: rapids_pytest_benchmark is not installed, " - "falling back to pytest_benchmark fixtures.\n") + print( + "\n\nWARNING: rapids_pytest_benchmark is not installed, " + "falling back to pytest_benchmark fixtures.\n" + ) # if rapids_pytest_benchmark is not available, just perfrom time-only # benchmarking and replace the util functions with nops @@ -29,6 +32,7 @@ def setFixtureParamNames(*args, **kwargs): pass + import rmm import dask_cudf from pylibcugraph.testing import gen_fixture_params_product @@ -65,7 +69,7 @@ def get_edgelist(self, fetch=False): if self._edgelist is None: self._edgelist = rmat( self._scale, - (2**self._scale)*self._edgefactor, + (2**self._scale) * self._edgefactor, 0.57, # from Graph500 0.19, # from Graph500 0.19, # from Graph500 @@ -73,22 +77,25 @@ def get_edgelist(self, fetch=False): clip_and_flip=False, scramble_vertex_ids=True, create_using=None, # return edgelist instead of Graph instance - mg=self.mg + mg=self.mg, ) rng = np.random.default_rng(seed) if self.mg: self._edgelist["weight"] = self._edgelist.map_partitions( - lambda df: rng.random(size=len(df))) + lambda df: rng.random(size=len(df)) + ) else: self._edgelist["weight"] = rng.random(size=len(self._edgelist)) return self._edgelist - def get_graph(self, - fetch=False, - create_using=cugraph.Graph, - ignore_weights=False, - store_transposed=False): + def get_graph( + self, + fetch=False, + create_using=cugraph.Graph, + ignore_weights=False, + store_transposed=False, + ): if isinstance(create_using, cugraph.Graph): # what about BFS if trnaposed is True attrs = {"directed": create_using.is_directed()} @@ -99,17 +106,21 @@ def get_graph(self, edge_attr = None if ignore_weights else "weight" df = self.get_edgelist() if isinstance(df, dask_cudf.DataFrame): - G.from_dask_cudf_edgelist(df, - source="src", - destination="dst", - edge_attr=edge_attr, - store_transposed=store_transposed) + G.from_dask_cudf_edgelist( + df, + source="src", + destination="dst", + edge_attr=edge_attr, + store_transposed=store_transposed, + ) else: - G.from_cudf_edgelist(df, - source="src", - destination="dst", - edge_attr=edge_attr, - store_transposed=store_transposed) + G.from_cudf_edgelist( + df, + source="src", + destination="dst", + edge_attr=edge_attr, + store_transposed=store_transposed, + ) return G def get_path(self): @@ -125,26 +136,27 @@ def unload(self): _rmat_scale = getattr(pytest, "_rmat_scale", 20) # ~1M vertices _rmat_edgefactor = getattr(pytest, "_rmat_edgefactor", 16) # ~17M edges -rmat_sg_dataset = pytest.param(RmatDataset(scale=_rmat_scale, - edgefactor=_rmat_edgefactor, - mg=False), - marks=[pytest.mark.rmat_data, - pytest.mark.sg, - ]) -rmat_mg_dataset = pytest.param(RmatDataset(scale=_rmat_scale, - edgefactor=_rmat_edgefactor, - mg=True), - marks=[pytest.mark.rmat_data, - pytest.mark.mg, - ]) +rmat_sg_dataset = pytest.param( + RmatDataset(scale=_rmat_scale, edgefactor=_rmat_edgefactor, mg=False), + marks=[ + pytest.mark.rmat_data, + pytest.mark.sg, + ], +) +rmat_mg_dataset = pytest.param( + RmatDataset(scale=_rmat_scale, edgefactor=_rmat_edgefactor, mg=True), + marks=[ + pytest.mark.rmat_data, + pytest.mark.mg, + ], +) rmm_fixture_params = gen_fixture_params_product( - (managed_memory, "mm"), - (pool_allocator, "pa")) + (managed_memory, "mm"), (pool_allocator, "pa") +) dataset_fixture_params = gen_fixture_params_product( - (directed_datasets + - undirected_datasets + - [rmat_sg_dataset, rmat_mg_dataset], "ds")) + (directed_datasets + undirected_datasets + [rmat_sg_dataset, rmat_mg_dataset], "ds") +) # Record the current RMM settings so reinitialize() will be called only when a # change is needed (RMM defaults both values to False). The --allow-rmm-reinit @@ -153,8 +165,7 @@ def unload(self): # (see conftest.py for details). # The defaults for managed_mem (False) and pool_alloc (True) are set in # conftest.py -RMM_SETTINGS = {"managed_mem": False, - "pool_alloc": False} +RMM_SETTINGS = {"managed_mem": False, "pool_alloc": False} # FIXME: this only changes the RMM config in a SG environment. The dask config # that applies to RMM in an MG environment is not changed by this! @@ -163,16 +174,16 @@ def reinitRMM(managed_mem, pool_alloc): Reinitializes RMM to the value of managed_mem and pool_alloc, but only if those values are different that the current configuration. """ - if (managed_mem != RMM_SETTINGS["managed_mem"]) or \ - (pool_alloc != RMM_SETTINGS["pool_alloc"]): + if (managed_mem != RMM_SETTINGS["managed_mem"]) or ( + pool_alloc != RMM_SETTINGS["pool_alloc"] + ): rmm.reinitialize( managed_memory=managed_mem, pool_allocator=pool_alloc, - initial_pool_size=2 << 27 + initial_pool_size=2 << 27, ) - RMM_SETTINGS.update(managed_mem=managed_mem, - pool_alloc=pool_alloc) + RMM_SETTINGS.update(managed_mem=managed_mem, pool_alloc=pool_alloc) ############################################################################### @@ -185,8 +196,8 @@ def reinitRMM(managed_mem, pool_alloc): # For benchmarks, the operations performed in fixtures are not measured as part # of the benchmark. -@pytest.fixture(scope="module", - params=rmm_fixture_params) + +@pytest.fixture(scope="module", params=rmm_fixture_params) def rmm_config(request): # Since parameterized fixtures do not assign param names to param values, # manually call the helper to do so. Ensure the order of the name list @@ -196,8 +207,7 @@ def rmm_config(request): reinitRMM(request.param[0], request.param[1]) -@pytest.fixture(scope="module", - params=dataset_fixture_params) +@pytest.fixture(scope="module", params=dataset_fixture_params) def dataset(request, rmm_config): """ @@ -261,27 +271,29 @@ def is_graph_distributed(graph): ############################################################################### # Benchmarks def bench_create_graph(gpubenchmark, edgelist): - gpubenchmark(cugraph.from_cudf_edgelist, - edgelist, - source="src", destination="dst", - create_using=cugraph.structure.graph_classes.Graph, - renumber=False) + gpubenchmark( + cugraph.from_cudf_edgelist, + edgelist, + source="src", + destination="dst", + create_using=cugraph.structure.graph_classes.Graph, + renumber=False, + ) # Creating directed Graphs on small datasets runs in micro-seconds, which # results in thousands of rounds before the default threshold is met, so lower # the max_time for this benchmark. -@pytest.mark.benchmark( - warmup=True, - warmup_iterations=10, - max_time=0.005 -) +@pytest.mark.benchmark(warmup=True, warmup_iterations=10, max_time=0.005) def bench_create_digraph(gpubenchmark, edgelist): - gpubenchmark(cugraph.from_cudf_edgelist, - edgelist, - source="src", destination="dst", - create_using=cugraph.Graph(directed=True), - renumber=False) + gpubenchmark( + cugraph.from_cudf_edgelist, + edgelist, + source="src", + destination="dst", + create_using=cugraph.Graph(directed=True), + renumber=False, + ) def bench_renumber(gpubenchmark, edgelist): @@ -289,8 +301,11 @@ def bench_renumber(gpubenchmark, edgelist): def bench_pagerank(gpubenchmark, transposed_graph): - pagerank = dask_cugraph.pagerank if is_graph_distributed(transposed_graph) \ - else cugraph.pagerank + pagerank = ( + dask_cugraph.pagerank + if is_graph_distributed(transposed_graph) + else cugraph.pagerank + ) gpubenchmark(pagerank, transposed_graph) @@ -319,7 +334,8 @@ def bench_jaccard(gpubenchmark, unweighted_graph): @pytest.mark.skipif( - is_device_version_less_than((7, 0)), reason="Not supported on Pascal") + is_device_version_less_than((7, 0)), reason="Not supported on Pascal" +) def bench_louvain(gpubenchmark, graph): louvain = dask_cugraph.louvain if is_graph_distributed(graph) else cugraph.louvain gpubenchmark(louvain, graph) @@ -342,8 +358,11 @@ def bench_overlap(gpubenchmark, unweighted_graph): def bench_triangle_count(gpubenchmark, graph): - tc = dask_cugraph.triangle_count if is_graph_distributed(graph) \ - else cugraph.triangle_count + tc = ( + dask_cugraph.triangle_count + if is_graph_distributed(graph) + else cugraph.triangle_count + ) gpubenchmark(tc, graph) @@ -353,12 +372,13 @@ def bench_spectralBalancedCutClustering(gpubenchmark, graph): gpubenchmark(cugraph.spectralBalancedCutClustering, graph, 2) -@pytest.mark.skip(reason="Need to guarantee graph has weights, " - "not doing that yet") +@pytest.mark.skip(reason="Need to guarantee graph has weights, " "not doing that yet") def bench_spectralModularityMaximizationClustering(gpubenchmark, graph): - smmc = dask_cugraph.spectralModularityMaximizationClustering \ - if is_graph_distributed(graph) \ - else cugraph.spectralModularityMaximizationClustering + smmc = ( + dask_cugraph.spectralModularityMaximizationClustering + if is_graph_distributed(graph) + else cugraph.spectralModularityMaximizationClustering + ) gpubenchmark(smmc, graph, 2) @@ -373,8 +393,11 @@ def bench_graph_degrees(gpubenchmark, graph): def bench_betweenness_centrality(gpubenchmark, graph): - bc = dask_cugraph.betweenness_centrality if is_graph_distributed(graph) \ - else cugraph.betweenness_centrality + bc = ( + dask_cugraph.betweenness_centrality + if is_graph_distributed(graph) + else cugraph.betweenness_centrality + ) gpubenchmark(bc, graph, k=10, random_state=123) @@ -385,8 +408,11 @@ def bench_edge_betweenness_centrality(gpubenchmark, graph): def bench_uniform_neighbor_sample(gpubenchmark, graph): - uns = dask_cugraph.uniform_neighbor_sample if is_graph_distributed(graph) \ - else cugraph.uniform_neighbor_sample + uns = ( + dask_cugraph.uniform_neighbor_sample + if is_graph_distributed(graph) + else cugraph.uniform_neighbor_sample + ) seed = 42 # FIXME: may need to provide number_of_vertices separately @@ -405,8 +431,9 @@ def bench_uniform_neighbor_sample(gpubenchmark, graph): def bench_egonet(gpubenchmark, graph): - egonet = dask_cugraph.ego_graph if is_graph_distributed(graph) \ - else cugraph.ego_graph + egonet = ( + dask_cugraph.ego_graph if is_graph_distributed(graph) else cugraph.ego_graph + ) n = 1 radius = 2 gpubenchmark(egonet, graph, n, radius=radius) diff --git a/benchmarks/cugraph/pytest-based/conftest.py b/benchmarks/cugraph/pytest-based/conftest.py index fd029471869..57329366a13 100644 --- a/benchmarks/cugraph/pytest-based/conftest.py +++ b/benchmarks/cugraph/pytest-based/conftest.py @@ -15,31 +15,37 @@ def pytest_addoption(parser): - parser.addoption("--allow-rmm-reinit", - action="store_true", - default=False, - help="Allow RMM to be reinitialized, possibly multiple times within " - "the same process, in order to run benchmarks with different managed " - "memory and pool allocator options. This is not the default behavior " - "since it does not represent a typical use case, and support for " - "this may be limited. Instead, consider multiple pytest runs that " - "use a fixed set of RMM settings.") - parser.addoption("--rmat-scale", - action="store", - type=int, - default=20, - metavar="scale", - help="For use when using synthetic graph data generated using RMAT. " - "This results in a graph with 2^scale vertices. Default is " - "%(default)s.") - parser.addoption("--rmat-edgefactor", - action="store", - type=int, - default=16, - metavar="edgefactor", - help="For use when using synthetic graph data generated using RMAT. " - "This results in a graph with (2^scale)*edgefactor edges. Default " - "is %(default)s.") + parser.addoption( + "--allow-rmm-reinit", + action="store_true", + default=False, + help="Allow RMM to be reinitialized, possibly multiple times within " + "the same process, in order to run benchmarks with different managed " + "memory and pool allocator options. This is not the default behavior " + "since it does not represent a typical use case, and support for " + "this may be limited. Instead, consider multiple pytest runs that " + "use a fixed set of RMM settings.", + ) + parser.addoption( + "--rmat-scale", + action="store", + type=int, + default=20, + metavar="scale", + help="For use when using synthetic graph data generated using RMAT. " + "This results in a graph with 2^scale vertices. Default is " + "%(default)s.", + ) + parser.addoption( + "--rmat-edgefactor", + action="store", + type=int, + default=16, + metavar="edgefactor", + help="For use when using synthetic graph data generated using RMAT. " + "This results in a graph with (2^scale)*edgefactor edges. Default " + "is %(default)s.", + ) def pytest_sessionstart(session): @@ -57,10 +63,11 @@ def pytest_sessionstart(session): if session.config.getoption("allow_rmm_reinit") is False: currentMarkexpr = session.config.getoption("markexpr") - if ("managedmem" in currentMarkexpr) or \ - ("poolallocator" in currentMarkexpr): - raise RuntimeError("managedmem and poolallocator markers cannot " - "be used without --allow-rmm-reinit.") + if ("managedmem" in currentMarkexpr) or ("poolallocator" in currentMarkexpr): + raise RuntimeError( + "managedmem and poolallocator markers cannot " + "be used without --allow-rmm-reinit." + ) newMarkexpr = "managedmem_off and poolallocator_on" if currentMarkexpr: diff --git a/benchmarks/cugraph/standalone/benchmark.py b/benchmarks/cugraph/standalone/benchmark.py index e02194648a6..74b73d9d8ff 100644 --- a/benchmarks/cugraph/standalone/benchmark.py +++ b/benchmarks/cugraph/standalone/benchmark.py @@ -21,6 +21,7 @@ class BenchmarkedResult: Class to hold results (the return value of the callable being benchmarked and meta-data about the benchmarked function) of a benchmarked function run. """ + def __init__(self, name, retval, runtime, params=None): self.name = name self.retval = retval @@ -45,16 +46,18 @@ def benchmark(func): functions to benchmark. """ benchmark_name = getattr(func, "benchmark_name", func.__name__) + @wraps(func) def benchmark_wrapper(*func_args, **func_kwargs): t1 = time.perf_counter() retval = func(*func_args, **func_kwargs) t2 = time.perf_counter() - return BenchmarkedResult(name=benchmark_name, - retval=retval, - runtime=(t2-t1), - params=func_kwargs, - ) + return BenchmarkedResult( + name=benchmark_name, + retval=retval, + runtime=(t2 - t1), + params=func_kwargs, + ) # Assign the name to the returned callable as well for use in debug prints, # etc. @@ -68,17 +71,21 @@ class BenchmarkRun: method, and results are saved as BenchmarkedResult instances in the results list member. """ - def __init__(self, - input_dataframe, - construct_graph_func, - algo_func_param_list, - algo_validator_list=None - ): + + def __init__( + self, + input_dataframe, + construct_graph_func, + algo_func_param_list, + algo_validator_list=None, + ): self.input_dataframe = input_dataframe if type(construct_graph_func) is tuple: - (construct_graph_func, - self.construct_graph_func_args) = construct_graph_func + ( + construct_graph_func, + self.construct_graph_func_args, + ) = construct_graph_func else: self.construct_graph_func_args = None @@ -89,15 +96,15 @@ def __init__(self, # add starting node to algos: BFS and SSSP # FIXME: Refactor BenchmarkRun __init__ because all the work # done below should be done elsewhere - for i, algo in enumerate (algo_func_param_list): + for i, algo in enumerate(algo_func_param_list): if benchmark(algo).name in ["bfs", "sssp", "uniform_neighbor_sample"]: - param={} - param["start"]=self.input_dataframe['src'].head()[0] + param = {} + param["start"] = self.input_dataframe["src"].head()[0] if benchmark(algo).name in ["uniform_neighbor_sample"]: start = [param.pop("start")] param["start_list"] = start param["fanout_vals"] = [1] - algo_func_param_list[i]=(algo,)+(param,) + algo_func_param_list[i] = (algo,) + (param,) self.algos = [] for item in algo_func_param_list: @@ -110,13 +117,11 @@ def __init__(self, self.validators = algo_validator_list or [None] * len(self.algos) self.results = [] - @staticmethod def __log(s, end="\n"): print(s, end=end) sys.stdout.flush() - def run(self): """ Run and time the graph construction step, then run and time each algo. @@ -124,8 +129,9 @@ def run(self): self.results = [] self.__log(f"running {self.construct_graph.name}...", end="") - result = self.construct_graph(self.input_dataframe, - *self.construct_graph_func_args) + result = self.construct_graph( + self.input_dataframe, *self.construct_graph_func_args + ) self.__log("done.") G = result.retval self.results.append(result) @@ -141,22 +147,21 @@ def run(self): if self.construct_graph.name == "from_dask_cudf_edgelist": # compute out_degree before renumbering because out_degree # has transpose=False - degree_max = G.degree()['degree'].max().compute() + degree_max = G.degree()["degree"].max().compute() katz_alpha = 1 / (degree_max) self.algos[i][1]["alpha"] = katz_alpha elif self.construct_graph.name == "from_cudf_edgelist": - degree_max = G.degree()['degree'].max() + degree_max = G.degree()["degree"].max() katz_alpha = 1 / (degree_max) self.algos[i][1]["alpha"] = katz_alpha if hasattr(G, "compute_renumber_edge_list"): - G.compute_renumber_edge_list( - transposed=True) + G.compute_renumber_edge_list(transposed=True) else: # FIXME: Pagerank still follows the old path. Update this once it # follows the pylibcugraph/C path if hasattr(G, "compute_renumber_edge_list"): G.compute_renumber_edge_list(transposed=True) - else: #set transpose=False when renumbering + else: # set transpose=False when renumbering self.__log("running compute_renumber_edge_list...", end="") if hasattr(G, "compute_renumber_edge_list"): if self.algos[i][0].name in ["wcc", "louvain"]: @@ -164,8 +169,7 @@ def run(self): # Update this once it follows the pylibcugraph/C path G.compute_renumber_edge_list(transposed=False) else: - G.compute_renumber_edge_list( - transposed=False) + G.compute_renumber_edge_list(transposed=False) self.__log("done.") # FIXME: need to handle individual algo args for ((algo, params), validator) in zip(self.algos, self.validators): diff --git a/benchmarks/cugraph/standalone/bulk_sampling/cugraph_bulk_sampling.py b/benchmarks/cugraph/standalone/bulk_sampling/cugraph_bulk_sampling.py index d2a3716da8a..a8c0658767d 100644 --- a/benchmarks/cugraph/standalone/bulk_sampling/cugraph_bulk_sampling.py +++ b/benchmarks/cugraph/standalone/bulk_sampling/cugraph_bulk_sampling.py @@ -66,19 +66,19 @@ def construct_graph(dask_dataframe): Returns: G: cugraph.Graph """ - assert dask_dataframe['src'].dtype == 'int64' - assert dask_dataframe['dst'].dtype == 'int64' + assert dask_dataframe["src"].dtype == "int64" + assert dask_dataframe["dst"].dtype == "int64" - if 'etp' in dask_dataframe.columns: - assert dask_dataframe['etp'].dtype == 'int32' + if "etp" in dask_dataframe.columns: + assert dask_dataframe["etp"].dtype == "int32" G = cugraph.MultiGraph(directed=True) G.from_dask_cudf_edgelist( dask_dataframe, - source="src", + source="src", destination="dst", - edge_type='etp' if 'etp' in dask_dataframe.columns else None, - renumber=False + edge_type="etp" if "etp" in dask_dataframe.columns else None, + renumber=False, ) return G @@ -86,66 +86,87 @@ def construct_graph(dask_dataframe): def symmetrize_ddf(dask_dataframe): source_col, dest_col = symmetrize( dask_dataframe, - 'src', - 'dst', + "src", + "dst", multi=True, symmetrize=True, ) new_ddf = source_col.to_frame() - new_ddf['dst'] = dest_col + new_ddf["dst"] = dest_col return new_ddf + def renumber_ddf(dask_df, persist=False): - vertices = dask_cudf.concat([dask_df['src'], dask_df['dst']]).unique().reset_index(drop=True) + vertices = ( + dask_cudf.concat([dask_df["src"], dask_df["dst"]]) + .unique() + .reset_index(drop=True) + ) if persist: vertices = vertices.persist() - - vertices.name = 'v' - vertices = vertices.reset_index().set_index('v').rename(columns={'index': 'm'}) + + vertices.name = "v" + vertices = vertices.reset_index().set_index("v").rename(columns={"index": "m"}) if persist: vertices = vertices.persist() - src = dask_df.merge(vertices, left_on='src', right_on='v', how='left').m.rename('src') - dst = dask_df.merge(vertices, left_on='dst', right_on='v', how='left').m.rename('dst') + src = dask_df.merge(vertices, left_on="src", right_on="v", how="left").m.rename( + "src" + ) + dst = dask_df.merge(vertices, left_on="dst", right_on="v", how="left").m.rename( + "dst" + ) df = src.to_frame() - df['dst'] = dst + df["dst"] = dst return df.reset_index(drop=True) -def _make_batch_ids(bdf: cudf.DataFrame, batch_size: int, num_workers: int, partition_info: Optional[Union[dict, str]] = None): + +def _make_batch_ids( + bdf: cudf.DataFrame, + batch_size: int, + num_workers: int, + partition_info: Optional[Union[dict, str]] = None, +): # Required by dask; need to skip dummy partitions. if partition_info is None: - return cudf.DataFrame({ - 'batch': cudf.Series(dtype='int32'), - 'start': cudf.Series(dtype='int64') - }) - - partition = partition_info['number'] + return cudf.DataFrame( + {"batch": cudf.Series(dtype="int32"), "start": cudf.Series(dtype="int64")} + ) + + partition = partition_info["number"] if partition is None: - raise ValueError('division is absent') + raise ValueError("division is absent") num_batches = int(ceil(len(bdf) / batch_size)) - + batch_ids = cupy.repeat( - cupy.arange(num_batches * partition, num_batches * (partition + 1), dtype='int32'), - batch_size - )[:len(bdf)] + cupy.arange( + num_batches * partition, num_batches * (partition + 1), dtype="int32" + ), + batch_size, + )[: len(bdf)] bdf = bdf.reset_index(drop=True) - bdf['batch'] = cudf.Series(batch_ids) + bdf["batch"] = cudf.Series(batch_ids) return bdf -def _replicate_df(df: cudf.DataFrame, replication_factor: int, col_item_counts:Dict[str, int], partition_info: Optional[Union[dict, str]] = None): +def _replicate_df( + df: cudf.DataFrame, + replication_factor: int, + col_item_counts: Dict[str, int], + partition_info: Optional[Union[dict, str]] = None, +): # Required by dask; need to skip dummy partitions. if partition_info is None: - return cudf.DataFrame({ - col: cudf.Series(dtype=df[col].dtype) for col in col_item_counts.keys() - }) - + return cudf.DataFrame( + {col: cudf.Series(dtype=df[col].dtype) for col in col_item_counts.keys()} + ) + original_df = df.copy() if replication_factor > 1: @@ -153,14 +174,24 @@ def _replicate_df(df: cudf.DataFrame, replication_factor: int, col_item_counts:D df_replicated = original_df for col, offset in col_item_counts.items(): df_replicated[col] += offset * r - + df = cudf.concat([df, df_replicated], ignore_index=True) - + return df @get_allocation_counts_dask_lazy(return_allocations=True, logging=True) -def sample_graph(G, label_df, output_path,seed=42, batch_size=500, seeds_per_call=200000, batches_per_partition=100, fanout=[5, 5, 5], persist=False): +def sample_graph( + G, + label_df, + output_path, + seed=42, + batch_size=500, + seeds_per_call=200000, + batches_per_partition=100, + fanout=[5, 5, 5], + persist=False, +): cupy.random.seed(seed) sampler = BulkSampler( @@ -172,35 +203,35 @@ def sample_graph(G, label_df, output_path,seed=42, batch_size=500, seeds_per_cal random_state=seed, seeds_per_call=seeds_per_call, batches_per_partition=batches_per_partition, - log_level = logging.INFO + log_level=logging.INFO, ) - n_workers = len(default_client().scheduler_info()['workers']) + n_workers = len(default_client().scheduler_info()["workers"]) - meta = cudf.DataFrame({ - 'node': cudf.Series(dtype='int64'), - 'batch': cudf.Series(dtype='int32') - }) + meta = cudf.DataFrame( + {"node": cudf.Series(dtype="int64"), "batch": cudf.Series(dtype="int32")} + ) + + batch_df = label_df.map_partitions( + _make_batch_ids, batch_size, n_workers, meta=meta + ) + # batch_df = batch_df.sort_values(by='node') - batch_df = label_df.map_partitions(_make_batch_ids, batch_size, n_workers, meta=meta) - #batch_df = batch_df.sort_values(by='node') - # should always persist the batch dataframe or performance may be suboptimal batch_df = batch_df.persist() del label_df - print('created batches') - + print("created batches") start_time = perf_counter() - sampler.add_batches(batch_df, start_col_name='node', batch_col_name='batch') + sampler.add_batches(batch_df, start_col_name="node", batch_col_name="batch") sampler.flush() end_time = perf_counter() - print('flushed all batches') - return (end_time - start_time) + print("flushed all batches") + return end_time - start_time -def assign_offsets_pyg(node_counts: Dict[str, int], replication_factor:int=1): +def assign_offsets_pyg(node_counts: Dict[str, int], replication_factor: int = 1): # cuGraph-PyG assigns offsets based on lexicographic order node_offsets = {} node_offsets_replicated = {} @@ -212,10 +243,19 @@ def assign_offsets_pyg(node_counts: Dict[str, int], replication_factor:int=1): count += node_counts[node_type] count_replicated += node_counts[node_type] * replication_factor - + return node_offsets, node_offsets_replicated, count_replicated -def generate_rmat_dataset(dataset, seed=62, labeled_percentage=0.01, num_labels=256, reverse_edges=False, persist=False, add_edge_types=False): + +def generate_rmat_dataset( + dataset, + seed=62, + labeled_percentage=0.01, + num_labels=256, + reverse_edges=False, + persist=False, + add_edge_types=False, +): """ Generates an rmat dataset. Currently does not support heterogeneous datasets. @@ -227,17 +267,20 @@ def generate_rmat_dataset(dataset, seed=62, labeled_percentage=0.01, num_labels= reverse_edges: Whether to reverse the edges in the edgelist (should be True for DGL, False, for PyG) """ - dataset = dataset.split('_') + dataset = dataset.split("_") scale = int(dataset[1]) edgefactor = int(dataset[2]) dask_edgelist_df = generate_edgelist_rmat( - scale=scale, edgefactor=edgefactor, seed=seed, unweighted=True, mg=True, + scale=scale, + edgefactor=edgefactor, + seed=seed, + unweighted=True, + mg=True, ) dask_edgelist_df = dask_edgelist_df.astype("int64") dask_edgelist_df = dask_edgelist_df.reset_index(drop=True) - dask_edgelist_df = renumber_ddf(dask_edgelist_df).persist() if persist: dask_edgelist_df = dask_edgelist_df.persist() @@ -247,96 +290,126 @@ def generate_rmat_dataset(dataset, seed=62, labeled_percentage=0.01, num_labels= dask_edgelist_df = dask_edgelist_df.persist() if add_edge_types: - dask_edgelist_df['etp'] = cupy.int32(0) # doesn't matter what the value is, really - + dask_edgelist_df["etp"] = cupy.int32( + 0 + ) # doesn't matter what the value is, really + # generator = np.random.default_rng(seed=seed) - num_labeled_nodes = int(2**(scale+1) * labeled_percentage) - label_df = pd.DataFrame({ - 'node': np.arange(num_labeled_nodes), - # 'label': generator.integers(0, num_labels - 1, num_labeled_nodes).astype('float32') - }) - - n_workers = len(default_client().scheduler_info()['workers']) - dask_label_df = ddf.from_pandas(label_df, npartitions=n_workers*2) + num_labeled_nodes = int(2 ** (scale + 1) * labeled_percentage) + label_df = pd.DataFrame( + { + "node": np.arange(num_labeled_nodes), + # 'label': generator.integers(0, num_labels - 1, num_labeled_nodes).astype('float32') + } + ) + + n_workers = len(default_client().scheduler_info()["workers"]) + dask_label_df = ddf.from_pandas(label_df, npartitions=n_workers * 2) del label_df gc.collect() dask_label_df = dask_cudf.from_dask_dataframe(dask_label_df) - node_offsets = {'paper': 0} - edge_offsets = {('paper','cites','paper'):0} - total_num_nodes = int(dask_cudf.concat([dask_edgelist_df.src, dask_edgelist_df.dst]).nunique().compute()) + node_offsets = {"paper": 0} + edge_offsets = {("paper", "cites", "paper"): 0} + total_num_nodes = int( + dask_cudf.concat([dask_edgelist_df.src, dask_edgelist_df.dst]) + .nunique() + .compute() + ) if reverse_edges: - dask_edgelist_df = dask_edgelist_df.rename(columns={'src':'dst', 'dst':'src'}) + dask_edgelist_df = dask_edgelist_df.rename(columns={"src": "dst", "dst": "src"}) return dask_edgelist_df, dask_label_df, node_offsets, edge_offsets, total_num_nodes -def load_disk_dataset(dataset, dataset_dir='.', reverse_edges=True, replication_factor=1, persist=False, add_edge_types=False): +def load_disk_dataset( + dataset, + dataset_dir=".", + reverse_edges=True, + replication_factor=1, + persist=False, + add_edge_types=False, +): from pathlib import Path + path = Path(dataset_dir) / dataset - parquet_path = path / 'parquet' + parquet_path = path / "parquet" n_workers = get_n_workers() - with open(os.path.join(path, 'meta.json')) as meta_file: + with open(os.path.join(path, "meta.json")) as meta_file: meta = json.load(meta_file) - - node_offsets, node_offsets_replicated, total_num_nodes = \ - assign_offsets_pyg(meta['num_nodes'], replication_factor=replication_factor) + + node_offsets, node_offsets_replicated, total_num_nodes = assign_offsets_pyg( + meta["num_nodes"], replication_factor=replication_factor + ) edge_index_dict = {} - for edge_type in meta['num_edges'].keys(): - print(f'Loading edge index for edge type {edge_type}') + for edge_type in meta["num_edges"].keys(): + print(f"Loading edge index for edge type {edge_type}") - can_edge_type = tuple(edge_type.split('__')) + can_edge_type = tuple(edge_type.split("__")) edge_index_dict[can_edge_type] = dask_cudf.read_parquet( - Path(parquet_path) / edge_type / 'edge_index.parquet' - ).repartition(n_workers*2) + Path(parquet_path) / edge_type / "edge_index.parquet" + ).repartition(n_workers * 2) - edge_index_dict[can_edge_type]['src'] += node_offsets_replicated[can_edge_type[0]] - edge_index_dict[can_edge_type]['dst'] += node_offsets_replicated[can_edge_type[-1]] + edge_index_dict[can_edge_type]["src"] += node_offsets_replicated[ + can_edge_type[0] + ] + edge_index_dict[can_edge_type]["dst"] += node_offsets_replicated[ + can_edge_type[-1] + ] edge_index_dict[can_edge_type] = edge_index_dict[can_edge_type] if persist: edge_index_dict = edge_index_dict.persist() if replication_factor > 1: - edge_index_dict[can_edge_type] = edge_index_dict[can_edge_type].map_partitions( + edge_index_dict[can_edge_type] = edge_index_dict[ + can_edge_type + ].map_partitions( _replicate_df, replication_factor, { - 'src': meta['num_nodes'][can_edge_type[0]], - 'dst': meta['num_nodes'][can_edge_type[2]], + "src": meta["num_nodes"][can_edge_type[0]], + "dst": meta["num_nodes"][can_edge_type[2]], }, - meta=cudf.DataFrame({'src':cudf.Series(dtype='int64'), 'dst':cudf.Series(dtype='int64')}) + meta=cudf.DataFrame( + { + "src": cudf.Series(dtype="int64"), + "dst": cudf.Series(dtype="int64"), + } + ), ) - + if persist: - edge_index_dict[can_edge_type] = edge_index_dict[can_edge_type].persist() - + edge_index_dict[can_edge_type] = edge_index_dict[ + can_edge_type + ].persist() + gc.collect() if reverse_edges: - edge_index_dict[can_edge_type] = edge_index_dict[can_edge_type].rename(columns={'src':'dst','dst':'src'}) - + edge_index_dict[can_edge_type] = edge_index_dict[can_edge_type].rename( + columns={"src": "dst", "dst": "src"} + ) + if persist: edge_index_dict[can_edge_type] = edge_index_dict[can_edge_type].persist() - + # Assign numeric edge type ids based on lexicographic order edge_offsets = {} edge_count = 0 for num_edge_type, can_edge_type in enumerate(sorted(edge_index_dict.keys())): if add_edge_types: - edge_index_dict[can_edge_type]['etp'] = cupy.int32(num_edge_type) + edge_index_dict[can_edge_type]["etp"] = cupy.int32(num_edge_type) edge_offsets[can_edge_type] = edge_count edge_count += len(edge_index_dict[can_edge_type]) - - all_edges_df = dask_cudf.concat( - list(edge_index_dict.values()) - ) - + + all_edges_df = dask_cudf.concat(list(edge_index_dict.values())) + if persist: all_edges_df = all_edges_df.persist() @@ -345,55 +418,65 @@ def load_disk_dataset(dataset, dataset_dir='.', reverse_edges=True, replication_ node_labels = {} for node_type, offset in node_offsets_replicated.items(): - print(f'Loading node labels for node type {node_type} (offset={offset})') - node_label_path = os.path.join(os.path.join(parquet_path, node_type), 'node_label.parquet') + print(f"Loading node labels for node type {node_type} (offset={offset})") + node_label_path = os.path.join( + os.path.join(parquet_path, node_type), "node_label.parquet" + ) if os.path.exists(node_label_path): - node_labels[node_type] = dask_cudf.read_parquet(node_label_path).repartition(n_workers).drop('label',axis=1).persist() - node_labels[node_type]['node'] += offset + node_labels[node_type] = ( + dask_cudf.read_parquet(node_label_path) + .repartition(n_workers) + .drop("label", axis=1) + .persist() + ) + node_labels[node_type]["node"] += offset node_labels[node_type] = node_labels[node_type].persist() if replication_factor > 1: node_labels[node_type] = node_labels[node_type].map_partitions( _replicate_df, replication_factor, - { - 'node': meta['num_nodes'][node_type] - }, - meta=cudf.DataFrame({'node':cudf.Series(dtype='int64')}) + {"node": meta["num_nodes"][node_type]}, + meta=cudf.DataFrame({"node": cudf.Series(dtype="int64")}), ) - + if persist: node_labels[node_type] = node_labels[node_type].persist() gc.collect() - - node_labels_df = dask_cudf.concat( - list(node_labels.values()) - ) - + + node_labels_df = dask_cudf.concat(list(node_labels.values())) + if persist: node_labels_df = node_labels_df.persist() del node_labels gc.collect() - return all_edges_df, node_labels_df, node_offsets_replicated, edge_offsets, total_num_nodes - + return ( + all_edges_df, + node_labels_df, + node_offsets_replicated, + edge_offsets, + total_num_nodes, + ) + def benchmark_cugraph_bulk_sampling( - dataset, - output_path, - seed, - batch_size, - seeds_per_call, - fanout, - reverse_edges=True, - dataset_dir='.', - replication_factor=1, - num_labels=256, - labeled_percentage=0.001, - persist=False, - add_edge_types=False): + dataset, + output_path, + seed, + batch_size, + seeds_per_call, + fanout, + reverse_edges=True, + dataset_dir=".", + replication_factor=1, + num_labels=256, + labeled_percentage=0.001, + persist=False, + add_edge_types=False, +): """ Entry point for the benchmark. @@ -430,47 +513,55 @@ def benchmark_cugraph_bulk_sampling( Defaults to False. """ print(dataset) - if dataset[0:4] == 'rmat': - dask_edgelist_df, dask_label_df, node_offsets, edge_offsets, total_num_nodes = \ - generate_rmat_dataset( - dataset, - reverse_edges=reverse_edges, - seed=seed, - labeled_percentage=labeled_percentage, - num_labels=num_labels, - persist=persist, - add_edge_types=add_edge_types - ) + if dataset[0:4] == "rmat": + ( + dask_edgelist_df, + dask_label_df, + node_offsets, + edge_offsets, + total_num_nodes, + ) = generate_rmat_dataset( + dataset, + reverse_edges=reverse_edges, + seed=seed, + labeled_percentage=labeled_percentage, + num_labels=num_labels, + persist=persist, + add_edge_types=add_edge_types, + ) else: - dask_edgelist_df, dask_label_df, node_offsets, edge_offsets, total_num_nodes = \ - load_disk_dataset( - dataset, - dataset_dir=dataset_dir, - reverse_edges=reverse_edges, - replication_factor=replication_factor, - persist=persist, - add_edge_types=add_edge_types - ) + ( + dask_edgelist_df, + dask_label_df, + node_offsets, + edge_offsets, + total_num_nodes, + ) = load_disk_dataset( + dataset, + dataset_dir=dataset_dir, + reverse_edges=reverse_edges, + replication_factor=replication_factor, + persist=persist, + add_edge_types=add_edge_types, + ) num_input_edges = len(dask_edgelist_df) - print( - f"Number of input edges = {num_input_edges:,}" - ) + print(f"Number of input edges = {num_input_edges:,}") - G = construct_graph( - dask_edgelist_df - ) + G = construct_graph(dask_edgelist_df) del dask_edgelist_df - print('constructed graph') + print("constructed graph") input_memory = G.edgelist.edgelist_df.memory_usage().sum().compute() - print(f'input memory: {input_memory}') + print(f"input memory: {input_memory}") - output_subdir = os.path.join(output_path, f'{dataset}[{replication_factor}]_b{batch_size}_f{fanout}') + output_subdir = os.path.join( + output_path, f"{dataset}[{replication_factor}]_b{batch_size}_f{fanout}" + ) os.makedirs(output_subdir) - output_sample_path = os.path.join(output_subdir, 'samples') + output_sample_path = os.path.join(output_subdir, "samples") os.makedirs(output_sample_path) batches_per_partition = 200_000 // batch_size @@ -487,30 +578,26 @@ def benchmark_cugraph_bulk_sampling( ) output_meta = { - 'dataset': dataset, - 'dataset_dir': dataset_dir, - 'seed': seed, - 'node_offsets': node_offsets, - 'edge_offsets': {'__'.join(k): v for k, v in edge_offsets.items()}, - 'total_num_nodes': total_num_nodes, - 'total_num_edges': num_input_edges, - 'batch_size': batch_size, - 'seeds_per_call': seeds_per_call, - 'batches_per_partition': batches_per_partition, - 'fanout': fanout, - 'replication_factor': replication_factor, - 'num_sampling_gpus': len(G._plc_graph), - 'execution_time': execution_time, + "dataset": dataset, + "dataset_dir": dataset_dir, + "seed": seed, + "node_offsets": node_offsets, + "edge_offsets": {"__".join(k): v for k, v in edge_offsets.items()}, + "total_num_nodes": total_num_nodes, + "total_num_edges": num_input_edges, + "batch_size": batch_size, + "seeds_per_call": seeds_per_call, + "batches_per_partition": batches_per_partition, + "fanout": fanout, + "replication_factor": replication_factor, + "num_sampling_gpus": len(G._plc_graph), + "execution_time": execution_time, } - with open(os.path.join(output_subdir, 'output_meta.json'), 'w') as f: - json.dump( - output_meta, - f, - indent='\t' - ) + with open(os.path.join(output_subdir, "output_meta.json"), "w") as f: + json.dump(output_meta, f, indent="\t") - print('allocation counts b:') + print("allocation counts b:") print(allocation_counts.values()) ( @@ -558,91 +645,87 @@ def get_memory_statistics(allocation_counts, input_memory): def get_args(): parser = argparse.ArgumentParser() - + parser.add_argument( - '--output_root', + "--output_root", type=str, - help='The output root directory. File/folder names are auto-generated.', + help="The output root directory. File/folder names are auto-generated.", required=True, ) parser.add_argument( - '--dataset_root', + "--dataset_root", type=str, - help='The dataset root directory containing ogb datasets.', + help="The dataset root directory containing ogb datasets.", required=True, ) parser.add_argument( - '--datasets', + "--datasets", type=str, help=( - 'Comma separated list of datasets; can specify ogb or rmat (i.e. ogb_papers100M[2],rmat_22_16).' - ' For ogb datasets, can provide replication factor using brackets.' + "Comma separated list of datasets; can specify ogb or rmat (i.e. ogb_papers100M[2],rmat_22_16)." + " For ogb datasets, can provide replication factor using brackets." ), required=True, ) parser.add_argument( - '--fanouts', + "--fanouts", type=str, - help='Comma separated list of fanouts (i.e. 10_25,5_5_5)', + help="Comma separated list of fanouts (i.e. 10_25,5_5_5)", required=False, - default='10_25', + default="10_25", ) parser.add_argument( - '--batch_sizes', + "--batch_sizes", type=str, - help='Comma separated list of batch sizes (i.e. 500,1000)', + help="Comma separated list of batch sizes (i.e. 500,1000)", required=False, - default='512,1024' + default="512,1024", ) parser.add_argument( - '--seeds_per_call_opts', + "--seeds_per_call_opts", type=str, - help='Comma separated list of seeds per call (i.e. 1000000,2000000)', + help="Comma separated list of seeds per call (i.e. 1000000,2000000)", required=False, - default='524288', + default="524288", ) - + parser.add_argument( - '--reverse_edges', - action='store_true', - help='Whether to reverse the edges for DGL (defaults to False). Should be True for DGL, False for PyG.', + "--reverse_edges", + action="store_true", + help="Whether to reverse the edges for DGL (defaults to False). Should be True for DGL, False for PyG.", required=False, default=False, ) parser.add_argument( - '--dask_worker_devices', + "--dask_worker_devices", type=str, - help='Comma separated list of dask worker devices', + help="Comma separated list of dask worker devices", required=False, - default="0" + default="0", ) parser.add_argument( - '--random_seed', - type=int, - help='Random seed', - required=False, - default=62 + "--random_seed", type=int, help="Random seed", required=False, default=62 ) parser.add_argument( - '--persist', - action='store_true', - help='Will add additional persist() calls to speed up ETL. Does not affect sampling runtime.', + "--persist", + action="store_true", + help="Will add additional persist() calls to speed up ETL. Does not affect sampling runtime.", required=False, default=False, ) parser.add_argument( - '--add_edge_types', - action='store_true', - help='Adds edge types to the edgelist. Required for PyG if not providing edge ids.', + "--add_edge_types", + action="store_true", + help="Adds edge types to the edgelist. Required for PyG if not providing edge ids.", required=False, default=False, ) @@ -655,18 +738,25 @@ def get_args(): logging.basicConfig() args = get_args() - fanouts = [[int(f) for f in fanout.split('_')] for fanout in args.fanouts.split(',')] - datasets = args.datasets.split(',') - batch_sizes = [int(b) for b in args.batch_sizes.split(',')] - seeds_per_call_opts = [int(s) for s in args.seeds_per_call_opts.split(',')] - dask_worker_devices = [int(d) for d in args.dask_worker_devices.split(',')] - - client, cluster = start_dask_client(dask_worker_devices=dask_worker_devices, jit_unspill=False, rmm_pool_size=28e9, rmm_async=True) + fanouts = [ + [int(f) for f in fanout.split("_")] for fanout in args.fanouts.split(",") + ] + datasets = args.datasets.split(",") + batch_sizes = [int(b) for b in args.batch_sizes.split(",")] + seeds_per_call_opts = [int(s) for s in args.seeds_per_call_opts.split(",")] + dask_worker_devices = [int(d) for d in args.dask_worker_devices.split(",")] + + client, cluster = start_dask_client( + dask_worker_devices=dask_worker_devices, + jit_unspill=False, + rmm_pool_size=28e9, + rmm_async=True, + ) enable_spilling() stats_ls = [] client.run(enable_spilling) for dataset in datasets: - if re.match(r'([A-z]|[0-9])+\[[0-9]+\]', dataset): + if re.match(r"([A-z]|[0-9])+\[[0-9]+\]", dataset): replication_factor = int(dataset[-2]) dataset = dataset[:-3] else: @@ -675,10 +765,10 @@ def get_args(): for fanout in fanouts: for batch_size in batch_sizes: for seeds_per_call in seeds_per_call_opts: - print(f'dataset: {dataset}') - print(f'batch size: {batch_size}') - print(f'fanout: {fanout}') - print(f'seeds_per_call: {seeds_per_call}') + print(f"dataset: {dataset}") + print(f"batch size: {batch_size}") + print(f"fanout: {fanout}") + print(f"seeds_per_call: {seeds_per_call}") try: stats_d = {} @@ -706,7 +796,9 @@ def get_args(): stats_d["batch_size"] = batch_size stats_d["fanout"] = fanout stats_d["seeds_per_call"] = seeds_per_call - stats_d["input_memory_per_worker"] = sizeof_fmt(input_memory_per_worker) + stats_d["input_memory_per_worker"] = sizeof_fmt( + input_memory_per_worker + ) stats_d["peak_allocation_across_workers"] = sizeof_fmt( peak_allocation_across_workers ) @@ -714,7 +806,7 @@ def get_args(): stats_d["output_to_peak_ratio"] = output_to_peak_ratio stats_ls.append(stats_d) except Exception as e: - warnings.warn('An Exception Occurred!') + warnings.warn("An Exception Occurred!") print(e) traceback.print_exc() restart_client(client) diff --git a/benchmarks/cugraph/standalone/cugraph_dask_funcs.py b/benchmarks/cugraph/standalone/cugraph_dask_funcs.py index c6aa4a06100..e8f2c3a62bb 100644 --- a/benchmarks/cugraph/standalone/cugraph_dask_funcs.py +++ b/benchmarks/cugraph/standalone/cugraph_dask_funcs.py @@ -26,8 +26,6 @@ from cugraph.testing.mg_utils import generate_edgelist - - def read_csv(input_csv_file, scale): """ Returns a dask_cudf DataFrame from reading input_csv_file. @@ -41,15 +39,16 @@ def read_csv(input_csv_file, scale): """ vertex_t = "int32" if scale <= 32 else "int64" dtypes = [vertex_t, vertex_t, "float32"] - names=["src", "dst", "weight"], + names = (["src", "dst", "weight"],) chunksize = cugraph.dask.get_chunksize(input_csv_file) - return dask_cudf.read_csv(input_csv_file, - chunksize=chunksize, - delimiter=" ", - #names=names, - dtype=dtypes, - header=None, + return dask_cudf.read_csv( + input_csv_file, + chunksize=chunksize, + delimiter=" ", + # names=names, + dtype=dtypes, + header=None, ) @@ -59,6 +58,7 @@ def read_csv(input_csv_file, scale): # The "benchmark_name" attr is used by the benchmark infra for reporting and is # set to assign more meaningful names to be displayed in reports. + def construct_graph(dask_dataframe, symmetric=False): """ dask_dataframe contains weighted and undirected edges with self @@ -72,28 +72,27 @@ def construct_graph(dask_dataframe, symmetric=False): G = cugraph.Graph(directed=True) if len(dask_dataframe.columns) > 2: - if symmetric: #symmetrize dask dataframe - dask_dataframe = symmetrize_ddf( - dask_dataframe, 'src', 'dst', 'weight') + if symmetric: # symmetrize dask dataframe + dask_dataframe = symmetrize_ddf(dask_dataframe, "src", "dst", "weight") G.from_dask_cudf_edgelist( - dask_dataframe, source="src", destination="dst", edge_attr="weight") - #G.from_dask_cudf_edgelist( + dask_dataframe, source="src", destination="dst", edge_attr="weight" + ) + # G.from_dask_cudf_edgelist( # dask_dataframe, source="0", destination="1", edge_attr="2") else: - if symmetric: #symmetrize dask dataframe - dask_dataframe = symmetrize_ddf(dask_dataframe, 'src', 'dst') - G.from_dask_cudf_edgelist( - dask_dataframe, source="src", destination="dst") + if symmetric: # symmetrize dask dataframe + dask_dataframe = symmetrize_ddf(dask_dataframe, "src", "dst") + G.from_dask_cudf_edgelist(dask_dataframe, source="src", destination="dst") return G + construct_graph.benchmark_name = "from_dask_cudf_edgelist" def bfs(G, start): - return cugraph.dask.bfs( - G, start=start, return_distances=True, check_start=False) + return cugraph.dask.bfs(G, start=start, return_distances=True, check_start=False) def sssp(G, start): @@ -116,39 +115,49 @@ def katz(G, alpha=None): print(alpha) return cugraph.dask.katz_centrality(G, alpha) + def hits(G): return cugraph.dask.hits(G) + def uniform_neighbor_sample(G, start_list=None, fanout_vals=None): # convert list to cudf.Series - start_list = cudf.Series(start_list, dtype="int32") + start_list = cudf.Series(start_list, dtype="int32") return cugraph.dask.uniform_neighbor_sample( - G, start_list=start_list, fanout_vals=fanout_vals) + G, start_list=start_list, fanout_vals=fanout_vals + ) + def triangle_count(G): return cugraph.dask.triangle_count(G) + def eigenvector_centrality(G): return cugraph.dask.eigenvector_centrality(G) + ################################################################################ # Session-wide setup and teardown + def setup(dask_scheduler_file=None, rmm_pool_size=None): if dask_scheduler_file: cluster = None # Env var UCX_MAX_RNDV_RAILS=1 must be set too. - initialize(enable_tcp_over_ucx=True, - enable_nvlink=True, - enable_infiniband=False, - enable_rdmacm=False, - #net_devices="mlx5_0:1", - ) + initialize( + enable_tcp_over_ucx=True, + enable_nvlink=True, + enable_infiniband=False, + enable_rdmacm=False, + # net_devices="mlx5_0:1", + ) client = Client(scheduler_file=dask_scheduler_file) else: tempdir_object = tempfile.TemporaryDirectory() - cluster = LocalCUDACluster(local_directory=tempdir_object.name, rmm_pool_size=rmm_pool_size) + cluster = LocalCUDACluster( + local_directory=tempdir_object.name, rmm_pool_size=rmm_pool_size + ) client = Client(cluster) # add the obj to the client so it doesn't get deleted until # the 'client' obj gets cleaned up diff --git a/benchmarks/cugraph/standalone/cugraph_funcs.py b/benchmarks/cugraph/standalone/cugraph_funcs.py index ba33471a54f..d53471fa828 100644 --- a/benchmarks/cugraph/standalone/cugraph_funcs.py +++ b/benchmarks/cugraph/standalone/cugraph_funcs.py @@ -1,4 +1,4 @@ -# Copyright (c) 2021-2022, NVIDIA CORPORATION. +# Copyright (c) 2021-2023, NVIDIA CORPORATION. # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at @@ -18,11 +18,12 @@ import cudf -def generate_edgelist(scale, - edgefactor, - seed=None, - unweighted=False, - ): +def generate_edgelist( + scale, + edgefactor, + seed=None, + unweighted=False, +): """ Returns a cudf DataFrame created using the R-MAT graph generator. @@ -43,7 +44,7 @@ def generate_edgelist(scale, """ df = rmat( scale, - (2**scale)*edgefactor, + (2**scale) * edgefactor, 0.57, # from Graph500 0.19, # from Graph500 0.19, # from Graph500 @@ -51,7 +52,7 @@ def generate_edgelist(scale, clip_and_flip=False, scramble_vertex_ids=True, create_using=None, # return edgelist instead of Graph instance - mg=False + mg=False, ) if not unweighted: rng = np.random.default_rng(seed) @@ -72,16 +73,17 @@ def read_csv(input_csv_file, scale): """ vertex_t = "int32" if scale <= 32 else "int64" dtypes = [vertex_t, vertex_t, "float32"] - names=["src", "dst", "weight"], + names = (["src", "dst", "weight"],) chunksize = cugraph.dask.get_chunksize(input_csv_file) - return cudf.read_csv(input_csv_file, - chunksize=chunksize, - delimiter=" ", - #names=names, - dtype=dtypes, - header=None, - ) + return cudf.read_csv( + input_csv_file, + chunksize=chunksize, + delimiter=" ", + # names=names, + dtype=dtypes, + header=None, + ) ################################################################################ @@ -90,6 +92,7 @@ def read_csv(input_csv_file, scale): # The "benchmark_name" attr is used by the benchmark infra for reporting and is # set to assign more meaningful names to be displayed in reports. + def construct_graph(dataframe, symmetric=False): """ dataframe contains weighted and undirected edges with self loops. Multiple @@ -103,15 +106,17 @@ def construct_graph(dataframe, symmetric=False): if len(dataframe.columns) > 2: G.from_cudf_edgelist( - dataframe, source="src", destination="dst", edge_attr="weight") - #G.from_cudf_edgelist( + dataframe, source="src", destination="dst", edge_attr="weight" + ) + # G.from_cudf_edgelist( # dataframe, source="0", destination="1", edge_attr="2") else: - G.from_cudf_edgelist( - dataframe, source="src", destination="dst") - #G.from_cudf_edgelist( + G.from_cudf_edgelist(dataframe, source="src", destination="dst") + # G.from_cudf_edgelist( # dataframe, source="0", destination="1") return G + + construct_graph.benchmark_name = "from_cudf_edgelist" @@ -138,24 +143,31 @@ def pagerank(G): def katz(G, alpha=None): return cugraph.katz_centrality(G, alpha) + def hits(G): return cugraph.hits(G) + def uniform_neighbor_sample(G, start_list=None, fanout_vals=None): # convert list to cudf.Series - start_list = cudf.Series(start_list, dtype="int32") + start_list = cudf.Series(start_list, dtype="int32") return cugraph.uniform_neighbor_sample( - G, start_list=start_list, fanout_vals=fanout_vals) + G, start_list=start_list, fanout_vals=fanout_vals + ) + def triangle_count(G): return cugraph.triangle_count(G) + def eigenvector_centrality(G): return cugraph.eigenvector_centrality(G) + ################################################################################ # Session-wide setup and teardown + def setup(*args, **kwargs): return tuple() diff --git a/benchmarks/cugraph/standalone/cugraph_graph_creation.py b/benchmarks/cugraph/standalone/cugraph_graph_creation.py index 1edf67bba44..70be0f8d470 100644 --- a/benchmarks/cugraph/standalone/cugraph_graph_creation.py +++ b/benchmarks/cugraph/standalone/cugraph_graph_creation.py @@ -210,7 +210,7 @@ def get_memory_statistics(allocation_counts, input_memory): print("-" * 40 + f"renumber completed" + "-" * 40) stats_df = pd.DataFrame( - stats_ls, + stats_ls, columns=[ "scale", "num_input_edges", diff --git a/benchmarks/cugraph/standalone/main.py b/benchmarks/cugraph/standalone/main.py index 206be6e9362..07e8eefde1d 100644 --- a/benchmarks/cugraph/standalone/main.py +++ b/benchmarks/cugraph/standalone/main.py @@ -1,4 +1,4 @@ -# Copyright (c) 2021-2022, NVIDIA CORPORATION. +# Copyright (c) 2021-2023, NVIDIA CORPORATION. # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at @@ -15,9 +15,10 @@ from cugraph.dask.common.mg_utils import get_visible_devices -from reporting import (generate_console_report, - update_csv_report, - ) +from reporting import ( + generate_console_report, + update_csv_report, +) import cugraph_funcs import cugraph_dask_funcs @@ -27,43 +28,45 @@ from pathlib import Path -def store_results_json(benchmark_dir=None, - algo_name=None, - algo_time=None, - n_gpus=None, - scale=None): +def store_results_json( + benchmark_dir=None, algo_name=None, algo_time=None, n_gpus=None, scale=None +): """ Store all benchmark results in json files """ benchmark_result = {} - benchmark_result['funcName'] = algo_name - benchmark_result['result'] = algo_time - benchmark_result['argNameValuePairs'] = [('scale', scale), ('ngpus', n_gpus)] + benchmark_result["funcName"] = algo_name + benchmark_result["result"] = algo_time + benchmark_result["argNameValuePairs"] = [("scale", scale), ("ngpus", n_gpus)] json_object = json.dumps(benchmark_result, indent=4) benchmark_dir_path = Path(benchmark_dir) - with open(f"{benchmark_dir_path}/benchmark_result_scale_{scale}_ngpus_{n_gpus}_{algo_name}.json", "w") as outfile: + with open( + f"{benchmark_dir_path}/benchmark_result_scale_{scale}_ngpus_{n_gpus}_{algo_name}.json", + "w", + ) as outfile: outfile.write(json_object) - def log(s, end="\n"): print(s, end=end) sys.stdout.flush() -def run(algos, - scale=None, - csv_graph_file=None, - csv_results_file=None, - unweighted=False, - symmetric=False, - edgefactor=None, - benchmark_dir=None, - dask_scheduler_file=None, - rmm_pool_size=None): +def run( + algos, + scale=None, + csv_graph_file=None, + csv_results_file=None, + unweighted=False, + symmetric=False, + edgefactor=None, + benchmark_dir=None, + dask_scheduler_file=None, + rmm_pool_size=None, +): """ Run the nightly benchmark on cugraph. Return True on success, False on failure. @@ -80,24 +83,24 @@ def run(algos, # Setup the benchmarks to run based on algos specified, or all. # Values are either callables, or tuples of (callable, args) pairs. - benchmarks = {"bfs": funcs.bfs, - "sssp": funcs.sssp, - "louvain": funcs.louvain, - "pagerank": funcs.pagerank, - "wcc": funcs.wcc, - "katz": funcs.katz, - "wcc": funcs.wcc, - "hits": funcs.hits, - "uniform_neighbor_sample": funcs.uniform_neighbor_sample, - "triangle_count": funcs.triangle_count, - "eigenvector_centrality": funcs.eigenvector_centrality, - } + benchmarks = { + "bfs": funcs.bfs, + "sssp": funcs.sssp, + "louvain": funcs.louvain, + "pagerank": funcs.pagerank, + "wcc": funcs.wcc, + "katz": funcs.katz, + "wcc": funcs.wcc, + "hits": funcs.hits, + "uniform_neighbor_sample": funcs.uniform_neighbor_sample, + "triangle_count": funcs.triangle_count, + "eigenvector_centrality": funcs.eigenvector_centrality, + } if algos: invalid_benchmarks = set(algos) - set(benchmarks.keys()) if invalid_benchmarks: - raise ValueError("Invalid benchmark(s) specified " - f"{invalid_benchmarks}") + raise ValueError("Invalid benchmark(s) specified " f"{invalid_benchmarks}") benchmarks_to_run = [benchmarks[b] for b in algos] else: benchmarks_to_run = list(benchmarks.values()) @@ -110,7 +113,7 @@ def run(algos, # If the number of GPUs is None, This is a MNMG run # Extract the number of gpus from the client if n_gpus is None: - n_gpus = len(setup_objs[0].scheduler_info()['workers']) + n_gpus = len(setup_objs[0].scheduler_info()["workers"]) log("done.") try: @@ -120,18 +123,18 @@ def run(algos, log("done.") elif scale: log("running generate_edgelist (RMAT)...", end="") - df = funcs.generate_edgelist(scale, - edgefactor=edgefactor, - seed=seed, - unweighted=unweighted) + df = funcs.generate_edgelist( + scale, edgefactor=edgefactor, seed=seed, unweighted=unweighted + ) log("done.") else: raise ValueError("Must specify either scale or csv_graph_file") - benchmark = BenchmarkRun(df, - (funcs.construct_graph, (symmetric,)), - benchmarks_to_run, - ) + benchmark = BenchmarkRun( + df, + (funcs.construct_graph, (symmetric,)), + benchmarks_to_run, + ) success = benchmark.run() algo_name = benchmark.results[1].name @@ -140,7 +143,7 @@ def run(algos, # Generate json files containing the benchmark results if benchmark_dir is not None: store_results_json(benchmark_dir, algo_name, algo_time, n_gpus, scale) - + # Report results print(generate_console_report(benchmark.results)) if csv_results_file: @@ -163,42 +166,75 @@ def run(algos, import argparse ap = argparse.ArgumentParser() - ap.add_argument("--scale", type=int, default=None, - help="scale factor for the graph edgelist generator " - "(num_verts=2**SCALE).") - ap.add_argument("--csv", type=str, default=None, - help="path to CSV file to read instead of generating a " - "graph edgelist.") - ap.add_argument("--unweighted", default=False, action="store_true", - help="Generate a graph without weights.") - ap.add_argument("--algo", action="append", - help="Algo to benchmark. May be specified multiple times. " - "Default is all algos.") - ap.add_argument("--dask-scheduler-file", type=str, default=None, - help="Dask scheduler file for multi-node configuration.") - ap.add_argument("--symmetric-graph", default=False, action="store_true", - help="Generate a symmetric (undirected) Graph instead of " - "a DiGraph.") - ap.add_argument("--edgefactor", type=int, default=16, - help="edge factor for the graph edgelist generator " - "(num_edges=num_verts*EDGEFACTOR).") - ap.add_argument("--benchmark-dir", type=str, default=None, - help="directory to store the results in json files") - ap.add_argument("--rmm-pool-size", type=str, default=None, - help="RMM pool size to initialize each worker with") - + ap.add_argument( + "--scale", + type=int, + default=None, + help="scale factor for the graph edgelist generator " "(num_verts=2**SCALE).", + ) + ap.add_argument( + "--csv", + type=str, + default=None, + help="path to CSV file to read instead of generating a " "graph edgelist.", + ) + ap.add_argument( + "--unweighted", + default=False, + action="store_true", + help="Generate a graph without weights.", + ) + ap.add_argument( + "--algo", + action="append", + help="Algo to benchmark. May be specified multiple times. " + "Default is all algos.", + ) + ap.add_argument( + "--dask-scheduler-file", + type=str, + default=None, + help="Dask scheduler file for multi-node configuration.", + ) + ap.add_argument( + "--symmetric-graph", + default=False, + action="store_true", + help="Generate a symmetric (undirected) Graph instead of " "a DiGraph.", + ) + ap.add_argument( + "--edgefactor", + type=int, + default=16, + help="edge factor for the graph edgelist generator " + "(num_edges=num_verts*EDGEFACTOR).", + ) + ap.add_argument( + "--benchmark-dir", + type=str, + default=None, + help="directory to store the results in json files", + ) + ap.add_argument( + "--rmm-pool-size", + type=str, + default=None, + help="RMM pool size to initialize each worker with", + ) args = ap.parse_args() - exitcode = run(algos=args.algo, - scale=args.scale, - csv_graph_file=args.csv, - csv_results_file="out.csv", - unweighted=args.unweighted, - symmetric=args.symmetric_graph, - edgefactor=args.edgefactor, - benchmark_dir=args.benchmark_dir, - dask_scheduler_file=args.dask_scheduler_file, - rmm_pool_size=args.rmm_pool_size) + exitcode = run( + algos=args.algo, + scale=args.scale, + csv_graph_file=args.csv, + csv_results_file="out.csv", + unweighted=args.unweighted, + symmetric=args.symmetric_graph, + edgefactor=args.edgefactor, + benchmark_dir=args.benchmark_dir, + dask_scheduler_file=args.dask_scheduler_file, + rmm_pool_size=args.rmm_pool_size, + ) sys.exit(exitcode) diff --git a/benchmarks/cugraph/standalone/pylibcugraph_bench.py b/benchmarks/cugraph/standalone/pylibcugraph_bench.py index eff3e2e8b7d..b958be8a4b8 100644 --- a/benchmarks/cugraph/standalone/pylibcugraph_bench.py +++ b/benchmarks/cugraph/standalone/pylibcugraph_bench.py @@ -1,4 +1,4 @@ -# Copyright (c) 2022, NVIDIA CORPORATION. +# Copyright (c) 2022-2023, NVIDIA CORPORATION. # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at @@ -31,8 +31,9 @@ st = time.time() G2 = cugraph.Graph(directed=True) print(f"cugraph Graph create time: {time.time()-st}") -G2.from_cudf_edgelist(edgelist_df, source="src", destination="dst", - edge_attr="weight", renumber=True) +G2.from_cudf_edgelist( + edgelist_df, source="src", destination="dst", edge_attr="weight", renumber=True +) st = time.time() result = cugraph.pagerank(G2, alpha=0.85, tol=1.0e-6, max_iter=500) print(f"cugraph time: {time.time()-st}") @@ -42,27 +43,49 @@ resource_handle = pylibcugraph.experimental.ResourceHandle() graph_props = pylibcugraph.experimental.GraphProperties( - is_symmetric=False, is_multigraph=False) + is_symmetric=False, is_multigraph=False +) st = time.time() G = pylibcugraph.experimental.SGGraph( - resource_handle, graph_props, srcs, dsts, weights, - store_transposed=True, renumber=True, do_expensive_check=False) + resource_handle, + graph_props, + srcs, + dsts, + weights, + store_transposed=True, + renumber=True, + do_expensive_check=False, +) print(f"pylibcugraph Graph create time: {time.time()-st}") st = time.time() (vertices, pageranks) = pylibcugraph.experimental.pagerank( - resource_handle, G, None, alpha=0.85, epsilon=1.0e-6, max_iterations=500, - has_initial_guess=False, do_expensive_check=True) + resource_handle, + G, + None, + alpha=0.85, + epsilon=1.0e-6, + max_iterations=500, + has_initial_guess=False, + do_expensive_check=True, +) print(f"pylibcugraph time: {time.time()-st} (expensive check)") st = time.time() (vertices, pageranks) = pylibcugraph.experimental.pagerank( - resource_handle, G, None, alpha=0.85, epsilon=1.0e-6, max_iterations=500, - has_initial_guess=False, do_expensive_check=False) + resource_handle, + G, + None, + alpha=0.85, + epsilon=1.0e-6, + max_iterations=500, + has_initial_guess=False, + do_expensive_check=False, +) print(f"pylibcugraph time: {time.time()-st}") ######## print() vert_to_check = 4800348 -p = result['pagerank'][result['vertex'] == vert_to_check] +p = result["pagerank"][result["vertex"] == vert_to_check] print(f"cugraph pagerank for vert: {vert_to_check}: {p.iloc[0]}") host_verts = vertices.tolist() @@ -70,7 +93,7 @@ print(f"pylibcugraph pagerank for vert: {vert_to_check}: {pageranks[index]}") vert_to_check = 268434647 -p = result['pagerank'][result['vertex'] == vert_to_check] +p = result["pagerank"][result["vertex"] == vert_to_check] print(f"cugraph pagerank for vert: {vert_to_check}: {p.iloc[0]}") index = host_verts.index(vert_to_check) diff --git a/benchmarks/cugraph/standalone/reporting.py b/benchmarks/cugraph/standalone/reporting.py index afc04241384..e486ccf2c44 100644 --- a/benchmarks/cugraph/standalone/reporting.py +++ b/benchmarks/cugraph/standalone/reporting.py @@ -1,4 +1,4 @@ -# Copyright (c) 2021-2022, NVIDIA CORPORATION. +# Copyright (c) 2021-2023, NVIDIA CORPORATION. # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at @@ -56,6 +56,7 @@ def generate_console_report(benchmark_result_list): return retstring + def update_csv_report(csv_results_file, benchmark_result_list, ngpus): """ Update (or create if DNE) csv_results_file as a CSV file containing the @@ -93,9 +94,12 @@ def update_csv_report(csv_results_file, benchmark_result_list, ngpus): # Add any new algos that were not present in the existing CSV. If there was # no existing CSV, then this is all the algos ran in this run. for name in new_names - names_from_csv: - rows.append({"name": name, - ngpus_key: new_times[name], - }) + rows.append( + { + "name": name, + ngpus_key: new_times[name], + } + ) with open(csv_results_file, "w") as csv_file: field_names = sorted(all_fields) diff --git a/benchmarks/dgl/create_dataset.py b/benchmarks/dgl/create_dataset.py index c47d9068844..f002f79831c 100644 --- a/benchmarks/dgl/create_dataset.py +++ b/benchmarks/dgl/create_dataset.py @@ -15,6 +15,7 @@ import rmm from cugraph.generators import rmat + def create_dataset(scale, edgefactor, folder_path): _seed = 42 num_edges = (2**scale) * edgefactor @@ -31,14 +32,15 @@ def create_dataset(scale, edgefactor, folder_path): create_using=None, # None == return edgelist mg=False, ) - filepath = os.path.join(folder_path, f'rmat_scale_{scale}_edgefactor_{edgefactor}.parquet') + filepath = os.path.join( + folder_path, f"rmat_scale_{scale}_edgefactor_{edgefactor}.parquet" + ) edgelist_df.to_parquet(filepath) - -folder_path = os.path.join(os.getcwd(), 'datasets') +folder_path = os.path.join(os.getcwd(), "datasets") os.makedirs(folder_path, exist_ok=True) rmm.reinitialize(managed_memory=True) -for scale in [24,25,26]: +for scale in [24, 25, 26]: edgefactor = 16 create_dataset(scale=scale, edgefactor=edgefactor, folder_path=folder_path) diff --git a/benchmarks/dgl/pytest-based/dgl_benchmark.py b/benchmarks/dgl/pytest-based/dgl_benchmark.py index 93e50736359..456fa8fedc6 100644 --- a/benchmarks/dgl/pytest-based/dgl_benchmark.py +++ b/benchmarks/dgl/pytest-based/dgl_benchmark.py @@ -18,39 +18,59 @@ import torch import dgl -def get_edgelist(scale, edgefactor, dataset_dir = '../datasets'): - fp = os.path.join(dataset_dir, f'rmat_scale_{scale}_edgefactor_{edgefactor}.parquet') + +def get_edgelist(scale, edgefactor, dataset_dir="../datasets"): + fp = os.path.join( + dataset_dir, f"rmat_scale_{scale}_edgefactor_{edgefactor}.parquet" + ) return pd.read_parquet(fp) - + + def create_dgl_graph_from_df(df): - src_tensor = torch.as_tensor(df['src'].values) - dst_tensor = torch.as_tensor(df['dst'].values) + src_tensor = torch.as_tensor(df["src"].values) + dst_tensor = torch.as_tensor(df["dst"].values) # Reverse edges to match cuGraph behavior - g = dgl.graph(data = (dst_tensor, src_tensor)) + g = dgl.graph(data=(dst_tensor, src_tensor)) return g - -@pytest.mark.parametrize("scale_edge_factor", [[24,16],[25,16]]) -@pytest.mark.parametrize("batch_size", [100, 500, 1_000, 2_500, 5_000, 10_000, 20_000, 30_000, 40_000, 50_000, 60_000, 70_000, 80_000, 90_000, 100_000]) +@pytest.mark.parametrize("scale_edge_factor", [[24, 16], [25, 16]]) +@pytest.mark.parametrize( + "batch_size", + [ + 100, + 500, + 1_000, + 2_500, + 5_000, + 10_000, + 20_000, + 30_000, + 40_000, + 50_000, + 60_000, + 70_000, + 80_000, + 90_000, + 100_000, + ], +) @pytest.mark.parametrize("fanout", [[10, 25]]) def bench_dgl_pure_gpu(benchmark, scale_edge_factor, batch_size, fanout): df = get_edgelist(scale_edge_factor[0], scale_edge_factor[1]) - g = create_dgl_graph_from_df(df).to('cuda') - assert g.device.type =='cuda' - seed_nodes = torch.as_tensor(df['dst'][:batch_size]) - seed_nodes = seed_nodes.to('cuda') - assert len(seed_nodes)==batch_size + g = create_dgl_graph_from_df(df).to("cuda") + assert g.device.type == "cuda" + seed_nodes = torch.as_tensor(df["dst"][:batch_size]) + seed_nodes = seed_nodes.to("cuda") + assert len(seed_nodes) == batch_size ### Reverse because dgl sampler samples from destination to source fanout.reverse() sampler = dgl.dataloading.MultiLayerNeighborSampler(fanout) - + input_nodes, output_nodes, blocks = benchmark( - sampler.sample, - g, - seed_nodes=seed_nodes + sampler.sample, g, seed_nodes=seed_nodes ) - assert len(output_nodes)==batch_size + assert len(output_nodes) == batch_size return @@ -61,33 +81,55 @@ def dgl_graph_26_16(): return g, df -@pytest.mark.parametrize("batch_size", [100, 500, 1_000, 2_500, 5_000, 10_000, 20_000, 30_000, 40_000, 50_000, 60_000, 70_000, 80_000, 90_000, 100_000]) +@pytest.mark.parametrize( + "batch_size", + [ + 100, + 500, + 1_000, + 2_500, + 5_000, + 10_000, + 20_000, + 30_000, + 40_000, + 50_000, + 60_000, + 70_000, + 80_000, + 90_000, + 100_000, + ], +) @pytest.mark.parametrize("fanout", [[10, 25]]) def bench_dgl_uva(benchmark, dgl_graph_26_16, batch_size, fanout): - g,df = dgl_graph_26_16 - assert g.device.type =='cpu' - seed_nodes = torch.as_tensor(df['dst'][:30_000_000]) - seed_nodes = seed_nodes.to('cuda') + g, df = dgl_graph_26_16 + assert g.device.type == "cpu" + seed_nodes = torch.as_tensor(df["dst"][:30_000_000]) + seed_nodes = seed_nodes.to("cuda") ### Reverse because dgl sampler samples from destination to source fanout.reverse() sampler = dgl.dataloading.MultiLayerNeighborSampler(fanout) dataloader = dgl.dataloading.DataLoader( - g, - seed_nodes, # train_nid must be on GPU. - sampler, - device=torch.device('cuda:0'), # The device argument must be GPU. - num_workers=0, # Number of workers must be 0. - use_uva=True, - batch_size=batch_size, - drop_last=False, - shuffle=False) + g, + seed_nodes, # train_nid must be on GPU. + sampler, + device=torch.device("cuda:0"), # The device argument must be GPU. + num_workers=0, # Number of workers must be 0. + use_uva=True, + batch_size=batch_size, + drop_last=False, + shuffle=False, + ) def uva_benchmark(dataloader_it): input_nodes, output_nodes, blocks = next(dataloader_it) - return input_nodes, output_nodes, blocks + return input_nodes, output_nodes, blocks # added iterations and rounds to prevent dataloader going over num batches dataloader_it = iter(dataloader) - input_nodes, output_nodes, blocks = benchmark.pedantic(uva_benchmark,kwargs = {'dataloader_it': dataloader_it}, iterations=10, rounds=10) - assert len(output_nodes)==batch_size + input_nodes, output_nodes, blocks = benchmark.pedantic( + uva_benchmark, kwargs={"dataloader_it": dataloader_it}, iterations=10, rounds=10 + ) + assert len(output_nodes) == batch_size return diff --git a/benchmarks/nx-cugraph/pytest-based/bench_algos.py b/benchmarks/nx-cugraph/pytest-based/bench_algos.py new file mode 100644 index 00000000000..971c3ff1032 --- /dev/null +++ b/benchmarks/nx-cugraph/pytest-based/bench_algos.py @@ -0,0 +1,212 @@ +# Copyright (c) 2023, NVIDIA CORPORATION. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import networkx as nx +import pandas as pd +import pytest +from cugraph import datasets + +# FIXME: promote these to cugraph.datasets so the following steps aren't +# necessary +# +# These datasets can be downloaded using the script in the 'datasets' dir: +# +# cd /datasets +# ./get_test_data.sh --benchmark +# +# Then set the following env var so the dataset utils can find their location: +# +# export RAPIDS_DATASET_ROOT_DIR=/datasets +# +from cugraph_benchmarking.params import ( + hollywood, + europe_osm, + cit_patents, + soc_livejournal, +) + +# Attempt to import the NetworkX dispatching module, which is only needed when +# testing with NX <3.2 in order to dynamically switch backends. NX >=3.2 allows +# the backend to be specified directly in the API call. +try: + from networkx.classes import backends # NX <3.2 +except ImportError: + backends = None + + +################################################################################ +# Fixtures and helpers +backend_params = ["cugraph", None] + +dataset_params = [ + pytest.param(datasets.karate, marks=[pytest.mark.small, pytest.mark.undirected]), + pytest.param(datasets.netscience, marks=[pytest.mark.small, pytest.mark.directed]), + pytest.param( + datasets.email_Eu_core, marks=[pytest.mark.small, pytest.mark.directed] + ), + pytest.param(cit_patents, marks=[pytest.mark.medium, pytest.mark.directed]), + pytest.param(hollywood, marks=[pytest.mark.medium, pytest.mark.undirected]), + pytest.param(europe_osm, marks=[pytest.mark.medium, pytest.mark.undirected]), + pytest.param(soc_livejournal, marks=[pytest.mark.large, pytest.mark.directed]), +] + + +def nx_graph_from_dataset(dataset_obj): + """ + Read the dataset specified by the dataset_obj and create and return a + nx.Graph or nx.DiGraph instance based on the dataset is_directed metadata. + """ + create_using = nx.DiGraph if dataset_obj.metadata["is_directed"] else nx.Graph + names = dataset_obj.metadata["col_names"] + dtypes = dataset_obj.metadata["col_types"] + if isinstance(dataset_obj.metadata["header"], int): + header = dataset_obj.metadata["header"] + else: + header = None + + pandas_edgelist = pd.read_csv( + dataset_obj.get_path(), + delimiter=dataset_obj.metadata["delim"], + names=names, + dtype=dict(zip(names, dtypes)), + header=header, + ) + G = nx.from_pandas_edgelist( + pandas_edgelist, source=names[0], target=names[1], create_using=create_using + ) + return G + + +# Test IDs are generated using the lambda assigned to the ids arg to provide an +# easier-to-read name from the Dataset obj string repr. +# See: https://docs.pytest.org/en/stable/reference/reference.html#pytest-fixture +@pytest.fixture(scope="module", params=dataset_params, ids=lambda ds: f"ds={str(ds)}") +def graph_obj(request): + """ + Returns a NX Graph or DiGraph obj from the dataset instance parameter. + """ + dataset = request.param + return nx_graph_from_dataset(dataset) + + +def get_legacy_backend_selector(backend_name): + """ + Returns a callable that wraps an algo function with either the default + dispatch decorator, or the "testing" decorator which unconditionally + dispatches. + This is only supported for NetworkX <3.2 + """ + backends.plugin_name = "cugraph" + orig_dispatch = backends._dispatch + testing_dispatch = backends.test_override_dispatch + + # Testing with the networkx <3.2 dispatch mechanism is based on decorating + # networkx APIs. The decorator is either one that only uses a backend if + # the input graph type is for that backend (the default decorator), or the + # "testing" decorator, which unconditionally converts a graph type to the + # type needed by the backend then calls the backend. If the cugraph backend + # is specified, create a callable that decorates the benchmarked function + # with the testing decorator. + # + # Because both the default and testing decorators assume they are only + # applied once and do bookkeeping to ensure algos are not registered + # multiple times, the callable also clears bookkeeping so the decorators + # can be reapplied multiple times. This is obviously a hack and networkx + # >=3.2 makes this use case properly supported. + if backend_name == "cugraph": + + def wrapper(*args, **kwargs): + backends._registered_algorithms = {} + return testing_dispatch(*args, **kwargs) + + else: + + def wrapper(*args, **kwargs): + backends._registered_algorithms = {} + return orig_dispatch(*args, **kwargs) + + return wrapper + + +def get_backend_selector(backend_name): + """ + Returns a callable that wraps an algo function in order to set the + "backend" kwarg on it. + This is only supported for NetworkX >= 3.2 + """ + + def get_callable_for_func(func): + def wrapper(*args, **kwargs): + kwargs["backend"] = backend_name + return func(*args, **kwargs) + + return wrapper + + return get_callable_for_func + + +@pytest.fixture( + scope="module", params=backend_params, ids=lambda backend: f"backend={backend}" +) +def backend_selector(request): + """ + Returns a callable that takes a function algo and wraps it in another + function that calls the algo using the appropriate backend. + """ + backend_name = request.param + if backends is not None: + return get_legacy_backend_selector(backend_name) + else: + return get_backend_selector(backend_name) + + +################################################################################ +# Benchmarks +normalized_params = [True, False] +k_params = [10, 100] + + +@pytest.mark.parametrize("normalized", normalized_params, ids=lambda norm: f"{norm=}") +@pytest.mark.parametrize("k", k_params, ids=lambda k: f"{k=}") +def bench_betweenness_centrality(benchmark, graph_obj, backend_selector, normalized, k): + result = benchmark( + backend_selector(nx.betweenness_centrality), + graph_obj, + weight=None, + normalized=normalized, + k=k, + ) + assert type(result) is dict + + +@pytest.mark.parametrize("normalized", normalized_params, ids=lambda norm: f"{norm=}") +def bench_edge_betweenness_centrality( + benchmark, graph_obj, backend_selector, normalized +): + result = benchmark( + backend_selector(nx.edge_betweenness_centrality), + graph_obj, + weight=None, + normalized=normalized, + ) + assert type(result) is dict + + +def bench_louvain_communities(benchmark, graph_obj, backend_selector): + # The cugraph backend for louvain_communities only supports undirected graphs + if isinstance(graph_obj, nx.DiGraph): + G = graph_obj.to_undirected() + else: + G = graph_obj + result = benchmark(backend_selector(nx.community.louvain_communities), G) + assert type(result) is list diff --git a/benchmarks/pytest.ini b/benchmarks/pytest.ini index 6af3aab27fe..b3d8a8bb36c 100644 --- a/benchmarks/pytest.ini +++ b/benchmarks/pytest.ini @@ -14,8 +14,10 @@ markers = managedmem_off: RMM managed memory disabled poolallocator_on: RMM pool allocator enabled poolallocator_off: RMM pool allocator disabled - small: small datasets tiny: tiny datasets + small: small datasets + medium: medium datasets + large: large datasets directed: directed datasets undirected: undirected datasets matrix_types: inputs are matrices diff --git a/benchmarks/shared/build_cugraph_ucx/test_client_bandwidth.py b/benchmarks/shared/build_cugraph_ucx/test_client_bandwidth.py index 681e304546a..3d52fb46421 100644 --- a/benchmarks/shared/build_cugraph_ucx/test_client_bandwidth.py +++ b/benchmarks/shared/build_cugraph_ucx/test_client_bandwidth.py @@ -20,51 +20,65 @@ import rmm from time import perf_counter_ns + def benchmark_func(func, n_times=10): def wrap_func(*args, **kwargs): time_ls = [] # ignore 1st run # and return other runs - for _ in range(0,n_times+1): + for _ in range(0, n_times + 1): t1 = perf_counter_ns() result = func(*args, **kwargs) t2 = perf_counter_ns() - time_ls.append(t2-t1) + time_ls.append(t2 - t1) return result, time_ls[1:] + return wrap_func + def create_dataframe(client): n_rows = 25_000_000 - df = cudf.DataFrame({'src':cp.arange(0,n_rows,dtype=cp.int32), 'dst':cp.arange(0,n_rows, dtype=cp.int32), 'eids':cp.ones(n_rows, cp.int32)}) - ddf = dask_cudf.from_cudf(df,npartitions= len(client.scheduler_info()['workers'])).persist() + df = cudf.DataFrame( + { + "src": cp.arange(0, n_rows, dtype=cp.int32), + "dst": cp.arange(0, n_rows, dtype=cp.int32), + "eids": cp.ones(n_rows, cp.int32), + } + ) + ddf = dask_cudf.from_cudf( + df, npartitions=len(client.scheduler_info()["workers"]) + ).persist() client.rebalance(ddf) del df _ = wait(ddf) return ddf + @benchmark_func def get_n_rows(ddf, n): - if n==-1: - df = ddf.compute() + if n == -1: + df = ddf.compute() else: df = ddf.head(n) return df + def run_bandwidth_test(ddf, n): df, time_ls = get_n_rows(ddf, n) time_ar = np.asarray(time_ls) time_mean = time_ar.mean() size_bytes = df.memory_usage().sum() - size_gb = round(size_bytes/(pow(1024,3)), 2) + size_gb = round(size_bytes / (pow(1024, 3)), 2) print(f"Getting {len(df):,} rows of size {size_gb} took = {time_mean*1e-6} ms") - time_mean_s = time_mean*1e-9 + time_mean_s = time_mean * 1e-9 print(f"Bandwidth = {round(size_gb/time_mean_s, 4)} gb/s") return - if __name__ == "__main__": - cluster = LocalCUDACluster(protocol='ucx',rmm_pool_size='15GB', CUDA_VISIBLE_DEVICES='1,2,3') + cluster = LocalCUDACluster( + protocol="ucx", rmm_pool_size="15GB", CUDA_VISIBLE_DEVICES="1,2,3" + ) client = Client(cluster) rmm.reinitialize(pool_allocator=True) @@ -74,7 +88,6 @@ def run_bandwidth_test(ddf, n): run_bandwidth_test(ddf, 4_000_000) run_bandwidth_test(ddf, -1) - print("--"*20+"Completed Test"+"--"*20, flush=True) + print("--" * 20 + "Completed Test" + "--" * 20, flush=True) client.shutdown() cluster.close() - diff --git a/benchmarks/shared/build_cugraph_ucx/test_cugraph_sampling.py b/benchmarks/shared/build_cugraph_ucx/test_cugraph_sampling.py index 110ad80838a..2903e1607df 100644 --- a/benchmarks/shared/build_cugraph_ucx/test_cugraph_sampling.py +++ b/benchmarks/shared/build_cugraph_ucx/test_cugraph_sampling.py @@ -23,19 +23,22 @@ _seed = 42 + def benchmark_func(func, n_times=10): def wrap_func(*args, **kwargs): time_ls = [] # ignore 1st run # and return other runs - for _ in range(0,n_times+1): + for _ in range(0, n_times + 1): t1 = perf_counter_ns() result = func(*args, **kwargs) t2 = perf_counter_ns() - time_ls.append(t2-t1) + time_ls.append(t2 - t1) return result, time_ls[1:] + return wrap_func + def create_mg_graph(graph_data): """ Create a graph instance based on the data to be loaded/generated. @@ -67,41 +70,47 @@ def create_mg_graph(graph_data): ) return G + @benchmark_func def sample_graph(G, start_list): - output_ddf = uniform_neighbor_sample_mg(G,start_list=start_list, fanout_vals=[10,25]) + output_ddf = uniform_neighbor_sample_mg( + G, start_list=start_list, fanout_vals=[10, 25] + ) df = output_ddf.compute() return df + def run_sampling_test(ddf, start_list): df, time_ls = sample_graph(ddf, start_list) time_ar = np.asarray(time_ls) time_mean = time_ar.mean() print(f"Sampling {len(start_list):,} took = {time_mean*1e-6} ms") return - if __name__ == "__main__": - cluster = LocalCUDACluster(protocol='ucx',rmm_pool_size='15GB', CUDA_VISIBLE_DEVICES='1,2,3,4,5,6,7,8') + cluster = LocalCUDACluster( + protocol="ucx", rmm_pool_size="15GB", CUDA_VISIBLE_DEVICES="1,2,3,4,5,6,7,8" + ) client = Client(cluster) Comms.initialize(p2p=True) rmm.reinitialize(pool_allocator=True) - graph_data = {"scale": 26, - "edgefactor": 8 , - } - + graph_data = { + "scale": 26, + "edgefactor": 8, + } + g = create_mg_graph(graph_data) for num_start_verts in [1_000, 10_000, 100_000]: start_list = g.input_df["src"].head(num_start_verts) - assert len(start_list)==num_start_verts + assert len(start_list) == num_start_verts run_sampling_test(g, start_list) - - print("--"*20+"Completed Test"+"--"*20, flush=True) + + print("--" * 20 + "Completed Test" + "--" * 20, flush=True) Comms.destroy() client.shutdown() - cluster.close() \ No newline at end of file + cluster.close() diff --git a/benchmarks/shared/python/cugraph_benchmarking/params.py b/benchmarks/shared/python/cugraph_benchmarking/params.py index ee63b8768a6..d82cfd26117 100644 --- a/benchmarks/shared/python/cugraph_benchmarking/params.py +++ b/benchmarks/shared/python/cugraph_benchmarking/params.py @@ -15,9 +15,11 @@ from pylibcugraph.testing.utils import gen_fixture_params from cugraph.testing import RAPIDS_DATASET_ROOT_DIR_PATH -from cugraph.experimental.datasets import ( +from cugraph.datasets import ( Dataset, karate, + netscience, + email_Eu_core, ) # Create Dataset objects from .csv files. @@ -26,67 +28,104 @@ hollywood = Dataset( csv_file=RAPIDS_DATASET_ROOT_DIR_PATH / "csv/undirected/hollywood.csv", csv_col_names=["src", "dst"], - csv_col_dtypes=["int32", "int32"]) + csv_col_dtypes=["int32", "int32"], +) +hollywood.metadata["is_directed"] = False europe_osm = Dataset( csv_file=RAPIDS_DATASET_ROOT_DIR_PATH / "csv/undirected/europe_osm.csv", csv_col_names=["src", "dst"], - csv_col_dtypes=["int32", "int32"]) + csv_col_dtypes=["int32", "int32"], +) +europe_osm.metadata["is_directed"] = False cit_patents = Dataset( csv_file=RAPIDS_DATASET_ROOT_DIR_PATH / "csv/directed/cit-Patents.csv", csv_col_names=["src", "dst"], - csv_col_dtypes=["int32", "int32"]) + csv_col_dtypes=["int32", "int32"], +) +cit_patents.metadata["is_directed"] = True soc_livejournal = Dataset( csv_file=RAPIDS_DATASET_ROOT_DIR_PATH / "csv/directed/soc-LiveJournal1.csv", csv_col_names=["src", "dst"], - csv_col_dtypes=["int32", "int32"]) + csv_col_dtypes=["int32", "int32"], +) +soc_livejournal.metadata["is_directed"] = True # Assume all "file_data" (.csv file on disk) datasets are too small to be useful for MG. undirected_datasets = [ - pytest.param(karate, - marks=[pytest.mark.tiny, - pytest.mark.undirected, - pytest.mark.file_data, - pytest.mark.sg, - ]), - pytest.param(hollywood, - marks=[pytest.mark.small, - pytest.mark.undirected, - pytest.mark.file_data, - pytest.mark.sg, - ]), - pytest.param(europe_osm, - marks=[pytest.mark.undirected, - pytest.mark.file_data, - pytest.mark.sg, - ]), + pytest.param( + karate, + marks=[ + pytest.mark.tiny, + pytest.mark.undirected, + pytest.mark.file_data, + pytest.mark.sg, + ], + ), + pytest.param( + hollywood, + marks=[ + pytest.mark.small, + pytest.mark.undirected, + pytest.mark.file_data, + pytest.mark.sg, + ], + ), + pytest.param( + europe_osm, + marks=[ + pytest.mark.undirected, + pytest.mark.file_data, + pytest.mark.sg, + ], + ), ] directed_datasets = [ - pytest.param(cit_patents, - marks=[pytest.mark.small, - pytest.mark.directed, - pytest.mark.file_data, - pytest.mark.sg, - ]), - pytest.param(soc_livejournal, - marks=[pytest.mark.directed, - pytest.mark.file_data, - pytest.mark.sg, - ]), + pytest.param( + netscience, + marks=[ + pytest.mark.small, + pytest.mark.directed, + pytest.mark.file_data, + pytest.mark.sg, + ], + ), + pytest.param( + email_Eu_core, + marks=[ + pytest.mark.small, + pytest.mark.directed, + pytest.mark.file_data, + pytest.mark.sg, + ], + ), + pytest.param( + cit_patents, + marks=[ + pytest.mark.small, + pytest.mark.directed, + pytest.mark.file_data, + pytest.mark.sg, + ], + ), + pytest.param( + soc_livejournal, + marks=[ + pytest.mark.directed, + pytest.mark.file_data, + pytest.mark.sg, + ], + ), ] managed_memory = [ - pytest.param(True, - marks=[pytest.mark.managedmem_on]), - pytest.param(False, - marks=[pytest.mark.managedmem_off]), + pytest.param(True, marks=[pytest.mark.managedmem_on]), + pytest.param(False, marks=[pytest.mark.managedmem_off]), ] pool_allocator = [ - pytest.param(True, - marks=[pytest.mark.poolallocator_on]), - pytest.param(False, - marks=[pytest.mark.poolallocator_off]), + pytest.param(True, marks=[pytest.mark.poolallocator_on]), + pytest.param(False, marks=[pytest.mark.poolallocator_off]), ] sg = pytest.param( @@ -117,17 +156,31 @@ for scale in _rmat_scales: for edgefactor in _rmat_edgefactors: rmat[f"{scale}_{edgefactor}"] = pytest.param( - {"scale": scale, - "edgefactor": edgefactor, - }, + { + "scale": scale, + "edgefactor": edgefactor, + }, id=f"dataset=rmat_{scale}_{edgefactor}", ) # sampling algos length of start list -_batch_sizes = [100, 500, 1000, 2500, 5000, - 10000, 20000, 30000, 40000, - 50000, 60000, 70000, 80000, - 90000, 100000] +_batch_sizes = [ + 100, + 500, + 1000, + 2500, + 5000, + 10000, + 20000, + 30000, + 40000, + 50000, + 60000, + 70000, + 80000, + 90000, + 100000, +] batch_sizes = {} for bs in _batch_sizes: batch_sizes[bs] = pytest.param( @@ -152,11 +205,10 @@ _num_clients = [2, 4, 8, 16, 32] num_clients = {} for nc in _num_clients: - num_clients[nc] = ( - pytest.param(nc, - id=f"num_clients={nc}", - marks=[getattr(pytest.mark, f"num_clients_{nc}")], - ) + num_clients[nc] = pytest.param( + nc, + id=f"num_clients={nc}", + marks=[getattr(pytest.mark, f"num_clients_{nc}")], ) # Parameters for Graph generation fixture diff --git a/ci/release/update-version.sh b/ci/release/update-version.sh index adf3273e311..aaeaa715434 100755 --- a/ci/release/update-version.sh +++ b/ci/release/update-version.sh @@ -82,6 +82,9 @@ NEXT_SHORT_TAG_PEP440=$(python -c "from setuptools.extern import packaging; prin DEPENDENCIES=( cudf cugraph + cugraph-dgl + cugraph-pyg + cugraph-service-server cugraph-service-client cuxfilter dask-cuda @@ -93,6 +96,7 @@ DEPENDENCIES=( librmm pylibcugraph pylibcugraphops + pylibwholegraph pylibraft pyraft raft-dask diff --git a/ci/test_python.sh b/ci/test_python.sh index 7b0077991ae..5183dc37a49 100755 --- a/ci/test_python.sh +++ b/ci/test_python.sh @@ -65,7 +65,10 @@ popd rapids-logger "pytest cugraph" pushd python/cugraph/cugraph -export DASK_WORKER_DEVICES="0" +DASK_WORKER_DEVICES="0" \ +DASK_DISTRIBUTED__SCHEDULER__WORKER_TTL="1000s" \ +DASK_DISTRIBUTED__COMM__TIMEOUTS__CONNECT="1000s" \ +DASK_CUDA_WAIT_WORKERS_MIN_TIMEOUT=20 \ pytest \ -v \ --benchmark-disable \ diff --git a/ci/test_wheel.sh b/ci/test_wheel.sh index 3ac3549f143..d6ec67cd9e9 100755 --- a/ci/test_wheel.sh +++ b/ci/test_wheel.sh @@ -21,5 +21,9 @@ arch=$(uname -m) if [[ "${arch}" == "aarch64" && ${RAPIDS_BUILD_TYPE} == "pull-request" ]]; then python ./ci/wheel_smoke_test_${package_name}.py else - RAPIDS_DATASET_ROOT_DIR=`pwd`/datasets python -m pytest ./python/${package_name}/${python_package_name}/tests + RAPIDS_DATASET_ROOT_DIR=`pwd`/datasets \ + DASK_DISTRIBUTED__SCHEDULER__WORKER_TTL="1000s" \ + DASK_DISTRIBUTED__COMM__TIMEOUTS__CONNECT="1000s" \ + DASK_CUDA_WAIT_WORKERS_MIN_TIMEOUT=20 \ + python -m pytest ./python/${package_name}/${python_package_name}/tests fi diff --git a/conda/environments/all_cuda-118_arch-x86_64.yaml b/conda/environments/all_cuda-118_arch-x86_64.yaml index c970d0f21cf..35330db5815 100644 --- a/conda/environments/all_cuda-118_arch-x86_64.yaml +++ b/conda/environments/all_cuda-118_arch-x86_64.yaml @@ -53,6 +53,7 @@ dependencies: - pydata-sphinx-theme - pylibcugraphops==23.12.* - pylibraft==23.12.* +- pylibwholegraph==23.12.* - pytest - pytest-benchmark - pytest-cov diff --git a/conda/environments/all_cuda-120_arch-x86_64.yaml b/conda/environments/all_cuda-120_arch-x86_64.yaml index d2b26312936..84967412dc5 100644 --- a/conda/environments/all_cuda-120_arch-x86_64.yaml +++ b/conda/environments/all_cuda-120_arch-x86_64.yaml @@ -52,6 +52,7 @@ dependencies: - pydata-sphinx-theme - pylibcugraphops==23.12.* - pylibraft==23.12.* +- pylibwholegraph==23.12.* - pytest - pytest-benchmark - pytest-cov diff --git a/dependencies.yaml b/dependencies.yaml index 4a57835e8ba..f5067ddae70 100644 --- a/dependencies.yaml +++ b/dependencies.yaml @@ -18,6 +18,7 @@ files: - depends_on_dask_cudf - depends_on_pylibraft - depends_on_raft_dask + - depends_on_pylibcugraphops - depends_on_cupy - python_run_cugraph - python_run_nx_cugraph @@ -39,6 +40,7 @@ files: - cudatoolkit - docs - py_version + - depends_on_pylibcugraphops test_cpp: output: none includes: @@ -225,6 +227,7 @@ files: conda_dir: python/cugraph-dgl/conda includes: - checks + - depends_on_pylibcugraphops - cugraph_dgl_dev - test_python_common cugraph_pyg_dev: @@ -234,6 +237,7 @@ files: conda_dir: python/cugraph-pyg/conda includes: - checks + - depends_on_pylibcugraphops - cugraph_pyg_dev - test_python_common channels: @@ -341,7 +345,6 @@ dependencies: - sphinx-markdown-tables - sphinx<6 - sphinxcontrib-websupport - - &pylibcugraphops pylibcugraphops==23.12.* py_version: specific: - output_types: [conda] @@ -368,26 +371,15 @@ dependencies: - output_types: [conda, pyproject, requirements] packages: - cython>=3.0.0 - - &pylibraft pylibraft==23.12.* - - &rmm rmm==23.12.* - scikit-build>=0.13.1 - python_build_cugraph: - common: - - output_types: [conda, pyproject] - packages: - - &pylibcugraph pylibcugraph==23.12.* python_run_cugraph: common: - output_types: [conda, pyproject] packages: - - &cudf cudf==23.12.* - &dask dask>=2023.7.1 - &distributed distributed>=2023.7.1 - &dask_cuda dask-cuda==23.12.* - - &dask_cudf dask-cudf==23.12.* - &numba numba>=0.57 - - raft-dask==23.12.* - - *rmm - &ucx_py ucx-py==0.35.* - output_types: conda packages: @@ -449,20 +441,6 @@ dependencies: packages: - *cugraph - cugraph-service-client==23.12.* - doc: - common: - - output_types: [conda] - packages: - - doxygen - - nbsphinx - - numpydoc - - pydata-sphinx-theme - - recommonmark - - sphinx - - sphinxcontrib-websupport - - sphinx-markdown-tables - - sphinx-copybutton - - *pylibcugraphops test_cpp: common: - output_types: conda @@ -492,6 +470,9 @@ dependencies: - *numpy - python-louvain - scikit-learn>=0.23.1 + - output_types: [conda] + packages: + - pylibwholegraph==23.12.* test_python_pylibcugraph: common: - output_types: [conda, pyproject] @@ -509,7 +490,6 @@ dependencies: - output_types: [conda] packages: - cugraph==23.12.* - - *pylibcugraphops - pytorch>=2.0 - pytorch-cuda==11.8 - dgl>=1.1.0.cu* @@ -518,7 +498,6 @@ dependencies: - output_types: [conda] packages: - cugraph==23.12.* - - *pylibcugraphops - pytorch==2.0 - pytorch-cuda==11.8 - pyg=2.3.1=*torch_2.0.0*cu118* @@ -527,7 +506,7 @@ dependencies: common: - output_types: conda packages: - - *rmm + - &rmm_conda rmm==23.12.* - output_types: requirements packages: # pip recognizes the index as a global option for the requirements.txt file @@ -546,13 +525,13 @@ dependencies: - {matrix: {cuda: "11.5"}, packages: *rmm_packages_pip_cu11} - {matrix: {cuda: "11.4"}, packages: *rmm_packages_pip_cu11} - {matrix: {cuda: "11.2"}, packages: *rmm_packages_pip_cu11} - - {matrix: null, packages: [*rmm]} + - {matrix: null, packages: [*rmm_conda]} depends_on_cudf: common: - output_types: conda packages: - - *cudf + - &cudf_conda cudf==23.12.* - output_types: requirements packages: # pip recognizes the index as a global option for the requirements.txt file @@ -571,13 +550,13 @@ dependencies: - {matrix: {cuda: "11.5"}, packages: *cudf_packages_pip_cu11} - {matrix: {cuda: "11.4"}, packages: *cudf_packages_pip_cu11} - {matrix: {cuda: "11.2"}, packages: *cudf_packages_pip_cu11} - - {matrix: null, packages: [*cudf]} + - {matrix: null, packages: [*cudf_conda]} depends_on_dask_cudf: common: - output_types: conda packages: - - *dask_cudf + - &dask_cudf_conda dask-cudf==23.12.* - output_types: requirements packages: # pip recognizes the index as a global option for the requirements.txt file @@ -596,13 +575,13 @@ dependencies: - {matrix: {cuda: "11.5"}, packages: *dask_cudf_packages_pip_cu11} - {matrix: {cuda: "11.4"}, packages: *dask_cudf_packages_pip_cu11} - {matrix: {cuda: "11.2"}, packages: *dask_cudf_packages_pip_cu11} - - {matrix: null, packages: [*dask_cudf]} + - {matrix: null, packages: [*dask_cudf_conda]} depends_on_pylibraft: common: - output_types: conda packages: - - *pylibraft + - &pylibraft_conda pylibraft==23.12.* - output_types: requirements packages: # pip recognizes the index as a global option for the requirements.txt file @@ -621,13 +600,13 @@ dependencies: - {matrix: {cuda: "11.5"}, packages: *pylibraft_packages_pip_cu11} - {matrix: {cuda: "11.4"}, packages: *pylibraft_packages_pip_cu11} - {matrix: {cuda: "11.2"}, packages: *pylibraft_packages_pip_cu11} - - {matrix: null, packages: [*pylibraft]} + - {matrix: null, packages: [*pylibraft_conda]} depends_on_raft_dask: common: - output_types: conda packages: - - &raft_dask raft-dask==23.12.* + - &raft_dask_conda raft-dask==23.12.* - output_types: requirements packages: # pip recognizes the index as a global option for the requirements.txt file @@ -646,13 +625,13 @@ dependencies: - {matrix: {cuda: "11.5"}, packages: *raft_dask_packages_pip_cu11} - {matrix: {cuda: "11.4"}, packages: *raft_dask_packages_pip_cu11} - {matrix: {cuda: "11.2"}, packages: *raft_dask_packages_pip_cu11} - - {matrix: null, packages: [*raft_dask]} + - {matrix: null, packages: [*raft_dask_conda]} depends_on_pylibcugraph: common: - output_types: conda packages: - - *pylibcugraph + - &pylibcugraph_conda pylibcugraph==23.12.* - output_types: requirements packages: # pip recognizes the index as a global option for the requirements.txt file @@ -671,7 +650,32 @@ dependencies: - {matrix: {cuda: "11.5"}, packages: *pylibcugraph_packages_pip_cu11} - {matrix: {cuda: "11.4"}, packages: *pylibcugraph_packages_pip_cu11} - {matrix: {cuda: "11.2"}, packages: *pylibcugraph_packages_pip_cu11} - - {matrix: null, packages: [*pylibcugraph]} + - {matrix: null, packages: [*pylibcugraph_conda]} + + depends_on_pylibcugraphops: + common: + - output_types: conda + packages: + - &pylibcugraphops_conda pylibcugraphops==23.12.* + - output_types: requirements + packages: + # pip recognizes the index as a global option for the requirements.txt file + - --extra-index-url=https://pypi.nvidia.com + specific: + - output_types: [requirements, pyproject] + matrices: + - matrix: {cuda: "12.2"} + packages: &pylibcugraphops_packages_pip_cu12 + - pylibcugraphops-cu12==23.12.* + - {matrix: {cuda: "12.1"}, packages: *pylibcugraphops_packages_pip_cu12} + - {matrix: {cuda: "12.0"}, packages: *pylibcugraphops_packages_pip_cu12} + - matrix: {cuda: "11.8"} + packages: &pylibcugraphops_packages_pip_cu11 + - pylibcugraphops-cu11==23.12.* + - {matrix: {cuda: "11.5"}, packages: *pylibcugraphops_packages_pip_cu11} + - {matrix: {cuda: "11.4"}, packages: *pylibcugraphops_packages_pip_cu11} + - {matrix: {cuda: "11.2"}, packages: *pylibcugraphops_packages_pip_cu11} + - {matrix: null, packages: [*pylibcugraphops_conda]} depends_on_cupy: common: diff --git a/python/cugraph-dgl/cugraph_dgl/dataloading/__init__.py b/python/cugraph-dgl/cugraph_dgl/dataloading/__init__.py index 6cabea198f6..2fd7d29bd49 100644 --- a/python/cugraph-dgl/cugraph_dgl/dataloading/__init__.py +++ b/python/cugraph-dgl/cugraph_dgl/dataloading/__init__.py @@ -13,7 +13,7 @@ from cugraph_dgl.dataloading.dataset import ( HomogenousBulkSamplerDataset, - HetrogenousBulkSamplerDataset, + HeterogenousBulkSamplerDataset, ) from cugraph_dgl.dataloading.neighbor_sampler import NeighborSampler from cugraph_dgl.dataloading.dataloader import DataLoader diff --git a/python/cugraph-dgl/cugraph_dgl/dataloading/dataloader.py b/python/cugraph-dgl/cugraph_dgl/dataloading/dataloader.py index 0480f61807a..f154b096256 100644 --- a/python/cugraph-dgl/cugraph_dgl/dataloading/dataloader.py +++ b/python/cugraph-dgl/cugraph_dgl/dataloading/dataloader.py @@ -21,7 +21,7 @@ from dask.distributed import default_client, Event from cugraph_dgl.dataloading import ( HomogenousBulkSamplerDataset, - HetrogenousBulkSamplerDataset, + HeterogenousBulkSamplerDataset, ) from cugraph_dgl.dataloading.utils.extract_graph_helpers import ( create_cugraph_graph_from_edges_dict, @@ -47,19 +47,20 @@ def __init__( graph_sampler: cugraph_dgl.dataloading.NeighborSampler, sampling_output_dir: str, batches_per_partition: int = 50, - seeds_per_call: int = 400_000, + seeds_per_call: int = 200_000, device: torch.device = None, use_ddp: bool = False, ddp_seed: int = 0, batch_size: int = 1024, drop_last: bool = False, shuffle: bool = False, + sparse_format: str = "coo", **kwargs, ): """ Constructor for CuGraphStorage: ------------------------------- - graph : CuGraphStorage + graph : CuGraphStorage The graph. indices : Tensor or dict[ntype, Tensor] The set of indices. It can either be a tensor of @@ -89,7 +90,12 @@ def __init__( The seed for shuffling the dataset in :class:`torch.utils.data.distributed.DistributedSampler`. Only effective when :attr:`use_ddp` is True. - batch_size: int, + batch_size: int + Batch size. + sparse_format: str, default = "coo" + The sparse format of the emitted sampled graphs. Choose between "csc" + and "coo". When using "csc", the graphs are of type + cugraph_dgl.nn.SparseGraph. kwargs : dict Key-word arguments to be passed to the parent PyTorch :py:class:`torch.utils.data.DataLoader` class. Common arguments are: @@ -123,6 +129,12 @@ def __init__( ... for input_nodes, output_nodes, blocks in dataloader: ... """ + if sparse_format not in ["coo", "csc"]: + raise ValueError( + f"sparse_format must be one of 'coo', 'csc', " + f"but got {sparse_format}." + ) + self.sparse_format = sparse_format self.ddp_seed = ddp_seed self.use_ddp = use_ddp @@ -156,11 +168,12 @@ def __init__( self.cugraph_dgl_dataset = HomogenousBulkSamplerDataset( total_number_of_nodes=graph.total_number_of_nodes, edge_dir=self.graph_sampler.edge_dir, + sparse_format=sparse_format, ) else: etype_id_to_etype_str_dict = {v: k for k, v in graph._etype_id_dict.items()} - self.cugraph_dgl_dataset = HetrogenousBulkSamplerDataset( + self.cugraph_dgl_dataset = HeterogenousBulkSamplerDataset( num_nodes_dict=graph.num_nodes_dict, etype_id_dict=etype_id_to_etype_str_dict, etype_offset_dict=graph._etype_offset_d, @@ -210,14 +223,23 @@ def __iter__(self): output_dir = os.path.join( self._sampling_output_dir, "epoch_" + str(self.epoch_number) ) + kwargs = {} if isinstance(self.cugraph_dgl_dataset, HomogenousBulkSamplerDataset): - deduplicate_sources = True - prior_sources_behavior = "carryover" - renumber = True + kwargs["deduplicate_sources"] = True + kwargs["prior_sources_behavior"] = "carryover" + kwargs["renumber"] = True + + if self.sparse_format == "csc": + kwargs["compression"] = "CSR" + kwargs["compress_per_hop"] = True + # The following kwargs will be deprecated in uniform sampler. + kwargs["use_legacy_names"] = False + kwargs["include_hop_column"] = False + else: - deduplicate_sources = False - prior_sources_behavior = None - renumber = False + kwargs["deduplicate_sources"] = False + kwargs["prior_sources_behavior"] = None + kwargs["renumber"] = False bs = BulkSampler( output_path=output_dir, @@ -227,10 +249,9 @@ def __iter__(self): seeds_per_call=self._seeds_per_call, fanout_vals=self.graph_sampler._reversed_fanout_vals, with_replacement=self.graph_sampler.replace, - deduplicate_sources=deduplicate_sources, - prior_sources_behavior=prior_sources_behavior, - renumber=renumber, + **kwargs, ) + if self.shuffle: self.tensorized_indices_ds.shuffle() diff --git a/python/cugraph-dgl/cugraph_dgl/dataloading/dataset.py b/python/cugraph-dgl/cugraph_dgl/dataloading/dataset.py index e0d51bcf4cf..815fd30d8eb 100644 --- a/python/cugraph-dgl/cugraph_dgl/dataloading/dataset.py +++ b/python/cugraph-dgl/cugraph_dgl/dataloading/dataset.py @@ -19,6 +19,7 @@ from cugraph_dgl.dataloading.utils.sampling_helpers import ( create_homogeneous_sampled_graphs_from_dataframe, create_heterogeneous_sampled_graphs_from_dataframe, + create_homogeneous_sampled_graphs_from_dataframe_csc, ) @@ -33,17 +34,19 @@ def __init__( total_number_of_nodes: int, edge_dir: str, return_type: str = "dgl.Block", + sparse_format: str = "coo", ): if return_type not in ["dgl.Block", "cugraph_dgl.nn.SparseGraph"]: raise ValueError( - "return_type must be either 'dgl.Block' or \ - 'cugraph_dgl.nn.SparseGraph' " + "return_type must be either 'dgl.Block' or " + "'cugraph_dgl.nn.SparseGraph'." ) # TODO: Deprecate `total_number_of_nodes` # as it is no longer needed # in the next release self.total_number_of_nodes = total_number_of_nodes self.edge_dir = edge_dir + self.sparse_format = sparse_format self._current_batch_fn = None self._input_files = None self._return_type = return_type @@ -60,10 +63,20 @@ def __getitem__(self, idx: int): fn, batch_offset = self._batch_to_fn_d[idx] if fn != self._current_batch_fn: - df = _load_sampled_file(dataset_obj=self, fn=fn) - self._current_batches = create_homogeneous_sampled_graphs_from_dataframe( - sampled_df=df, edge_dir=self.edge_dir, return_type=self._return_type - ) + if self.sparse_format == "csc": + df = _load_sampled_file(dataset_obj=self, fn=fn, skip_rename=True) + self._current_batches = ( + create_homogeneous_sampled_graphs_from_dataframe_csc(df) + ) + else: + df = _load_sampled_file(dataset_obj=self, fn=fn) + self._current_batches = ( + create_homogeneous_sampled_graphs_from_dataframe( + sampled_df=df, + edge_dir=self.edge_dir, + return_type=self._return_type, + ) + ) current_offset = idx - batch_offset return self._current_batches[current_offset] @@ -87,7 +100,7 @@ def set_input_files( ) -class HetrogenousBulkSamplerDataset(torch.utils.data.Dataset): +class HeterogenousBulkSamplerDataset(torch.utils.data.Dataset): def __init__( self, num_nodes_dict: Dict[str, int], @@ -141,18 +154,18 @@ def set_input_files( ---------- input_directory: str input_directory which contains all the files that will be - loaded by HetrogenousBulkSamplerDataset + loaded by HeterogenousBulkSamplerDataset input_file_paths: List[str] - File names that will be loaded by the HetrogenousBulkSamplerDataset + File names that will be loaded by the HeterogenousBulkSamplerDataset """ _set_input_files( self, input_directory=input_directory, input_file_paths=input_file_paths ) -def _load_sampled_file(dataset_obj, fn): +def _load_sampled_file(dataset_obj, fn, skip_rename=False): df = cudf.read_parquet(os.path.join(fn)) - if dataset_obj.edge_dir == "in": + if dataset_obj.edge_dir == "in" and not skip_rename: df.rename( columns={"sources": "destinations", "destinations": "sources"}, inplace=True, @@ -181,7 +194,7 @@ def get_batch_to_fn_d(files): def _set_input_files( - dataset_obj: Union[HomogenousBulkSamplerDataset, HetrogenousBulkSamplerDataset], + dataset_obj: Union[HomogenousBulkSamplerDataset, HeterogenousBulkSamplerDataset], input_directory: Optional[str] = None, input_file_paths: Optional[List[str]] = None, ) -> None: diff --git a/python/cugraph-dgl/cugraph_dgl/dataloading/utils/sampling_helpers.py b/python/cugraph-dgl/cugraph_dgl/dataloading/utils/sampling_helpers.py index bdac3b1a323..a4f64668348 100644 --- a/python/cugraph-dgl/cugraph_dgl/dataloading/utils/sampling_helpers.py +++ b/python/cugraph-dgl/cugraph_dgl/dataloading/utils/sampling_helpers.py @@ -11,10 +11,12 @@ # See the License for the specific language governing permissions and # limitations under the License. from __future__ import annotations -from typing import Tuple, Dict, Optional +from typing import List, Tuple, Dict, Optional from collections import defaultdict import cudf +import cupy from cugraph.utilities.utils import import_optional +from cugraph_dgl.nn import SparseGraph dgl = import_optional("dgl") torch = import_optional("torch") @@ -401,3 +403,154 @@ def create_heterogenous_dgl_block_from_tensors_dict( block = dgl.to_block(sampled_graph, dst_nodes=seed_nodes, src_nodes=src_d) block.edata[dgl.EID] = sampled_graph.edata[dgl.EID] return block + + +def _process_sampled_df_csc( + df: cudf.DataFrame, + reverse_hop_id: bool = True, +) -> Tuple[ + Dict[int, Dict[int, Dict[str, torch.Tensor]]], + List[torch.Tensor], + List[List[int, int]], +]: + """ + Convert a dataframe generated by BulkSampler to a dictionary of tensors, to + facilitate MFG creation. The sampled graphs in the dataframe use CSC-format. + + Parameters + ---------- + df: cudf.DataFrame + The output from BulkSampler compressed in CSC format. The dataframe + should be generated with `compression="CSR"` in BulkSampler, + since the sampling routine treats seed nodes as sources. + + reverse_hop_id: bool (default=True) + Reverse hop id. + + Returns + ------- + tensors_dict: dict + A nested dictionary keyed by batch id and hop id. + `tensor_dict[batch_id][hop_id]` holds "minors" and "major_offsets" + values for CSC MFGs. + + renumber_map_list: list + List of renumbering maps for looking up global indices of nodes. One + map for each batch. + + mfg_sizes: list + List of the number of nodes in each message passing layer. For the + k-th hop, mfg_sizes[k] and mfg_sizes[k+1] is the number of sources and + destinations, respectively. + """ + # dropna + major_offsets = df.major_offsets.dropna().values + label_hop_offsets = df.label_hop_offsets.dropna().values + renumber_map_offsets = df.renumber_map_offsets.dropna().values + renumber_map = df.map.dropna().values + minors = df.minors.dropna().values + + n_batches = renumber_map_offsets.size - 1 + n_hops = int((label_hop_offsets.size - 1) / n_batches) + + # make global offsets local + major_offsets -= major_offsets[0] + label_hop_offsets -= label_hop_offsets[0] + renumber_map_offsets -= renumber_map_offsets[0] + + # get the sizes of each adjacency matrix (for MFGs) + mfg_sizes = (label_hop_offsets[1:] - label_hop_offsets[:-1]).reshape( + (n_batches, n_hops) + ) + n_nodes = renumber_map_offsets[1:] - renumber_map_offsets[:-1] + mfg_sizes = cupy.hstack((mfg_sizes, n_nodes.reshape(n_batches, -1))) + if reverse_hop_id: + mfg_sizes = mfg_sizes[:, ::-1] + + tensors_dict = {} + renumber_map_list = [] + for batch_id in range(n_batches): + batch_dict = {} + + for hop_id in range(n_hops): + hop_dict = {} + idx = batch_id * n_hops + hop_id # idx in label_hop_offsets + major_offsets_start = label_hop_offsets[idx].item() + major_offsets_end = label_hop_offsets[idx + 1].item() + minors_start = major_offsets[major_offsets_start].item() + minors_end = major_offsets[major_offsets_end].item() + # Note: minors and major_offsets from BulkSampler are of type int32 + # and int64 respectively. Since pylibcugraphops binding code doesn't + # support distinct node and edge index type, we simply casting both + # to int32 for now. + hop_dict["minors"] = torch.as_tensor( + minors[minors_start:minors_end], device="cuda" + ).int() + hop_dict["major_offsets"] = torch.as_tensor( + major_offsets[major_offsets_start : major_offsets_end + 1] + - major_offsets[major_offsets_start], + device="cuda", + ).int() + if reverse_hop_id: + batch_dict[n_hops - 1 - hop_id] = hop_dict + else: + batch_dict[hop_id] = hop_dict + + tensors_dict[batch_id] = batch_dict + + renumber_map_list.append( + torch.as_tensor( + renumber_map[ + renumber_map_offsets[batch_id] : renumber_map_offsets[batch_id + 1] + ], + device="cuda", + ) + ) + + return tensors_dict, renumber_map_list, mfg_sizes.tolist() + + +def _create_homogeneous_sparse_graphs_from_csc( + tensors_dict: Dict[int, Dict[int, Dict[str, torch.Tensor]]], + renumber_map_list: List[torch.Tensor], + mfg_sizes: List[int, int], +) -> List[List[torch.Tensor, torch.Tensor, List[SparseGraph]]]: + """Create mini-batches of MFGs. The input arguments are the outputs of + the function `_process_sampled_df_csc`. + + Returns + ------- + output: list + A list of mini-batches. Each mini-batch is a list that consists of + `input_nodes` tensor, `output_nodes` tensor and a list of MFGs. + """ + n_batches, n_hops = len(mfg_sizes), len(mfg_sizes[0]) - 1 + output = [] + for b_id in range(n_batches): + output_batch = [] + output_batch.append(renumber_map_list[b_id]) + output_batch.append(renumber_map_list[b_id][: mfg_sizes[b_id][-1]]) + mfgs = [ + SparseGraph( + size=(mfg_sizes[b_id][h_id], mfg_sizes[b_id][h_id + 1]), + src_ids=tensors_dict[b_id][h_id]["minors"], + cdst_ids=tensors_dict[b_id][h_id]["major_offsets"], + formats=["csc"], + reduce_memory=True, + ) + for h_id in range(n_hops) + ] + + output_batch.append(mfgs) + + output.append(output_batch) + + return output + + +def create_homogeneous_sampled_graphs_from_dataframe_csc(sampled_df: cudf.DataFrame): + """Public API to create mini-batches of MFGs using a dataframe output by + BulkSampler, where the sampled graph is compressed in CSC format.""" + return _create_homogeneous_sparse_graphs_from_csc( + *(_process_sampled_df_csc(sampled_df)) + ) diff --git a/python/cugraph-dgl/cugraph_dgl/nn/conv/base.py b/python/cugraph-dgl/cugraph_dgl/nn/conv/base.py index 307eb33078e..ddd95a76366 100644 --- a/python/cugraph-dgl/cugraph_dgl/nn/conv/base.py +++ b/python/cugraph-dgl/cugraph_dgl/nn/conv/base.py @@ -248,6 +248,13 @@ def csr(self) -> Tuple[torch.Tensor, torch.Tensor, Optional[torch.Tensor]]: value = value[self._perm_csc2csr] return csrc_ids, dst_ids, value + def __repr__(self) -> str: + return ( + f"{self.__class__.__name__}(num_src_nodes={self._num_src_nodes}, " + f"num_dst_nodes={self._num_dst_nodes}, " + f"num_edges={self._src_ids.size(0)}, formats={self._formats})" + ) + class BaseConv(torch.nn.Module): r"""An abstract base class for cugraph-ops nn module.""" diff --git a/python/cugraph-dgl/tests/test_utils.py b/python/cugraph-dgl/tests/test_utils.py index 740db59ce7f..4be66758b43 100644 --- a/python/cugraph-dgl/tests/test_utils.py +++ b/python/cugraph-dgl/tests/test_utils.py @@ -22,6 +22,7 @@ create_homogeneous_sampled_graphs_from_dataframe, _get_source_destination_range, _create_homogeneous_cugraph_dgl_nn_sparse_graph, + create_homogeneous_sampled_graphs_from_dataframe_csc, ) from cugraph.utilities.utils import import_optional @@ -50,6 +51,23 @@ def get_dummy_sampled_df(): return df +def get_dummy_sampled_df_csc(): + df_dict = dict( + minors=np.array( + [1, 1, 2, 1, 0, 3, 1, 3, 2, 3, 2, 4, 0, 1, 1, 0, 3, 2], dtype=np.int32 + ), + major_offsets=np.arange(19, dtype=np.int64), + map=np.array( + [26, 29, 33, 22, 23, 32, 18, 29, 33, 33, 8, 30, 32], dtype=np.int32 + ), + renumber_map_offsets=np.array([0, 4, 9, 13], dtype=np.int64), + label_hop_offsets=np.array([0, 1, 3, 6, 7, 9, 13, 14, 16, 18], dtype=np.int64), + ) + + # convert values to Series so that NaNs are padded automatically + return cudf.DataFrame({k: cudf.Series(v) for k, v in df_dict.items()}) + + def test_get_renumber_map(): sampled_df = get_dummy_sampled_df() @@ -176,3 +194,13 @@ def test__create_homogeneous_cugraph_dgl_nn_sparse_graph(): assert sparse_graph.num_src_nodes() == 2 assert sparse_graph.num_dst_nodes() == seednodes_range + 1 assert isinstance(sparse_graph, cugraph_dgl.nn.SparseGraph) + + +def test_create_homogeneous_sampled_graphs_from_dataframe_csc(): + df = get_dummy_sampled_df_csc() + batches = create_homogeneous_sampled_graphs_from_dataframe_csc(df) + + assert len(batches) == 3 + assert torch.equal(batches[0][0], torch.IntTensor([26, 29, 33, 22]).cuda()) + assert torch.equal(batches[1][0], torch.IntTensor([23, 32, 18, 29, 33]).cuda()) + assert torch.equal(batches[2][0], torch.IntTensor([33, 8, 30, 32]).cuda()) diff --git a/python/cugraph/cugraph/gnn/feature_storage/feat_storage.py b/python/cugraph/cugraph/gnn/feature_storage/feat_storage.py index e3fdeb7f150..77a53882fc4 100644 --- a/python/cugraph/cugraph/gnn/feature_storage/feat_storage.py +++ b/python/cugraph/cugraph/gnn/feature_storage/feat_storage.py @@ -17,23 +17,77 @@ import cupy as cp import numpy as np import pandas as pd -from cugraph.utilities.utils import import_optional +from cugraph.utilities.utils import import_optional, MissingModule torch = import_optional("torch") +wgth = import_optional("pylibwholegraph.torch") class FeatureStore: - """The feature-store class used to store feature data for GNNS""" + """The feature-store class used to store feature data for GNNs""" + + def __init__( + self, + backend: str = "numpy", + wg_comm: object = None, + wg_type: str = None, + wg_location: str = None, + ): + """ + Constructs a new FeatureStore object + + Parameters: + ---------- + backend: str ('numpy', 'torch', 'wholegraph') + Optional (default='numpy') + The name of the backend to use. + + wg_comm: WholeMemoryCommunicator + Optional (default=automatic) + Only used with the 'wholegraph' backend. + The communicator to use to store features in WholeGraph. + + wg_type: str ('distributed', 'continuous', 'chunked') + Optional (default='distributed') + Only used with the 'wholegraph' backend. + The memory format (distributed, continuous, or chunked) of + this FeatureStore. For more information see the WholeGraph + documentation. + + wg_location: str ('cpu', 'cuda') + Optional (default='cuda') + Only used with the 'wholegraph' backend. + Where the data is stored (cpu or cuda). + Defaults to storing on the GPU (cuda). + """ - def __init__(self, backend="numpy"): self.fd = defaultdict(dict) - if backend not in ["numpy", "torch"]: + if backend not in ["numpy", "torch", "wholegraph"]: raise ValueError( - f"backend {backend} not supported. Supported backends are numpy, torch" + f"backend {backend} not supported. " + "Supported backends are numpy, torch, wholegraph" ) self.backend = backend - def add_data(self, feat_obj: Sequence, type_name: str, feat_name: str) -> None: + self.__wg_comm = None + self.__wg_type = None + self.__wg_location = None + + if backend == "wholegraph": + self.__wg_comm = ( + wg_comm if wg_comm is not None else wgth.get_local_node_communicator() + ) + self.__wg_type = wg_type if wg_type is not None else "distributed" + self.__wg_location = wg_location if wg_location is not None else "cuda" + + if self.__wg_type not in ["distributed", "chunked", "continuous"]: + raise ValueError(f"invalid memory format {self.__wg_type}") + if (self.__wg_location != "cuda") and (self.__wg_location != "cpu"): + raise ValueError(f"invalid location {self.__wg_location}") + + def add_data( + self, feat_obj: Sequence, type_name: str, feat_name: str, **kwargs + ) -> None: """ Add the feature data to the feature_storage class Parameters: @@ -49,9 +103,31 @@ def add_data(self, feat_obj: Sequence, type_name: str, feat_name: str) -> None: None """ self.fd[feat_name][type_name] = self._cast_feat_obj_to_backend( - feat_obj, self.backend + feat_obj, + self.backend, + wg_comm=self.__wg_comm, + wg_type=self.__wg_type, + wg_location=self.__wg_location, + **kwargs, ) + def add_data_no_cast(self, feat_obj, type_name: str, feat_name: str) -> None: + """ + Direct add the feature data to the feature_storage class with no cast + Parameters: + ---------- + feat_obj : array_like object + The feature object to save in feature store + type_name : str + The node-type/edge-type of the feature + feat_name: str + The name of the feature being stored + Returns: + ------- + None + """ + self.fd[feat_name][type_name] = feat_obj + def get_data( self, indices: Union[np.ndarray, torch.Tensor], @@ -87,26 +163,67 @@ def get_data( f" feature: {list(self.fd[feat_name].keys())}" ) - return self.fd[feat_name][type_name][indices] + feat = self.fd[feat_name][type_name] + if not isinstance(wgth, MissingModule) and isinstance( + feat, wgth.WholeMemoryEmbedding + ): + indices_tensor = ( + indices + if isinstance(indices, torch.Tensor) + else torch.as_tensor(indices, device="cuda") + ) + return feat.gather(indices_tensor) + else: + return feat[indices] def get_feature_list(self) -> list[str]: return {feat_name: feats.keys() for feat_name, feats in self.fd.items()} @staticmethod - def _cast_feat_obj_to_backend(feat_obj, backend: str): + def _cast_feat_obj_to_backend(feat_obj, backend: str, **kwargs): if backend == "numpy": if isinstance(feat_obj, (cudf.DataFrame, pd.DataFrame)): - return _cast_to_numpy_ar(feat_obj.values) + return _cast_to_numpy_ar(feat_obj.values, **kwargs) else: - return _cast_to_numpy_ar(feat_obj) + return _cast_to_numpy_ar(feat_obj, **kwargs) elif backend == "torch": if isinstance(feat_obj, (cudf.DataFrame, pd.DataFrame)): - return _cast_to_torch_tensor(feat_obj.values) + return _cast_to_torch_tensor(feat_obj.values, **kwargs) else: - return _cast_to_torch_tensor(feat_obj) + return _cast_to_torch_tensor(feat_obj, **kwargs) + elif backend == "wholegraph": + return _get_wg_embedding(feat_obj, **kwargs) + +def _get_wg_embedding(feat_obj, wg_comm=None, wg_type=None, wg_location=None, **kwargs): + wg_comm_obj = wg_comm or wgth.get_local_node_communicator() + wg_type_str = wg_type or "distributed" + wg_location_str = wg_location or "cuda" -def _cast_to_torch_tensor(ar): + if isinstance(feat_obj, (cudf.DataFrame, pd.DataFrame)): + th_tensor = _cast_to_torch_tensor(feat_obj.values) + else: + th_tensor = _cast_to_torch_tensor(feat_obj) + wg_embedding = wgth.create_embedding( + wg_comm_obj, + wg_type_str, + wg_location_str, + th_tensor.dtype, + th_tensor.shape, + ) + ( + local_wg_tensor, + local_ld_offset, + ) = wg_embedding.get_embedding_tensor().get_local_tensor() + local_th_tensor = th_tensor[ + local_ld_offset : local_ld_offset + local_wg_tensor.shape[0] + ] + local_wg_tensor.copy_(local_th_tensor) + wg_comm_obj.barrier() + return wg_embedding + + +def _cast_to_torch_tensor(ar, **kwargs): if isinstance(ar, cp.ndarray): ar = torch.as_tensor(ar, device="cuda") elif isinstance(ar, np.ndarray): @@ -116,7 +233,7 @@ def _cast_to_torch_tensor(ar): return ar -def _cast_to_numpy_ar(ar): +def _cast_to_numpy_ar(ar, **kwargs): if isinstance(ar, cp.ndarray): ar = ar.get() elif type(ar).__name__ == "Tensor": diff --git a/python/cugraph/cugraph/tests/data_store/test_gnn_feat_storage.py b/python/cugraph/cugraph/tests/data_store/test_gnn_feat_storage.py index 2d1537d11e3..2b0ec4b11d0 100644 --- a/python/cugraph/cugraph/tests/data_store/test_gnn_feat_storage.py +++ b/python/cugraph/cugraph/tests/data_store/test_gnn_feat_storage.py @@ -10,7 +10,6 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -# Import FeatureStore class import pytest import numpy as np diff --git a/python/cugraph/cugraph/tests/data_store/test_gnn_feat_storage_wholegraph.py b/python/cugraph/cugraph/tests/data_store/test_gnn_feat_storage_wholegraph.py new file mode 100644 index 00000000000..1892e8a85a6 --- /dev/null +++ b/python/cugraph/cugraph/tests/data_store/test_gnn_feat_storage_wholegraph.py @@ -0,0 +1,85 @@ +# Copyright (c) 2023, NVIDIA CORPORATION. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import pytest +import numpy as np + +from cugraph.gnn import FeatureStore + +from cugraph.utilities.utils import import_optional, MissingModule + +pylibwholegraph = import_optional("pylibwholegraph") +wmb = import_optional("pylibwholegraph.binding.wholememory_binding") +torch = import_optional("torch") + + +def runtest(world_rank: int, world_size: int): + from pylibwholegraph.torch.initialize import init_torch_env_and_create_wm_comm + + wm_comm, _ = init_torch_env_and_create_wm_comm( + world_rank, + world_size, + world_rank, + world_size, + ) + wm_comm = wm_comm.wmb_comm + + generator = np.random.default_rng(62) + arr = ( + generator.integers(low=0, high=100, size=100_000) + .reshape(10_000, -1) + .astype("float64") + ) + + fs = FeatureStore(backend="wholegraph") + fs.add_data(arr, "type2", "feat1") + wm_comm.barrier() + + indices_to_fetch = np.random.randint(low=0, high=len(arr), size=1024) + output_fs = fs.get_data(indices_to_fetch, type_name="type2", feat_name="feat1") + assert isinstance(output_fs, torch.Tensor) + assert output_fs.is_cuda + expected = arr[indices_to_fetch] + np.testing.assert_array_equal(output_fs.cpu().numpy(), expected) + + wmb.finalize() + + +@pytest.mark.sg +@pytest.mark.skipif(isinstance(torch, MissingModule), reason="torch not available") +@pytest.mark.skipif( + isinstance(pylibwholegraph, MissingModule), reason="wholegraph not available" +) +def test_feature_storage_wholegraph_backend(): + from pylibwholegraph.utils.multiprocess import multiprocess_run + + gpu_count = wmb.fork_get_gpu_count() + print("gpu count:", gpu_count) + assert gpu_count > 0 + + multiprocess_run(1, runtest) + + +@pytest.mark.mg +@pytest.mark.skipif(isinstance(torch, MissingModule), reason="torch not available") +@pytest.mark.skipif( + isinstance(pylibwholegraph, MissingModule), reason="wholegraph not available" +) +def test_feature_storage_wholegraph_backend_mg(): + from pylibwholegraph.utils.multiprocess import multiprocess_run + + gpu_count = wmb.fork_get_gpu_count() + print("gpu count:", gpu_count) + assert gpu_count > 0 + + multiprocess_run(gpu_count, runtest) diff --git a/python/nx-cugraph/_nx_cugraph/__init__.py b/python/nx-cugraph/_nx_cugraph/__init__.py index 9b3332106ec..ba7fe5e91d7 100644 --- a/python/nx-cugraph/_nx_cugraph/__init__.py +++ b/python/nx-cugraph/_nx_cugraph/__init__.py @@ -75,7 +75,7 @@ def get_info(): return d -__version__ = "23.10.00" +__version__ = "23.12.00" if __name__ == "__main__": from pathlib import Path From 40a5f8e1dd868b6a9faf9f70dfcd6a70fa78e461 Mon Sep 17 00:00:00 2001 From: Ray Douglass <3107146+raydouglass@users.noreply.github.com> Date: Fri, 6 Oct 2023 16:05:30 -0400 Subject: [PATCH 036/111] Add wget to test_notebook dependencies (#3918) Running certain notebooks requires downloading datasets via https://github.com/rapidsai/cugraph/blob/branch-23.10/notebooks/cugraph_benchmarks/dataPrep.sh which uses `wget`, so this PR ensures the `test_notebooks` environment created in `rapidsai/docker` will have `wget` installed. Authors: - Ray Douglass (https://github.com/raydouglass) Approvers: - Jake Awe (https://github.com/AyodeAwe) --- conda/environments/all_cuda-118_arch-x86_64.yaml | 1 + conda/environments/all_cuda-120_arch-x86_64.yaml | 1 + dependencies.yaml | 3 +++ 3 files changed, 5 insertions(+) diff --git a/conda/environments/all_cuda-118_arch-x86_64.yaml b/conda/environments/all_cuda-118_arch-x86_64.yaml index 87179ef892e..dea52887f23 100644 --- a/conda/environments/all_cuda-118_arch-x86_64.yaml +++ b/conda/environments/all_cuda-118_arch-x86_64.yaml @@ -74,5 +74,6 @@ dependencies: - sphinxcontrib-websupport - ucx-proc=*=gpu - ucx-py==0.34.* +- wget - wheel name: all_cuda-118_arch-x86_64 diff --git a/conda/environments/all_cuda-120_arch-x86_64.yaml b/conda/environments/all_cuda-120_arch-x86_64.yaml index d54dc0abf51..2d55f73c5d1 100644 --- a/conda/environments/all_cuda-120_arch-x86_64.yaml +++ b/conda/environments/all_cuda-120_arch-x86_64.yaml @@ -73,5 +73,6 @@ dependencies: - sphinxcontrib-websupport - ucx-proc=*=gpu - ucx-py==0.34.* +- wget - wheel name: all_cuda-120_arch-x86_64 diff --git a/dependencies.yaml b/dependencies.yaml index 292fcf0baed..f330361ba88 100644 --- a/dependencies.yaml +++ b/dependencies.yaml @@ -463,6 +463,9 @@ dependencies: packages: - ipython - notebook>=0.5.0 + - output_types: [conda] + packages: + - wget test_python_common: common: - output_types: [conda, pyproject] From c02b295d040f209db025e04b29d146fc00fb5ee8 Mon Sep 17 00:00:00 2001 From: Ralph Liu Date: Tue, 10 Oct 2023 12:53:01 -0700 Subject: [PATCH 037/111] update Dask worker min timeout --- ci/test_python.sh | 2 +- ci/test_wheel.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ci/test_python.sh b/ci/test_python.sh index df0f34377a3..c7687396ff0 100755 --- a/ci/test_python.sh +++ b/ci/test_python.sh @@ -68,7 +68,7 @@ pushd python/cugraph/cugraph DASK_WORKER_DEVICES="0" \ DASK_DISTRIBUTED__SCHEDULER__WORKER_TTL="1000s" \ DASK_DISTRIBUTED__COMM__TIMEOUTS__CONNECT="1000s" \ -DASK_CUDA_WAIT_WORKERS_MIN_TIMEOUT=20 \ +DASK_CUDA_WAIT_WORKERS_MIN_TIMEOUT="1000s" \ pytest \ -v \ --benchmark-disable \ diff --git a/ci/test_wheel.sh b/ci/test_wheel.sh index d6ec67cd9e9..c4adb638437 100755 --- a/ci/test_wheel.sh +++ b/ci/test_wheel.sh @@ -24,6 +24,6 @@ else RAPIDS_DATASET_ROOT_DIR=`pwd`/datasets \ DASK_DISTRIBUTED__SCHEDULER__WORKER_TTL="1000s" \ DASK_DISTRIBUTED__COMM__TIMEOUTS__CONNECT="1000s" \ - DASK_CUDA_WAIT_WORKERS_MIN_TIMEOUT=20 \ + DASK_CUDA_WAIT_WORKERS_MIN_TIMEOUT="1000s" \ python -m pytest ./python/${package_name}/${python_package_name}/tests fi From 81e33bdf3a31d1087bfb33e465862315f252ac4f Mon Sep 17 00:00:00 2001 From: Jake Awe <50372925+AyodeAwe@users.noreply.github.com> Date: Wed, 11 Oct 2023 08:16:36 -0500 Subject: [PATCH 038/111] Link wholegrah and cugraphops XML docs (#3906) Configures `breathe` and links the uploaded `wholegraph` and `cugraph-ops` XML docs artifacts. Authors: - Jake Awe (https://github.com/AyodeAwe) Approvers: - Brad Rees (https://github.com/BradReesWork) - AJ Schmidt (https://github.com/ajschmidt8) URL: https://github.com/rapidsai/cugraph/pull/3906 --- ci/build_docs.sh | 7 +++++++ conda/environments/all_cuda-118_arch-x86_64.yaml | 1 + conda/environments/all_cuda-120_arch-x86_64.yaml | 1 + dependencies.yaml | 1 + docs/cugraph/source/conf.py | 9 ++++++++- 5 files changed, 18 insertions(+), 1 deletion(-) diff --git a/ci/build_docs.sh b/ci/build_docs.sh index 7b4cf152b4a..e774a6f9871 100755 --- a/ci/build_docs.sh +++ b/ci/build_docs.sh @@ -40,6 +40,13 @@ rapids-mamba-retry install "${PYTHON_CHANNEL}/linux-64/cugraph-dgl-*.tar.bz2" export RAPIDS_VERSION_NUMBER="23.12" export RAPIDS_DOCS_DIR="$(mktemp -d)" +for PROJECT in libcugraphops libwholegraph; do + rapids-logger "Download ${PROJECT} xml_tar" + TMP_DIR=$(mktemp -d) + export XML_DIR_${PROJECT^^}="$TMP_DIR" + aws s3 cp --only-show-errors "s3://rapidsai-docs/${PROJECT}/xml_tar/${RAPIDS_VERSION_NUMBER}/xml.tar.gz" - | tar xzf - -C "${TMP_DIR}" +done + rapids-logger "Build CPP docs" pushd cpp/doxygen doxygen Doxyfile diff --git a/conda/environments/all_cuda-118_arch-x86_64.yaml b/conda/environments/all_cuda-118_arch-x86_64.yaml index 35330db5815..42a218fcf66 100644 --- a/conda/environments/all_cuda-118_arch-x86_64.yaml +++ b/conda/environments/all_cuda-118_arch-x86_64.yaml @@ -11,6 +11,7 @@ channels: - nvidia dependencies: - aiohttp +- breathe - c-compiler - cmake>=3.26.4 - cuda-version=11.8 diff --git a/conda/environments/all_cuda-120_arch-x86_64.yaml b/conda/environments/all_cuda-120_arch-x86_64.yaml index 84967412dc5..0a1774b769f 100644 --- a/conda/environments/all_cuda-120_arch-x86_64.yaml +++ b/conda/environments/all_cuda-120_arch-x86_64.yaml @@ -11,6 +11,7 @@ channels: - nvidia dependencies: - aiohttp +- breathe - c-compiler - cmake>=3.26.4 - cuda-nvcc diff --git a/dependencies.yaml b/dependencies.yaml index f5067ddae70..7b63f6dc536 100644 --- a/dependencies.yaml +++ b/dependencies.yaml @@ -334,6 +334,7 @@ dependencies: common: - output_types: [conda] packages: + - breathe - doxygen - graphviz - ipython diff --git a/docs/cugraph/source/conf.py b/docs/cugraph/source/conf.py index 58ecca27150..470086b4faa 100644 --- a/docs/cugraph/source/conf.py +++ b/docs/cugraph/source/conf.py @@ -37,6 +37,7 @@ # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ + "breathe", "sphinx.ext.intersphinx", "sphinx.ext.autodoc", "sphinx.ext.autosummary", @@ -205,4 +206,10 @@ def setup(app): # The following is used by sphinx.ext.linkcode to provide links to github linkcode_resolve = make_linkcode_resolve( "https://github.com/rapidsai/cugraph/blob/{revision}/python/{path}#L{lineno}" -) \ No newline at end of file +) + +breathe_projects = { + 'libcugraphops': os.environ['XML_DIR_LIBCUGRAPHOPS'], + 'libwholegraph': os.environ['XML_DIR_LIBWHOLEGRAPH'] +} +breathe_default_project = "libcugraph" From bbc8fed271deb973de01d227e81a0a2981ac2b38 Mon Sep 17 00:00:00 2001 From: Ray Douglass Date: Wed, 11 Oct 2023 10:28:07 -0400 Subject: [PATCH 039/111] Update Changelog [skip ci] --- CHANGELOG.md | 94 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4832bc2fb04..33a5b2bc5e7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,97 @@ +# cuGraph 23.10.00 (11 Oct 2023) + +## 🚨 Breaking Changes + +- Rename `cugraph-nx` to `nx-cugraph` ([#3840](https://github.com/rapidsai/cugraph/pull/3840)) [@eriknw](https://github.com/eriknw) +- Remove legacy betweenness centrality ([#3829](https://github.com/rapidsai/cugraph/pull/3829)) [@jnke2016](https://github.com/jnke2016) +- Remove Deprecated Sampling Options ([#3816](https://github.com/rapidsai/cugraph/pull/3816)) [@alexbarghi-nv](https://github.com/alexbarghi-nv) +- cuGraph-PyG Loader Improvements ([#3795](https://github.com/rapidsai/cugraph/pull/3795)) [@alexbarghi-nv](https://github.com/alexbarghi-nv) +- Expose threshold in louvain ([#3792](https://github.com/rapidsai/cugraph/pull/3792)) [@ChuckHastings](https://github.com/ChuckHastings) +- Fix ValueError Caused By Batches With No Samples ([#3789](https://github.com/rapidsai/cugraph/pull/3789)) [@alexbarghi-nv](https://github.com/alexbarghi-nv) +- Update to Cython 3.0.0 ([#3716](https://github.com/rapidsai/cugraph/pull/3716)) [@vyasr](https://github.com/vyasr) + +## 🐛 Bug Fixes + +- Add wget to test_notebook dependencies ([#3918](https://github.com/rapidsai/cugraph/pull/3918)) [@raydouglass](https://github.com/raydouglass) +- Increase dask-related timeouts for CI testing ([#3907](https://github.com/rapidsai/cugraph/pull/3907)) [@jnke2016](https://github.com/jnke2016) +- Remove `dask_cudf` dataframe for the `_make_plc_graph` while creating `cugraph.Graph` ([#3895](https://github.com/rapidsai/cugraph/pull/3895)) [@VibhuJawa](https://github.com/VibhuJawa) +- Adds logic to handle isolated vertices at python layer ([#3886](https://github.com/rapidsai/cugraph/pull/3886)) [@naimnv](https://github.com/naimnv) +- Update Allocator Selection in cuGraph-DGL Example ([#3877](https://github.com/rapidsai/cugraph/pull/3877)) [@alexbarghi-nv](https://github.com/alexbarghi-nv) +- Add file to update-version.sh ([#3870](https://github.com/rapidsai/cugraph/pull/3870)) [@raydouglass](https://github.com/raydouglass) +- Fix torch seed in `cugraph-dgl` and `-pyg` tests for conv layers ([#3869](https://github.com/rapidsai/cugraph/pull/3869)) [@tingyu66](https://github.com/tingyu66) +- MFG C++ code bug fix ([#3865](https://github.com/rapidsai/cugraph/pull/3865)) [@seunghwak](https://github.com/seunghwak) +- Fix subtle memory leak in nbr_intersection primitive ([#3858](https://github.com/rapidsai/cugraph/pull/3858)) [@ChuckHastings](https://github.com/ChuckHastings) +- Uses `conda mambabuild` rather than `mamba mambabuild` ([#3853](https://github.com/rapidsai/cugraph/pull/3853)) [@rlratzel](https://github.com/rlratzel) +- Remove the assumption made on the client data's keys ([#3835](https://github.com/rapidsai/cugraph/pull/3835)) [@jnke2016](https://github.com/jnke2016) +- Disable mg tests ([#3833](https://github.com/rapidsai/cugraph/pull/3833)) [@naimnv](https://github.com/naimnv) +- Refactor python code for similarity algos to use latest CAPI ([#3828](https://github.com/rapidsai/cugraph/pull/3828)) [@naimnv](https://github.com/naimnv) +- [BUG] Fix Batch Renumbering of Empty Batches ([#3823](https://github.com/rapidsai/cugraph/pull/3823)) [@alexbarghi-nv](https://github.com/alexbarghi-nv) +- Temporarily disable the deletion of the dask dataframe ([#3814](https://github.com/rapidsai/cugraph/pull/3814)) [@jnke2016](https://github.com/jnke2016) +- Fix OD shortest distance matrix computation test failures. ([#3813](https://github.com/rapidsai/cugraph/pull/3813)) [@seunghwak](https://github.com/seunghwak) +- Use rapidsai/ci:cuda11.8.0-ubuntu22.04-py3.10 for docs build ([#3811](https://github.com/rapidsai/cugraph/pull/3811)) [@naimnv](https://github.com/naimnv) +- Fix ValueError Caused By Batches With No Samples ([#3789](https://github.com/rapidsai/cugraph/pull/3789)) [@alexbarghi-nv](https://github.com/alexbarghi-nv) +- Update `python_run_cugraph` in `dependencies.yaml` ([#3781](https://github.com/rapidsai/cugraph/pull/3781)) [@nv-rliu](https://github.com/nv-rliu) +- Fixes `KeyError` for `get_two_hop_neighbors` when called with a small start vertices list ([#3778](https://github.com/rapidsai/cugraph/pull/3778)) [@rlratzel](https://github.com/rlratzel) + +## 📖 Documentation + +- Update the docstrings of the similarity algorithms ([#3817](https://github.com/rapidsai/cugraph/pull/3817)) [@jnke2016](https://github.com/jnke2016) + +## 🚀 New Features + +- WholeGraph Feature Store for cuGraph-PyG and cuGraph-DGL ([#3874](https://github.com/rapidsai/cugraph/pull/3874)) [@alexbarghi-nv](https://github.com/alexbarghi-nv) +- similarity notebook to compare link prediction algos ([#3868](https://github.com/rapidsai/cugraph/pull/3868)) [@acostadon](https://github.com/acostadon) +- adding dining preference dataset ([#3866](https://github.com/rapidsai/cugraph/pull/3866)) [@acostadon](https://github.com/acostadon) +- Integrate C++ Renumbering and Compression ([#3841](https://github.com/rapidsai/cugraph/pull/3841)) [@alexbarghi-nv](https://github.com/alexbarghi-nv) +- Sampling post processing functions to accelerate MFG creation. ([#3815](https://github.com/rapidsai/cugraph/pull/3815)) [@seunghwak](https://github.com/seunghwak) +- [REVIEW] Add Pure DGL Dataloading benchmark ([#3660](https://github.com/rapidsai/cugraph/pull/3660)) [@VibhuJawa](https://github.com/VibhuJawa) + +## 🛠️ Improvements + +- nx-cugraph: handle louvain with isolated nodes ([#3897](https://github.com/rapidsai/cugraph/pull/3897)) [@eriknw](https://github.com/eriknw) +- Pin `dask` and `distributed` for `23.10` release ([#3896](https://github.com/rapidsai/cugraph/pull/3896)) [@galipremsagar](https://github.com/galipremsagar) +- Updates the source build docs to include libcugraphops as a build prerequisite ([#3893](https://github.com/rapidsai/cugraph/pull/3893)) [@rlratzel](https://github.com/rlratzel) +- fixes force atlas to allow string as vertex names ([#3891](https://github.com/rapidsai/cugraph/pull/3891)) [@acostadon](https://github.com/acostadon) +- Integrate renumbering and compression to `cugraph-dgl` to accelerate MFG creation ([#3887](https://github.com/rapidsai/cugraph/pull/3887)) [@tingyu66](https://github.com/tingyu66) +- Enable weights for MG similarity algorithms ([#3879](https://github.com/rapidsai/cugraph/pull/3879)) [@jnke2016](https://github.com/jnke2016) +- cuGraph-PyG MFG Creation and Conversion ([#3873](https://github.com/rapidsai/cugraph/pull/3873)) [@alexbarghi-nv](https://github.com/alexbarghi-nv) +- Update image names ([#3867](https://github.com/rapidsai/cugraph/pull/3867)) [@AyodeAwe](https://github.com/AyodeAwe) +- Update to clang 16.0.6. ([#3859](https://github.com/rapidsai/cugraph/pull/3859)) [@bdice](https://github.com/bdice) +- Updates to build and test `nx-cugraph` wheel as part of CI and nightly workflows ([#3852](https://github.com/rapidsai/cugraph/pull/3852)) [@rlratzel](https://github.com/rlratzel) +- Update `cugraph-dgl` conv layers to use improved graph class ([#3849](https://github.com/rapidsai/cugraph/pull/3849)) [@tingyu66](https://github.com/tingyu66) +- Add entry point to tell NetworkX about nx-cugraph without importing it. ([#3848](https://github.com/rapidsai/cugraph/pull/3848)) [@eriknw](https://github.com/eriknw) +- [IMP] Add ability to get batch size from the loader in cuGraph-PyG ([#3846](https://github.com/rapidsai/cugraph/pull/3846)) [@alexbarghi-nv](https://github.com/alexbarghi-nv) +- Refactor legacy k truss ([#3843](https://github.com/rapidsai/cugraph/pull/3843)) [@jnke2016](https://github.com/jnke2016) +- Use new `raft::compiled_static` targets ([#3842](https://github.com/rapidsai/cugraph/pull/3842)) [@divyegala](https://github.com/divyegala) +- Rename `cugraph-nx` to `nx-cugraph` ([#3840](https://github.com/rapidsai/cugraph/pull/3840)) [@eriknw](https://github.com/eriknw) +- Add cuGraph devcontainers ([#3838](https://github.com/rapidsai/cugraph/pull/3838)) [@trxcllnt](https://github.com/trxcllnt) +- Enable temporarily disabled MG tests ([#3837](https://github.com/rapidsai/cugraph/pull/3837)) [@naimnv](https://github.com/naimnv) +- Remove legacy betweenness centrality ([#3829](https://github.com/rapidsai/cugraph/pull/3829)) [@jnke2016](https://github.com/jnke2016) +- Use `copy-pr-bot` ([#3827](https://github.com/rapidsai/cugraph/pull/3827)) [@ajschmidt8](https://github.com/ajschmidt8) +- Update README.md ([#3826](https://github.com/rapidsai/cugraph/pull/3826)) [@lmeyerov](https://github.com/lmeyerov) +- Adding metadata getter methods to datasets API ([#3821](https://github.com/rapidsai/cugraph/pull/3821)) [@nv-rliu](https://github.com/nv-rliu) +- Unpin `dask` and `distributed` for `23.10` development ([#3818](https://github.com/rapidsai/cugraph/pull/3818)) [@galipremsagar](https://github.com/galipremsagar) +- Remove Deprecated Sampling Options ([#3816](https://github.com/rapidsai/cugraph/pull/3816)) [@alexbarghi-nv](https://github.com/alexbarghi-nv) +- [REVIEW] Cugraph dgl block improvements ([#3810](https://github.com/rapidsai/cugraph/pull/3810)) [@VibhuJawa](https://github.com/VibhuJawa) +- Simplify wheel build scripts and allow alphas of RAPIDS dependencies ([#3809](https://github.com/rapidsai/cugraph/pull/3809)) [@vyasr](https://github.com/vyasr) +- Allow cugraph-nx to run networkx tests for nx versions 3.0, 3.1, and 3.2 ([#3808](https://github.com/rapidsai/cugraph/pull/3808)) [@eriknw](https://github.com/eriknw) +- Add `louvain_communities` to cugraph-nx ([#3803](https://github.com/rapidsai/cugraph/pull/3803)) [@eriknw](https://github.com/eriknw) +- Adds missing copyright and license text to __init__.py package files ([#3799](https://github.com/rapidsai/cugraph/pull/3799)) [@rlratzel](https://github.com/rlratzel) +- cuGraph-PyG Loader Improvements ([#3795](https://github.com/rapidsai/cugraph/pull/3795)) [@alexbarghi-nv](https://github.com/alexbarghi-nv) +- Adds updates to build wheel and conda packages for `cugraph-nx` ([#3793](https://github.com/rapidsai/cugraph/pull/3793)) [@rlratzel](https://github.com/rlratzel) +- Expose threshold in louvain ([#3792](https://github.com/rapidsai/cugraph/pull/3792)) [@ChuckHastings](https://github.com/ChuckHastings) +- Allow models to use a lightweight sparse structure ([#3782](https://github.com/rapidsai/cugraph/pull/3782)) [@tingyu66](https://github.com/tingyu66) +- Clean-up old testing conventions in `test_ecg.py` ([#3779](https://github.com/rapidsai/cugraph/pull/3779)) [@nv-rliu](https://github.com/nv-rliu) +- Calling `dataset.get_edgelist()` returns a copy of an edge list instead of global ([#3777](https://github.com/rapidsai/cugraph/pull/3777)) [@nv-rliu](https://github.com/nv-rliu) +- Update dgl benchmarks ([#3775](https://github.com/rapidsai/cugraph/pull/3775)) [@VibhuJawa](https://github.com/VibhuJawa) +- Forward-merge branch-23.08 to branch-23.10 ([#3774](https://github.com/rapidsai/cugraph/pull/3774)) [@nv-rliu](https://github.com/nv-rliu) +- Migrate upstream models to `cugraph-pyg` ([#3763](https://github.com/rapidsai/cugraph/pull/3763)) [@tingyu66](https://github.com/tingyu66) +- Branch 23.10 merge 23.08 ([#3743](https://github.com/rapidsai/cugraph/pull/3743)) [@vyasr](https://github.com/vyasr) +- Update to Cython 3.0.0 ([#3716](https://github.com/rapidsai/cugraph/pull/3716)) [@vyasr](https://github.com/vyasr) +- Testing util improvements and refactoring ([#3705](https://github.com/rapidsai/cugraph/pull/3705)) [@betochimas](https://github.com/betochimas) +- Add new cugraph-nx package (networkx backend using pylibcugraph) ([#3614](https://github.com/rapidsai/cugraph/pull/3614)) [@eriknw](https://github.com/eriknw) +- New mtmg API for integration ([#3521](https://github.com/rapidsai/cugraph/pull/3521)) [@ChuckHastings](https://github.com/ChuckHastings) + # cuGraph 23.08.00 (9 Aug 2023) ## 🚨 Breaking Changes From 06a840ed5a27cce954a7ef4488e1f5b430add4bf Mon Sep 17 00:00:00 2001 From: Ralph Liu <137829296+nv-rliu@users.noreply.github.com> Date: Wed, 11 Oct 2023 11:12:41 -0400 Subject: [PATCH 040/111] Update Broken Links in README.md (#3924) Closes #3915 This PR fixes two broken links in our README.md file. Authors: - Ralph Liu (https://github.com/nv-rliu) Approvers: - Don Acosta (https://github.com/acostadon) - Brad Rees (https://github.com/BradReesWork) URL: https://github.com/rapidsai/cugraph/pull/3924 --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 3daeb0570b0..560d1483242 100644 --- a/README.md +++ b/README.md @@ -43,8 +43,8 @@ - [Contributing to cuGraph](./readme_pages/CONTRIBUTING.md) - General - [Latest News](./readme_pages/news.md) - - [Current list of algorithms](./readme_pages/algorithms.md) - - [Blogs and Presentation](./docs/cugraph/source/basics/cugraph_blogs.rst) + - [Current list of algorithms](./docs/cugraph/source/graph_support/algorithms.md) + - [Blogs and Presentation](./docs/cugraph/source/tutorials/cugraph_blogs.rst) - [Performance](./readme_pages/performance/performance.md) - Packages - [cuGraph Python](./readme_pages/cugraph_python.md) From f605d6a5d842a9075dba5631d5cc12769f5777a6 Mon Sep 17 00:00:00 2001 From: Bradley Dice Date: Wed, 11 Oct 2023 19:41:32 -0500 Subject: [PATCH 041/111] Use branch-23.12 workflows. (#3928) This PR switches back to using `branch-23.12` for CI workflows because the CUDA 12 ARM conda migration is complete. Authors: - Bradley Dice (https://github.com/bdice) Approvers: - Jake Awe (https://github.com/AyodeAwe) URL: https://github.com/rapidsai/cugraph/pull/3928 --- .github/workflows/build.yaml | 20 ++++++++++---------- .github/workflows/pr.yaml | 30 +++++++++++++++--------------- .github/workflows/test.yaml | 10 +++++----- 3 files changed, 30 insertions(+), 30 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index b02b134b337..f8f8bbc1166 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -28,7 +28,7 @@ concurrency: jobs: cpp-build: secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/conda-cpp-build.yaml@cuda-120-arm + uses: rapidsai/shared-action-workflows/.github/workflows/conda-cpp-build.yaml@branch-23.12 with: build_type: ${{ inputs.build_type || 'branch' }} branch: ${{ inputs.branch }} @@ -38,7 +38,7 @@ jobs: python-build: needs: [cpp-build] secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/conda-python-build.yaml@cuda-120-arm + uses: rapidsai/shared-action-workflows/.github/workflows/conda-python-build.yaml@branch-23.12 with: build_type: ${{ inputs.build_type || 'branch' }} branch: ${{ inputs.branch }} @@ -47,7 +47,7 @@ jobs: upload-conda: needs: [cpp-build, python-build] secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/conda-upload-packages.yaml@cuda-120-arm + uses: rapidsai/shared-action-workflows/.github/workflows/conda-upload-packages.yaml@branch-23.12 with: build_type: ${{ inputs.build_type || 'branch' }} branch: ${{ inputs.branch }} @@ -57,7 +57,7 @@ jobs: if: github.ref_type == 'branch' needs: python-build secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/custom-job.yaml@cuda-120-arm + uses: rapidsai/shared-action-workflows/.github/workflows/custom-job.yaml@branch-23.12 with: arch: "amd64" branch: ${{ inputs.branch }} @@ -69,7 +69,7 @@ jobs: sha: ${{ inputs.sha }} wheel-build-pylibcugraph: secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/wheels-build.yaml@cuda-120-arm + uses: rapidsai/shared-action-workflows/.github/workflows/wheels-build.yaml@branch-23.12 with: build_type: ${{ inputs.build_type || 'branch' }} branch: ${{ inputs.branch }} @@ -82,7 +82,7 @@ jobs: wheel-publish-pylibcugraph: needs: wheel-build-pylibcugraph secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/wheels-publish.yaml@cuda-120-arm + uses: rapidsai/shared-action-workflows/.github/workflows/wheels-publish.yaml@branch-23.12 with: build_type: ${{ inputs.build_type || 'branch' }} branch: ${{ inputs.branch }} @@ -92,7 +92,7 @@ jobs: wheel-build-cugraph: needs: wheel-publish-pylibcugraph secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/wheels-build.yaml@cuda-120-arm + uses: rapidsai/shared-action-workflows/.github/workflows/wheels-build.yaml@branch-23.12 with: build_type: ${{ inputs.build_type || 'branch' }} branch: ${{ inputs.branch }} @@ -105,7 +105,7 @@ jobs: wheel-publish-cugraph: needs: wheel-build-cugraph secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/wheels-publish.yaml@cuda-120-arm + uses: rapidsai/shared-action-workflows/.github/workflows/wheels-publish.yaml@branch-23.12 with: build_type: ${{ inputs.build_type || 'branch' }} branch: ${{ inputs.branch }} @@ -115,7 +115,7 @@ jobs: wheel-build-nx-cugraph: needs: wheel-publish-pylibcugraph secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/wheels-build.yaml@cuda-120-arm + uses: rapidsai/shared-action-workflows/.github/workflows/wheels-build.yaml@branch-23.12 with: build_type: ${{ inputs.build_type || 'branch' }} branch: ${{ inputs.branch }} @@ -125,7 +125,7 @@ jobs: wheel-publish-nx-cugraph: needs: wheel-build-nx-cugraph secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/wheels-publish.yaml@cuda-120-arm + uses: rapidsai/shared-action-workflows/.github/workflows/wheels-publish.yaml@branch-23.12 with: build_type: ${{ inputs.build_type || 'branch' }} branch: ${{ inputs.branch }} diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml index e8834e58ace..5f954975818 100644 --- a/.github/workflows/pr.yaml +++ b/.github/workflows/pr.yaml @@ -27,41 +27,41 @@ jobs: - wheel-tests-nx-cugraph - devcontainer secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/pr-builder.yaml@cuda-120-arm + uses: rapidsai/shared-action-workflows/.github/workflows/pr-builder.yaml@branch-23.12 checks: secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/checks.yaml@cuda-120-arm + uses: rapidsai/shared-action-workflows/.github/workflows/checks.yaml@branch-23.12 with: enable_check_generated_files: false conda-cpp-build: needs: checks secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/conda-cpp-build.yaml@cuda-120-arm + uses: rapidsai/shared-action-workflows/.github/workflows/conda-cpp-build.yaml@branch-23.12 with: build_type: pull-request node_type: cpu32 conda-cpp-tests: needs: conda-cpp-build secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/conda-cpp-tests.yaml@cuda-120-arm + uses: rapidsai/shared-action-workflows/.github/workflows/conda-cpp-tests.yaml@branch-23.12 with: build_type: pull-request conda-python-build: needs: conda-cpp-build secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/conda-python-build.yaml@cuda-120-arm + uses: rapidsai/shared-action-workflows/.github/workflows/conda-python-build.yaml@branch-23.12 with: build_type: pull-request conda-python-tests: needs: conda-python-build secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/conda-python-tests.yaml@cuda-120-arm + uses: rapidsai/shared-action-workflows/.github/workflows/conda-python-tests.yaml@branch-23.12 with: build_type: pull-request conda-notebook-tests: needs: conda-python-build secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/custom-job.yaml@cuda-120-arm + uses: rapidsai/shared-action-workflows/.github/workflows/custom-job.yaml@branch-23.12 with: build_type: pull-request node_type: "gpu-v100-latest-1" @@ -71,7 +71,7 @@ jobs: docs-build: needs: conda-python-build secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/custom-job.yaml@cuda-120-arm + uses: rapidsai/shared-action-workflows/.github/workflows/custom-job.yaml@branch-23.12 with: build_type: pull-request node_type: "gpu-v100-latest-1" @@ -81,7 +81,7 @@ jobs: wheel-build-pylibcugraph: needs: checks secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/wheels-build.yaml@cuda-120-arm + uses: rapidsai/shared-action-workflows/.github/workflows/wheels-build.yaml@branch-23.12 with: build_type: pull-request script: ci/build_wheel_pylibcugraph.sh @@ -91,14 +91,14 @@ jobs: wheel-tests-pylibcugraph: needs: wheel-build-pylibcugraph secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/wheels-test.yaml@cuda-120-arm + uses: rapidsai/shared-action-workflows/.github/workflows/wheels-test.yaml@branch-23.12 with: build_type: pull-request script: ci/test_wheel_pylibcugraph.sh wheel-build-cugraph: needs: wheel-tests-pylibcugraph secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/wheels-build.yaml@cuda-120-arm + uses: rapidsai/shared-action-workflows/.github/workflows/wheels-build.yaml@branch-23.12 with: build_type: pull-request script: ci/build_wheel_cugraph.sh @@ -108,27 +108,27 @@ jobs: wheel-tests-cugraph: needs: wheel-build-cugraph secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/wheels-test.yaml@cuda-120-arm + uses: rapidsai/shared-action-workflows/.github/workflows/wheels-test.yaml@branch-23.12 with: build_type: pull-request script: ci/test_wheel_cugraph.sh wheel-build-nx-cugraph: needs: wheel-tests-pylibcugraph secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/wheels-build.yaml@cuda-120-arm + uses: rapidsai/shared-action-workflows/.github/workflows/wheels-build.yaml@branch-23.12 with: build_type: pull-request script: ci/build_wheel_nx-cugraph.sh wheel-tests-nx-cugraph: needs: wheel-build-nx-cugraph secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/wheels-test.yaml@cuda-120-arm + uses: rapidsai/shared-action-workflows/.github/workflows/wheels-test.yaml@branch-23.12 with: build_type: pull-request script: ci/test_wheel_nx-cugraph.sh devcontainer: secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/build-in-devcontainer.yaml@cuda-120-arm + uses: rapidsai/shared-action-workflows/.github/workflows/build-in-devcontainer.yaml@branch-23.12 with: extra-repo-deploy-key: CUGRAPH_OPS_SSH_PRIVATE_DEPLOY_KEY build_command: | diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index dc40eae5f0d..6d2c1566a3c 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -16,7 +16,7 @@ on: jobs: conda-cpp-tests: secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/conda-cpp-tests.yaml@cuda-120-arm + uses: rapidsai/shared-action-workflows/.github/workflows/conda-cpp-tests.yaml@branch-23.12 with: build_type: nightly branch: ${{ inputs.branch }} @@ -24,7 +24,7 @@ jobs: sha: ${{ inputs.sha }} conda-python-tests: secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/conda-python-tests.yaml@cuda-120-arm + uses: rapidsai/shared-action-workflows/.github/workflows/conda-python-tests.yaml@branch-23.12 with: build_type: nightly branch: ${{ inputs.branch }} @@ -32,7 +32,7 @@ jobs: sha: ${{ inputs.sha }} wheel-tests-pylibcugraph: secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/wheels-test.yaml@cuda-120-arm + uses: rapidsai/shared-action-workflows/.github/workflows/wheels-test.yaml@branch-23.12 with: build_type: nightly branch: ${{ inputs.branch }} @@ -41,7 +41,7 @@ jobs: script: ci/test_wheel_pylibcugraph.sh wheel-tests-cugraph: secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/wheels-test.yaml@cuda-120-arm + uses: rapidsai/shared-action-workflows/.github/workflows/wheels-test.yaml@branch-23.12 with: build_type: nightly branch: ${{ inputs.branch }} @@ -50,7 +50,7 @@ jobs: script: ci/test_wheel_cugraph.sh wheel-tests-nx-cugraph: secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/wheels-test.yaml@cuda-120-arm + uses: rapidsai/shared-action-workflows/.github/workflows/wheels-test.yaml@branch-23.12 with: build_type: nightly branch: ${{ inputs.branch }} From 63e282e28554645a0026f2fa8e5b321fda208369 Mon Sep 17 00:00:00 2001 From: Brad Rees <34135411+BradReesWork@users.noreply.github.com> Date: Thu, 12 Oct 2023 10:22:11 -0400 Subject: [PATCH 042/111] new build all option (#3916) option to build all Renamed the old buildAll function to buildDefault Added a check for the "all" argument Authors: - Brad Rees (https://github.com/BradReesWork) Approvers: - Joseph Nke (https://github.com/jnke2016) - Rick Ratzel (https://github.com/rlratzel) URL: https://github.com/rapidsai/cugraph/pull/3916 --- build.sh | 36 +++++++++++++++++++----------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/build.sh b/build.sh index 8dca89aeedd..99082fa96fb 100755 --- a/build.sh +++ b/build.sh @@ -24,14 +24,15 @@ VALIDARGS=" uninstall libcugraph libcugraph_etl + pylibcugraph cugraph cugraph-service - pylibcugraph - cpp-mgtests cugraph-pyg cugraph-dgl nx-cugraph + cpp-mgtests docs + all -v -g -n @@ -52,13 +53,14 @@ HELP="$0 [ ...] [ ...] libcugraph - build libcugraph.so and SG test binaries libcugraph_etl - build libcugraph_etl.so and SG test binaries pylibcugraph - build the pylibcugraph Python package - cugraph-pyg - build the cugraph-pyg Python package cugraph - build the cugraph Python package - nx-cugraph - build the nx-cugraph Python package cugraph-service - build the cugraph-service_client and cugraph-service_server Python package - cpp-mgtests - build libcugraph and libcugraph_etl MG tests. Builds MPI communicator, adding MPI as a dependency. + cugraph-pyg - build the cugraph-pyg Python package cugraph-dgl - build the cugraph-dgl extensions for DGL + nx-cugraph - build the nx-cugraph Python package + cpp-mgtests - build libcugraph and libcugraph_etl MG tests. Builds MPI communicator, adding MPI as a dependency. docs - build the docs + all - build everything and is: -v - verbose build mode -g - build for debug @@ -71,7 +73,7 @@ HELP="$0 [ ...] [ ...] --clean - clean an individual target (note: to do a complete rebuild, use the clean target described above) -h - print this text - default action (no args) is to build and install 'libcugraph' then 'libcugraph_etl' then 'pylibcugraph' then 'cugraph' targets + default action (no args) is to build and install 'libcugraph' then 'libcugraph_etl' then 'pylibcugraph' and then 'cugraph' targets libcugraph build dir is: ${LIBCUGRAPH_BUILD_DIR} @@ -119,7 +121,7 @@ function hasArg { (( ${NUMARGS} != 0 )) && (echo " ${ARGS} " | grep -q " $1 ") } -function buildAll { +function buildDefault { (( ${NUMARGS} == 0 )) || !(echo " ${ARGS} " | grep -q " [^-][a-zA-Z0-9\_\-]\+ ") } @@ -170,7 +172,7 @@ fi if hasArg --without_cugraphops; then BUILD_WITH_CUGRAPHOPS=OFF fi -if hasArg cpp-mgtests; then +if hasArg cpp-mgtests || hasArg all; then BUILD_CPP_MG_TESTS=ON fi if hasArg --cmake_default_generator; then @@ -240,7 +242,7 @@ fi ################################################################################ # Configure, build, and install libcugraph -if buildAll || hasArg libcugraph; then +if buildDefault || hasArg libcugraph || hasArg all; then if hasArg --clean; then if [ -d ${LIBCUGRAPH_BUILD_DIR} ]; then find ${LIBCUGRAPH_BUILD_DIR} -mindepth 1 -delete @@ -270,7 +272,7 @@ if buildAll || hasArg libcugraph; then fi # Configure, build, and install libcugraph_etl -if buildAll || hasArg libcugraph_etl; then +if buildDefault || hasArg libcugraph_etl || hasArg all; then if hasArg --clean; then if [ -d ${LIBCUGRAPH_ETL_BUILD_DIR} ]; then find ${LIBCUGRAPH_ETL_BUILD_DIR} -mindepth 1 -delete @@ -301,7 +303,7 @@ if buildAll || hasArg libcugraph_etl; then fi # Build, and install pylibcugraph -if buildAll || hasArg pylibcugraph; then +if buildDefault || hasArg pylibcugraph || hasArg all; then if hasArg --clean; then cleanPythonDir ${REPODIR}/python/pylibcugraph else @@ -328,7 +330,7 @@ if buildAll || hasArg pylibcugraph; then fi # Build and install the cugraph Python package -if buildAll || hasArg cugraph; then +if buildDefault || hasArg cugraph || hasArg all; then if hasArg --clean; then cleanPythonDir ${REPODIR}/python/cugraph else @@ -355,7 +357,7 @@ if buildAll || hasArg cugraph; then fi # Install the cugraph-service-client and cugraph-service-server Python packages -if hasArg cugraph-service; then +if hasArg cugraph-service || hasArg all; then if hasArg --clean; then cleanPythonDir ${REPODIR}/python/cugraph-service else @@ -365,7 +367,7 @@ if hasArg cugraph-service; then fi # Build and install the cugraph-pyg Python package -if hasArg cugraph-pyg; then +if hasArg cugraph-pyg || hasArg all; then if hasArg --clean; then cleanPythonDir ${REPODIR}/python/cugraph-pyg else @@ -374,7 +376,7 @@ if hasArg cugraph-pyg; then fi # Install the cugraph-dgl extensions for DGL -if hasArg cugraph-dgl; then +if hasArg cugraph-dgl || hasArg all; then if hasArg --clean; then cleanPythonDir ${REPODIR}/python/cugraph-dgl else @@ -383,7 +385,7 @@ if hasArg cugraph-dgl; then fi # Build and install the nx-cugraph Python package -if hasArg nx-cugraph; then +if hasArg nx-cugraph || hasArg all; then if hasArg --clean; then cleanPythonDir ${REPODIR}/python/nx-cugraph else @@ -392,7 +394,7 @@ if hasArg nx-cugraph; then fi # Build the docs -if hasArg docs; then +if hasArg docs || hasArg all; then if [ ! -d ${LIBCUGRAPH_BUILD_DIR} ]; then mkdir -p ${LIBCUGRAPH_BUILD_DIR} cd ${LIBCUGRAPH_BUILD_DIR} From 3003307f21e46689cccfd623ebc40cf60f751c2d Mon Sep 17 00:00:00 2001 From: Ralph Liu Date: Thu, 12 Oct 2023 12:57:24 -0700 Subject: [PATCH 043/111] TODO: revert this commit for re-running ci --- print_env.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/print_env.sh b/print_env.sh index 6f2d33b0eb8..f117efb3ba5 100644 --- a/print_env.sh +++ b/print_env.sh @@ -84,4 +84,4 @@ echo " " print_env | while read -r line; do echo " $line" done -echo "" +echo "" From 60c2d46614c50bff664179ac3fa269472df8adf4 Mon Sep 17 00:00:00 2001 From: Ralph Liu Date: Thu, 12 Oct 2023 12:57:46 -0700 Subject: [PATCH 044/111] DONE: reverted to re-run ci --- print_env.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/print_env.sh b/print_env.sh index f117efb3ba5..6f2d33b0eb8 100644 --- a/print_env.sh +++ b/print_env.sh @@ -84,4 +84,4 @@ echo " " print_env | while read -r line; do echo " $line" done -echo "" +echo "" From d52344db280cbd178e4950c421b620c29d2f2176 Mon Sep 17 00:00:00 2001 From: Ralph Liu Date: Thu, 12 Oct 2023 16:40:56 -0700 Subject: [PATCH 045/111] style fix for merged changes --- conda/environments/all_cuda-118_arch-x86_64.yaml | 1 + conda/environments/all_cuda-120_arch-x86_64.yaml | 1 + 2 files changed, 2 insertions(+) diff --git a/conda/environments/all_cuda-118_arch-x86_64.yaml b/conda/environments/all_cuda-118_arch-x86_64.yaml index 42a218fcf66..71a8d10b935 100644 --- a/conda/environments/all_cuda-118_arch-x86_64.yaml +++ b/conda/environments/all_cuda-118_arch-x86_64.yaml @@ -75,5 +75,6 @@ dependencies: - sphinxcontrib-websupport - ucx-proc=*=gpu - ucx-py==0.35.* +- wget - wheel name: all_cuda-118_arch-x86_64 diff --git a/conda/environments/all_cuda-120_arch-x86_64.yaml b/conda/environments/all_cuda-120_arch-x86_64.yaml index 0a1774b769f..9179fba788e 100644 --- a/conda/environments/all_cuda-120_arch-x86_64.yaml +++ b/conda/environments/all_cuda-120_arch-x86_64.yaml @@ -74,5 +74,6 @@ dependencies: - sphinxcontrib-websupport - ucx-proc=*=gpu - ucx-py==0.35.* +- wget - wheel name: all_cuda-120_arch-x86_64 From c4570901fb1f3f2cf3a3d2099f53286c3fc83d8a Mon Sep 17 00:00:00 2001 From: Ralph Liu Date: Fri, 13 Oct 2023 08:46:07 -0700 Subject: [PATCH 046/111] bump wheel-build-plc to cpu32 --- .github/workflows/build.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index f8f8bbc1166..c92c11941f9 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -79,6 +79,7 @@ jobs: extra-repo: rapidsai/cugraph-ops extra-repo-sha: branch-23.12 extra-repo-deploy-key: CUGRAPH_OPS_SSH_PRIVATE_DEPLOY_KEY + node_type: cpu32 wheel-publish-pylibcugraph: needs: wheel-build-pylibcugraph secrets: inherit From ebe792b95d70de68470fce4b98118647c95535ef Mon Sep 17 00:00:00 2001 From: Ralph Liu Date: Fri, 13 Oct 2023 10:56:07 -0700 Subject: [PATCH 047/111] update pr.yaml to bump to cpu32 --- .github/workflows/pr.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml index 5f954975818..55068b16cc7 100644 --- a/.github/workflows/pr.yaml +++ b/.github/workflows/pr.yaml @@ -88,6 +88,7 @@ jobs: extra-repo: rapidsai/cugraph-ops extra-repo-sha: branch-23.12 extra-repo-deploy-key: CUGRAPH_OPS_SSH_PRIVATE_DEPLOY_KEY + node_type: cpu32 wheel-tests-pylibcugraph: needs: wheel-build-pylibcugraph secrets: inherit From 89e884a40ba287f0bc14d4161695268beb28b792 Mon Sep 17 00:00:00 2001 From: Seunghwa Kang <45857425+seunghwak@users.noreply.github.com> Date: Sat, 14 Oct 2023 16:55:02 -0700 Subject: [PATCH 048/111] Silence spurious compiler warnings (#3913) We see compiler warnings basically saying some internal data member in `std::optional` may be used uninitialized. This lengthy warnings are very annoying and can hide important warnings/errors. Seeing https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80635, it seems like this warning is not valid and due to the fact that the compiler cannot perfectly track when an `std::optional` object is valid or not in every code path. Anyways, this PR tweaks the code to silence the warnings. There is very little practical difference between the old and new code, but I don't see the annoying warnings anymore with this update. See the below for the actual warnings. ``` [1/1] Building CUDA object CMakeFiles/cugraph.dir/src/structure/graph_view_mg.cu.o In member function 'cugraph::detail::edge_minor_property_t& cugraph::detail::edge_minor_property_t::operator=(cugraph::detail::edge_minor_property_t&&)', inlined from 'cugraph::edge_src_property_t::edge_src_property_t(const raft::handle_t&, const GraphViewType&) [with GraphViewType = cugraph::graph_view_t; T = int]' at /home/seunghwak/RAPIDS/development/cugraph/cpp/include/cugraph/edge_src_dst_property.hpp:425:13, inlined from 'void cugraph::detail::per_v_transform_reduce_e(const raft::handle_t&, const GraphViewType&, EdgeSrcValueInputWrapper, EdgeDstValueInputWrapper, EdgeValueInputWrapper, EdgeOp, T, ReduceOp, VertexValueOutputIterator) [with bool incoming = false; GraphViewType = cugraph::graph_view_t; EdgeSrcValueInputWrapper = cugraph::detail::edge_endpoint_dummy_property_view_t; EdgeDstValueInputWrapper = cugraph::detail::edge_endpoint_dummy_property_view_t; EdgeValueInputWrapper = cugraph::edge_dummy_property_view_t; EdgeOp = __nv_dl_wrapper_t<__nv_dl_tag (*)(const raft::handle_t&, const cugraph::graph_view_t&), cugraph::_GLOBAL__N__fbd7e12b_16_graph_view_mg_cu_854a5764_83484::compute_minor_degrees, 1> >; ReduceOp = cugraph::reduce_op::plus; T = int; VertexValueOutputIterator = int*]' at /home/seunghwak/RAPIDS/development/cugraph/cpp/src/prims/per_v_transform_reduce_incoming_outgoing_e.cuh:522:20: /home/seunghwak/RAPIDS/development/cugraph/cpp/include/cugraph/edge_src_dst_property.hpp:285:7: warning: '((const std::size_t*)((char*)& + offsetof(cugraph::edge_src_property_t, int>,cugraph::edge_src_property_t, int>::property_.cugraph::detail::edge_minor_property_t::keys_.std::optional >::.std::_Optional_base, true, true>::_M_payload.std::_Optional_payload, true, true, true>::.std::_Optional_payload_base >::_M_payload)))[1]' may be used uninitialized [-Wmaybe-uninitialized] 285 | class edge_minor_property_t { | ^~~~~~~~~~~~~~~~~~~~~ /home/seunghwak/RAPIDS/development/cugraph/cpp/src/prims/per_v_transform_reduce_incoming_outgoing_e.cuh: In function 'void cugraph::detail::per_v_transform_reduce_e(const raft::handle_t&, const GraphViewType&, EdgeSrcValueInputWrapper, EdgeDstValueInputWrapper, EdgeValueInputWrapper, EdgeOp, T, ReduceOp, VertexValueOutputIterator) [with bool incoming = false; GraphViewType = cugraph::graph_view_t; EdgeSrcValueInputWrapper = cugraph::detail::edge_endpoint_dummy_property_view_t; EdgeDstValueInputWrapper = cugraph::detail::edge_endpoint_dummy_property_view_t; EdgeValueInputWrapper = cugraph::edge_dummy_property_view_t; EdgeOp = __nv_dl_wrapper_t<__nv_dl_tag (*)(const raft::handle_t&, const cugraph::graph_view_t&), cugraph::_GLOBAL__N__fbd7e12b_16_graph_view_mg_cu_854a5764_83484::compute_minor_degrees, 1> >; ReduceOp = cugraph::reduce_op::plus; T = int; VertexValueOutputIterator = int*]': /home/seunghwak/RAPIDS/development/cugraph/cpp/src/prims/per_v_transform_reduce_incoming_outgoing_e.cuh:522:20: note: '' declared here 522 | minor_tmp_buffer = edge_src_property_t(handle, graph_view); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ In member function 'cugraph::detail::edge_minor_property_t& cugraph::detail::edge_minor_property_t::operator=(cugraph::detail::edge_minor_property_t&&)', inlined from 'cugraph::edge_src_property_t::edge_src_property_t(const raft::handle_t&, const GraphViewType&) [with GraphViewType = cugraph::graph_view_t; T = int]' at /home/seunghwak/RAPIDS/development/cugraph/cpp/include/cugraph/edge_src_dst_property.hpp:425:13, inlined from 'void cugraph::detail::per_v_transform_reduce_e(const raft::handle_t&, const GraphViewType&, EdgeSrcValueInputWrapper, EdgeDstValueInputWrapper, EdgeValueInputWrapper, EdgeOp, T, ReduceOp, VertexValueOutputIterator) [with bool incoming = false; GraphViewType = cugraph::graph_view_t; EdgeSrcValueInputWrapper = cugraph::detail::edge_endpoint_dummy_property_view_t; EdgeDstValueInputWrapper = cugraph::detail::edge_endpoint_dummy_property_view_t; EdgeValueInputWrapper = cugraph::edge_dummy_property_view_t; EdgeOp = __nv_dl_wrapper_t<__nv_dl_tag (*)(const raft::handle_t&, const cugraph::graph_view_t&), cugraph::_GLOBAL__N__fbd7e12b_16_graph_view_mg_cu_854a5764_83484::compute_minor_degrees, 1> >; ReduceOp = cugraph::reduce_op::plus; T = int; VertexValueOutputIterator = int*]' at /home/seunghwak/RAPIDS/development/cugraph/cpp/src/prims/per_v_transform_reduce_incoming_outgoing_e.cuh:522:20: /home/seunghwak/RAPIDS/development/cugraph/cpp/include/cugraph/edge_src_dst_property.hpp:285:7: warning: '*(long unsigned int*)((char*)& + offsetof(cugraph::edge_src_property_t, int>,cugraph::edge_src_property_t, int>::property_.cugraph::detail::edge_minor_property_t::key_chunk_size_.std::optional::.std::_Optional_base::))' may be used uninitialized [-Wmaybe-uninitialized] 285 | class edge_minor_property_t { | ^~~~~~~~~~~~~~~~~~~~~ /home/seunghwak/RAPIDS/development/cugraph/cpp/src/prims/per_v_transform_reduce_incoming_outgoing_e.cuh: In function 'void cugraph::detail::per_v_transform_reduce_e(const raft::handle_t&, const GraphViewType&, EdgeSrcValueInputWrapper, EdgeDstValueInputWrapper, EdgeValueInputWrapper, EdgeOp, T, ReduceOp, VertexValueOutputIterator) [with bool incoming = false; GraphViewType = cugraph::graph_view_t; EdgeSrcValueInputWrapper = cugraph::detail::edge_endpoint_dummy_property_view_t; EdgeDstValueInputWrapper = cugraph::detail::edge_endpoint_dummy_property_view_t; EdgeValueInputWrapper = cugraph::edge_dummy_property_view_t; EdgeOp = __nv_dl_wrapper_t<__nv_dl_tag (*)(const raft::handle_t&, const cugraph::graph_view_t&), cugraph::_GLOBAL__N__fbd7e12b_16_graph_view_mg_cu_854a5764_83484::compute_minor_degrees, 1> >; ReduceOp = cugraph::reduce_op::plus; T = int; VertexValueOutputIterator = int*]': /home/seunghwak/RAPIDS/development/cugraph/cpp/src/prims/per_v_transform_reduce_incoming_outgoing_e.cuh:522:20: note: '' declared here 522 | minor_tmp_buffer = edge_src_property_t(handle, graph_view); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ In member function 'cugraph::detail::edge_minor_property_t& cugraph::detail::edge_minor_property_t::operator=(cugraph::detail::edge_minor_property_t&&)', inlined from 'cugraph::edge_src_property_t, int>& cugraph::edge_src_property_t, int>::operator=(cugraph::edge_src_property_t, int>&&)' at /home/seunghwak/RAPIDS/development/cugraph/cpp/include/cugraph/edge_src_dst_property.hpp:375:7, inlined from 'void cugraph::detail::per_v_transform_reduce_e(const raft::handle_t&, const GraphViewType&, EdgeSrcValueInputWrapper, EdgeDstValueInputWrapper, EdgeValueInputWrapper, EdgeOp, T, ReduceOp, VertexValueOutputIterator) [with bool incoming = false; GraphViewType = cugraph::graph_view_t; EdgeSrcValueInputWrapper = cugraph::detail::edge_endpoint_dummy_property_view_t; EdgeDstValueInputWrapper = cugraph::detail::edge_endpoint_dummy_property_view_t; EdgeValueInputWrapper = cugraph::edge_dummy_property_view_t; EdgeOp = __nv_dl_wrapper_t<__nv_dl_tag (*)(const raft::handle_t&, const cugraph::graph_view_t&), cugraph::_GLOBAL__N__fbd7e12b_16_graph_view_mg_cu_854a5764_83484::compute_minor_degrees, 1> >; ReduceOp = cugraph::reduce_op::plus; T = int; VertexValueOutputIterator = int*]' at /home/seunghwak/RAPIDS/development/cugraph/cpp/src/prims/per_v_transform_reduce_incoming_outgoing_e.cuh:522:18: /home/seunghwak/RAPIDS/development/cugraph/cpp/include/cugraph/edge_src_dst_property.hpp:285:7: warning: '*(const int**)((char*)& + offsetof(cugraph::edge_src_property_t, int>,cugraph::edge_src_property_t, int>::property_.cugraph::detail::edge_minor_property_t::keys_.std::optional >::.std::_Optional_base, true, true>::))' may be used uninitialized [-Wmaybe-uninitialized] 285 | class edge_minor_property_t { | ^~~~~~~~~~~~~~~~~~~~~ /home/seunghwak/RAPIDS/development/cugraph/cpp/src/prims/per_v_transform_reduce_incoming_outgoing_e.cuh: In function 'void cugraph::detail::per_v_transform_reduce_e(const raft::handle_t&, const GraphViewType&, EdgeSrcValueInputWrapper, EdgeDstValueInputWrapper, EdgeValueInputWrapper, EdgeOp, T, ReduceOp, VertexValueOutputIterator) [with bool incoming = false; GraphViewType = cugraph::graph_view_t; EdgeSrcValueInputWrapper = cugraph::detail::edge_endpoint_dummy_property_view_t; EdgeDstValueInputWrapper = cugraph::detail::edge_endpoint_dummy_property_view_t; EdgeValueInputWrapper = cugraph::edge_dummy_property_view_t; EdgeOp = __nv_dl_wrapper_t<__nv_dl_tag (*)(const raft::handle_t&, const cugraph::graph_view_t&), cugraph::_GLOBAL__N__fbd7e12b_16_graph_view_mg_cu_854a5764_83484::compute_minor_degrees, 1> >; ReduceOp = cugraph::reduce_op::plus; T = int; VertexValueOutputIterator = int*]': /home/seunghwak/RAPIDS/development/cugraph/cpp/src/prims/per_v_transform_reduce_incoming_outgoing_e.cuh:522:20: note: '' declared here 522 | minor_tmp_buffer = edge_src_property_t(handle, graph_view); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ In member function 'cugraph::detail::edge_minor_property_t& cugraph::detail::edge_minor_property_t::operator=(cugraph::detail::edge_minor_property_t&&)', inlined from 'cugraph::edge_src_property_t, int>& cugraph::edge_src_property_t, int>::operator=(cugraph::edge_src_property_t, int>&&)' at /home/seunghwak/RAPIDS/development/cugraph/cpp/include/cugraph/edge_src_dst_property.hpp:375:7, inlined from 'void cugraph::detail::per_v_transform_reduce_e(const raft::handle_t&, const GraphViewType&, EdgeSrcValueInputWrapper, EdgeDstValueInputWrapper, EdgeValueInputWrapper, EdgeOp, T, ReduceOp, VertexValueOutputIterator) [with bool incoming = false; GraphViewType = cugraph::graph_view_t; EdgeSrcValueInputWrapper = cugraph::detail::edge_endpoint_dummy_property_view_t; EdgeDstValueInputWrapper = cugraph::detail::edge_endpoint_dummy_property_view_t; EdgeValueInputWrapper = cugraph::edge_dummy_property_view_t; EdgeOp = __nv_dl_wrapper_t<__nv_dl_tag (*)(const raft::handle_t&, const cugraph::graph_view_t&), cugraph::_GLOBAL__N__fbd7e12b_16_graph_view_mg_cu_854a5764_83484::compute_minor_degrees, 1> >; ReduceOp = cugraph::reduce_op::plus; T = int; VertexValueOutputIterator = int*]' at /home/seunghwak/RAPIDS/development/cugraph/cpp/src/prims/per_v_transform_reduce_incoming_outgoing_e.cuh:522:18: /home/seunghwak/RAPIDS/development/cugraph/cpp/include/cugraph/edge_src_dst_property.hpp:285:7: warning: '*(const int**)((char*)& + offsetof(cugraph::edge_src_property_t, int>,cugraph::edge_src_property_t, int>::property_.cugraph::detail::edge_minor_property_t::key_chunk_start_offsets_.std::optional >::.std::_Optional_base, true, true>::))' may be used uninitialized [-Wmaybe-uninitialized] 285 | class edge_minor_property_t { | ^~~~~~~~~~~~~~~~~~~~~ /home/seunghwak/RAPIDS/development/cugraph/cpp/src/prims/per_v_transform_reduce_incoming_outgoing_e.cuh: In function 'void cugraph::detail::per_v_transform_reduce_e(const raft::handle_t&, const GraphViewType&, EdgeSrcValueInputWrapper, EdgeDstValueInputWrapper, EdgeValueInputWrapper, EdgeOp, T, ReduceOp, VertexValueOutputIterator) [with bool incoming = false; GraphViewType = cugraph::graph_view_t; EdgeSrcValueInputWrapper = cugraph::detail::edge_endpoint_dummy_property_view_t; EdgeDstValueInputWrapper = cugraph::detail::edge_endpoint_dummy_property_view_t; EdgeValueInputWrapper = cugraph::edge_dummy_property_view_t; EdgeOp = __nv_dl_wrapper_t<__nv_dl_tag (*)(const raft::handle_t&, const cugraph::graph_view_t&), cugraph::_GLOBAL__N__fbd7e12b_16_graph_view_mg_cu_854a5764_83484::compute_minor_degrees, 1> >; ReduceOp = cugraph::reduce_op::plus; T = int; VertexValueOutputIterator = int*]': /home/seunghwak/RAPIDS/development/cugraph/cpp/src/prims/per_v_transform_reduce_incoming_outgoing_e.cuh:522:20: note: '' declared here 522 | minor_tmp_buffer = edge_src_property_t(handle, graph_view); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ In member function 'cugraph::detail::edge_minor_property_t& cugraph::detail::edge_minor_property_t::operator=(cugraph::detail::edge_minor_property_t&&)', inlined from 'cugraph::edge_src_property_t, int>& cugraph::edge_src_property_t, int>::operator=(cugraph::edge_src_property_t, int>&&)' at /home/seunghwak/RAPIDS/development/cugraph/cpp/include/cugraph/edge_src_dst_property.hpp:375:7, inlined from 'void cugraph::detail::per_v_transform_reduce_e(const raft::handle_t&, const GraphViewType&, EdgeSrcValueInputWrapper, EdgeDstValueInputWrapper, EdgeValueInputWrapper, EdgeOp, T, ReduceOp, VertexValueOutputIterator) [with bool incoming = false; GraphViewType = cugraph::graph_view_t; EdgeSrcValueInputWrapper = cugraph::detail::edge_endpoint_dummy_property_view_t; EdgeDstValueInputWrapper = cugraph::detail::edge_endpoint_dummy_property_view_t; EdgeValueInputWrapper = cugraph::edge_dummy_property_view_t; EdgeOp = __nv_dl_wrapper_t<__nv_dl_tag (*)(const raft::handle_t&, const cugraph::graph_view_t&), cugraph::_GLOBAL__N__fbd7e12b_16_graph_view_mg_cu_854a5764_83484::compute_minor_degrees, 1> >; ReduceOp = cugraph::reduce_op::plus; T = int; VertexValueOutputIterator = int*]' at /home/seunghwak/RAPIDS/development/cugraph/cpp/src/prims/per_v_transform_reduce_incoming_outgoing_e.cuh:522:18: /home/seunghwak/RAPIDS/development/cugraph/cpp/include/cugraph/edge_src_dst_property.hpp:285:7: warning: '((std::size_t*)((char*)& + offsetof(cugraph::edge_src_property_t, int>,cugraph::edge_src_property_t, int>::property_.cugraph::detail::edge_minor_property_t::key_chunk_start_offsets_.std::optional >::.std::_Optional_base, true, true>::_M_payload.std::_Optional_payload, true, true, true>::.std::_Optional_payload_base >::_M_payload)))[1]' may be used uninitialized [-Wmaybe-uninitialized] 285 | class edge_minor_property_t { | ^~~~~~~~~~~~~~~~~~~~~ /home/seunghwak/RAPIDS/development/cugraph/cpp/src/prims/per_v_transform_reduce_incoming_outgoing_e.cuh: In function 'void cugraph::detail::per_v_transform_reduce_e(const raft::handle_t&, const GraphViewType&, EdgeSrcValueInputWrapper, EdgeDstValueInputWrapper, EdgeValueInputWrapper, EdgeOp, T, ReduceOp, VertexValueOutputIterator) [with bool incoming = false; GraphViewType = cugraph::graph_view_t; EdgeSrcValueInputWrapper = cugraph::detail::edge_endpoint_dummy_property_view_t; EdgeDstValueInputWrapper = cugraph::detail::edge_endpoint_dummy_property_view_t; EdgeValueInputWrapper = cugraph::edge_dummy_property_view_t; EdgeOp = __nv_dl_wrapper_t<__nv_dl_tag (*)(const raft::handle_t&, const cugraph::graph_view_t&), cugraph::_GLOBAL__N__fbd7e12b_16_graph_view_mg_cu_854a5764_83484::compute_minor_degrees, 1> >; ReduceOp = cugraph::reduce_op::plus; T = int; VertexValueOutputIterator = int*]': /home/seunghwak/RAPIDS/development/cugraph/cpp/src/prims/per_v_transform_reduce_incoming_outgoing_e.cuh:522:20: note: '' declared here 522 | minor_tmp_buffer = edge_src_property_t(handle, graph_view); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ In member function 'cugraph::detail::edge_minor_property_t& cugraph::detail::edge_minor_property_t::operator=(cugraph::detail::edge_minor_property_t&&)', inlined from 'cugraph::edge_dst_property_t::edge_dst_property_t(const raft::handle_t&, const GraphViewType&) [with GraphViewType = cugraph::graph_view_t; T = int]' at /home/seunghwak/RAPIDS/development/cugraph/cpp/include/cugraph/edge_src_dst_property.hpp:527:13, inlined from 'void cugraph::detail::per_v_transform_reduce_e(const raft::handle_t&, const GraphViewType&, EdgeSrcValueInputWrapper, EdgeDstValueInputWrapper, EdgeValueInputWrapper, EdgeOp, T, ReduceOp, VertexValueOutputIterator) [with bool incoming = true; GraphViewType = cugraph::graph_view_t; EdgeSrcValueInputWrapper = cugraph::detail::edge_endpoint_dummy_property_view_t; EdgeDstValueInputWrapper = cugraph::detail::edge_endpoint_dummy_property_view_t; EdgeValueInputWrapper = cugraph::edge_dummy_property_view_t; EdgeOp = __nv_dl_wrapper_t<__nv_dl_tag (*)(const raft::handle_t&, const cugraph::graph_view_t&), cugraph::_GLOBAL__N__fbd7e12b_16_graph_view_mg_cu_854a5764_83484::compute_minor_degrees, 2> >; ReduceOp = cugraph::reduce_op::plus; T = int; VertexValueOutputIterator = int*]' at /home/seunghwak/RAPIDS/development/cugraph/cpp/src/prims/per_v_transform_reduce_incoming_outgoing_e.cuh:524:20: /home/seunghwak/RAPIDS/development/cugraph/cpp/include/cugraph/edge_src_dst_property.hpp:285:7: warning: '((const std::size_t*)((char*)& + offsetof(cugraph::edge_dst_property_t, int>,cugraph::edge_dst_property_t, int>::property_.cugraph::detail::edge_minor_property_t::keys_.std::optional >::.std::_Optional_base, true, true>::_M_payload.std::_Optional_payload, true, true, true>::.std::_Optional_payload_base >::_M_payload)))[1]' may be used uninitialized [-Wmaybe-uninitialized] 285 | class edge_minor_property_t { | ^~~~~~~~~~~~~~~~~~~~~ /home/seunghwak/RAPIDS/development/cugraph/cpp/src/prims/per_v_transform_reduce_incoming_outgoing_e.cuh: In function 'void cugraph::detail::per_v_transform_reduce_e(const raft::handle_t&, const GraphViewType&, EdgeSrcValueInputWrapper, EdgeDstValueInputWrapper, EdgeValueInputWrapper, EdgeOp, T, ReduceOp, VertexValueOutputIterator) [with bool incoming = true; GraphViewType = cugraph::graph_view_t; EdgeSrcValueInputWrapper = cugraph::detail::edge_endpoint_dummy_property_view_t; EdgeDstValueInputWrapper = cugraph::detail::edge_endpoint_dummy_property_view_t; EdgeValueInputWrapper = cugraph::edge_dummy_property_view_t; EdgeOp = __nv_dl_wrapper_t<__nv_dl_tag (*)(const raft::handle_t&, const cugraph::graph_view_t&), cugraph::_GLOBAL__N__fbd7e12b_16_graph_view_mg_cu_854a5764_83484::compute_minor_degrees, 2> >; ReduceOp = cugraph::reduce_op::plus; T = int; VertexValueOutputIterator = int*]': /home/seunghwak/RAPIDS/development/cugraph/cpp/src/prims/per_v_transform_reduce_incoming_outgoing_e.cuh:524:20: note: '' declared here 524 | minor_tmp_buffer = edge_dst_property_t(handle, graph_view); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ In member function 'cugraph::detail::edge_minor_property_t& cugraph::detail::edge_minor_property_t::operator=(cugraph::detail::edge_minor_property_t&&)', inlined from 'cugraph::edge_dst_property_t::edge_dst_property_t(const raft::handle_t&, const GraphViewType&) [with GraphViewType = cugraph::graph_view_t; T = int]' at /home/seunghwak/RAPIDS/development/cugraph/cpp/include/cugraph/edge_src_dst_property.hpp:527:13, inlined from 'void cugraph::detail::per_v_transform_reduce_e(const raft::handle_t&, const GraphViewType&, EdgeSrcValueInputWrapper, EdgeDstValueInputWrapper, EdgeValueInputWrapper, EdgeOp, T, ReduceOp, VertexValueOutputIterator) [with bool incoming = true; GraphViewType = cugraph::graph_view_t; EdgeSrcValueInputWrapper = cugraph::detail::edge_endpoint_dummy_property_view_t; EdgeDstValueInputWrapper = cugraph::detail::edge_endpoint_dummy_property_view_t; EdgeValueInputWrapper = cugraph::edge_dummy_property_view_t; EdgeOp = __nv_dl_wrapper_t<__nv_dl_tag (*)(const raft::handle_t&, const cugraph::graph_view_t&), cugraph::_GLOBAL__N__fbd7e12b_16_graph_view_mg_cu_854a5764_83484::compute_minor_degrees, 2> >; ReduceOp = cugraph::reduce_op::plus; T = int; VertexValueOutputIterator = int*]' at /home/seunghwak/RAPIDS/development/cugraph/cpp/src/prims/per_v_transform_reduce_incoming_outgoing_e.cuh:524:20: /home/seunghwak/RAPIDS/development/cugraph/cpp/include/cugraph/edge_src_dst_property.hpp:285:7: warning: '*(long unsigned int*)((char*)& + offsetof(cugraph::edge_dst_property_t, int>,cugraph::edge_dst_property_t, int>::property_.cugraph::detail::edge_minor_property_t::key_chunk_size_.std::optional::.std::_Optional_base::))' may be used uninitialized [-Wmaybe-uninitialized] 285 | class edge_minor_property_t { | ^~~~~~~~~~~~~~~~~~~~~ /home/seunghwak/RAPIDS/development/cugraph/cpp/src/prims/per_v_transform_reduce_incoming_outgoing_e.cuh: In function 'void cugraph::detail::per_v_transform_reduce_e(const raft::handle_t&, const GraphViewType&, EdgeSrcValueInputWrapper, EdgeDstValueInputWrapper, EdgeValueInputWrapper, EdgeOp, T, ReduceOp, VertexValueOutputIterator) [with bool incoming = true; GraphViewType = cugraph::graph_view_t; EdgeSrcValueInputWrapper = cugraph::detail::edge_endpoint_dummy_property_view_t; EdgeDstValueInputWrapper = cugraph::detail::edge_endpoint_dummy_property_view_t; EdgeValueInputWrapper = cugraph::edge_dummy_property_view_t; EdgeOp = __nv_dl_wrapper_t<__nv_dl_tag (*)(const raft::handle_t&, const cugraph::graph_view_t&), cugraph::_GLOBAL__N__fbd7e12b_16_graph_view_mg_cu_854a5764_83484::compute_minor_degrees, 2> >; ReduceOp = cugraph::reduce_op::plus; T = int; VertexValueOutputIterator = int*]': /home/seunghwak/RAPIDS/development/cugraph/cpp/src/prims/per_v_transform_reduce_incoming_outgoing_e.cuh:524:20: note: '' declared here 524 | minor_tmp_buffer = edge_dst_property_t(handle, graph_view); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ In member function 'cugraph::detail::edge_minor_property_t& cugraph::detail::edge_minor_property_t::operator=(cugraph::detail::edge_minor_property_t&&)', inlined from 'cugraph::edge_dst_property_t, int>& cugraph::edge_dst_property_t, int>::operator=(cugraph::edge_dst_property_t, int>&&)' at /home/seunghwak/RAPIDS/development/cugraph/cpp/include/cugraph/edge_src_dst_property.hpp:467:7, inlined from 'void cugraph::detail::per_v_transform_reduce_e(const raft::handle_t&, const GraphViewType&, EdgeSrcValueInputWrapper, EdgeDstValueInputWrapper, EdgeValueInputWrapper, EdgeOp, T, ReduceOp, VertexValueOutputIterator) [with bool incoming = true; GraphViewType = cugraph::graph_view_t; EdgeSrcValueInputWrapper = cugraph::detail::edge_endpoint_dummy_property_view_t; EdgeDstValueInputWrapper = cugraph::detail::edge_endpoint_dummy_property_view_t; EdgeValueInputWrapper = cugraph::edge_dummy_property_view_t; EdgeOp = __nv_dl_wrapper_t<__nv_dl_tag (*)(const raft::handle_t&, const cugraph::graph_view_t&), cugraph::_GLOBAL__N__fbd7e12b_16_graph_view_mg_cu_854a5764_83484::compute_minor_degrees, 2> >; ReduceOp = cugraph::reduce_op::plus; T = int; VertexValueOutputIterator = int*]' at /home/seunghwak/RAPIDS/development/cugraph/cpp/src/prims/per_v_transform_reduce_incoming_outgoing_e.cuh:524:18: /home/seunghwak/RAPIDS/development/cugraph/cpp/include/cugraph/edge_src_dst_property.hpp:285:7: warning: '*(const int**)((char*)& + offsetof(cugraph::edge_dst_property_t, int>,cugraph::edge_dst_property_t, int>::property_.cugraph::detail::edge_minor_property_t::keys_.std::optional >::.std::_Optional_base, true, true>::))' may be used uninitialized [-Wmaybe-uninitialized] 285 | class edge_minor_property_t { | ^~~~~~~~~~~~~~~~~~~~~ /home/seunghwak/RAPIDS/development/cugraph/cpp/src/prims/per_v_transform_reduce_incoming_outgoing_e.cuh: In function 'void cugraph::detail::per_v_transform_reduce_e(const raft::handle_t&, const GraphViewType&, EdgeSrcValueInputWrapper, EdgeDstValueInputWrapper, EdgeValueInputWrapper, EdgeOp, T, ReduceOp, VertexValueOutputIterator) [with bool incoming = true; GraphViewType = cugraph::graph_view_t; EdgeSrcValueInputWrapper = cugraph::detail::edge_endpoint_dummy_property_view_t; EdgeDstValueInputWrapper = cugraph::detail::edge_endpoint_dummy_property_view_t; EdgeValueInputWrapper = cugraph::edge_dummy_property_view_t; EdgeOp = __nv_dl_wrapper_t<__nv_dl_tag (*)(const raft::handle_t&, const cugraph::graph_view_t&), cugraph::_GLOBAL__N__fbd7e12b_16_graph_view_mg_cu_854a5764_83484::compute_minor_degrees, 2> >; ReduceOp = cugraph::reduce_op::plus; T = int; VertexValueOutputIterator = int*]': /home/seunghwak/RAPIDS/development/cugraph/cpp/src/prims/per_v_transform_reduce_incoming_outgoing_e.cuh:524:20: note: '' declared here 524 | minor_tmp_buffer = edge_dst_property_t(handle, graph_view); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ In member function 'cugraph::detail::edge_minor_property_t& cugraph::detail::edge_minor_property_t::operator=(cugraph::detail::edge_minor_property_t&&)', inlined from 'cugraph::edge_dst_property_t, int>& cugraph::edge_dst_property_t, int>::operator=(cugraph::edge_dst_property_t, int>&&)' at /home/seunghwak/RAPIDS/development/cugraph/cpp/include/cugraph/edge_src_dst_property.hpp:467:7, inlined from 'void cugraph::detail::per_v_transform_reduce_e(const raft::handle_t&, const GraphViewType&, EdgeSrcValueInputWrapper, EdgeDstValueInputWrapper, EdgeValueInputWrapper, EdgeOp, T, ReduceOp, VertexValueOutputIterator) [with bool incoming = true; GraphViewType = cugraph::graph_view_t; EdgeSrcValueInputWrapper = cugraph::detail::edge_endpoint_dummy_property_view_t; EdgeDstValueInputWrapper = cugraph::detail::edge_endpoint_dummy_property_view_t; EdgeValueInputWrapper = cugraph::edge_dummy_property_view_t; EdgeOp = __nv_dl_wrapper_t<__nv_dl_tag (*)(const raft::handle_t&, const cugraph::graph_view_t&), cugraph::_GLOBAL__N__fbd7e12b_16_graph_view_mg_cu_854a5764_83484::compute_minor_degrees, 2> >; ReduceOp = cugraph::reduce_op::plus; T = int; VertexValueOutputIterator = int*]' at /home/seunghwak/RAPIDS/development/cugraph/cpp/src/prims/per_v_transform_reduce_incoming_outgoing_e.cuh:524:18: /home/seunghwak/RAPIDS/development/cugraph/cpp/include/cugraph/edge_src_dst_property.hpp:285:7: warning: '*(const int**)((char*)& + offsetof(cugraph::edge_dst_property_t, int>,cugraph::edge_dst_property_t, int>::property_.cugraph::detail::edge_minor_property_t::key_chunk_start_offsets_.std::optional >::.std::_Optional_base, true, true>::))' may be used uninitialized [-Wmaybe-uninitialized] 285 | class edge_minor_property_t { | ^~~~~~~~~~~~~~~~~~~~~ /home/seunghwak/RAPIDS/development/cugraph/cpp/src/prims/per_v_transform_reduce_incoming_outgoing_e.cuh: In function 'void cugraph::detail::per_v_transform_reduce_e(const raft::handle_t&, const GraphViewType&, EdgeSrcValueInputWrapper, EdgeDstValueInputWrapper, EdgeValueInputWrapper, EdgeOp, T, ReduceOp, VertexValueOutputIterator) [with bool incoming = true; GraphViewType = cugraph::graph_view_t; EdgeSrcValueInputWrapper = cugraph::detail::edge_endpoint_dummy_property_view_t; EdgeDstValueInputWrapper = cugraph::detail::edge_endpoint_dummy_property_view_t; EdgeValueInputWrapper = cugraph::edge_dummy_property_view_t; EdgeOp = __nv_dl_wrapper_t<__nv_dl_tag (*)(const raft::handle_t&, const cugraph::graph_view_t&), cugraph::_GLOBAL__N__fbd7e12b_16_graph_view_mg_cu_854a5764_83484::compute_minor_degrees, 2> >; ReduceOp = cugraph::reduce_op::plus; T = int; VertexValueOutputIterator = int*]': /home/seunghwak/RAPIDS/development/cugraph/cpp/src/prims/per_v_transform_reduce_incoming_outgoing_e.cuh:524:20: note: '' declared here 524 | minor_tmp_buffer = edge_dst_property_t(handle, graph_view); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ In member function 'cugraph::detail::edge_minor_property_t& cugraph::detail::edge_minor_property_t::operator=(cugraph::detail::edge_minor_property_t&&)', inlined from 'cugraph::edge_dst_property_t, int>& cugraph::edge_dst_property_t, int>::operator=(cugraph::edge_dst_property_t, int>&&)' at /home/seunghwak/RAPIDS/development/cugraph/cpp/include/cugraph/edge_src_dst_property.hpp:467:7, inlined from 'void cugraph::detail::per_v_transform_reduce_e(const raft::handle_t&, const GraphViewType&, EdgeSrcValueInputWrapper, EdgeDstValueInputWrapper, EdgeValueInputWrapper, EdgeOp, T, ReduceOp, VertexValueOutputIterator) [with bool incoming = true; GraphViewType = cugraph::graph_view_t; EdgeSrcValueInputWrapper = cugraph::detail::edge_endpoint_dummy_property_view_t; EdgeDstValueInputWrapper = cugraph::detail::edge_endpoint_dummy_property_view_t; EdgeValueInputWrapper = cugraph::edge_dummy_property_view_t; EdgeOp = __nv_dl_wrapper_t<__nv_dl_tag (*)(const raft::handle_t&, const cugraph::graph_view_t&), cugraph::_GLOBAL__N__fbd7e12b_16_graph_view_mg_cu_854a5764_83484::compute_minor_degrees, 2> >; ReduceOp = cugraph::reduce_op::plus; T = int; VertexValueOutputIterator = int*]' at /home/seunghwak/RAPIDS/development/cugraph/cpp/src/prims/per_v_transform_reduce_incoming_outgoing_e.cuh:524:18: /home/seunghwak/RAPIDS/development/cugraph/cpp/include/cugraph/edge_src_dst_property.hpp:285:7: warning: '((std::size_t*)((char*)& + offsetof(cugraph::edge_dst_property_t, int>,cugraph::edge_dst_property_t, int>::property_.cugraph::detail::edge_minor_property_t::key_chunk_start_offsets_.std::optional >::.std::_Optional_base, true, true>::_M_payload.std::_Optional_payload, true, true, true>::.std::_Optional_payload_base >::_M_payload)))[1]' may be used uninitialized [-Wmaybe-uninitialized] 285 | class edge_minor_property_t { | ^~~~~~~~~~~~~~~~~~~~~ /home/seunghwak/RAPIDS/development/cugraph/cpp/src/prims/per_v_transform_reduce_incoming_outgoing_e.cuh: In function 'void cugraph::detail::per_v_transform_reduce_e(const raft::handle_t&, const GraphViewType&, EdgeSrcValueInputWrapper, EdgeDstValueInputWrapper, EdgeValueInputWrapper, EdgeOp, T, ReduceOp, VertexValueOutputIterator) [with bool incoming = true; GraphViewType = cugraph::graph_view_t; EdgeSrcValueInputWrapper = cugraph::detail::edge_endpoint_dummy_property_view_t; EdgeDstValueInputWrapper = cugraph::detail::edge_endpoint_dummy_property_view_t; EdgeValueInputWrapper = cugraph::edge_dummy_property_view_t; EdgeOp = __nv_dl_wrapper_t<__nv_dl_tag (*)(const raft::handle_t&, const cugraph::graph_view_t&), cugraph::_GLOBAL__N__fbd7e12b_16_graph_view_mg_cu_854a5764_83484::compute_minor_degrees, 2> >; ReduceOp = cugraph::reduce_op::plus; T = int; VertexValueOutputIterator = int*]': /home/seunghwak/RAPIDS/development/cugraph/cpp/src/prims/per_v_transform_reduce_incoming_outgoing_e.cuh:524:20: note: '' declared here 524 | minor_tmp_buffer = edge_dst_property_t(handle, graph_view); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ``` Authors: - Seunghwa Kang (https://github.com/seunghwak) - Chuck Hastings (https://github.com/ChuckHastings) Approvers: - Chuck Hastings (https://github.com/ChuckHastings) - Naim (https://github.com/naimnv) URL: https://github.com/rapidsai/cugraph/pull/3913 --- ...v_transform_reduce_incoming_outgoing_e.cuh | 28 ++++++++----------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/cpp/src/prims/per_v_transform_reduce_incoming_outgoing_e.cuh b/cpp/src/prims/per_v_transform_reduce_incoming_outgoing_e.cuh index 1349454f5b6..0b6c6a554bb 100644 --- a/cpp/src/prims/per_v_transform_reduce_incoming_outgoing_e.cuh +++ b/cpp/src/prims/per_v_transform_reduce_incoming_outgoing_e.cuh @@ -513,24 +513,20 @@ void per_v_transform_reduce_e(raft::handle_t const& handle, static_assert(is_arithmetic_or_thrust_tuple_of_arithmetic::value); - [[maybe_unused]] std::conditional_t, - edge_dst_property_t> - minor_tmp_buffer(handle); // relevant only when (GraphViewType::is_multi_gpu && !update_major + using minor_tmp_buffer_type = std::conditional_t, + edge_dst_property_t>; + [[maybe_unused]] std::unique_ptr minor_tmp_buffer{}; if constexpr (GraphViewType::is_multi_gpu && !update_major) { - if constexpr (GraphViewType::is_storage_transposed) { - minor_tmp_buffer = edge_src_property_t(handle, graph_view); - } else { - minor_tmp_buffer = edge_dst_property_t(handle, graph_view); - } + minor_tmp_buffer = std::make_unique(handle, graph_view); } using edge_partition_minor_output_device_view_t = - std::conditional_t>; + decltype(minor_tmp_buffer->mutable_view().value_first())>, + void /* dummy */>; if constexpr (update_major) { size_t partition_idx = 0; @@ -549,7 +545,7 @@ void per_v_transform_reduce_e(raft::handle_t const& handle, } else { if constexpr (GraphViewType::is_multi_gpu) { auto minor_init = init; - auto view = minor_tmp_buffer.view(); + auto view = minor_tmp_buffer->view(); if (view.keys()) { // defer applying the initial value to the end as minor_tmp_buffer may not // store values for the entire minor range minor_init = ReduceOp::identity_element; @@ -558,7 +554,7 @@ void per_v_transform_reduce_e(raft::handle_t const& handle, auto const major_comm_rank = major_comm.get_rank(); minor_init = (major_comm_rank == 0) ? init : ReduceOp::identity_element; } - fill_edge_minor_property(handle, graph_view, minor_init, minor_tmp_buffer.mutable_view()); + fill_edge_minor_property(handle, graph_view, minor_init, minor_tmp_buffer->mutable_view()); } else { thrust::fill(handle.get_thrust_policy(), vertex_value_output_first, @@ -699,7 +695,7 @@ void per_v_transform_reduce_e(raft::handle_t const& handle, if constexpr (update_major) { output_buffer = major_buffer_first; } else { - output_buffer = edge_partition_minor_output_device_view_t(minor_tmp_buffer.mutable_view()); + output_buffer = edge_partition_minor_output_device_view_t(minor_tmp_buffer->mutable_view()); } } else { output_buffer = vertex_value_output_first; @@ -913,7 +909,7 @@ void per_v_transform_reduce_e(raft::handle_t const& handle, auto const minor_comm_rank = minor_comm.get_rank(); auto const minor_comm_size = minor_comm.get_size(); - auto view = minor_tmp_buffer.view(); + auto view = minor_tmp_buffer->view(); if (view.keys()) { // applying the initial value is deferred to here vertex_t max_vertex_partition_size{0}; for (int i = 0; i < major_comm_size; ++i) { From 11279aa46663eaa3190ddee06c247e8f7a28d764 Mon Sep 17 00:00:00 2001 From: Brad Rees <34135411+BradReesWork@users.noreply.github.com> Date: Mon, 16 Oct 2023 09:44:21 -0400 Subject: [PATCH 049/111] Updating Docs (#3923) starting with all the packages in a table Authors: - Brad Rees (https://github.com/BradReesWork) Approvers: - Don Acosta (https://github.com/acostadon) - Rick Ratzel (https://github.com/rlratzel) URL: https://github.com/rapidsai/cugraph/pull/3923 --- docs/cugraph/source/images/cugraph_logo_2.png | Bin 0 -> 4952011 bytes docs/cugraph/source/index.rst | 49 +++++++++++++----- 2 files changed, 37 insertions(+), 12 deletions(-) create mode 100644 docs/cugraph/source/images/cugraph_logo_2.png diff --git a/docs/cugraph/source/images/cugraph_logo_2.png b/docs/cugraph/source/images/cugraph_logo_2.png new file mode 100644 index 0000000000000000000000000000000000000000..62dd79c4b988ad58771e893c117b6e0a3b2c7074 GIT binary patch literal 4952011 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

rp2Dp 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 z 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𝔍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 z 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

!vo>l|fD5q&$q<55duT4JbV;+7#!Gx~KVB(b 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%0AmZ_rb~N9K(;X14mRbDvCB}2n#PMmI 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

k{X)4vDN<9Np*6WL*L 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

_BCZ{TpC5+Q~Hi%ex^HLtoo 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 Q}QmWiTF2df0Oq(^wbWRE!~vc{PW%A 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�wy>x*gU#-!W)%xi4wLt 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

g##_C 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

_zSQIyTZjXnvQ`DqY}WVzhZ z2g_OiI0gFGg{OEEFZDWK4Y^r)kf9R%xs;(Kn}XR*bjO_4pHux%k*Vfk=hXv&c-uTj^K6}B2#5d$-UN@{D?+w17PWEYjxW2Zv zSY`1B|IWG^*VyOJIXOZDZx8^ZytA8tAGu0CUvP6;=;tdm=ROV@^p!wj4xcBeKX z&^f{DIYyruP=!+bj$i1IlXae0Apd-cafF>-_bxhw ze_|{;VlebjrmJZ4K3{Wzd@EUqozx^9Q;uQR;@+OpjNbXwB0DQTh-c z5)uUB3H=)cf7tkn7n_MhvCCfpeNy#w+;+-$V0wfj4VJki$%z&A`*taDX(pI);5gV6 zugG@fL?K=gDbwotntBA|$P%fd@n>f-KE2E1H;4gxbcz+r1+axu1 z9?Iz=M8g>ShG^C$2Lr#Tepir!GrO)%1{Y`R5fdAw$~v1 zgt~?kSDQ;ov`1QWv97hpGCZ)00lT=uvIC}PCL$_KW5&j+RTua4(-kFkri>qA(7b18 zYkRN$4^gKZcIkyl4$#p=M2xab18X~cf=qaXIN($(J9aBdOW~h$YHr%EVOZGAtl+cI z8Q`&$Ek~r3o`3xRW9lrp;)uGejT1aTf;#~M!5Vi79-s*pTpM?HcS7Ut?j8sh+}#}- zcbCR(cxS$E&HRL_Rjcl~XYc*&T9+Vgt-hJ!omlcfk^U0%+rH+jA(W+e;d`aQ5*jM{ z(A6ce=k$U8aZ#Y#*}uNOBn{)E*LrE~E)7zQ+Q1}C)i)>vRx=3`2AiVspuob1{T)#EJWGWbe^g7tH^^tpCW37Cx36l+ zy!MbA>Z@YPvTd#94SAw#VRh``%&;=yhD5D(Xf~Lc;l;tlGA6O0a-Q4_So1Qy5iq)S8Zw* zm#kA4!z3#Qh3OBX;y$0NPJ+s9hL&9%J5Mv@t?-ME*JReiHQSAgyuSFVjny194@9z3qJ6euhFni9u&F!p`cF?%DN>GM&eG1lFIPA0pZ& zgnh^Fy~Y;>KqQ$n5>S>Jo2w1J34jA@)?Ac&%HsJiAGaO8I&kOd`H^GYXrrjdZlHO@ z%McxZyY&dUH4ek>nDSc~Iq0r7wtr|*MNG_Tp?p}|39hfqnv;%?PCh-ZB)(~M;#$Ye zTxa(S;JL7k5yaj|T7wyxOY$h$2GZK;c#h2Sqpkl0o%4`_e<3&|pOYlUA2n91U}?@T zUd@qTt;ZqK?jd6nF4>;+`-SPV+{N$m+YXhFjiPcoQQ!|dHwAAMZ)r<^ypS4!PEL|K zH{XN9v^NjSC-4*MBPbVQ5T?P@)5E|Hpmq1tLrNt^aVp@^QEm9mrr+0Q1Y zq9$1WqYIu5iAkR#*XC&&pX;{o(lbVpycv~Ye8QxXPWO-F3&hml#2O5^M#t8>ooWNH zi7*bywX54U`q{nqzqDjcWcN+h;NY(_ugf**j~@+k_|;y=XcZ)1N=EI@^h!tWdSu%%=Z>0d41#OI)^E--MN9dx13dKBvi&PkINc z`0T0*54yQI%~ilhV9v}Gv$CAKcyEfSZyvOM_-$Wkn8b`^;1qAWq#=-czJ3bu32Qad z-Va}@c7mLXaU8+=wtk?PAZHjI<{utZ92xk^6JPfnle#0>pzL@h*_>JNlqXx9gLWJq z7v05_8Srky;{0xV+rVN6g%yItDNmlxj4w>|t#+a9@gaKeDSu`ANMQ4rsw|mOV&3kj zfXIYJ-G)k_0omU3yV`n0kWrM(qxxa;$Ar*Pxh>xa1NY-KeUd|`bfFs}a%0zv1WoZ52I~rEEi+;nf$oAe##RdFZ#;u|dizA)3 z@&+cx{4=f1(EY?!m7?&MyRBUN*_GSpVZN4cdjMSr>K~|@ z>Ca7=lTYX6nqnZw!`B<;A+D#B7i~D0#`)Nbka2xHNq;^TTsYOizkxHq3aDTyrc*LS@ChsPRx*-@!AiK~&kk*|heUpejZ<*{1=DFh=DP|_N^ znk=bc?|y)R;87;4@=HkihyU;YtnbdLkc(V3ZKbu>wtw!mUuC|1tp8UNS@!~_>DOFq z^Z(hw5?Z@04B|~p-O%sDcG6M&7KTcf>h*>SBp3PE_YotILLvDd&yLY(k0Y5X2lF@h zq6oSSzu&P6o0E3Z)NmN#T>auct$HnfM9*Q^E&s_heDVS7v;Lm^SOr|RpE4vw$E{-Z zf=llA#3#@qH6&<`ri5)MV>G=NJ2y)x!~P{yLT24W1DUAOAt-FSVo;f{Y%<{IuT`R8 zMWOzcI&D~DFvdahxuByNiq*i_*yH2D9XJ<-)?11nBPfRZZ+; zSZf*!VexP*zAwN6h&_PwXZTqYX@#KEOg##$XRw-woz2nvbC8H;na7vRM6I~pu(x+; zME}DXSCBoUuHW$nIyDrm1#CAdX)5j64VRQ5+B0|_Ao#b{0z2>TA+03MQ)+l5P@FD94x}i_?5H*Vt3mT|_s3aW;DU`==-rHV0KgsBw z#7ymE2|!}uJrYD}WR-@8ilILP{^mU?Y%odgDu_hZE->})T;a(FZ|=XZT@EPGWy~RK zu^~A`Iau!q1*;SL&hUdxru%B!Q{)lla`>q-8lFIhNi$T@fjy>+28~oIaSy$#Pbn9w zLZ3T0#G_YBY&jhwY{_Se2<*sCGD!`be%rrDAQYkZ8aeMQ01I{BtS0V@*NPN9MuMGe zCyf9G+c<;tV5ko{Fa-V9`80vcLb_Osa_F*cb&MWAA)r2>4a)R~yg5O;LugbROz8L#8 z_p&8FcWWWjc-ywvE5W7&o@AQ@+fFoBXqKpJZo|Cfr6=YFjTj-#ndA`#A zPrIY%k)*<)rgWIzBEh;)mOV&W-~o6qp!zCyNvR~0rN zQrUn)Gsm7lNC()wiCnkEe-L9Ex`zkpN*nqc4O(mNxg7N$J|MgK9Fly*G|-Hi?D93j zhG&ERNX_cVQ3xpF`#KZRwl6&QA#)}v+w+Jm{j1&biz+%h{SR`>i!>h-kpFhPz2KCo z82py=BY<_>Ad37xSB`Qxw$_px-%!xioeO=Eka{=?L?2QD5Gl;u)Jv?E7oWuV+R%d9 z;mjj78aahQZkI~djQM8CxJY*xvQbr4mQ^K0G<5j=-)s}4v~Hjb(9fsZ(NpBB$X(rh zDUrtY0T%m5`v>f7k$aBcpYW(QJuf?e38okr=MHfKjtR%775l_dR3&~K3M$w)rF-2( zYx3qe`n_70%wK|1fD6@JL9V%ci}5yV^PJ}?-+r z5d>Xsp&V49O{9XxQ$Rv*hx^(Sce-BV^auN+ygSWZ3!HShp@Qz?-){-e$1msB4-gj8 z_99=IeJ52CWWE0K_u^PnO6Mc~x#aU^*)KfugtH%)7g}&XYND%4#srOtd6GsCrxmQ~e5C6)>j;%u42)OIJ-Gfx${R6XFZf{r~G4dud|#oA!`U08hhxEe53>S@nEv2<*V3) zGh6q8#J_|_5;j&aL-u>FZeJ?bxF`H0_X?@?Hc#4*I@NmG;QCxH{b^eaKOHe_rRya| zd7zb8CZ7@Y(Wnd)Pm`xQ1-7rWicqq!3EoK=i!c;w0h^;@_-ee8VD`m(LcDVKb_lCi zZ-?ZmxQTg!5!uHOs!H3jZPUPK(_`d{>*Pm+5gpFX1wuFVjUrr&KEiAr% zuLgvo*3N9PIv|AW6}Kj`u7;;U`qO!?AdR^2=wsOv`nqeQGq$uZx-hFg>)3-t_O}{@ zZ&fB~JzAluyJrVc8oE6duhbiUFG47VR{^URN^HWdsX{wb6AdCcUf769VY(0nyzQXAvu?NRn#;MiSiw{sP`b6D(Iy#CsiNtB=!IjPp@u%WZl$?|-qw@N5hEVVxJ zagaUxnlxTq2y>%3%hrE*F@pT`Szq!pI=H2H5sYMV|2~VQV5<+{@q8%Gv25AQa4Z*a z-QO6M4>ASS8gwwf0c^zYoG)D}_g{AW&Y555Y@f|+??`j`wxI|30P#1=kfz7(+VV5I z)_>zxBw7*!;0ACXs| zO~@#a@;+_BH_`^S$Es zgG4x{UBg1SO@%IOaDy}n2B4Tf@}t9W2YQR$3_b+)T5paW9I3TSuJE$U&YxQ2-)`xCCDMSv)C45C33r366O#IwbNe2 zH`+!&RK@yCZB-mQbyYvtT2UrhCh)PFDpU9JaLtRPX_z?JWVa~oJIuY3P2D)#x{|m2 zePGOTU``Df3Bfa%A2Je(w; zA@S>rww@_KzdeEN0(~Qqtf2^jj;J8fAfHLD%1eUG|WE5{_@q;Yl;bgpx0WiR< z<>niX$*-t3CAqeHKQ4u6%w0x@#X3Fv0diXyNg%OgLL?(d5UWTI=_?fYqTvZ`s@GEL zLBRNdT7BvapZhFob@-kb3^!i2Eob^bsar4q9{L1I2~K~AeLOaDHM>ht3X>OJ*FAyg z>iA(jTYb_G&d0k5!vZDU2hm6_v@c;fV_5p0KY#9)ME$PMZlct$rL3~~AxTG8Z|)!K z3Rujf4|4ypr%6948@C-pPSkDvr)n#U+^}SSXA1em#A43I1><#ge@8bTzJ*_vLFM*} z2gFJwHDRBWOJ`~B!X_KwR;tJv60El1Om*DP%J)Ge?SREx|n+-wyR~S*V>JMd6*G(_ODqYZ#Kr!$HIX z;&+SWe0GDjk)m_frUbbi_s?<1UgwCC2al#1K|VeqaxD)qFVtubahEwu%25hgWO+{} z_>$Lj1Q}$JGlQTL3(*1e$I+Q!PLGbAPfqXnvro`=2R^M*=ZM#iz$zsuk=(~f;F2QZbawkAtIM%UCeMr7j=eIT`KG2S@fh{o>PMw@X{6DSe zle8ZklR3&;Qj>;;K#rlghKmL^SGT#Q#>|nEdUiyQBgri8LM`I-%E%Wq`GZ0Zde<$-Uk4(IG3TCV zu1$OW)1g6yRqh3x94ke60jy{q_FcS`o#`_~gxthWd)Ih;}5rb%Qj0)jP0x{t-?CsF=g0`I#* z2nQBw#%@T=Cv~o=nc$L-4=iH}$=tTI%^;j~I0|iZ1A=d3PsAT`2PBp*l1qRE!&B0- z9T=}PzHMzF5;Jql!<88B^LhEB@6w-??cYTkSeGxH;xYyUql_AI9(Hk|+541N&&s!K zd&NHx5901a5XCTW%1No^Qm0MlgK?R;(I{t*5`KJXTgG9NmTm5IC46x~T&eYU6KDG3 zQ}vO3OIdJFl_~v7&($pXbz$zU=%Tu(aj%-hjTG6%Xx!kFX-l#VGVONM&R4|i;_s3n z$A6N}E-v8D?km1og8lOif-4G<=*8vMpPfnH4K@FdgZH1_8Lt@J=% zeSJdektldee`wpqNG8p;p!wM+ZC>YJ?Z<#L&8j(**!;n!+!HKffWbLY1 zclSM6dL8`_>1W;TUfWfV@dQmfpYwWgM*~GAI>MiZTz#*Bf64Q(XKkqUtOcHb1218L z__j!Dc|YBuo0t{B)=KQCeg$0#PIfwCZq#=5 z3A0|e-p3DT$qYCvlBr(3Fwvcgx=(uMzk``|zvonltnHRE^^r<;N2$^5IXm~>@3^(w|J&_rLiY?7mYIK!UaAAqgSX$s>E^%_=!IuG zM~GF(+gXkC%ZA;2?7~!M-UqldKCzT`mj%j5iXSBx)<7|=Jqc%fQzkYMwfCLszh3{= zd=K~lHmP>l=VKAiHS3PeO4F={Tt{#h`c?p*OH04knP&Ip+U}9y3M;MA)m_g6+pmoW z0ni%OGbi50whwwg<9@hf*3sh?B3(yAY;1o71K6LR{0Q{(BRuNxae+k75{$q0du_>t z;N^rYd<8+FWj#_>Ix+erKz*9x9EMs|f#9}zAbvvd8sG8fGMvryEsLz4hU#*Hq{*&P zuzgOiOb+EWL!Vn^nRL-_U^E${wON;gOhJZ3plOnok_0k_B$8FI(rBKpJ>CaqkYrPy z^3B*cy~iT9fUQv*wPGld<;i`1rfSU(Eckv)U!Ku9ix%nK4 zhvw_}>Ry{8&Ad9hz>i)_lLT_~@njL2(Xq#o<`6D%0rE3q@hlne6pvk!Q>)p=eh7vd zpy(UPaNe$M>Rvw;sqKJW=GW6`acPYxwU14dQW?Z02-hKvzq#~a-3q+(bUpdRH} zGFdMAYHejdaMYleERJq$F*4DjXk0Mhu3sooH-g+MYLBoVwRChP4~*+w0yiIMtOa|2 za(I8t8o@oTncgaP#>bfUk1sVko*NShBe}#xj^BYZfjzsG*AE6*N9Awy%@Y=VaUK+V zoOmGQsPi(Pd(Kz(*jPziY!Sd;CK&5wxiJ(vIP1mC<9?6AHReVws=7|)2g zT~sIDY>I^Pq{UDkFG#oU=?}C3x9Q?8S9+-S?Gv_ z{0-mm+5Sdv*0UuxU~V5?ppj&R1AWPrbhkyprG&~0R;>BTri1bJ>^}|C6t7}KP=42W z+iv_I_{uswCfIZJi(8CXQw~kXwzNZipyO z{*2JL0x-&c_$c<5UhKy5R;SBI_2TJx4`+2AW=~%nziT=7ZLM_^o_0IKdtma2$uzU`pKm zWY(rhjjopT&d(&*P!?<;X{4RnHMbuPDXet(m$dH0dTg!*0R@~rmSd7_RNw)( zT!a2%E&OcmzSoh*Y0%|$(526BSdVPKD%h45^=J3nt%3`#_ca}FadwAW*WO2M zf?U0f?7(c^wA#?uu(SDgE;}yD7Ni_Gfk4Tv#%o2&^?4_SdFwo{q^TQ9;9( zHcyjOy3ZQvu$J+SRP6I+S{*FKEYFZE^ zm14J@*jj==-70oR{w-`JJBHzQ;xo(#gJK4ikMA^;zg%tJOH(_U<*{%IbvU<+ar2U& z)>X*~nrmG53@5@eTrzO=e)Aa&?~1=DbYmZaEo3w_-=*IK<14pdva8*l+2=gbwI%c3 z7;xY{e=~3uX(?jHm4TI1q@Sdp>_#a0!~Gg_H!W5`TR|Q}q<|Fen)WA!lX~%b-!Izd zn@DXO>&|=&ik7;t?;bHj_TXr0!%0yFZ7%(Ft!zyk&m}$lCix>gQ(Sg0<`ZDbbC&hW;au*n_{R{XT&u zuTk*t2RYguf|Rz;Xnxm|kD9d$jF@@g76!wks()qto2fPb@ja|cTFj*UEk`569KN0L5$XC zWr=a7>EKDSdv%5V-GnCMq9vvuodjv<(pA2Kc14C{Wj=2H z-g0g6+MF|NS7QiYTlMbZ-Cp@}@neHAKk(G{b1L0$zk305B&|cV)W9%)VSPCkRb+#I z62I>Q9JK*baArw!BxrMl9!u9Ts=J`o9HZQjwFFl1jQrSiJlV|3?*>=gt z-*&oMQ$wsrs^ew*F@;in=*R|mA~xNrjCjt1t_eT92|uyC!INuxo5I+(p1<9`J(|sb zNdML0Uh;I+x4*ue9v#62qot|f!eocRV zEyn-1wsyZ^0ycWRY+f&Gg=DYq)4p3q#=D z(4}R(u@JO+BUw#7M|Xq=iL)`+F>D8kc;{jAz8vWN(xFl&J0RnWj+$yEY>d*=E?m%G zgOJdHNFZA{ZMw{`p;?1O{hShZ=<9aVs6Nwl1q3x#8#NSHVZTpA1Rk#Z{ea$t=?V9aoN;si_&%@YI=0Y-r~^cbe+XN(_KTp| zrBV1Ag#^`ObqZ7xC_N89A)EhsNtolttM2YE*O>By#gI#}F*`&3qs&LUC&5jFI~bwl zO)*2B^wqCLL6JS=+o+BL&c9Q0VS>1&kEh%wvVPtHsTO6>!!?1AuMk&3;8~%Q#W{LF zefTML^Nt^s=3YawnH@QtWN4m&Kw9!k#-4)x&O}8591xWL4PjBav6xDQOstj~h>9Fy z(664;K`MJMMF1A!6$Z^>G%&`2?XLOa0c%?fiRLNDUgBz-S&(i@Q2roXN;-2`M7}t?o@7O47_D^ z&X8hFg4oLMlLQBQM$x#Ck#M@Eij|tv&sZUKRV+Kv>(XvlE%g>nzSi1-I`^h@Wx_|q zs0&Qfup$gW*EfOUYLX6z!&d0}i#~8t#A=1WY0lAh!=8w1b|_A3>+XkJ*-zU`Bk67U z3e3Eh@iTte5W_I{k;Bfc`cmDVi=&uGu1QOx5YiuYNIP799<$X%SC%{On@HpbYSm)X zhU$pEKbXD(>Vw0K%ilK8`iq1{gV(S6f5GNovKEyVSbmXvlPXpwgt@Klw?VP=3nrjHr{#pO?#LP689=g`2_I_4iU-S1kNV>px_v=eH=w(-jxp$5>zjpN|SVo zs;h$s`)xi=71D_;@L-PutjXV9D-d08EE&XYnYoMDXW9OPo4;%l1fX-aVdGKV05y_Z z>U*lO(dbwMLPQV=agoC5;TWTJ{3s#1AxB~RGK?hAasAOCJ>C58O&It~q5&JQABj1| zxbsl>+;9M&C_cOw#x2Dn?r2?+K8b~s8yNQLGBTT$;cb}c#xAXqPDSf1jt`UP(mPw! zv$C}g&=33kzECl<8$!0YvMEEr*qTgi0WHhL~=~P z2Ww*z-rYD}3!v{$1iE&YSc6(cE>NQOu4)O&_=1!%^Q(l8wg! z99q$#E3+;2)vtq}>_$T@Qx?bY2S~xrcQ3+T)78SwH2}tmkXKSQMKywWOxq z1?Vb9eN@xI-1b+5I((AipX7 zf=3gxQHUK-I-{4y zej1g%aUZ0`Jn94WN2l`$xU+#LIYlPi>~%KM{4V`%W-p!}6td4>W^pG0B6kST>pJ#+ zl=Mx%$w;HT84XZVsWkdjUB4gmpiDMTj!m))>5H`W)O@ zYwxY6ORyg2OoecCo2wE!;pb(h?Yv;dx9b-ij`DE#_#SA^sOLvQZ4kunTW)Y3YtWx^ z!}+d?$=O7X48=@(F@y!`PeF2huFULf_||a1Nk8OG5!`lXx0`iA%*95&ogBXEJgjCd z6+r6oFr~1N7I>6NPDiU>DY8XFsM+z7n$co}=k-T_$mp)O^oVw3{QPA+Mbs_23^Llp z=U;wazwr1d{PTV*0;Z;oQuLd)9Us5_RSsf3S)6lj5lX)4j4#;3%Yl_zf`I9T%n%(c z)txWr^muC=U@)N{5(zmwiwx#L+S%p#gh}Y``_k2;)jD4JZb&U-M??Mx8K=wZcc{SY zzbHw;66|K?a7xw^amIE)n;(^piT{UqwqfnCq4-`(F?BT1@hZ%C<^10h9R80JtUg_# zbuU(0su^b=l3%Y1SN2~&ZU+B<7JzyvzhLU|X(Gu5HK6*Hr`o^cC3Ahr^Oc0!34gJD zqsNClv~0xB`+eHXaWSM0+3^5i6TeE8S*e1F?! z+?#LPH2XEZt;6>+8^+b|smiv9KSPi7yp{N5jPaTFD>+hZl7V1IM_-$!sk-k~XsyQ@ z{Ue!3)3B&on4YO<4zb$&D1GK?^>#ynaFW}t^W^(XwkdL)*zq!=*LLmW3ICVGLdVeN zIDRdsdV%mLW}a?DeO_h3@XB&rfxDVV@Y%`pWt??oEoc2mp>_#+je~6TMEK--iyn1Ctw$*s!}^FxZ7~y zF2#<_i1v01N=Iyp`44gkm)X&8nuq@i=ecDjE(z5LiJ6@Y{RkMr+J68ydu6MFxAiVB zeEbS4nmA!14a)jUwTZ;tQn}~8l9&_em62nz=3LL}VJJF=r`Jpf93j3L*7Qc_1D2^Z z^2@{+(V|(Kpg|MI#9-6|^5ZG>jo&StExip?93D6hZ%KTF1%aUJpQ^v*#IO{Aw*~K* zVoHhC<{i^ol_*8MyxdrXXJb=hr>d1B%(zW1V=nV9K01n;>t|-`WhAlUBef{^OubEv zB=<*1T;lZPqqj4!8(WXiz(rL^VlJz|`)Y~p$L3JUN5)X_Wq0C!Bp;}jFAETnmf{kaf`GmCa0Rc--8XN38b8w=j9@uKJi+CCW{j8QXoL<7S3imcFG0X`1Y z{`nEq=zXZsCw~A_v5FScJL`5=rMrl$=5>!)E!BQ%wktLmf2`6a_0<##+khBr%*nA32H~B;K;a;$P@W|0Aj(-u zel6_!Bu#z#*Ftfp?hxPLLmtz(=d9@IwgdW{_Gy!O{(Dl7iagf$sHI(~pFnqMXV?VQ zMj-zgj@skM&dUAATZLoqET1yQQP zK{w&BK7f&|fqoXE0{o@!qk&%=2_z`)w#A^ywsW|^#kf~E!FP*na8iaD5a$w}^wZ8) zf*#G)WBWCAN&IsNUl5YzFCBcOjvr)l3`|cngYgN$ZYj?4B=X4g^YJ&} z(t(@MXIkGadJcarlI)h`y^dRTr<(a|%a=R_JUVe-tw-iZbMcl9*3DdR9+^wVOSzEZ z$v`MIW%&6X-iZ$|%!lz#{Q(e3++CgxwU4rQ;X3)r%{Si+#S%o!?9hUiIo3ZyGvqsT z+XX<%VaWsq$*>+kEV}I6!7bTmaO8pW4S$P}gJ@w)?s(>%r{K90_3-&Cb8fB9E-Kb$ z>=(6+a80h|&gI`FMMX5-<}5A3=`oX4#R`I#FXnqV*RvYa(5rhIF9Y3Nt3ytx2v=?n zrYoKy6K-#ypN-n!W|G6S*siow>;#b&IqCNG_7u%TNQHOZ8N#^G#g5}X$j2Q$dJPX6yAyyyfiGzkoY~{zdP)98|dr_^hEJZQ%tL zywLo2rJE|mPQX>2MNU-o<8xm+FD$Eea@C?==`e%&H-We<4JGd3p!OdR4tM=eMd}S& zSC7-SsBa$NZ@t6*YL5C+dt|I8ML0+#mfO4~$3tl$EA_fYI!9^UH>K{J-Dt~sdEuHr zFacCI=mC2a85RiKtohX(V}qYBF-h?>Apg5yd&gb4@Lqh4oXmb1CTDa|1^vC*?w6Qv z<*F4Xn5mhHp6@t8K7Pqn_x;jvJK}(UAG=un{1|=f107gl^LDz=6vUYFy_$HVLvzPl z)vyZe>1&R#$vUrNKF43%d4kWWai`Q5IELzWMdX_ejzo;%yp2=&rcO7;KN1L2=&+I9 z)^8h|7LPr}xW!42z-L{at;CwPi^!bU-%2t0V_PMh-Vge$x6<%FPIC9CPfuw%)Xx@Y za)l?nL|3t9ev=$TxQ^2=kXV})Zqd+${){8m@Rz&WiF8ojDu<_Ae;MP4i8em7!_4w! zTm?5xEGkqDa7LWgk0NJIKFoDM22y1`fVFyvcgT7sQ1 zci7vaoygKMj*4-3P*}_#$<5_cl2f~tb0Vys)r_qD)&%P7H$&Bh>fM#w9vSu@pK3<|)muGE zy6;Kny_fC2>=RlDakixT$fZ}7(r2E#1A^+*e4MC&lG;4XjmI5R-}j~dxbmc$8p*-y zqwniUBi;gzZM;I2&hpPu4=>~kw2tR}_jd2}n|6h!xk@7Gvnoi3^Od&{*`xj5cS+G# zs%K2H5UK3%M^37A#A@YrNcjEhPpLD{{>+Y=0<65Yd?k+%zg<$(%K`25=D8nb2U|pO z6^*+|TpzD@R_59SG3&GLsgUyYg39JGD#=OUk6WKufo1F&+b-OJyn@fVAs+pezy0ln z4l(=Pj=`1kj9&fl%wk#5=YP;?&552u5T~NYMd%6CI(7oTTua+oXAUj1x;NQwwQao{ zzwkoWU!K-q=BiI0eCh@sR&v~`WVQF3T-1`i%qE!hKA-Q5oOu?SO0BPOeQ#tN@cQ&U z@&RnQEjT`A!|LrU2Ms`UZd)r+Pb`OS%v1FLZDoLORV_QZ;_av{2(nD5Oz?NSc}Jaz zk=QXitvyklIUDnLye!O3Ieq9jmP;=>ozw<3Zx-R?GeHXKM~Pv&jpS%6ME$guS?yk8 zvh0qK8^D3AemHA?Ly&-8t-fw5YT&V#l69w-NhGC6g03&)mI~LsVeBC%?oIZ9yfqm5 z7BTNl8UIFPf=NRs=brG=`2)T_r^xI-WZ2f=TvqMw4H0U%lY2wP!YMtyt?T7h`0Dwx zVh*{mDT<_;8L6dN+E;>?$UA)VQ0-hnBDX=iR~Z!Q)^y@^E%Xrn_4^|K5R({5IrD{0 zosMg6=PweZYruf5iWIa@U2M}B_>qXo@mW&gRA#eF(E6NDN&TxdYsBk9$CuPo=H9iY z|HxY;;Lv|LDS5=y8ya8Q#&g@9>C0xv|I!gP*q1ma^VecBkzS$iQg^C9{TpQVKph`1 zuSjeIV6(Gbk&nPf9;7>e$bh=^H?JEh>m0e3n_yaS1-f6)pC}!SHISbSv2_ok^B|25 zxali#`PXu^$hE>mexC1tS(QssSE2+!h;SMHh-3m+`)Lh5ie|0i>-s-~0hM%5x*!gk z`H~N%NxVc#=HiFsVN2A0EtR2~bMf{27h$sb9AGEMJi(A+qC|^(jZ;6VX_$6`MCCQ* zMS8Mjt7r!sz1a9Y7&4F-ebm|(^spzN4a(S>OW~nOWK_l16)9A(FP%2oh;P5*#BOq2tCi5fEAa0l4x)SXf>bx9PfzNj~N#v0u1Av$c zB;=M|!8wm3ZhT}^!?pQags8mv?e~Ex+#EMjf%P83j~@DPmMEwolI;iPjd8;C5g(VH z&tE@Oe5ZURBA+`a!epp@XwsuLT#?IO2w}(q3vr8(do_>^l}Q!?+$VsL>4FHlk4|Ub z=o-QM83AWQ$3$NI?GP(CVYQoD$A&^I&O9lyV}=J6$vq^5+(e%tN!KuTpklVgxql#WAx*K&!$X% zSn@cK>*tuv7FbN-R<)>hO{z-qo5uM&sLkI280DSHiQ}EDJj`jOid6?x5=O<{1`U^U zPfWZEW+lgcb8AbE%rE#E9&d4I@K9Ok@@Hb}gI)ttB0G&p0UzH4!E-&L+w3fAu&ui>f8H z*t)-pNCu^C(r8+J%?i%mEYu(Vq}z|lCeR<s{ic=oT9MJ&SGg7NN{&L0Xtv1Ek$aqQ$cz z*+fDP4TqVX3-5v@%ZR_s4uPK7iA7KpCF5%Vfnv1_g4dE`wM>$)9Om%@#IP&IRN zd`CgH8eX%!E})#Qf#!7m7%VbPB&Cb@3B6PApXa*-d`3huc+bT#2A}f6b&Tifz{{+C z^N#{iOcNFSx^P@P3WK5X(KibdIYS?HqGHXj1G$lg*FnJhz-FkaYS*mxG`(u=Y=01r zCLuH_LWbKZ?T;a1;On;*5}7;U)!q7U1z+JbDrH={!BF4NH+>S zog1XQ;k$w-d-#!q9o6s*qR?JnLcmxWTleY%xpC1l-sBtiC;~osj&978=uf)(JZhd^ znI~5@S8>cfvG(%Ug)-mK?r)k0TKC=_o3uF-{ST_!RH>&z>xWz^Y+9Sz@x4AY-d_3G z;a`Q}M*7|kb*w!NTnig)#PK^xMd}aKZ2xQJlIHet#YMxxl3?{FQn{bRh!;Zf;0W{> z;MEkEy_4@y^J27AUwj+qTNg8TYOnw0wIY6Zj(+slIkI;2wD6{X>UDwdYuxC46}ka<<%Y{4I~~ zoVCxGa_6<}FrqKxU(dTtcKO@v8-?MQP+cB$n);PHIraGWmT$sjR{<6jLb)*Ge(iB=*0K`>!O@DeZi1HQ{X}KFBf$P*GhaB>mbRK6sYirXn}dGx2}-o|~@ZwqY1_ z)6?>rp)GG4;{7tk#N67({G8sT&1VT$@{J4ss2_VE!!&|}4fYy(JM#|hB%MuvGu?@> zdSeu*l`t+DqwMn}n~3Ind9ZqlK;W5ZfLRNbSGeNX!zM4%GOF-)7+?=&2SA3HJ?%F3 z6q;_}r;9WEUjXAk9KYnw0|6r#qxii&h9t&37${@-c*KSiZMbU1!ux@;r%lE{nz0k( zBj~%q05b3Ws9B?)Yfq)rP_^r4tc9O=*NiuaB0#&KCw-=memRH{wl9CLM?R|lBj}>HpijPm45tBg48Ju*$58j=($nPCK}BiqbhevqL~ltq zy32jXq&#hxT!gMR4yd~959w0&JbgG9eHfhx4%wodT7UV5m8H5o#o9~RD)|CV^rH#P9rHp3JFOoVgqAd4f9Y6i9_z;rvRdbZ7FA2^QBEgHwmdf z=R%Orc>W~Zd1eeB>F+@jq z(8)Q~8agu8_aLdP)rDX08O z3w?Uc>E0J7>Jv^#cl=5VZA$CJZCm(@-b^E{6sITKMx91R&g2WG>-36MvGj%Iap@EJ zXuV;esg{~wrs3vETZlVD3Fz<^K#JFGv`gAYovGJyFrK9FI!2Q4B*u_2h=e{f(ch|! zX|02tsONNnh0iB!WO4H35CiZs688k+V>c84pY$nTj(?3=&oDsu)bkfyFP~o%#{hkt zetv$&Dd`xCt23<6{&%Aj=N&f~Ink&zM(9uXKE~+$lW${yp6fJmjnkh%gLNxc8PANB zOYpS;x<=@pf_}v*=+~Tr?ijaU-RS2?dYzMeeR5>_TkoiRBpXe!0Ci#_<2L94P4Rsg4^dCYfz3wTvAmheOX}oIl3F>7Y4pj<*mz0fXF%h^< z5l2y2^ef-d`@l+f=v04r4f>NY(2Y`{Hlf{^fvDgXBFeDZ%8VDyrW?;Xkn!p!7;c79 zr@fzmj&{=Ci{lgYk{hEqb3zkzxDIR`wa?}$Hl)arH&QKV6FBuui6#WZ2;TOvK04rX z9xZ&(r+xv4tm^U5NgutN ztMY70x2w2Yck8Jf>s;M8$AQs7-5&~fdhE+lBo)XLYCOC#G`+tQ z>LVTU=O9z3R-(Io#&Cyd9`@Q-bOh;(U>+ikzvpi0L*&O`?6PEBw~-@<^cd7rANQCy zfjpH$g7d*|I@z6PSSLuk&g=M=KX7iPJuLF{d%t@JP<`-NjLeq$^TEb`4)%T{LtibP zE<5!(0QnUJcGs^V_}%Z61y#D+zE___APk!zoc?eL&f=ZE`YQ+K92larNg^S#g*-*1 zjE~&N@&>X13T~dd7;YTN+=?uMPjxQmPN`W(O)&yXsJGI#BJ+~>hrd9P`;omqXUX_a zgM|;x`Rs9@^904!-Oqug6D_}zQ_z)lhqUvL!TFlPxybbT79jq2e(v7~ZhmuZ@EF#4 z^EU5>eV*SvA$J$%i^S*a$YYEhzl@td``c$e>pqq>-4frHo^ek(nKTa3cVJ|A_<+VZ za9L+^(ikUg%$3s|_%TqIQPj5rB(+Kfn{fUh2+pK}r(O+IJg`0Q24K`UJnMKHZt2<0 zdLFZUybV69Cp_N5$UVB?|At6U;2wXn+JUB`1@sqZt)(sj$G{}2 zCrl%I0szqUNY)mu1;|vV*po>T7?*WNhlv|Be^!jxwhNlvw$sNY?=j~f%#fTCMjYTVRBM3tU~MEbBa5-t&;%fc5$FRSH1wJl$2cif zI2puz7U3g)bw@VE(TIpdKR8ZiPUO;PvkWS(FiO4Rlx9!-)EMdVWaUh$yux6C&byV;Iwhd_^Q>xZNF^4d`bO||6 z@3{W1E8J<>9QD*L;k%8SvJ<^?(l*)eS;&TeH4TZJZ1HihLi8Ds1a_)ZZc4I71|SZP zyc5uK>?xa#%6!I<9|9j7T*qi9MomEIdmS+zJo!8ZJ&cQE!dDtSVc_I91F>3iGY7{X z<0ZF@ON|sT)*;c;$S*Mz-}eYfj6xl=8X$Ud`XQF+N5_m!a2{Ydq*3F3tZW?@jz8%0 zBy(_DUuXi>_kVn|41Jt@3{5!=9fQLdtTOguEKa_Bj3K%>(r|9@Pvb{Fde$K5khlhj zIi-?1<;kH&>x0jZtvXFpV@o&;PADrKWt(M#fBNGRB4cvGVYMNEoEqUl6Tkom{+haj z52bPAN7=(D?1&8jj@TUIkbcTjq^$PIJByC z$UMoh4{_?X7<5v7l7Q*I0u+h@)~7-vXgvq$M)hMZ>-Dk;VG=wUbtswkpC6;QY#n$a zI()YuZZyzJ8SAkcIVz8fSEP_C7M)0kn*}Q+6^>F}mNW{AJ_dN`-x#6~U9Rjy93UGS zl|yCn24mM7jMk(jr?RK5b~^F_|5F2vv~0Io3PNpYD|#l{ssRI{Q^Or1_@R*ye6%A{ z?|`;H;S?|>yD{LFZ~0SRx%kr+`=zPIcgB(P4jKIyP&?aif!uT=LyoXm@TxsQmj=bS zhxGbp#JU)>@P?6-aCqW;rhMfJDyc~{Q=Q)>38EprIwqhK)m4vl4q7A2Lw0S0>AH+v z3=w;i1*C|LFwjlR@yAD3F+%tB-_4ktwWpdGx`HgdWqy%8rF9!BVRN6#n>6p=yU}C^ ze{p>qiKQu@K~iDkO2gc3#jS49Rhjg!C`X-!?97Zk(j!ChMIs>B5ogD3uWh0`$*ncf z;ocKAuy}%@|I-+t<3Gg^9V2v4MUTD)_J*;fA^7>(<=)Ge=X)<-a6Q)`-P6x83Xc)G z2Iy?=fu8mhboG{!qqDE-bYtt`B=l!b_CEUf>E0)wcnbPQ7@$ARDd^@MV-W8q7Pj>Y z1*vMkOeNY0?t`qrTi2{vEnWFVUO) z=3OOJ%&>n+Q@I4a@dy8DL36X=IRr-mF1S_XcP*&&TGTz znh#uCITvy{XFIK>^Qk~T7oHUpu6)BAj!S&aOQh=r9#_gGEjiVDfj8QZ%abxp`%c3dB#OVakypVFaDD??dmry}&n-4pCx3HI>4Je7AU%&9)( z9UiJn*tms9@jUyH-g8+qZB{&Jf=W7?mVvwBJ6%n*owXgC;Mc&)ft3R*2UZTO99TK< z);PfHw#VU4e(pCH4bW}9A2qM;)f{+RAFIE**5%y!2V;IPs3FK>|LEBE`*Hg2DcRrt z_P^Qt`-}g`FN7?#U2E$N-yeZ$y-tFGj$Ju z#-@uWSf_L8pV4jQ8>df6tE(Q!I-GtMzWZ3;IIcc2NNAIvoN)`>0G_A68 z-f^nGJb6n71>16TPhZaZ+0nm|m6ENNmlhmh{NbQsCZNQgM-%0Db?qu9e$P3na@>1EY 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≫`y%hM=5`b(1}EoBi@ryca>%qq}rHsn|p5 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

2qzX?@q$hwUS@R@ld+l^&c7qsa*5C zl!@HaP!SrRc>N2~cqj}ptAu5nXxiVX%6Z7GK@~0nrBWBwhcdb{!8*?{MkSHu{0X<) zHUTW0ghTYDU9EGiW0hWN!vg2DXOfmBZ+()pMZH*Hc^7XtJui|jRjVp8&-yI!-&Q?$nYWD%?TDee zp|1On`r+s5Z+dK-)sm*C@sDiOzFsqz)WM1l- z;+x9hlf+;);Eqp>cU$14up_$QibG_YGGuSSHhoz zHGIx%2%(t^Y3umddRnMmzC#JmwyHv0JNpZ~i%RK5iV)Nb_BX?r@H<>S*`Jwm;e7|@%N~nCf9&%8Eq8EOs*f_s6J`Od-o{Y@n0=%r>^ewhBDZWYU}P&=fZyR$ z-sF3i6^?nG&L52g7c779bpl1EI-+|;ZU`(@73luwKuq0Y zcEMheblOhn_=u$Mn)*(j;Lr@5N9a{8o4)l`jmvjfXx5MX;Ho18lWrtFaIKsF-t~to z%gP)15;Je#bzdH3mOdc;7Muf@KK^^Z{?%P6Mt;h=vNrv1-*umLy!tD;nsEz4%5?~h zPLKY(TcMC3EoZ$@=#n0N?H`cZ6-$mPbLWMO^`${#4m;+P3`#eN6h--tv+(Tw9iH#XI)M*e`7O`_l}`DeCT4F z+qJsu^W2pV4u34%$`@DK;zc*$p!rChRj)I)7{wPE=8bE5#|vzh(Fh&hlhr7{(@_4V z4j7yOvwfQ%#~Z06q9F2z(u~bfTpDRBc?pYdt$BtFBlO0r{E3!K3_eBx!oZmbb~-t; zM^|WmmmaVrEAF$h2hOb6L~P(Ut7*(GDbiEBqC=Dxr}3oWnqj1;aH^!<=d>w0ntMI< zbn_Xhz0tSd{c!N#{||q5@HM|DF1;TrkVkDu)cw;EOuuzv7EX9X5;vUnuc(b%1 znNN)_IiY_`bI{U!qfC1<%BRZL?#|VH=b%r&5|TM>deZ$V9^>5)L^A(mo(XE^6N|@g z1P=mZj_Nt4LNdj(a`Aj5oTUYOHM~+U;<@aE{_|HS^nJZE^>cM+^?P-!&uKo&qGg7h z$bo+HcT-s@Xm!|t;mPg<|;mF}z$s?z`vnavZi4cdhC*03RT-o|D? zylu*Wn>WwTI#&AP+3|+bGj5sCCO_$7c3P{>4T>~A@>=@c>1&^d7T|*zI z!pKW7;yuV!i1fdJ1w6@53HmF4-E701@q>MS2wuU4Eu ze0hxH&v6_vuTGP!6#L{ajnrAE1ni6L6vby}K1AGi%0 zl!^(~8_@s&KmbWZK~x-hAuVFzjS8eTVb<@(Q}T(CrV|dg%HD-^wq>eZ*c;$^yfG^j zcYp3S?o(1lw{+EVq@y6ltH06*R9z>A)(6hzfG$6tpt;c_oy|S@?c|(3yXND295;?V z4cZU!aoD-xE2v|gy*=uZcU_(HJS8w6u`FkLK;8E~BoE0+YeEfp zXr@8aOq14xZ=k2u8;B!m+TCU?Gqo9Mq>&>INU_OtHc)qc-|yj>vDV5eQ~}s$7f=<2 z%(!`Yc({9bL@uOf;Dr9(SF_Yb$4fIV{RC9Y%suGP(dYdZT(00&c@X(~_>z+GX3OQkK?Uzv^vSznxldot!ED>m(yj(wYwAh>jzm zogpvt39xl>#bdOw)bl}Fh;sP9;ntDp!4^Nci6>WG+T_=fhGWRsuIvqU@ejwJ- zDt`^<$<6oJ9rfY}Im!Q+?sZdJYta zx15gd82QwJ&L5bj#HTv0Ayb_aud!QkA_AArp*J~oQuN3}2TAbShp!jKxd=FE@n85l z=pY9i7dW)Fo=n{i=+bw~bC)*PW^sgvhGU&xRTSEtaPsm|99>FJ+M&g<*vN6|Knq$f z@a3dt`DMRhRCH09=s>8>o#>RO_kbf|0MZ@b7Uv9Hv9W*UK;s7)91?lP>&F?Q-}cR4 z?&-?{FfkvF-}`X}9f9E{e8{}c$(cH0frHBv9OH;1q2LZC`^^;=W(=W4n9mJg(DZY= zj9_S7Qgueb8}*?tw9FwnR&~G=mUVX3neh^*k6Y&05judW!I)?#PQtI2XJmw&z?iO% z&`Irvd3&>Gul|$2b{!c5@t#TLDbTLPI-bV?eRf2r?|cY>MxKS7k?7fmYN$3|odUrf=Ucmm@OJ*zXxw8<9W9hdRP;fo9!y|4 zq2rvG)6iMt>LhgMIPHd`JSf8p?V~o}n3oy-07y=9hj#f2+Q{J;zPrwxKznEs2mH?o z?C?@u)EX$Q*id6~b&{*=ogZ0&UI1f5;i>()COWR#|A8OduNL~j72DTG0&w;{$I(|O zbR3<2WL^IOum3Y_+TGCGE98EH!&5$Pfqn$9w%AKqGd@O_zx(mg4{fh>+h4|MnydF> zwUW6v#g41b`QSpGhORTZj>+moY#e+GZ;|IM0`S8rI$zbv!#bejC>ouI|MdIL57yXe zUnHa>{Bz_aqSK`1!y2U3M*D%*;tlAE$!tM)n~wL}SD#h7_P4YJx4+4Sb}1NWBD)2g ze$^lN<%cJ-*QTBGRVQ?VcR$P^oa-3h%RpCASVGoJ?3g_BH1v<|%Sh`aYn`2jC-f($ zcKVLsgejff{7B(KA;r&DVS zZbYy)shHga)Jfj1V8YE1JOTwJFIUS)Pa>j@~>f|9feIL46f#T&7;CA-}y&V<(d7p z4(RC1mCrFac`i=p?gQEZeK?`3b3Tjb13u9c*B14o^upoUb@D|`kpi8T*G>8gW|6ft zt0U19DRq8}Ah&?IhGlJb9Uf`ddixSCZP7c|%j_$#w>%zTbC}P)zZ6oG5n|B{s`O&H zm`Y1$-cmH##e%9nP5bw`p@t}LkRJqOC_z>@Ei)K9rwsi;3DZ4a-}Avd$uOVO(Sdc2 zYb;APwdPuHevR!%dkY|a9G}!Pg}qwVznl1ho#gY;FZx0D+SDudZCBW->udIge5mS< zeSh~i({4IrFa~$-be-P45p0(Lfc4$J*xSIiDY%LYpDjwJ#NcU|Lip)BWDTsqMmH$f z3*GBp@iVgG9AkV&tDhTm;k(qqyM^x$cI+4q`6Ku;x+IodVOp{!TlLBPaz1Q@9_ftk zn&%qjhmmp(ef9$wuIE?UA3l_EgUzbF61doxx%YIAMjJ>bu3qb|fT7NNjZb3C8Je)p zcU-jri*D|fkLoXry6-Q;@-4KM@3XEwh2~GeD;D>Gt1O>__48mpJuf~F@7HJ*`?aG= zJDJbu;vt^S>vT|kq4bAV9q@mAY?nAe$qSx)H4V9Nnr(R|M@7VoRx){Dk{ddZ^dti&`M;IP=HnFJRh{!6x$+7@Gr^atRu3Vx6tCjW_#>#TJONWw$TT2vf3)UQ)cj zd0H0yk*J7q$^#$#2=XO-N{*X#gs>ivELv=w^FxkxG)P^NgCT{=W^0&vWygk;RYm%( z(z-A3OQOvAk|mL}H}H)}nFV|5EKg#Tkq)uIWb-fimQ_A9Fs9a@;G~-0dRLsH%jNAb zta@@-cTfU(TQ88lYUkm7wkpgno6}v^OpTm}yd%?5AzUk{a#qOdZQv)EkzD#Rzu!m0 zyuf%%kG}U8NVnDxMf13$iaJsP?744VwNSn4W?Xi}|J*1z%50|&5hV;fosL3;g{AI-x~`f+*6@Ro?O9Riqz(K4}a0q-_8ZveYP-sUX|t`+WCwp~;& zC~Uo8{%61WZ!d_#H&R{_2cBeG*Qo*NBg5R$Lgqh(m-)kFvf8VsTF+Al-DQGIpvznq zMw~q_lV5LNGB3C0#cO&7Te(=NfBf6O+&l&SpFaK{`=F2JcT_`2WsLlg;>eV&{U75h zGf!GLb%6i0PTsv*_T=P+9qGWBZ+P37wqD(G&nG^L(zo?Rge9q~IQRAWP8@-&^5vfL zCHHM4qITNU0I5!|4U^2~ZQd4y{L!SI`DwDDHzKNf{zH`$&5zEwh>y?zwDLHd zpZxmIH~aejapGjM3&CC=PkT(;Ee-s9Qf8b zfYIF92f4qm6_b%^Q3=| z`_{*CV8U4f&^sJg&v?1o7pc#57V$J-oY3Pm5>H>8)&SJ`SP$JJ9USolGuFuhj9fMN zdQqO9=r1Emzm?Cc0gjKwYvl!W0b@x5bt{*uNV=Co+br z0qI)UhBw~tct1b)^EK0@7h<$WKkzS4)4TeTGWAS2_?zbh9Tt7zz8o5Ec&Yw94rn^^ zs+&60oLnBgyFe#ya9Bf)2$bV&m{T-y9utqwiRZi^eU5|C6(_`9Q|?KoIu({PIr?Y+ zLk|WumJI$4u1RDFmcHR6KoMKtrM(V8>iZG;eWIf;zwO>Je{{6PeV_opTjfP-iH5D_wWn)J9!R&FC!{ zAS-;}Qp%XXZypYZUVorYYsU{AaXJT=jl-Y~(Yc5`1_oQH* zoIL>)w(x2@c7O}VJ{{1Hk*j^_ym^DnwFh8EX5hUm2Xx&b-|Kj-qZYVMu@R0z=WkE! zj#Dbmnuu9;D~O?Y!ic=aD_@mo%is5kJoJzCH33&v`hZynGq|gjnQ>2M?}s z5GsdRLFFturdJw$sf(rGrC|;*bcCi8H<7`2d3sY&r+) zWUCXp+>XvO);I%rBC_^dIzXx3c#^yhaODtR&c5~~wayzI;dH$8)OAl|l}ksQOs-R1 zXkggco2P|z72WEfFg1U1I;3IJ;*P#EfCT5S>i~J2>-YzL$Kt$|209>!H|EQJGFMV; zMI{n9PN|=YNBT*Tcp9e-8qt#)e=bnzo)Tr0AX#JkFI_3Jbl#98d8=M3yGtBBKI*v1 z{5aO|=_~>xb#BsT4uyx8I)m1w$}3IOfwG#ZWY9oy%eX05-t@bpFd(^d+@ z7@W)KMC>4Zxf_>L&AFyin{et`?FHNqpmXXNvG$~5*9+jrUPDLPasUU1Q!q3_JAKv3 z#5D)fhx~zfXKk$M1IGv$P09&J}^N%9MmI==CQnpO_49~A6za+!RMX+?AO02oAUGmcP_xK z@Gn8Spu@GG9PU6!UYkpUG&y2yrAzc9?c#^e(WP6~1KVP=PSqJzn7-=-ZT5G5SRr++ z9XH3|C5HYJczANgTHhCaoI#^!%=%G{$RA9ZGtL_ZDsJs+oFe51`PdfPuVIF$=Cv&hb?JDHZ5B;ixVAX-x*{xlZFY=Tk86SRh zCS`%7&LHn8l+HC&+IwMLYdR&KxkZ<+v7Hx$DMRt1OSR=-2gET1H0gq^`KeB_W^;P@ zDK^J>rC#WOu00Cv>Wz(|Uq{G(Muq>9Bh~C+_`-PvQ@4_o@)K3ekK?s2uKn=ab;Bo9)(*)WnPi?8X zbi)U&{D_wNq650R75)0i$;0D=2A7?cYj=)OYs|o~-;H zn{mvCMsBd(e!wa84jq;ka#H(r0|6}LiXC31)ngYz9>o?C zCqyRQAGEilDfgOPw5+OzgBqx}#K%%Bl*{N(wPGCz?U1@rjLXdo-*#i3QCrVEq9mfPBzoKr& zJB`S}H1$(`TO84&N6cMf_v_jxmZ0coordm5GFbQWu|Ge6fR1MCs^nQ;q5>Dx}1 zOU8~BqxMG0HD#s^#`BdzZqi)vrEOjQt;deP$Nu%k;@pq@0p))B>-DmWX|*?NdDF0# zJT3JQuC4N?Vf<34*3-r_%Wb!==G+Wtx^%ZoSG`|L)>PwrsLey=Vwl^m7N-tSfe` zk3ny}^z*;`umAT~8t%(SqqD>QOMp_H(~tJ`J9$j3an~(r%6TB^mqTkWPi5-4`8?tS z3lEv)z~Dx17(eQ=IiosaNz6^_*b9zjS%7G?biSLk=H-PkjwXz3M(IEuTL$bn9iMf= zTV)vuAIeineXlA$zp`J>Qb$>SWVdnJ1Ku&s9}G*~lBi~Dw)zhO3qd*}>FO?078vX2 z#)FJ5vx=)W;RDvZ^@*u(Jy4HKQ=T&WGd-@-6~?`GPTimnu+IrwFOa@Q=iv{xfByjc zWWG`-o@5cFMOO9e`H;a}ANaYQmBLrF+@sqR?w2nBFF)a0vZY7g`wOI7jw=6l{a+py z*ev6r8quT4R%9IkZl3o+xWK4u>I=4^aXi#x`Wu~&=poRz^5VdgUkaZ1sTk(@yOZCa z-w4(Zkko#m?7Ol~o5mw<%RVJv;lF{fwb%UK*O+fWEH9eGE!&zkdYLE_`O{onO6R#V zrw71_rW(7)T=rEx|A1ZKMw!jO*1UY17eg3R^#A^sznXHDRPG;c|L&mt!GaQLRd-q= zJMER1^;HSy`3<=WJNDo2{SUwF?|7+IZ2{yz@-y*9`NF`;?{s0? z1WUVvN=~@~@jeqYxoeIOjFKVdyTJt3VJ3ylJmDf!m>V?@wYC^v(6yfX|1W> z7KIm(n=x0nkEL{dNga3sCoC}PPx7O1sw;@M`!~k{L^{iOO;ge9i7*D^C9Uv<>2eZ_ zZK2Fy-K-;v{-k#E_S*Nb{y)N2_+0z3gDbKa0$JqZTwethJ3yn3BfqvE+6CIhcDSyH zBXz^ZEM=4n=gqxYmrrF`muycsuqzghI+LFhUaKB$eE{Y0j~+ggyR%$YwONt0OdHO) zFQHOyL&E@?>!;#?yxrb7@Wz2R4t%#9=+$?u!+D0IEGDw;WLZkr(Zzg{DC;?3Txa3Y z`eG-U#^L(A?7is6bp8Qd$+UA#_C@kw9_68f=H=YOOQ(KKarB(t#7)S{(Y$!>X@5Fv z>9}zhZ*g8O=f&z%PBOnf)e%c4A78N6+0@vVt_R)r=}XeU*U4WvD&Yj8Gl-5Yz9@U6 zGdpj^`g@6|?CN}`6WTkR+dLK1J*c$m6bkl?D=)`WW-oLR>NBqNCo2YDGZgvLK*XB? zK>yi(s;~4^GVl%T1M&cB!qCYy?93FGj4cSxr7Pj*HgkFbn(%!6WOQmqD-)v|#Q7&3 zMOS{OO0^HDrBBO=a5^^VcpR6r@k0LD(Yy1HIPc(Ge!@xH$2i^`ak^@p1ASp1ol{TF zIa$@~>Y`4ByU{5SJ-K1S+76A<#80h}n-s~>MHfu?FBRFnrk$#jOTFT{%^U&l8s`d} zDs&Wz9`h2m2+A3Qv7v)^90BOqJQY@qan$myZPNM@sMB!><}_8&N{^?c=R|5^9Tdyy zmo#+QRK4@PmNE52RoCDzxg`|GOYbeTONzra08aDNhHt&|GqsM>&-jSR_kl)wlQl^JOFZ zSBUM1uG6bQdZf=FpLA4InGG44yk3&g|o*v zL*Wdp6QlgqAQ`n#1_On_r zrqGQ}$QdskKx9YXHQSRlxH?Zbm%4*3H8A4 z)If!OOPM_HOJ|c@(OtAs(nyzcHgZD#ed4D25hq~cd_+RtD=+A9{neSQF4J5(pHrq@ zmV??z+eQwcds*9c7^lxVc@aHiUi-nB3mrp$xIphcd6*9_==4fMbxxbvm$*Smg|iA= z{2lu-nmD5CyzW(c1{7(>$r!z&=QU10XmLC~-hjIpFjA=;R=tuvWp9ARew72MYs@(x zJAp5phJLIQy7t34ja+oJy}^m_y>ik(f=|0)xgQqrlzaCwveP|MY#+;p_KacB5A-s2 z$Dz$pz(=lk#P$C>Wqakry>k#X2tOnqIQzS0EVijVn`J8>#OIjopVauPv}LiucB`#$ zj};EXpQ^MM#)_;#s*UcoyxRq}@?~re^B!5~@O^fs3~TS_oX_vj;7XU$mJ7qiBN04RzKtyPIct?S8|FZ){K zk@o>gt#fM>BwsgimmXyZ^r@cuA-*H_?YBIO`26Z`CAY@zC*Yy@GiPZYSs|0`O<2R+ zH`NDr9n$V59rGAcipvX;V{FX}V)}BgUSm_ESm|1iBwjJoT$bF?rxrXo z$=BG@d%@J;sV6LQNUmx|(x&OSN^|cyEvLr6;F)nv;NNAkRkL`}e1jkGvU7u3JM61= zL*EwNrG@Rdd_Gr?T9@3Hx=v*M9ddHd>3W_$F8dbtZfh_X`95)#tcDv z2bzh_Uh4}cLS1S{!>$@}T3A&IvXt(DeN)+%&Cfw}&zZJQpWBD+4UKOLPwQscMq$;1 zvWr{rPbusEs{1d)^_2b=7>4VNs{W&FOVw^-yKbvAx=Z8KHM16ElrqTH`lbCAoNXF# zO152GP|{N{4%w~cqxnYp4D;;tLiDp+ZZ^nxsL8$(+>O5DrO{N-Y)R323uyCyX5Q3v zLDz}YHNq|=eyCiKto0LYCv6jUKLpT#pB<+MT8Fx6o>ty5XDGRK+2?lFeSL2G-&Z>a z`?+KN+;+dOu^smFv#Hi$S1IvP^+ko({NkyC*n0V=9S9dY)_UzGFd9<@*%N}p2iZ;^ zyhv)DKMDp$gW>{X;GPFcQ{qiY&AlvOOYc>uB}})~cTh~p z8MzSG6$QHDu-&K&N^bm1%eFA1eU(+cKpBg5!gD2d2&NNTM=R^SS)R*VrBhzSSKTOI zGl6{Yw)Ntlz^~DH_`tznuvvbT>^e2{B=y`Pqn3$zt~BsA$|Kays{6v3_ZDE{Z#Qh| z(Q9xr{iLJH5dR|CMJ0y(t%x3U+~vjqM?Xf{k{__@&9EP>W^FG-`z2xfg988dB`Ck? zwm0|x-$K7Dc?oSPni2|=kZ7a@Hw+sPyWawv;e;7Y$|fusYo_o}`2_O9l3VuSH9EGv zFtNO7ayD)2&EIX)JWomOWakr4^JPkk$ZDshal%ghlu2^rr{Zq5(RjD&f!y1?*k#Or z|F3_w`C+hszWnQ5z{hgP=TM#$q8Bm2SyMqUd7C`#Vjs3Xb`#VH!)sT{YBqPhB;N;{TCC{j*=@~}|W^ghNanYWDHM%6I) z;8`k#=08*t0akD$H*id_qk0dj&}%aiCV29#Dx8x_`>Y9$yy`PS)a6? z!Uc#py;LKL9$j0+@xsF0<-b7Dt{wdFJ5LGz#}`KFH$sl;(E5`N2?ebpNvI@4cPw9i zh}|0Bg6yF3weMm7;cpI4LGS-ZIAGQ@3l?z=F{OFd3Q}FRh!K0x`&a9{`2UCeU&GkH z_CB`Z^3rnJ10Za#L!*7!7t1ZEuxVG;>IQpuytM|shTjObK7@SmTl90dxi1TqXunK+ zOBsozjT?AWcHV63z^D>M!VJ@8Z}A%k-Z=2af$xz6nn*%^yYQX|uu)i5%Y8AGBw{AJ zX35YlP3t$Jz;hF)=C)s)t}d*#zZsYRm+pUtTjyo)RWSb83&iySM5a=1nsWbq@%-46 zp@rjxWlyxs3+dqZbgwI%l1@%IMd|dKm!&xcStp=D>j{o6hI-)|rwx9DvCbD^bp+9| z*ca$^)VSqzWq+gb^h;lo=T{F}^0GDijW|d7Vm~kF3q+IbSNuIF&Ym)XAnrT^Gt?v7 zg^pA$x2A0=_+(-O$$pz=Ddi@skA0xW@+$&+lkXLbJ3WdLJ%Oef{0q~iyN+w_cc!bf z+<^2~Kd)1-#|zlt&!F zurtTJ0DmoyIAPIn4`0hhiJ^n38dG>gHhn>V#*KrA4(%s=*uoDAEGG!kN?G>D4f;2I zPiwzMjyhPDvsZB`)H{YTo)A!sOz+w$ZB%H<5RqT&QfOfuGFd3}Sexg|{^2or<>3hq z0-jEH7x~}6JH;vGYpOK{x+FRrWiH#np%cz8ZA=_Db->oySBDVvHTDcy$S&;y>H?q9nQ=Kd9c1sdBk~oVFAkcS zi_%k9?FWjyIGv&o>bCZ%PS{jubW8z{41hj)=!Ym~2Xw?9XB6Wb>gvLVp+h<=*(eU+ z&?K!jugU@a90&BX3mn$CbOt@gK~raM=eq55cGCepCyB@D1zpqr=Jy3?XRNuT5L>nI zl0>DY*fADA;%JZK`zdorN3V7!4qsu5&}EM*owo9j_U)kKDe6s(Yq%5)N*Pd{z^A6)t2EAq-^Pz?lXg^UdjN6auoai}A4C**t-?I+j zb564oLToR1@BH}2kW17|cQQ^ha&NC_X#;A1m7+j$bNA#`Oe z*Ku8aKnI}WPVZdyj~oe&gFl3zXVF6#6t_6q-@6|3=SO0+L$O7U5!8A^``DUH`>v*o) zN{hbuZLK^T{h3N##nvWRExrE9BQAqF6WbmeZn=1iR&_n|6vu5p5aEYdPH-wUI8Sc* za7^Z;3dt8KJl)*AmCk87^;{Y2ybsT6uTVNV7zf3rd*OMAwnSVKYiN2*Ryt)4Bo|RY zxzWBpt^rO=K=zDGge)_dkoWEtcxE_du6hc3Ih=bEIu7WZdLAcqoX#(((*`-OU4OHt zN{6}t4LaAi6L5|38b|Z1IG+FR-^MBEzvU!!uAg*3j}y8)a9@JzqSH4xpu2_|Vo$Jz z+A>3ZM5NYel$D;D6TXv-PQmf;{0P3QpIM9H_q9B@V*KGj*%R0EviDZjg?53Dv^K{5 z3oy=6rH~JJ^_-w5u+t72qY$=JF(@)0d?PZO3i}`!MSfJ+RSLa>fqbe5*a8=r=iJ2d zlu*3~GWykRy1e5q--ri@d=o_tD6_nPQ!jUvXYpyNRUYFn`bk1f}(u3+#{9OEVUI&2aMq8otIs1t`b6j8X%;U#^+#9gB;{$Al zeqJ%{UeEoBj_A@LjW>K`M>>jL`H7C?l7?7>)R_YV>84SP^0wp}8yGoq z(qZJ8Kw4@n;x*LF=v)=;=b4m90#6G9^e|_qud(Qll7twOdKW;Wo z+2}81Sg@Zj`br;7r%@G_cCAI&rJd#j>MiKZn-Wgq;Je79?m z9ewS>pD`{A+2fh@^z%F2$1;yG{9%y$Fc0=NF?qL5*=^fJR>lt^`8pNzIKB?XJuD2c zK5(>GzLDMK#V$bC{oMT6?RCwifA>?%bIw}#by7V-<4!l`(yi6DcKPSV;{T;M`5c;` zLgN;{p3}$cgndy?=)O3%_0rFu{pP>D8tH7keEOVI(A`sIQuBb9|MaL_(y=ElIz3m6 zeFosl3t%LZ*Cnjh6y5>3%7nt4xcVfA(1ID;4>TqR7{D+7`T73zmg! zY(_HPF|HnCV*7|rz!UYwbjQ{ex?tVE)y8Nl1^%(H^&yJa@bdYVgIm9JKBT(Kw&vrI z-6C_LmIei+4EiNctCVT3#wgghmia#F4&>KuUU}< z^S}#K;6fdSxo%D$LcXYyE&K3VPTG7?g7}7r*|M$I_y4-{|6%(kaUGtMqiczsYdiC0 zWD@PwH*D;%?c8&MZm0d)Q&NxB?SanOw|OyhzI5+5M-su#)fZLnH`O}5)CC*{dXBA~qtMt5|-v^lL zqi$1vp?ofz(+hx!2P<}<%xpQ4TK>I%h|Vz@w$_|~x%%s>{M`G@{_G<_gg~?2n4H#v z{D$Q^l;3#J9Dh$ty5N^|x3O;4Et)RtOv6ZbXqX6@l2*Tk?}P&}6pTgrJ|^SEpa`Tg z?vH;o-Y?G@Jm5F*zxF-sKmXI8?KAS-*ENU^|y} z+M}`WUH7tL1z32IBsOnf77#3R-;{h8R(a~b@V50^^!NT7!PYabZ0z5nzmTPS&5@a6 z>q}WiA1r9OPW9^6yiXRJpmP{{I2XO8-#GBbfj16(-yFbR@^9RBhPrPem&yk#Ry;vT zR?srNb4m4COIyzTA5pu^-`e?a(R#&7I;*2Ec7EIDEp& z&#{N9;&E?GgM+fO>!CsX%igCDQMN#YMz>14VXaSj>bq<|o?9Lunku{9T6Ea&TDO}L zFJm9*Gb~Cnt_85iPe0ui@Zt1mSWZLi7R?7ty+Rk1C!0zD96IYMph>kOXFN5oBN2~*|eKJ*YNk^PA3t?!L?h4W>J%qvEl-CfUK{~C=zktZ! zTol#^DFko^CCVI4pYnwwX}Y<>LF?nqyGtAme>%Iw>EuJ^txa^Gy}ssjW1QJM(R^{( zyv0!s=ZdSVORlSOsE{5V7?Gj4o%@cn9CC4d@g&77c&$!c!f#LdhE5$~r31H|$i+6k z^K-;xFTWTnLvQI;^DN9#&(w#su~%8ei}>{I2M=_Zl}FmE6C6A&Q)YQd-?#Lw6}L^7 zs}pKMf%v|Tf5|(^qDMq1VjRn;d5_oGdg8OTF3^!jJDI2lH#!F6z~&WaxeO0H(3sPx zCB=7{(gy?MLmlruW?AP}c>R9#2yVh8(# zR;FlzI%|lnWxmu2@7OGk*UpK~d>libWj|4X;f6LMmt~a515YN`nKY-xGZs&XKIJs@ z*fi|ijr$vU3vX`t2*a^!f|?5n^2*a+bwt-O)o_H|r1zAM@W>zWGOKaUK0~%R@%aG< z1fUb_dp};{oTr0&ucMrQ^KljCv45H)X|`TFaf}nX&hYnM64tV096Bv(8=ZrT&3BMB z`kKSC&X4`L$6^6+Jq3M0j20W_n9YyPRU)!C=tR~soX0)Q_vG|L?3GHT-rsT3_`ROC zcb5X^;~92YC+VsWvJT$h0DW|J^X?egdmX_?ozd?|-@@bT2p`(i7nIXKZK_wTW%qIL z*O5U-bT8Km>vdSqM`0YFG6aVXmf1^=I3I(EF49I`%LG})l_enADp1d)dY7jZr9sEx zR$zk5VuEc3CzG%AUYUI-Ft!D!ZGj$S*>1PiIx4x0y^21?iyT~M23pIr=g!IDN7+lA z;XHlpN$B*KHAWtd4)58c+_^!Kqu_~iEquFS|5AMGKA{%-5t=*BI2>PxL4>p>IF2UG zw7Htk;U_dKe0E+i=P_B)G1o=ETX1xS?3wF2nP2>BIH7wI`nWv#T>Inx%=xG7kw31( zjzyk1N3}biPW_QfhhWn>q5u2;@bvSaa{Bp4@|Pc|_j12U8*Nt);s{;6$vj6xqfgGK z8moM%HJK_IjNetReDsmTJ$l2@ZA3! zN_War-qA)U^wt_+XW168kKznS&HR*NyvPHRo0E{oYF4%7f|sgHGor? z+tRY}?G_y7($_{)A&b7bkD}l_PF|f)HNTFK}Y0J24Jx2`4 zQoCKxHmsNKoz*S%hB&97kGP!ASu-!V3~t-ycL4PRC5?_b3@8aLjHgDJp`|zJ=n(otr1T48reKqJ z;1;L>TZE+5fG%9gG|Yr&w@BC!5Qw&|y+-uh4n~>oXaIM8Uh!0az1P3v`Ox1$Jkk7` z4`W^7ZMO$ zxT{>p^HF`2g^vbU&YvIeLxms$7~qtpK^dq~OFxk$2!pd)+ka#?;B8lVftunf_1+=4 z>8It~{O$U;cyM6wy17~`Ex8q+`wqOYT%`v?FPPi%IC_v9Fhj&GYRm|F$ECQet6MD_ z6eTQlkQ-uE@)bHZC!qO3OpDnGLsu)f>7fT7&cm}fi zDWN-^+cvJQdkUUs!eq6^uCiv&MEWf7uQ#~-ZU)7!frsJi^wV>FYyPyj4*Py? ziS&OxY+JN`o{~C_E#5vfzM1Epeop&`@V@-={pEC~*g5xTJb&STiCEPtSOm>tx29?3L#CU2qPze`+WMWBN?joN zCtNmniD@e?E9|FX06Q>zw*98U7*o^i*ur6AH_haHkX<%){CFDP_=B{t@W;p>+MBmL zO;JeRhiVucEHYedA{?Q6O4iJh04Q`&umJ7JvM8(i(b6dBgNxgO)0)b-U?pt8lzbP^ z(jDn={I-uSQ14L4Mt24e4s^qMf$XoxV#gFcCv3ey`al20_iqQbUjEy3bP7L*Oo^8H z4wFkh#_C|_TP74lwsQ>3A|P8lB&>(ZY4SqDYkYxJQEWM?%rB4*%{i1>L_Fkd)N?rY zg96Laxa?cKEL=9K^({!-b_l+wQh9hCT?FmEE~_)1AWY%-g(J`SQ>y=+AQK+2nn#p)FI_PsN)@ z2G>uX+j=k8<(*HgzPLB%|E9C{_Wqpz)Cn%@#A)nsq(S`N7CkX-CFSa1(xae?m-^0r zmvIkyOsg8>C{z|b-(*qGxgACTYum=NQ`Z?Aa|t(voou&X7UmW3{aHDYpZ@wUQs^_1 z`=L6$oc}od^`Ec6FEG`P=8~o(vFq$>_#o8R6vfu_q4?*${p0<$f=y6e6ul-h^3lsy zPV~`aen$|_kZ6KeXHzC*5&>Io8%0aqMqD01xAwZOF{5m%TcO@k-wy{6@UpL9RKs>; z8Jtujx+7c?WAsVyDM;6MvH4E#G3_<)VcoOt2iJ(@Ic2}{8Tl5$!uQyHN|ggqK4{_I zHFMu`($ikH_qmJB$(!y#rwy>+Ngq4=*TPf!V_Jf_3A^Z53Tq@&vI;)`zV%!5_r4Bo zeZb(u?caYs6X%wTY=(}cPKldOG;TxYDWj3?1W?}SN`lI=1T}8XFBOxHtYvShHx9gU z;Ee;{6$iA9344|>X0czf(yj{Bux2_v%9m{0Qwr+e1lx7Cp?W7<2)Za+1c5AY^JgM4 zX_DD16j4P9Wxjy@-k0q8flDViPvbh~1@z0H_GQa*EW5%fUFQ~GT#qwlc;E}o)awky zOW3|}tz(4_cE>nN+~atRs|C&){)aw}O#Ei1gM}}~*9-kPDB%d~isTE@@7<$t`BF5m z!slga$CMY`fw{lO0V8yItIK@K@-jTdw4_M|5(Qt`wJqffPy+{Xu}lo3er%-|1OuuY zE%Aac0FAukLRW3O%(&ByrX43)XTvWC;m zAS?%_oDPtZKccE%wzECLZ3iHo(0y-1{ffimxsKh}<+!KQPWtc#eNOMhq47>-0g=8L zs1SYltc$~%x&SQx82-dcKnKFN!K?73IqNtA#7#nl(Qyab{1Z%Cr3uF?9hEHdx!MkN zYO4<|&@YbhI*dx64xV=7i~7=*GMyIV;E3+W!I(BUoajt>%?n@pd3idz`mRGDehP_o z5>WZ_CoTD-NYWrMGNF8w6nzpcfI&JcRWwym(y0!PnlUzW52w3In8!Oac&R`K^Bjl6 zIA(Do`swjc!RQ>my!`m?Cmg_ZKE-bnc?1dLz=81A)9P{5BK9;~of&i<(=O=D5;oF* z#v7*{d90JGd||G0(zh_4CK;WR4-~`+MB(aS6-H+yGABWqCdn;*nooh8V5|tmlihNv zs1D~8d-|)6=|?!@X@6EH3)^@R#1Z5{HoBv3$U(bQN`o%Kx9CZlNPtbD9|5aR;qxFg zJunU(MlI3-InJZ-GWmJ1eBCMMkK<}zw^^eg#*># z8TlnJV1m5)_JZSfa7<$OUITT&lzXXboi@%8M|tY3*G7yB``}u6f`gZ9qK=d2%$3Ar z9aVQUqqjKs=Hya(QG#Kg__^ezdS9nms)vDqCP_rM7X6Y;wU$dAc22S8qE2N$ne%Z# z=fe<-1G-M=IL!GGgE*@}j~{K&>Ct8T6g&6>4xqpKgCE}gsvSW&iP&yWw96PZU*jstSY|nhZK~Mddg6&SEA7ULdlx3`O(u58VbUr($5B#{? zXXtdv;OI+(I(`q8O@l{SS4b-rMWnqhJ3VB8EF9Mz^MY=}qdQJ2z2QS5S4Y?=*B9pu z=<~6X*e~`r;5_D(=E#e7t_|7*Pe4CAJ%4v`_5*#L;y}(ve3StM-_X}7Yrqv_QzqKE zS__mfFwj=`{16280K0I@yuXE>%zt%*K?tXPlh^kQC^B!G=QzQw?)Y(!%;&HOW-5OI zXb5sdJ^@YNa=HR>+XQ5i6LZmrk4GPABF(IC@cQbSJ>kVUV-g(aw)Z#h812BKp1*&0 z&YU?yU?+Th?PDq$`qlrs~7G^ z&iQD}`48wgABH&d<14ItW$Se?_u~o#^^bXYjGi$IY#lqH{MSpk(Rrw`<)EraBu|il z`e!Epato-V6WDtxVd*hFx`sI4wT;X}*G5kjKW45R<4Ave!QA@c-Mb&KF+cq13FzZ8 z{X;pRyB_94SlS-Or2Nw(403Js6z6gbzRJgtbTro)U8nQk{q|#=&_Dc+)6W;@bM}2W z;b(6)=FA|!#^vzjx#RADz@2mQU-hvsT2mkZ06+jqL_t(e>vqI5$}u0xhy%KOrS!%R z5zUV1IvX=5@?jhe6hU@IzM3_zti$$8U3m9iM|60kqG<={3$V~-W816NHZU43M`X#6 z;YfwF2`se(LCb6)5^5j?D#)vcs_@>pyMC=%QVl}WYVCc@hcS72UfIT4^}C@hTl=Y; z_rleQ?qn)oL6fp8*T`a4RocqFsInkc=BlcRG?+2`~g;+?T7=UXIQ7-s|z)TxT7o+h-a}n;q$PYaQ zI3E;pP2i7}lf3Jv>ntU*fWMPt_WIb0Grk~l%HG7ivNpmK&_Dk4kqYoDr6+Jn2C%Uy@cCfKIR)PZZ9%TKWp$`shxC?~l4 zEIv$xt&o4G7{l!E>_P;IXroLc*Cfqyxxe%|2VSS3=*mZ@w12GEu8ZQ0^byi6>jq0~ zhXi~&c^eozyd_j!l1CjLE-#oVkwQN7^!|c{e)OJu7`(d$f z`xP%X-#i}Nj)E13i&xEi;l?G*K3fi|OPH}K*Z^~7b;YM_CL0Np{SUXJC0QXwQ$W zx$_iU&4-R3YV!~;Esq^Y!-r&5yjgKP1ou$yU#C31u0)iIeI3+Fd{uuKVqcGrlFEWw z36M+9Hvb(4YGT0DDI1SVz!)(aJ=)!>`!&IS)aYvw(x+T`+45Ds5#|NDc9Iy|$xho@EX=Vdsdk3ns{ zIQE)eRNi{YIHw=&>#}(l9^=%Zv@CX|Af)*LWdFr4>*^}l`f-Os_xxzd;ftiyWdRj_ z2q@ileiTl1E7S{3Nh{=caB=NkOLMjt&+2FS>8Z^M_ykk1xXcsv6I-$-pUyeXzo`(PUh-8HN#6#=J1b6@9Ds zEq!_|CvASKGW#Xz-mlj8|=}4_t6q zXZeU73Q4c*-P5lHRc4fDgcOZ(3k`eOD*TS`J>ErNdrW|L^*lxS1e0gTZu<~>B3vpT zh2y_ua4y#YcXclR&5L(1cgQ-K8~nYd*HE9?JT1!9HW3L0kDPUiaFA z(Bm|U{FdL5gwJ|HC2Nc_&Tom!8U*ZEU(}h>4N~erE(%GiN@eRTH!lojbu;l6ULXgw z*Z=%ayM&z{{V@*AyJgSKsdBRps0zuW4>2R=8)+*~7Z^u(1lY-Ri-Y(z?_sx|Q~v$t zKOgYu*#(OXM#~an$t`ODvgz3UDfO`rLbPpI>!usO) zTDPpdS@g@|TNV&+0j6DV_;jEgjh3!LLN(G6D$ZlTl;f{`Q8r$f_GS7yHBE;{okw)! z;t~g!IO8ZxzxH&(OYJB1W;*bmM9fRr)VSry3;mwti{pszWxr>0qGOpax8KHD0}6m+ z%i=2ll2Lu6Bhz+BFGfq5bXzAdp_ygjC`x%jRdVw_8ia3?EO%hj(veQIwk4Qckctv_WwL`N`f^ka3 z>B!S9oflr$&JSRN0|yYDf^{k?y|36#XXoqUca0@1pr4}MxOh?{@?OF6z>uw$72CqO zw$=gX;Dgh&4wlR@Xz)bGk4IOLZyZTE>HH%nYCWOalEk;9m^99PQ|7{AtZ=wU&+K7hc(v8GgDI^NlyecF%&IOB-Cwfp2I9Yl7^ok zq;Gggr6xAB$wvC((}w7Xex6H5QDv#yqV8cJw7-}jWru!nA-<$ZqDShC zd_|@)63#vC5f^K*@5j|VEgiVmSrz*uu5vtQjm4494?tY!1auw8*N;S8#bGYqWljhGFHcC$M*tiHH0q>p zdn{j^(9chBLO=fjJZJCj{3wL|^P!Gw4h;XmDX7QVzwspi$Du<#x!ylav14JAV&SGuzy)^ z!Q=c@jt)~v=RWcXQ$?&YJ4|Z5M>bWQx`%GDhntnktU^*EOjwbcvUDveh`vXUY1cN~ z4~JxaFFG!yWu@0=tQY4OIHKc(o)1O&K?KIDQ+AvcfmPRUS(BZAaYWY{dRh;oT;LC3 z5a$eP-i5AZ9MsL}VluD>8oFm7_D@q~g*L@2PUpx@$NAXE%uD7B^YR1-^phX3lRv)a zB=qyx8J*G3z1WlF!xz|1KXj$oG9K2ZWnJOWAgo%v6%Ogj$wlxCNqP)b3ywLC(z@pBeI2Xhhs7K zvh&ErkN#Z68J#vNx^q+=o%>$Wq3GRv_*Ne)p*LW)nf9SwN^D1Sh){AXnk(rrfaf;y ziIcXFi%L_D%FtkQDiwVz;LaV!qFj+I^)^c}{7l%1c9Bjhx3m|$phiP$mX4;f$c#FQ z>`HkqVGtpHOU*hBvgTwxD+hEx)^dd&U4GO7y&TbXLXXpVJD>AW7uUb69mAH3D?Ks+ zTr>PY#4%qOVg^J8>Yg7U$qDGfDoguU1`w0ya}t?LE*1vM`P&&sO02M*wX7o<40~{J zI!7{ZDYKn)oS*xA#GE@jd-vmyKVmzu8#KE5$j7Nz%UnC1UpG7(WNu+^OH|?y5!t`^ zjA2(r&!7>4V+Zo!Hqr%@AqdrCPr*}f;@As|$Vc4@0$tFyAF623IaPK^+jhrOW14_S zE*L@74(Hc2PJ8!Uu0_mmozUwy5a)Pfr#+iH@t(Mb@_~ta7en!aZ-o;cDB9VyF82bo zL1y(Vnr61lS`mBCx?4CXLy>&e%m)bWaC9$)h|Eey7)b^LoYXtr7cxtx_8_e-@6<00 zu=b%)h@@qATo1uNzdUo|`ORKv3z!;~4VgGN^g&j8@4pBv1FxE{6|i7>cp_7BtR1fN z+259)r6ZdQVmo3b93?A6q=T@L?Zt(AuQ0fRb3tk+`@yzP4TtTW2Q`16isMV*W?E|= zZrPJB>ETmtf3CUn>3$1Bes6`}pA5EoU(3{x2S{9jJ~uU~-&6W+wQV%fkpmJ?r&8l*YEi{We{h1)d zT1(z&V3g!oqN4*vbYSE|!w`2_^v!+-9)}z}X>Uh_;H~6leyZx^mL2%sU;Nv8arD4> zc;TgqX7=EJib(f7k6h#^71renkcDgBqWz$Lbi4h!ZzTU(Um)FfRQbDAYWwnTH4Krw*eAPeMvd?W5yxQ{Sdqp^8NZ}=6z}Ydk)eh9M zpB^%c?0`_V7LCjQBa7UpGM_C{p69p$JHw51p7ZRqahd3Ky32{{8Dc8fOPYOkL7o{7 zgx3|+$;%N|9#XItBhDGVp(~EF`LA&{zxdNQotr;8uhY(VA%FSY>|ic6K8v~U@&iC` zPUgQq`E2b2ztlM_)T^_Ts_u*^$#h34IYw z%Pq2@)V!kdcW;wjZ>)XrB}!vuxWv{4Ixwd5{}L9Zop?zkEqc>a8DKm6u5M@imBMR% z7TWqP`qAH;p2Tkjxfe!}ItEB6c|c2T)k5Fx8R zw%?PjZ*QuNzp3=8f;x5RFyg7MiE$#Sn3tg~*AXMJFGu@Q^>|4dPmB{!#^j2_9ewBk zbB#xm4k^6e4i)v1wAApeP0#-N5C``X2UizA#8g}$H)fDEbU?e|Pen`n7j;~Qtbo6g#Jx+heE`2kW zmg4buQ1dms(N$U(4A=0^H^7%zV5(8dNR+MK*qn$W`jS~}&?w+jkb&|8YW&NrANpO~m2iWA)kco>Uk%cMR%`w2GHxZY;Gqzn=_GTF zbL7YOcuG?5Dd$($r#Ktxc+V;D!wFqyD*PQ7xAT+O_isdOIsAwNdK@XF8j9&qhdG^1 zYkm*g0w2PIQ=AZWB-6n~dxArv&bCS><8aU&z!90IHbSj@BypRdDMqlDQw0KN~*s396oTXc~Gd8+n50$C2f=RY#Jg&o7i;e zVcA#b2KonIbtqFG_-4nu^BYe_L>EBRPUz?7XW<)YozgEaIi(WMsdicl z@0>#NpvP7Wp+KG25uZwlTlhfIU~z|x#f*jX)nJrayub5=aa6bwTbl!wm3@>NIWI6b51XE<2K`P`3ip!=tMOyDHWipo!02kyubJw_M2`UKTD zkps|waeB<0q!-s=of3&rX@U;io=DCIU0h?CbK0{$tyM?N%MYH|IxZdOhV!ncXR1f) zXea8|ouY&y_k&v3L-kL8K9?mD4Ur!CbP4-j$RspmSw zGDe;Hj*oR}vpsy$Zs3UR{1D$UJo1F(Q%=@)KHuD)px-BqQTwTboxD>J$GIpq}D zbDYpmaYnyq-X2R6r@~)i>rRh90y7*AJ-s_Jp?QW`;|x#mPt^(CamGoQ^gWJ=_wI)P zQ@7l6IhK2zRkQz6*P_6~-lVSNG4lnnq(e1xa3rb9T4@k(tyQH*IP^l(6`u4bmpJp{ zlz7Sdeec?Ep-hlJ`h1G>VotGE2hn+XjDIEKL_VZ%pz#c5;lBRv47r`b0I%#07?>YV zaIdEhm5%73Je;X7m?!k3<8Ah)w7bF?5)OqAagJo4afR*ka?O=@&LsuxKJ;Fm#I|Yg zm2q((@{690eE29ID#*Vt58{~cs27wd!>5FnVbMZc##Cg{K4@!<>O$rzcIGTL#*@(7 z2_1)MKJ0*0T;}6!?MD!9_=v#ji0*_yG2MtT2aS68&djE^N78YPfm{G8c4!%2H=@9@ z{#Zt6)&Ak^8atTh1NDfq3v}g2bmhmRIG+D-alyw|a6m`zvS(nNhS)@u#rdz}`3)Z? z(AiUm?s7n9?%{m?@k5<A&GDx_(y>L9p_z?*mj%8?mThsC1Jrr=tQ=M05>*QiSu%M&h=vRvJCr)vr z*Y`M*#twi(Iiv z9anC?SGTs`3SHjqJ8xX+YTxzZsC4cMA)#Y3GNeP~p$!n1BkI3_eRSv8op0(y>_6+2 zYh?5)>!-R^4(P_Nt@*G^*3!}`_j9pBtykd0%ApHfeB8y4v>o%2h{#MG2u)@GPO$&3 zkACFiSRF^^M}ANygt-nuH@ey(k{+}>lGqGmCcA&p8D(iwWh1By>X44-t~a3$+kJM* z8qdd$XbgSuiau}o@W(kHruz61x-VPzYyyz_XbfD)tEA{gKoc{zHJ?3jXBVh+xmWu1 zEZqh{Vu{}g#gi`DnFkAO5xl4nz{2^Le{+RnOgr23xaCuXO_B0*Pd`40fz!TD=&ot* zJ<4$$%;F!HALhV9Hi^Z)kmRi{N$`qYiY*eCHby*lOTWnX2wPumEK`5bnaGDd?ZDy3 z{<7W!6o z^r~E?k+(J_FM(+Qv*RQ7p^?b;+1v>u8pDjcN~l|EtHie7T9D=*M$@0Ns&Lbup=Vr_ zg;zB3#})bI4>jzoOBC9`Pc3DmZURdg9q-id zz}aWv?hSi)vBV=$I0o`DP8B7ji<5r^+rs%to7`z0PB8>3{`vaWk5eyuP5 zZM`_!4hALdT^_EoD^9L?;93qgNwd*bz0p3T32`OJQLfMf?`^4|ob+g(X5Yx|wd&E9 z1HNC z@%-f1f4&0!f~hU%a>;3jy*CH*Ur;z3U4M~t*rb<1y_g^><~q~4y4-l^wdIDLeSa5L z)$aYVx?RZqoO5MAv|jF)e{cD%b!X>elusS-KKql$Dj838$!p}Yc2I$Q-}9fxi}R>2 zLb=BH+rjv4$~9%d)A&ZbzZW_94Pom0sY7{#`-V8M*p@-_etz2j1VVj?i9x5j&Nc_( z0MP-21zR7mcrDL%|NX!I)d5fZ{pa!DfDW1*7VOX2$gHGQ`$`2!q2<~N;=Zb!dVeE# z?J&-McG#1Kw^z%>6FXBl5B9Hstu_VNpDApuL9aoNKK$;F4mkR(=iGzVdj%00vNaf) z&+p9ZnzH;KmUXj;)eS@TB&5}y#<2rfU`)7;ogkj~4e zdAP6-u=4)$ZC+^grD{*I#0iQ2YtMhT8}t9~^PA6Vztg8I!VU4j@bN(FI)4H zyDwc^=S$c+R!F~29^s4q`r@qPp{ViZL`0lva+=(5Oe&`$IS`_2NHz~oq}`Wr`z7!i zV*v?M-IW|@-$5P+!{Em#U-ml_s8uQNwuuF3?8c9UsxMYU^&alWT7K<1RjBl-G4~j1 zjLJbLV!1ZMt?8=EUB?a~xwXD3BONF1JzpS zQ)eEXB6y+*E;pq*n$^ZOq=aWWF6P}3NoF8L6LIJ@Z#kD#g#e#GV86-gd|j4WS#8j} zq31fCLLrBfTZR*-j?6lc>(B=6_xERoj@I~1MW@wePq1?i=m2WR>Y#H*XHFf8Y_B7! zj@LJwI{wCisBk4JK8-NFI_T64IS(GL?+;&X`b94N|)8*V6j`3uWOP zqX;KO$DMXGa?Ux|J;0#{Q>Vy63k`H!y~b(s>gpJ06P!`OD;%81EHP5P}dP@>Jip;D)swimGxI-S95{bHQEI^syb_>^57shu$X5E7wg zV8E6P?{w&dX*&COqNqIgMBIE319H?kL*WEU5XS6S*JH@YHL1!xCQ!dgeAM$cY z$|`#d650rPb9bxLv}4w8)ulXaTc?}CPaO)GpIJAcEn{)Wz-vdGn`ft5GVBa=)n08*dwm@ zI4rtXV1(d-m%tsx;a(eb_YU>RsqiPr>XeTMoFTijJK92*OSWS<&BtC!&r3&;4?HYq z>a*-8)kEjh&E0kO71w+yi9uz(RmVJu_$E&1oVxCs4e7Q1Wr1SoILxcmL&k88KNuq0 zL}et860EVrYYpQ2+z?C-4jf{ME(O`zN24#5wOqN?rB0XpSNIU_cxb3IqWcl|&N_3) zp*vp!(77L7LC4kA);*>VYtC?VMURfgiRey>JO{wP&`5XA*PW3kQ*`7`K5L~hEbcWi zdTHB~LnDSox%M8QZQ;qu8S}>dAiD7bjcuLiqOsbC25Y)Yjbf*R!^|k4Nm_;BrV2-tEU+Wr<%um*55eK0)=f-gz*V5wB06;o z{f`4W`k@2*H6LNi3F!H-L9L&auT#+DgwEbGdwy(ZIiQ0OBUgL#TFab+`QS;M(ETup zIxR`sTENk{TlMP%{eXxgJ|?3BdYsS)4**|n6^+e@?!@4sgb~F4b5Gime2~V05n?ZR zCU?*5M;u)5q(c37E*kPYbHkkEc`f#Y=lIxn^iF+?y(-oUojp?iKLKK*ErZX+aN!r= z;P+-pfyt+$e5rgW@c>-U!EnUkj8L|=3Z%ijvE<0NrmN~YFI@omFLXCHX(Z5D>1~qX zpZ3VgeAotN`++Y%w&&cB&4b_lTZm=aLTyYrl4Hl^sei?g(P3w*Tv~*mubIA1JH~sy zm{a?z(tYvT&VGGf%CnKrM=X{F?{M8)09fo=1}&cs3%fcTmr*Hb8bp)%Mbu;Jx{^t^ z-;KMjVRvg^UT&LC8CLxGe2T+BnuCj^80GZx=N`(7XO;T>%eh@^WcswZ`oco&{#M(g z9Hck4(C7Dc(o_db!pw>EC6?;DDOty4NryG<%r8V;C)Jiz&Dy@}x8qgcs{Jm`?KWN7 z`qTL@kJs@PuBPeBF@Fj}>sH6YZ;fxiY(Kx>&!_6Q;Cl{tUCF3(zi^C*qInSww@F0m z-fn1l&gEU!kW<}~EE~MTyRJ*;-L~Sc+9Rea_u&Vyvn2SPheGBy0oNi)1!YSc5@dy{ zV9IL%q>Wa6orSf38{onY&?!-sZ6A_bfXIzD>88qy;wrbqanxU-%DS?JhbEF(p|Et$ zxJg)HMy-7t_7yXOSt<+W;BJRAjOkE4AgeCVgyN?vb8u;9qC?{`KbM=|s@sP-Y-P*m zhRjEpG$?;=bKsZ3eMZ~4N#p5wuG>lj7a0=UWQS*4@NZ?$uq+9FA`~qohx)Jas<3$(BC=8omsX)Y;`({VGG?=ua^TFBe2K>M|_cyhC zOa3ez7;+2lNgm0!Os9^O>jsjG{Hs=1${E*tem)BZ4Q zYmknGAuM~tz<1?rZwl^H_pJ}1*yhJS`2IggxwXe|?DI36`KN^CS+9XzNS5D`kc(@H z!M#3JS&!5S7< zudvP=wxwdzT(brVv$cN}>??<@-=e>FJ$h*UdGd4aQ)KYaTwY{I!u0++lrkFkn$&vH zYfZP$+Vm7c-eBH1@Wz2R4tyURD6`l`a2=E8c3DGXd%0EN3Y3C_!l`~u?(fg?f9O`H zYj5df#9cAg^rhqKD{HxsAgy%0@Rj?p^D*z-;)IoE_CekXHWSjLvs)Y}aL~|+Sf@H2 zM(%Ku44S;~>Wj_QmsE)(zoQz{o_DWv!sk8htB#jtb^0LhOaEtaVjE6)I-Rg*DQCPv z8~MEC#$G25g6?U6izB)u@A258;)-_%Ry=V)rmCJqhm^B3Vi1nGL-Vlng{NDmDCi=>~o{_6%AFr}40`15><|G434^W$`aWv(zdE7o$btj=&BR#iKn77H{)2Vb3=Nw&oQq_8;~dwiOJ@vE(ADwE7w3=O z>l7g^B`?TIo%kXzk$TxP-BMbzLdSHbCi-BsRTs1y!{e&8qRYd6!rD2#Z|O7L2P#A4X>Mb?Y%w9{#~$ z>_SepjBRlK^cso$(0NZpZwGXpLgFy3jl?lBr>$~IuP2r3H0f#OmMxCxc8X5rt!2dV*q$yu(57j*m4&FX2nsvW}fQ@GDoulX;|W zm#!;2#=$Q{8$MnqF1--5baX^RuTO4-%a}N_+0$jD%k&HC}NAojILq9vcz>yI8 zu}e2se6-}GPC>5|aEE;vj_6MZ1!e{hTJElV;=f&$8&4&&6pn&T+?Mhza z6CS<->-rjpJE@M0vGpi-=^gxGysopi>M^=^r&DFyO(g4-&{^1BN?(-7feg12W-4o4 zhnf=5Nyu7S1vRb)`eZCVc~W?G!S~Cm?;2;#E#afrqy35oSa};cssrvpYR>4OoR9k; z57)IjKGdKSx|;63NvCuU4u`jVB!CYc9I*k34C9pM1XULFt4lXX-MTGjHW&KKx=oC}%#HBfn%4hQKx7y^J(S zPpJ;Lq?37h2l2P=o0&s?ghWU5BRp8VUA6E(HnK`8z3JT>)bm=#(to~ z50dBztX+H0oYQ1xyEt?&_EMb~b2?7w?70nM3XdFm_LdX+ zRru{Q&&>_?PFr`1?Zvj~B!1!?R`&kQ#M#FZ9K&I~{5I^iD>(NF_k8%}{#2POPuGWX zK-USq9no)n28Q<9*D#vcLmA>f2U&gCPxsfcWlMmTT{@(zYnMb9yv5JpfSO$#ELqf? z_Fj9u%=6d?C*#nG=nFOmJ=77sE?R)7_ZT4oxhc>5(bRs1TBSPqq^hp(6&F>e_bIF< z(o*#7wi^gHqUyWb2r#EN8vN`J_!V$c* zsr-c7ruI=c6W>X2+~HIWEeoU%OJv7*J1tN0pHbGahWW!#KJ81K^1(yDv>ZML*I<)K zIuzp0t^Erl-sW-oA7F!)wNc5r@>HgN^wI6?pMOiwDfdRnpdnID(IWm})`95&{i<8uBj*C=-mW>0F|Wz5E&o0+g(KH7`RK=EdHsC)zXZC(^p{^4)_ z@_;*^eG0mmlmYv+*wx5#k|}xD)WG-*fc$e3tufDNo=DrzYI#Pm%r@d2?P|YnI|?o5 z>as<5EX!Vz`T!XdgntW9=fEDnj(+%)oW6SYKmBjSfBtkl&xF*$yg$s>4!Qq7d++jO z$&TD-<~!@&ZWAeOh8t?eD4B5~nQ1fHa6j{JYsW1~GtGi#qBTuiX(7qj?5fIl&iDNs z9)WW*PF2>c@2$#2Rs;Zt!{Kl^9EellfnKxxt)yX}NcI)|JCMNE^Pz0-?(Lt^kx6&U z7Fov>_QYh$xw!_#P6;$rMRAt>zBVXj7O(jAn?M(Qdc5U?{+IvhuRop8cUa?}{^9Am z^ZVPs{mh<>!5)!BGF};T@=n!gUn$>qO!!iXR9cnzN_ff{)4(ZN*j+)xT_2nyNIR?8KCv?*Xz8IYZ5>o>$+ zCwCBkz|24W!+&_vt55lG(v!*`0AySyThg|meiL$>8KjKUh}&-X{|?#Hf)g7vtv$Vt zSdVL2OS-Q7SXMU^JIMlko%>7FqyPCo{``sX-rxV@6WAvKGG22H0b>%I?90K<^UNqW zGIY5*5QS(M`Nj#NZ~+tThQG$I5_pxss|0>%3A8cH%DnW{)pZB5lNo5K+O~74Iu({p z%`&WwCS<2ZmD;f-Q3c#>QFv-$v-6z6OQ#z6qEt9$#LaVp1t~SA_E__qepn1u%j!kf z*%{Oq<*DW_+u)!aZsmiHq z>RgE&Lr zkm1SR;-8-45P@^P^IKk0k2!s>A){|`z|@IOK}f+anIaj7|2QJzXm*2R5IQ8Dw#F0f zZ#Y%-mRKJAXaTKLT|31olw=i;c@0r`AH+LdO=Tp~W@HQfXqa+_AD!N-?;R@`d9}Z! z!S_$t9480ZcbxnPLEBe9+7Maf+08bb6Y7`F#yX>yW2SvK9f!lAfA)J0%BU}UTKy_6 z);X;_d);FF5tSvCNL_8r|zHuf_oMD~bvzs;MT;zb17 zF6@NsSk^V2#2J@3i<8a=92moLB1Y$!OX|B0&bNG^LZ>#J!{5EVIeN?WPDgQ1@zv&` z!;ZJ}S!XcE{qoKE(VL$xh`Hj>sgs+d<@$;9uyYf;bBo=1ixc`E{^kAAAAWyDj59ip z=sLRVH11mLxT#+x;J4`K^iw`Qk^V>*9nsmv4~X1w!l$})ivw_-u8sq!4tky#E5Fdy z2|aUOJ#JM}&!yuU9w+qDE%ob|4~*dP1`9apaXPZcXdf$2g;^5E+~CNCZMygJ#CPT5 z1QJ8K_CDj&HByrD*2p=o`2a3_DTn$X1hIRM`NB3Cd>7H?w1Z_wbS7-)RGhGJFnh$= zP}_5M?ng&zyvzB~e$=>Uen2C^SP@BAK4_~P(6L+Sl-C0~r9+uDlt;#djHjV19`#ZF zbt;VW{#c8uMB-e?M`;_1e+Vq|wcz0cTxIGyeaFV?(Z!H{pWyJ1hyBqNQr6G?8eP1M zQ|=wZ=Be#EG&=&U>y+KPzTxPoE;?4RZ;+6`OC2$BXqV^6B0ZBG<;oxRv+m0uD7Alp zC)Y}3Yd^~gSv>=eu07_Wee@m{si$3Ylw28mb%cSmW#6mM%|YeT+_*Jlj@<*u=M4Km zTH;=5j70q#^_xh|zoN3j76vVl21T}AYp_6Zm6I1Ky5{8`D$f4so^$Du{3+vijFX!C z}Z!=!bHE@PbHr=#={9K2--85xe8JE#Ck#O5P=EEsEM3#0#DW)gcJ zb*zZgxL^pGU;VAVYptYWd!kJ|OS~4lU}o-OrxWwb=dVc(zzR48wYM<2uYK=($?ISg+MF*H@j@ z^U)RU6U3w|vpSh;$4*_-vi7@oBaH$$?|g^diDutxUr{Uy?meIDnE}?)Pq7nhf-5Ez zSegV7IT|M|8>UAU@v|RiPGB#cLpkN$xswk$@G&FjEA!TMzU;Ow*E*x#)?9W!11wKVoMbLDXkd*GBhmS>u%%;Xt{^*0W98+WMp=+&auMU>m zPTCTeal~?A8??dLi+eu+=Njss(q}Gom$f;ZbZ#!X- zr#TtEQ;_24+`6yN!Tt5&tYac?J$Ac%4gswLUBxZK2oM}eDp% zdR0=F{9sY-rL;xrkIkrO6`G5Yt;oU<8uK?$(^52e@2jb$Qh|db?=y9Iw!W}|8@xle zx$YsHmj`!Vbl6Ha%(&ByZ@dMY22G3^{VSy%?{5|0SC+epJ@(bQrB5jvx|ccwc|_rG z2fpg*0!};Jrg5nDKSpcn|3XaGcH0v)uz4SNf2BM_&ZDyXSFu!A!2 zEa*8qK&fFmgh|YB3peVBvaM%UAGQa2)s%@jy*AL+CT#|d+fFFledrF#A`x*Lv(ugL z8kKJjY=6b*)LPoLN+_0h*;=xb3W*K8xti&2YNzruGmi7Y`UeXR5T6-4fN~K0xgGgf zy)WVEAhW+nf%-p9%Y}!Yf(zh_605v^Sts;K_g>-)KRt$?Ub5xj^81^=*`2o;H-u)) zRsR9fb6YsTl#%9vpqv?q`4K?{nqMMS^j=M?Vk8KWm$qtZdUh4F+&6l(RrpWZ5$9-z`JG3n1m>0vo#QPAk58V>~fkDGQTe4S*G{wx%&c$ts2eRWDSdXa{x@@W^*s&LPD;$Dv;r_y~m$;s6IjVf{1=5V?I^JqT1=kKV zBv+@)4!EY&IzwUVcic8X3Ik_Q+l^9b7xbF@h7y?NJ<>hU#@QQ~7-cj1bGQorr&f9n z;cttxWglLh_`a>ozh~-)Z0qJ4J$H1O8rsik;`Lsueto5eh?MtyGVZ!vEii5k-)U#e z;jOY6?*nO_|6b?C(UFdTTl=w3apvhETgJ-8wD(pN`Yh%;TI(qQ(>}8?KH1wlj^g-| znsDv3F`z0R@Zf>Ftjngs@zyXvfvIca*UiLEvH-95=Sbk<&+4@EI`#ZtKc~vGaC9_x z9crg?o##6JECR1cKcob{)HzJMayY~&?Ait^OqJ?~Z*2cT7*}D=`=Esz3k6Sh)`&fyK%a!`> zb0sQ%$|?;;LdrU0Y~61VUaB7Xe^LA4(?7JfPhsSzv0CS@shaIImLg4Ku1*b5jDhQs zp_Hf6YK-|&G=Pbo{NL4F_?mu|z^epaCGew2U@@CzFw4f)y&!W(3%)=`wqA}aXqnVD zs+5@I=Ih^2L*a$YpcUH-Vy)Ry_K(80)r}YXEgWRX>5{b%AncK zSa|$Upc90DI3r1zy+`Wuvb&BGx3@REFzvcgS2aG;!f;+Wr{?+bIkc$mWnG=IbU5*4 zd0(0)j#H@)B{)RsfTq*HF?gIEIQX^8cF4<4Rw{)fA7BdH0O6P*DrHaizt;DnzeP&K zDWu}a7hdD%_dsPo>Q^jk!+>=4rakhBmb$xj%zcee<5~@_>1L$I$@1!Z^}oyIKzZ{b zJRlz<^4qFlf^fSzqVzZ~O6MhfUXo7iOZmeI-8|#`$f>A0Z0Z1^1BMPtyJ9iu002M$ zNkl)K*TIpD&vl3+p#d#uMIs@G4WQxwd zzhHixUWXZ-{5**+C&PQHFSzwGIpk@Zd-~!%a#Wt_1x1N10p^WREml3#)!!;}kRElq z(ou>6Ke}rBao)7k{@_iU`heD4*1-WWFb+o-$dc2EPnesY;OI%9aiF2dzL)GBN1Y4@ zKgN``gHNR`H_Z7n#`F%k=)mJVuaj*{Hd+&vtMn6xi*y#^_90_gGS*S% z24}}}9BDjRRQ>YBdv&TD)zB5v{G-pxLFYUjIN-%BH+yNS=cGCFb#HmfgY~#zOgXFn zNk^9oCR~`#`3j;&%DkNi#2*MtAUFD1@{BI14;sRODu?zdr`ge{I#%<9bmmLzeD!1@ zo`j8YW$>$Ev8b8LM%G$#)0dUuqm_)f=JrHSlqX@d;TYIv=siLD6$2b0|KOb4lq2sL z)>xW~JRE!Vh$~K)%9ZiamgT}G!~tE$OvehjD0|04r_kGLoO<73AO3KQgZTB)zx@7v zu0L>c`P;YGIKbV+QOJ4fiM-kvPZ9p<=ZmACe{-4Z&Ci!Oqia8*3&%SC-f*J&osR4} zwL?pK|04Z=_EdD8#&6=>u5Ofr99;31s*dw-emXz8#KB!>_fwqGb@0|9`TFJ>Y|Jk6 zO*`rQ(wY6rlh$!;Hx9ozOX@tygp-}rp;`rGs4dmzI02c(Ab8p~&QdtDVq$QX!g*FH zF<0Y^ZntpILIQC>_ahm1cR2LXj=yz`sYRXfr>;`Pt3&B=mje|i^9(lOC64E;Cpr|~ z8c$B>e(=Ojxa9Q5EaYA1o;a&wF!KQd9Qd>`I_v2`U(V-@cg9@F861Dp!YdyqHk!zq z0WUhSvn(9lsV`iop8BrwglF~VH0^0=O4sb0^1k8Y9y$l+LkF=Fwn9JKuz_dOO2IL( zdHba9N;|}J$Urz68nD%f5&mmEJyy9W3Sz^W1n3c;w*{7@ilYh*7k?Z7oJnl8LK1KSvSls zXX?ZW23jX&!8;%%I(6eS7W!JjhaxF_(1P=%vRe2f5Itb zF^XmnLtz#?beuDUqDeaWDlc40hApb~0X=X+BL~z6U3ZOr#0mTXxE~DQgE+`%(LJiG z+jTiNq>8M7ORqW%kI+ir2goMkn3@l6VC&ciEw&Gs&X3w9^|Ik{JlB!hQ-Oygx+kA^ zc~0@Ab?1Z606Iykb9c=5tm!)c>R=uxbRB4Ka5m?{7Ud%tIu7S!7(I`Do}f=I$}UDY9zop{<6|Gvo#>r=-7pm88fn{>$INh-D=BeJw&b_dEm|B{Kjg>e4l)ut0hwU&@9LNHH;U?o@1%LyVf)CuBw~LIX zTBQrLnf57=q&yf`;fPXTApuUnx46Vtd{tT9t1as^*TRcNpa>;e)$L|nsyFssJNoD% zn_AZ3vcEPZuInrxlQ}u9)6-*@L>po0D@cyL~TF%=i7OldyJ* z_W0N-JCr#@>k$4f+@~dN46vxh*f!q7MZI4RK(qey!BjaT;lpowE>Wp?#LSD z*`-=s?#}onNHf`qK84j~VG6B}B$iHfpm{4y?p;-srCu`Q)(CSvmz(akJ6_AvD!1rl zqb}_fzLcL8T2Y&ouJmjI&7ZMXhbGvB=Ca0_) zL;O95-lyR#X5iDR9l+k|%ZkdQ`Y#_Y{*V9sgo%3j`emFrvvqoj&VyxirU8s$-Izoe zW{aXY8tJZ*^vEQlt}uSo36Awl@Gt6}*!;jFnUJ_MPloC{2ULHT&A5U{SCKvf?M1c0 z$}2E%@}J(u5uVkMr?MUSRj_*^8uS-v^5oKpV=_YE-Uh_68)3mQzu=9+6>XHs;{Z-H zLbq{^N4huTp*EKTSjwU!Vhn_(ZgbWZR}`^77PenN|FP}B-~ZiTJrPSg4=*-g5vK`d zJYvT)$%aXaw6HGg^s1<9ndnc#p!o#;eE?qS3#3~wA@96EsvgW?Pi1;4FR_W%on=j! zbR%UFA+%i)wi4J$&S0@VHM~UI@EjQ}HsV9b8JDsgE86~A(?em>^rZJBAlV`k)d#t8dcx}%aLtl`|3k$%E zM_I=i^1z2Kn6?phB=pUbSoY_Z0b6uJq!FfMgI?op3Ap!ge)wC(*`NN!Hqs|R`&9FG zIQMkjKIQxq$h@My5D9#J=P<0snEz#a6lB?uvNfF^c1GM>qmuS(z&^T1OqH(p@d10O zAEG<-G0L51xQ~l37|S!k5W9;*{RdJ51|hOF-nDMHmh^rj@Z@W+k#)vFm&9_d`TV>f ze1vt)8Gxy*aenRb_A}?p_#nd98yhu;!5`7`Tfy8wDc1^F@?D9dDY+uqwSVm>d{>)he#f;lNPvCdO* zGhl)j=ZhzpAtArjqM0;3^0Xg-{UoK6(m*aJt+YHaN*1XI?$aP);iIu2%y_m6yK_U#c#_${Xzz6masVd@p5}P-3+H( zbS&^VUjW190yl>^(Z%5sJt@a$oS-=|`y8jYGvaF zR!$ye+BsD);n970A?<4=y=OM@=qYxPwTbkNKIur}Nw=4mIt)<&F&&I_+RW*gI2lQ` z=7A@n!}s}3+Kgiz4kx!dc;HCs$#ii(R7SLtcAR_U>R#=dj)sP`w>30o>9=^@zX1W= z8dVEyJtOcUi6gpwjQlC5Upk*Z;sIICTFzCRoNyLD<8lLD*=L(q6k{yx*-(Y1^ed%y+(DBT)mt~F~ zS3LOCXZiqSnO_qP%|nOq1!5dlj8n;q<09&@t2z__hz^f92ouf1>v|Scp>+&3ziTXe zK2(W~8oKeyrw?;cA&vVk{cd9i<$KD|o&45{p48k@8YPc{aA%O}4_w3;XJH()ba*|* znYvFwk5e%4I1cN0%b(+`Q>2d0H=LyU{@raH!{2gBd7RKawVbpkqkEb(*sPWERF0gU zg8mbZ=Rf`Y1}F44NuTo(5BVr3Bjy7R;qU+X{n2~qNJ}U5Kj3`+2b|sC{^9QE3MWnH zto+ox#!U&QN6GX=(U*J>CH4sAfaml- z)@43P03Wo8y+t*Su5dtaC-f^IHxTet_Y<9Q{a}mE$2#;HbJd!FzQM42-#+nN=i;mP zd^F*mW9NquR4dwL-OowujN1cFqc(cNhE(TsFMH(J*y)mY#gU->uutjw((?l34lMuX zGApb>S09GR64#bQS^{iUgtn)yeY>+7ZPzQ8i z#V!DQz!Cgb2XyvNI>kmdp$iT4+5V_rUbPNUNpkM5Ti>Hk@~SQ&>wGjq$8%a?OmjLn zd!P$!(K&HlWwUfdFNbvSbU2Um`4r7Kx613M9CY{po<)}g#kI=?2yw&6Hir<~Vw zL4BkEePG_+dGfgq>pBw0u^81rFCOs3&-`)UAH68uu#==ByO<)nU2~Uys;}&Ka*8ng z`tgrT9nj;5juSfb@`3gLo{H)ugzJ)Cg0jMCm;H|Q((f@pMb-Y*Da57Aw>SX1AG%|2 zWtld`J(13$ckYeeXj?Ci?tZ#tE;!2=2gbx8k9Ny>@37YTZW%a^DFf^ndrP{iQR*N| zVGi(ZwyKlRPRsx@OPTO558*>z5-RrI%_g0U+{`Qs90iHtJO#;|C|S*KvGT_raqJxw>DrCy$c!89TJW z({A`Dr)pU7D^|&k#Iu6(=&+^+1i%gq+@Mc&x~{q)c#bWT_ua6cw+$N_y#U{RU;TD6 zYEmV1KvOO5k=2PG+Zn%;*k#Kfu%RdUCj z*oRP8sG^VEtBKN=r?^e4rUYp;&7};fPq&w!2bH_hJ z#io^01vPX2v%Y^dK*rrh;fGo^1J)=@N4lJvAwJXSbm4dp2C&;Q`F%iF zO8mjX9n3#wrpNSKwJH7C&du!|O7HVx{Ta8*SGpNcz#WV6wdFn2ZVtX?48O5G`+P?e zM?=j+#UIwUEfJI)qyIb2j5c;)dwn}pb_mW`gv-pm=6j|&U^N-5fOO{UvhSJkNzb(C z{AwjAJH*E(e6*c1(~UI4tL^Amz@x1DWig=H7A5VVq#uLqf>ZQzx4Au>j~I`svPpRf zkpXYsMmFW0t~eOIw(S;8xt4>VeVUC1Jc_qbo-qM$vz@{pUH<_6ZpYZR<~-@S+j~V% zvvcJ+EiZHaY0VYV4IaYZkV7gT!f(nSD{s6niJSK?q4CGA`*FGaDxA=_Uif)QFJvEn z!FUYdG>(}f$vdHY17#ZJ;wjDzl@A^D(hg~UfXnG4!r8>99ORw-Sn3YtG(CkF?HM+w zC%o@2X+IWwL#7RF;G;0G6;X8UwQHvWf8@Mh zmoWM1FOaGmj`z?PvjG9aq?PR1P%+c*7+(= z+9rnWUh>`SV$*xvX1a5_D}0~vmVJ0h`_1oj)P7m6x7O%SdpW=DC>A&CGxLdm&Ov8e zZ@~nZ_dl!(kBn1~I>94Zc@qTdTOiIChAoGympCuBJ~;5Dr=ZUcfEicDs^cEdJVrRq zj(wih)BzB^T#tRs@B$F{NC&QVv|c3*z7<<95_hJG#CQ!KmVh5DalP=w^Dhar)49(y zFL7RdiTqxA-L1X-r`V(&`AudZi^`mz5d&|#LP6s-Jh%gBr2XkIB^&hIcxxSc87CP( z{rHI=qx-adMoW2P05V3SoOITZdEM~01J<4VhLD)F>yG1U?0SaC7fK*4BcRv^l93uWM~nUOu2uN?n~q^@X_iv}azhHpC%HM+zJ#bj;&*=3MqI zCjs&zzRnouo+@~JQ73irmbxb)o}c5`!3+ETf2eHwa=uRJH#fXYjRQLGD?7VqN;6K@XnWLgp}IOzq2KAFvUB*82A26br0BaI^R{yZ<@ zm*bC4=)OF!!^kNPil>~mssq*W$qhAydxBdza_XE>Kh8Una#XV19WzT-)-1Ds=_Ac2 z7^~O7O{!KpqdepkULo*?5}uyk#?i_XDs`5MlNNfL6EE8loqniaI=h_X?4fg&&XqaU z5(j|mn{zH50B~S|hVv$}MOKlHa;|^Ci|oyIY6cUnw^o`EPZOy-^;xnJX9S z-S9HTgu`)O? zW6iWRH779h$1w`r8~i5I)J80BgFvbITRlvjF0=j=I)~Usu&vRaoQG&pZu*guN;7C# zx7uqOUmh1b5|&owzMSedh4j&;v_ZDUEth(yeNhh6UnD*7)x|jd;NVCCJHY2OhO=Qg z1T&9V3tK+wyLzhw`V|i7o>ul&H0o@PJICm9(eXPd)0R0aiIG5hx#LCAzl&=nP+M}G@sd}smJkkOQ;XxbVr9H55 zo##kvA7aOJV)d#$$E}X6aZoJ#aRQC-6+@ZEAYAlw=VrW43HoS<$5pmLwt7BVwiH7f z-q=F9mg(5nA3;zD?WcUUZI*)lw#@Lr+Nhr8(rH$wLY*L0N0?GA!$ue!?FxvzGLu86 zZiBz6pH?fVHdBK9)22pt?)5yywLS*(NQ3tX>OIbk?>I&G3divq`oypuJ)%#2D)uEO zI=|r~3Vul8^37$O(a+A#(=T;Lr?~5DKBVx@@ux1rxrNBdm0UU&I&G@&uAlV5?dOAy z$SWZ?QAwC~{ERBuDdG-Yfqbe=`9?`Bg$kw`Yp8S=?ZiFrcYtPQ0)vvc&X1}i^=_eS zcfR9-+q?7--3kBDiH?wY#BQ9hhpCSeFop`$iy#YikvCzmZ0?Y#g-;fQ@rihvHm1pL;^{ozExq-=pA}U-mEh8w;;48-PS+7*P-Wg+y>H3A4_qXRa=zl>!D84XD+htU=j zBPfSqD3pW3GD?uhbHk_LF3hAqOLL>C9ZEZyEnWt~Vt~mn1kr)4Db6jxt~<>R( zxmKi(`|xY_81Two-I~RpPUt>!t8>l;N{};@=o@gJ|1)=(3h5d(yIoZ&JPkgY8-Y=? zr4)oeQpg-gyPwlw1eNhfFVi_-$OCBrx5EOfr`=5JoX+U3l_>`;r=Yw4aXohs`YMbj zRbZuB1J%?icTTI^s9Wr^`vC3hIpf7xf!}#Qh-C10l~X<}5SvT8hU~>$&w17^{gi(g zv`PDBJN(%_O=*gVtE(c7{!&D8gicw(VBU1Yg?)?A?R*Q`+cYzF+eM!S-Dh2a3(I6t zc6DWJNO!zSU-aF-h9n;Lu`C0SouPoX|9}WWUv?{r>Og_r=h44(Vc-uk{?;zJ&Jp)R^z;##(DXRW_jO@0Wh-JenMvTRc~5 z-6#7q>tVrz@61^kZ+vTS*AZUwm}O+WEqv)1=C;j?-(fBra2m+iCF^YuKQ^FXJ%7_i zKr>%c8%v#{XWv~f)|p!HT{<=mJS()kp<=!XM3jd5Vfn;N>2JU8cl+n$C+AxfuGx0P_v zs0S}e2?$2jnDjP)*W0&|K#M)&F`9_%2Y|>?Sw=?ijJu=whe&TkFhA zun&JM`TmIQY^~9=ZT(=MmpKKN)5`q*oOBHb9&@@g-$v!O#o{%MLye|N*E@vt6#hCd zj*kBR@BZqEV07ep?pT*I&X9dmYMa?$;#xLYM|qB`aR+F*m(O-S!zBiH!Li)3!s~rq zEOqR(>vwX6AT2_{Mn=GtH2WHMC7{!}*PH+I|Jf~gX71JD{F#D!MSHdcw)XbVb}-zZ z@5}l@!CYoKSHq1#7S@R7O$*cJ7)DeX^TP)1!xx0ZiLDO=y@U_ZDUmPQW6UDml6#K{ zL&N(972HdJTcgFj$Fl}^9oL;Pr)uhO>vqgbr1K=#--N2ExokjQqhFx}wm!CW_xQiR zKBuL0+jK2kbaWR*75z}vm%4{_+_u(;FLfYZWWCI76G9h2P5x<*T8`S@)GrEd;R&x9)hnI_qgtqF!Vw~i#!D! zJv6szCma7RE1KS8+AG_I6$9G^S1$KaQ2*CnFCnLreX`7(P9~lxnHSdBB+?mPN#~-B za@k4SI;819F;0SuLnzKe z3>|OZ$}RXu_%Co&ee}{Hud!o)k+xogQzPCpS20UQz#& zk1O2Xa+>^yVNWFOSJ6BogGqYfkkXpm1K!Bq_Fr0#6`T-|c1QZjEa> za7s!zlIEt&vO#`DJP-RHys`Ba1M(T4USTtIO?g&o>ZEXY2!>Y97$=` zHmrxA;n9&ND@zw=DV=HKlml2f>$qB{N6NDfIm*RTk+BVSmpDBj6Q{cBWaLwhr8@Ei zk+E|8sN&cmbIVH$*&st;#BKKyH*77Yfq>*k_j#9OR#Q%2(OTW(V+NV$hE4HZCJHY zHlVm$?gO`KO0`NV?zHeP3sM@R_AEkAT3$M}T|GTbUutfs->5&jNV~4TUW{i>Ae_fl zv|Q*XiAUDEI3c08I-zSL)aN(~Qr=ERj^(Y+l{b(4j)0Rb*SlOgpX)46jI-r?oY4KS z0|xWxoH5jX=rrVs=a)a}z>5R=&%`+MUb1fbafW;p1KPJYb$awWY2{S)w{b#$%gO5R z-r>~AiRR9$6YQ*f>ZEju&3f~u9nj0ER0l|h^YI88zE?hcpp0yXOR0y6v`JkV8ex$YKqy1o3x{C(U_09CGuHzhDs^=ltI6Nyb592mm@b_+ryUt&?Ju80*%uAQs6?DXKT$xe+~r*U>`lG|?pki5)) z!D)JlZ3K+StS~EYp?$8tCN?ct8K-k#*g;@Un*`$8tDzT+Yn_6w^ZCX2Fa|aW8>KV4 z_A1WiLm#7;v5o3!>1xeo_Z?@<9Y5AXTj-Du<@r#@d+Zi=?Cl@k#V+cEUJmHssDH~v zJ?6KL=X^OR{Ht}^h)5t~XkSMk?N6?fPhr_Z=bboMbmosbSWf7k^3L;%HkZrw0n_fAiRkqIgq8FRZoH@rK^z!lz_LUDVP~Q)3DPx_=Z;r1az@Cb}9v|t9ULSSPhUBoaNj;C(Lb@ISz2J{SzE*r zUZkKaj-ddyWBWdLxW{mQsPhkxeoV>_#JRq@->@Eh!lTaUhV0#?&pO}-Q|{m)9|1#u zT`S5teh8gN9;#LE!t0W*QmQhN9|>D_bgpvHUeXR-rCD!>!(2c1SN?XYC{cnFm(<8r z=X?&!h?l(l=u7&S`rN4E2$}71&quZxL+5wz4bw;Iry-E3P;wH)UzgDr`OV*uxmC(m z`7u9#V?ny?>l^@eFEBoaq1}!XI*YPvyrG_-bU?4?=@UQjH-)sdwCC?aGA0j+z@*=# zTg$B4U5K25Sba&p3#T@|PEx`!@+%0@A^W`-GKPDw&UPJT^XG4W6DUz_HD>yu9@j2`c$>c z3pN+nHhfmpXx}`ZZN;~0Y`={|dUFWge9h(3T=R@>1lMuN{AFTq`BdD)!N56adO$&Pjoz=PXu!LN1Acy9?aQeH&*aJB#k{*njZ`W-sm z@qjy)Yp{)xpTUg~*hWECgz8?SCeDIA@v&cjBDLtpK8}PYydC<7GBmk$+TcrY4%~Wq zG)_-jGHlp1>=CJOE;qdv)m$%F#$t?j?ZQA5S0w(C8i=N+kh0t+;IBWq^#bXSzMgEo zKQu&NCjuhHj9;QiY_c~bSm<2nZJ zz_bprjLn4bsblr2_)mK9%K={Ue)E@;`uE-P)*5|q4(`v5WtvgYWyZ_ZaAT0ORu9;c zSAG&Aw4JuTAvl%%lKbntI6C_8|Ls3M5#hhwZF~^y2^N5i#~73z8-UAm8z@;jt;-lL z&Vc@Q@Bc670^2g`&sS($&RA!ywvK5FY)kysv(^v5)i#L^;=YC_5>WRq{_H>J8Dwi7 zJgKk;=+n(R-Yb61zDnS;C9t)(_k`?E4$Is(G1d~}wik;5k+N_i%M451$}ZLS;lXc1 zq)i%hMzxPcTTbXN;X`!F<$wNg&v7Cxo6o>3V>7Kmy`Qjbx%SqZX`QJxXp~dI zkuAEkVxjAhVSf*jwsWiU@ASU47X19%fBsH;^NrbFr+qc_vEh$0?eP@YDEL_YscQZs z8|~a0uOI!lAH}fsA=}SdBPN-y_G17Y5A31lz^epaCGa97fZ5BlmL`%AvsnxCu!k7MHlI~kh%uH`DeG>atk(f) zNIOj|U2b_!#>@P^%*^}hd8wUutHY~~6gsi_zH>RdUDRo(0DMW>6VaDFWt^yV+Ti7M z2#{-Xxj;xCeAzkoNL-^sIw7@4bu6%vgD{BB@ELdD7}>UAuX+K$-d)}8OSvnXj%_7l zF>pHnq1H1OM6ix>Pi)>%sctE=8mkhAV-oKjobznYuB#u8bsQ~qK4**_&l4Ok@&6;% zafk6Gt>e)h*9}e~I*I5Xk<)E$k$TcJ*s2_K-gGv{DN_z6N*-Oq`Z|(?XXwPq2`4*T zfPljvdf85!H*d-Tu-KKhku;dnJ|{= zxag6!B`I4|f8k0nk^DhghWRtl*I{>z%7MJf)^}g3?@HJ|@g0+RDA|&ZS`hOEQ|D_O z4@tBGVjRYic{`+gQfYK2%y=p%&Qmz@=)~g*lGiv+>4@6Sq}3D-trw0yK!?r!Ji{a?1VvDNeOIq00|;N+F1fOdoKN(bLDVi2hNe6R3`g+OoV3 zq#i^s)N$#G#J>_=Xh|>QEu)6yjX_KpavSoKgdwbiUZg77Tq#>{ZBzJ&O(!oFO$@y= zcI+NTo`OPXd@56xENIEO&1Y+LPk;?(E=5TQ%@uolI(=^k;BEK{ufAn{>Iu*hI zWeHuFxHC&BC@XAfs%n1zT|zUlwTVxo`3dzL3r_$&*6ENpHobHx_tf7wq|k0LN`*PI~!4rlYea{9SW>74j`h12CVPSHAl zNtRhhecFJgrwhly69;r-og)391DbGj%K{h2Mo*FE^k5y?bwq!MbGr`i@1T2i<%clr z2kR`v)jjRm1&-`*JjM8@azH=3piO0gZuk+Ic7lu^dit^@Df>`%%v3 z^c&`5#)nS%-eN z9_r-lc>3WJomB79#o{q7hyOG?=6vx3kpy`gYmjTNPOZ^>>^1A}Ib(4aT}E&0hhqXi z>I5uhJnvxa8mG#)I-y^`#|a(2eft@`J%`rCPnVIwxjKgx*yz9O6 zbb{nZtjO_}F>-A=zoLCUPIGpSb}&a>vy@MUTL!|AOpQL5Lp% zta;^I_@<;=fXY=_$fbaB1C;+aPQ@Tkv~8bq+FJN1lmQ=^k4Cn_HTsjB>4m!{FvHX* z{*u9U4F$ewVj&?|Uo_xEwY#Kz?0Xd7~`!{vTXr))oNp&h&OBOQLA14nf2qYlFEIh>n$_t!;Tak%hv zDP#9i>Bi_wP*PyO3?P9Xow4mmq~VUgHlJcH?cnNcZ-48GQG!`qN!hE|kHSwK`-hEn zk8^&0$;Y>l$AwPl7qQp2e|O9Y)7T_Gc5!lqGdemP$K;%_jbrltZRWMk=XT1jIRD}x ztn8T&%C!2<*gi1Pz=M0bB^=vGKVoTPPoe>pw-{Nce!86*9eLRI=qH$c0&fJsI`Wzv#DXqPF2Y`vZ08 z9H;Xqr6SwJN5NN#x|Od&?2CywX3Fb1p>dUaNqemonIW!%yN+wmG~O*svXqzlDA&C! z<1=8xe%em`WAx&HbCxw$b_h_)zDDNG6BiAE)>{j76iO4(1jJX@Oo1>$u?* zE%U`RnjCxj6kc>%)^j_DX?2~YjWl2Lc9o0sr`qc!z&Vw$%q=h$)}6hh*ogVtA^Sc` zGaqL-J4|b>aO*%NRVQj)C+#=9+W`3xJhWDP$!1?7V`Q4+wi9W#C$40Jlv=^JTJcti z;3v7H%+C%cI6M>_Zv8j zX*0I8%;i*l+V)D}LuHLM<2;v_xsG(qTX!FB*>h=%`x-t~0{#s1i-xV2PJLEZXcf4e;<&W~x1)YRav(M%cXJnVH0FweOiGo47L-oWo=W?0RYmK=yPH)!@p z3-&Prv14Rd%ruZ$h`aw<wSdjr_(mQb>!?yJ3M_*62Um*R*!y}RLR1XM49WX%A zg{%>j`@@w*mA8WBM2o(VF;7@+R}(Av?tLKd0$+1qTLRA#vt#F28AgTVg$o+6l<(g@ zRJM=%>&kEKH|6k0w-3LrQTV>N*;=EY<>0YQ6YXfvHEn3w)!5s4?#=VA>vlCo=A3MJ zi!k$4q>Zx1c}Dy?FOK*R3ymJ{;JY%DNLvCbEc&g{UxBX@_!<(} z-rN7~*`((sq$>p$vrx97^21KF9WlEimW>)VsGki&5B&ZeDZe=kGTqjv#M{qqzyA~x z-zDzW9>b1o{tocb9!>+$V`v<3mF?(6UXrq$viyEXdWlE9)TdTn)M%LbT9-;uK;qxJ z?to{o?rV6Jz^6)JM@c_k`6cgRx9rCkI}q=S+|8$JgMo_L1% zmOYA0a``f!FQ(t)h;B1yXF83@DKA2w-tv<49jBV!9bM?ucBzw<&PO^l;h;WVvenVU zy2m;L>J;dquQm%J;;>`;L74SB-YxH?b*(Di79HyiK9;y?9m4POFOTh8zZBW=c7Nx# z_V}`sbU1zOt^~!KWhsvA)tCG^7=&`7)5#5;xVXn*hcRQUh3lB%8G^i1e{{)s=}gG5 z;ur!2s{8W1emahQE(&#yvmeUew&Y2@R+qzHaYP#B;$P`ym<>O<$u9)sh)B^1dU}JC z-1!+!p4Vq_T+u1?`~qF(()mwbZNN5lyfS!-puE_ZIFjO+_0#PIJll8p0iIKn&v1Uh zdGPic=WZR_b%3;w^s9BI#424c>YCzK5a&X{EldBIC8Wq!zpXEZucVfPQt5i?Q+IiZ z728mA1pbw+CydfHr)A_1U&=qHjgM0$b<)u>N5{@MNYI8(yna03?won;nAkQlD#uq& z#np2wveKEOJ@(C0Ar~if=k= zGq6sF>IGv3Dkp}HM|v^5k=kDQl%-l~dV~MSnl_Df{AdRw+0p5#PP(^!+o{pg2_P4J zI>GjM0%07F9GlQl2XqwD>COwMG@UPTHo3tm)03Mmi(H{w9}!TV$_jeRReG^A$3+S= z#>GtEBkP`jIun!U|op^Ttuh8Sp7-o7xOr(6O989W%9iCoBwhPf9kW zlh9x;o_M-%9KOrxTc@mi39K>`ah=a~h|>vL-F22q)46i4ljk^rw_-l%fa7@6hN=dw8K zp({EA-$M7s)538Cy}!C ze#>t2Y&^zLK1`~QM1h<3pJmo5cnYVRl|peBRuzF&S?Dhq+8V1BRq3WJ%Li=9+p5TO z04M7V)uo`?6b?Dk)~)&@rM_9;8+I+R>0E{594uKmhUzK;6g3xmKt1h1Vr)izD9C*b zW8((_#+c}E{s#N>hK~%iBl<-?l7ZtRxY9_wME;0b{1elsd*-Tue8d9#bbEEgM>^1> zci6DEI0kFKh;>HSNm$#aO>%zv`=`2k?B0E7W@3a>e<59cqUmbc{!urV9Tgg(SP7jzEbJ{S=pKO?BlJe*LJS1wXRT!F%TiT>Z|f!#4&VU)Qk+W_xFpU)3IDEHGd`$^Va*LjOvuK{HobU1gw?u3?jQoX{Qfurv7S z@dK7QH)6r&`}w2}oyj|PN~BMqwwbFbz(4PzCfkqjOk(>Q=gu*v4AsYr|hB)jaISU%S1gzt6SsZmV(3 zH>}gK*8|?LeOl^9mG#=Qi_tW9q1(CUWvlcU5$YoS{s`?>mYE;3z4R} zOn_zn6IN?^AqIKi?&3Q~K18ebIi;U7ty(V{4(V>YnEH>VBwfkIZZKlO*uVJE8x{Up!&>C7gcT zo!)$T?Cy76yvAkKV-U7q{sL74s-ZDjVw1xFjbe+k;LQ1fVogI9`_QR;(H!X?EwZ1B z5hUXR$Y!Y)Bs<%JXl23~NB{sp07*naR0GPwJU)DI^LA<3$FX1GttrtjlUP9g< z(@AAobuzpQ9tqRxQQMWI#Zt#C97@&j#uvq4zQdt(mwzPQa!%0^>@_@70*m}fENSY_ zX-UobIh`^AW6VwU`e)g95?-o(_)doBSIOq~8vVP2ysk534<++yOe&+;P-ANV5SGfd z&d1RY9Dl@kuG8P=&dJw#adhPA-~Rjm ziqrZ3_xjP2=L+o=?~5g{wYPu1P1e)8Ml&Ukj^TDNM?vBEOG z(i-}*>|dtlliZ=XU-!ZEK!T8i44eX%({dtFi;*X|&WC zMa%wXMLpa2zl4`yJCwk-{rHSHG*#P``%|w1aqmnO`-RT_Reg$M-IP=BzlVeWb!rAx=huc$}(N9lraPr_J zK#+WS{(?8^^I|@@cRKBHBH;Zarv@J53BV~`Cpfd8f`=~%?3eS^BhE?>_q=A~x;ECM zb}ZAGH%?h`FvIC3js-Xn_yRX{d>KC-LKbnf;>G*CRBjJPkxhYGohIraJbR}Akl2tx zUaU(d0N~2LE>XLVD9if~A!b>KjoVIUp+94oE}dMVO~2zV>HNyy%8NK%z8_Q4B$l=s zXDJxha@mA;3m>j?d@78nGEYx`^-2%l}UNYX<8(B>q zcBX-V>fPfMco#=>9XxT8^CVt)^2A>CMTepr90sqhbWDdgot12q`Z^)|F$UYe#F6gt z@shg7^asaEPRD$A$qC5s&f{ctg|lc*V&s%X`-S=!Uofd26|>$UmYf|RZ_yHj>4>MS zxx$9}HxSwmr0*q)+232nMOQ!Pla4XY$r~MUD1X2SOb4)>R_><)Xh??` zogp&*w38Q2%Q(K2mL&w2Z_t(5L*_h;2of z7RZ}AQgozQj;zQA`KUB@TSsvnmKMiQV9G&R+9yxX)R9d+$_wPw)Oo3#oz!hVdT@ix z&@p9!$Zl{(yL#`bu%158+<~T!Y3H1Tc%ObK8}(EAb=l$NrQh&PC4y^0`k!>V$gRqP zU_J58aI?Q4aH)R_K;S40LZM_}in{kt52IE{79UL`GVJ2fWSEpCk(BYc%+^>;`ZZ&Wo}i4IJsCRcuk-AVd919&v9CH?$?pl{rJh9MB)w zvuMX2S>Kd=<5e6j4d34EeTVR;9i?A7q3d+-7?xbMyMC+%ogE)K(LUD41UUUvI}JVO zy?TAi7~NtAV+*B8Gujauf1ppYUyhF5e$_wC!iz5Igo~Wh=hW(vDtr3~FPJNIgp;eW zRadt-3}c(FZ?2DSlnLu(#xc&E$OmUm{6S$td2%Ud$8{_m`EU_y%wxX9;C>PNhz{t4 z{u7SQcfWl@Wu5L=XZ>gk*B$%Gdv%eIx8$QQG6Ze==!c3N!;F=^P}bwB&VxqRm3am@ z)vB(2mW*)?PsNV2&}GP^2PCisSmXj8x8eb;QMGDmlO*Ms7>ZIQNrP-_Szk~}_Y&b1 zyy}OT#gBJ+tG{Z;jZfAZ*8=88rarbzy?pQlWG3+49qR~lU7eSvy5s&tyXV-cch-w8 z2@U`e#>>^zDQfDo-;VvvqN8}+*A03StVnW^f66yCk;SYZl)as z*EwR7kv|Eyb8#|{-Jw|TKb&lx!;x7&dw_gsD{D9HU7ld$Pv68b-~HMnA5l2wWb(69 zZ0egg`Cy1Lb)3sqV@FxLudn?0h#w*0BGy(r*PZj(7b`ni~diy;m>>4Y`011GPu6VxWMa z311=^3$4&NQ$9(Nbh{lvnVZW#Lt5lWmp+>wm{hBIS?$DDK*Ce+Z8uK*&WG3!Mo<}% z4xR%Jj+yKWd4|+s+O@)GK;`86;D-(TSc~@c9eSq|`n4Zh@k3Bter$=UlFRlSKc6Yv zL0wu|f9#LCLq9xC+VXoos&>!*`95RC8lrx}pOO1DA834rkmnBeEB2p+Iv`o+pH9MJ zsS@|tzI&g8`QXPLA3V@z-r-2@Glsn8BV5WsVv)1=vLjTFT*`?VjRsFA^aq^FA8|mh zdB9kx_qNUcARkYl&EsRAU1(4H&Hh%|++h#g2Z>9I`Qt|3TSFXsXaa!gPvIkqUmdbyJ!mc~;#h9!(yBPKrNCAt8sK})FBJKEE5;>Vz z!-CVGkScnyp+4)N?Alc2V_SSPfN^ka)NgI1AFM!+d7kz+7Uz_@rcPyz^@A189on!g zbDuSzM~I^7IH7m>vz*J&b2B=M?np!Z_v2$;nP09g?lnevjbU1XPOT>;!=@R^q2IR> zmogHETu|Yr;~-Z3l9x5s(0mHn*P;FVT5C5o)7ULphL-KJLTVImWC(_sr3nw~kx&S2`tAjgPhh+@*bakiRkeTGxb)`tlEY>ZprDqJqJo zA1zkvBvXLGvtrdxKGcxl0AzEE#-2Llu?vI5SyXWsX>$rnUSm`TAVWp$SCGSepINpw}U|m~&a9)^O95 zxn?9e!AUyDM!|?juJOUBzs6eUq$tZghF0cS&_<1r7Jrlsd^xCA2d%(YS}B{`FFP2jv>yPV)&jV1;Zvg|}dP&Z5Pao64C%3gvRn+-8 zwxsh-z%~pOjZ7eRfP6YK!&A6mthp&=|EDS~ik7;VV_K$Ca3}nUQj+2-yB^_2UFmiW zA&hAXQfV}44X&A~F>2O4Jm0I;)D>|w-0cghAf&jdyQr&PG*-o8G=5Xsg6-Do+|27v zcN*a%am#(T?##zW2z+MoYzx;uw6yk#Hb49D&bR!`AI6d{tTQ%AU|97PyIJ)s%@5_Q@^zCDE9> z@kfe9UJTKUoAeBr$Q}Ss7#~f()R)h<91K?H;RAx5$HMf`on=!ZEUHrVm-$b&?LY=Z z9e<*yHB`}os$PKa`>>8 z_d{$J1zTnNxUc!ICjp11W!`a9H@^?FEO2g){TSM=@}tFDjwLU_KKy9o_{Z$e)*8KO zTep~UZaJ^Uyjy>_=zY1Bs?fE#)@t!bg+wF0qMGVzmHxu}>%2HR`lo;R4^O1{``c$a z@=Qat5OEC@&XsXr;xWF>$4$D>S$;Q&PBgv{9^PnGDW55}@&?q}y6@3|(SKiAJozxk8>J;`*@8%$8l;2vQYa=H1@4=M;I^9FE7D!0_HJJ-X}P0_+f%G zKTLqrgHCdufSwb7k=q$gHojP^gPacN7ZhIQR9>8va2(OG$9bebn=V6fNONzj{L%$) zjsbc?(!TgRu(Ak?`Te)_*E*>}neU!F>UaWwx75AHIjeXo?+ahJkMqxaPim#iQ$q0; zQIB=zvaPhw0P9$Ka_T9`oR*x^LvdEck?Y-?Yn)53kKUf+5W}hHI^@f%KSt|Y;#_rp z)$anvzzp^wnE{Yr>ga5s^GzICq@yEV=ym^6wR^0I+bn-0~=PX{+y_ag;5s|nY6>H-I@ z<6|5uFLkui;jkQ}jv4pIafL2r>8FlGI0xl)b_PRdi2g8vHb?nsiph$&*(|^&5t_swbYU6BS`!t|jv}2Bys%KiKIUVDD@iruBsGa6YKad5Y(6PQq2G%i?_=5TU6VBW}4~K3~c((nVXzeNJ@9yGs?g{8`aYAp$H}%zF(b-J0ekC*Ub4=`@d(|Aj{4?FueUm*d*u{uKO}*YLECzY zMcs4Gl|w0~8S9A7rGvBOw>XCCRHP0%(CW8*hPF<6p0bVl9pfB>`k+7MV3hRGWxxd6 zb3{z>H^yM7Vc?}m>Z#);t~#t7&`b4NQxQ1)$6-#}dJ&uN>F>VCK86~;aWHi5-0+c- zYfjpBOz&Jz-~$>qpZy65)77>Ol#R8l+EO1~D=3>Zvh}Ls!G7$Xe7o{^tq$mMLia&i z9ED@YjnmDc^LD#PaW5IUt~jte)fHWET*@pd!9@=lxkpax+H){Hpe#m^*7srDhYv+)8UF_G; zKc$c%!}jTXey_gxVGtZNdp{Q4#fe-Ra*_`STwjOI15TE#i?w zvOCA|oqgo>BXzO{xTmY}wQ=`@$9xkGhFULg`=62LJ&V8%4SC94_}uapq2S%55|k0MVhB^@zSe zIk4!cbJ2Z>aa7q-_P;oxyRY`rfmO1u8E(m4hwEcHuG0{wu5(ZaZP)794E8NLJYVRD zPV9ar4(Zs>bFMS(2z9eYOCSEVSGRnqAP$Sb+)w1g4>(ajuz$LJf1K0L|Db*R1AC#j z!vP)VbDe^7p+D_}9>;UX$>#uL=5S<<;1!8|s(Ym(u8ckC%ENg`{FvvE>Yw(;KKfCT zV;qVfnY(@j3(k?57jq0jDv#srS(uCLQ{ONrAMT)qt?_bibk6#rW0*YE$=Fa~p6GP` z?(I81Nb-*OeXZv>p(`8rZQ&EGO*`;Luf{w>2J8)QY#f+0^PvFN13%((ae15Zhz-xU zf!1TL4I=H@;kt&I7&@UIF-H)g>kcDba8f#=(=xGgtv2ViG%{Y596Huf=DC?j(0RG- zM*3Drr5i}r(aN8Fb$kpC*_d;At^%BC3QXIWJzwU$dq4MIb*URwSA6G;PO*$%_K7|V z);gJ!&~pm9I`C=rYNDX7Pg_kFk9=%#gV!P+pmU*jvUmj`TR7pWN07=EvyW z3H+&NkLsxAOyagYWUYK_TxWKw11?eKIJEkC%X#bv6^^mHdOK&`cR9gs zMI;X^P76WI_~oQxbX|FEE}WRY0?eu{7$1? zWr=|8$_*E;%pA1Jyc~D5X(-Ct*Lk&XO1aYSBp0wU-QDY$_F$jqvOZ z)JraZ+i<_>M`4an_`n~nIyZdgvgJ66i=!htaNE^^{WLbTnpEG^SFe8lDx2p#mREHM zTLg_fZ(+A?ZjQNXohlsXUhylFQA76GNA4HN1G^6+%w9=IDE)yU<+v_=MdcaMlaK;xDLWM?F)}520idnY5GQtP; zQf4ABk;NZ($vPhk-#hbVK?EMeDyE+?t;Dr~ayB;Tdv|V}G6c>}k}M zzUucf)&$m_?8ovvNgdWLIgMV8y-Vm96KSM4t^4Q{6$_zyxgrm&W_yVb!AEBoyd@F( z8?|CEZEq?Q*X)QE8c9%qkb+I!u2t+)K~e{TMZp)%#K5hwYz`<0U1AIJ&-6Mclo}>@Py1Bp$FX6HXd)I_Ouu#bz~BK%)qo(*x&+ zi|ixaUIPH+>0gXq%V1prmkMjOMp~(SH078jjChO00$UP?$vy+6E3d`|$Xstu?-X>+ z5%1vbm3?A%>&3s9`U2_Zi+{>`nwP3!1?Ds9(w+eX;BR`_lr1(w?4{C9Qt-YF7{tH3 zXiJY?!g{jx0_p6;ux#%3M|4t&=uq<9mNli-rVzCm$E+=_Z^LSVRF*R2qjOWqq__Lc zKR5qyHLZ!G(pI0JOYeKa`{_6T{Dj@}lRqukG=5Kz8t?>%u?cJ{n8#=xIw&x%J8NDO z{XXNZkDa^(`|y2^)~|!>EhmrJw(f8_O{u`0OAVXT4Wn3at>>h&S5M6~z1U*x?o4B# z?L?tF5#K31oO$W<;-CKEKW+-=xlZT~OTr?0*T{{*^7#v#!XM@SAHi}_=-$mWR=mcS zZt8I}ZCLWqne?XGEtJNaaxXIZsm|w~p!#qBeL0`MNP)d}`d1@?t$pt1GvXG0I2FxRDz+>`Z7`MrgZZ9YfZtN`C5n>jPph z5_pxscP)W3ceByUYP{Dc%eUZrXiIjcH>Vebk+AGg`5}1I=*571)&ui(m5CqqjW$`| zrC-$QZUz4zG|LBQ#&J4uoe4egJjIC@+dW0?Rwwkl!0t=Z^HZe}Sg zT9ut&YMRpv@&^))-*KZB&z;+kn{WFDYcn(ZsN~01V<|D8z7yA8Q>PZ4{giv1Af6NT za8A?l!_!T#a4hrmHbYCjotATIF=HR+XB^Sba7Mdg%+oH~l2ZY1IkDRl3>imW4-XMX z)?rmoGdNVpZ@BU$EQJ(7JNssFt&@9=r_Lt6bgy&g747=Mw=YubSf!(<&W`Ht84gK0 zqYnO)p~Da-E_-_AIWPYk=j3YmiVqHS>%>PLwP>HYBR3JhLTSz{ z3y(sOX>@n=+m1wn;&-q|Rb#DJWzzQLP@QOWpgHCR_&BT3?^8~7bgoKS=aq6|v_JIE zabRAa<|6`;Gz1x4lo|lxyPI?`j|P*6lZ*W zq52rd%43B~S$c-UPR1LDonst}9z2$LjCmZ7DEAdDKV}e-H6M<#umYw`srWY}5&lPU zSXWln7gLsiOQgwY8F>m}9GsAq{jzQI0@4F+P?q`)Xs+V?9i2{5@O13Z+1C#% zs2eKf-TiP5rc4KPb|CY`5ZU_>LfTNNlH%=gNO z;1wZ~Djhpy7c%S;`jqg|O7^q3XtL^M`0GIpf^u-2tG|l_$LPCd>eVAo=sI7WzrneZ zQ_ORs@0&{;D=#xnI+(P>7E1>K8-~12(8n`$*g38frl*K|c@nyp^E}RDh)6xS#=c#> zuT!nt;q3}1xSZzAoU=zdwd?ew1A3g<7bkR_;yDTZ5{J(?huJUmNj=x;-1!=(b7I%K zIKnxX$9&dy#evsWkWJIzXf$oDh0RsZoyBus#mkG<2Cah{Ac5F_k8Ks?Y8zkPu~F%2vCB zqxqT6o7$>!{C0i!6i}V#Fdh~{U#hoV{`k#^-y&b>^lD|K}qVBpDT?iOCz zG1CV7WRPQRuu4J)JUw}&3+|dHT>Prja#ld0#x1cr>-aK$j%}T0Y8_}Goch2Dwve%E zXOnZs4gGPAjPon$`j`oht=Ps#2CL=*Jj52k1%2vXjFvNou^X}9mU4wTJ>RDUWj-{TLxi$hEHyvQgH zAnGUr#LLaOl{G1QnEE({&dm965_7QEXq{Bgux&b`TV5A)(KOCaXSv+ZxVT~av^_MT z&2Y_fUOKkk9Hj@dW`$3F5Ag*!e}o%Y0MVcGXlp1zI#yO%S6!bc1|iB?5ZX7y1C;fxP@ zn=zuJu`7J&;{jXq$R*L(f5#eqRF}&xaq2ex)A>C8eE*)4-s==}=XKT=XrIF58IHf% z{}B6pV|TI-#pY*!hD^(up6;`c%sOyeW8r?YK0bg5bpA|V#dDl$Ox%w;;gy5WQ%X=P z>phVTs)zXl7GTR%d8`mqMFHRk!x*RFjI@gFqzL_{qu$bGZZS2{BW$OkRZ z5AwI{i6i=D91A3P$C)eG4yoxpWnsxcw4_X1cWu4APjCfA<;~Cm8b^JiH2nBNPDLLT)sL(v%;}Tt z$<=q(fAlZUE72VYW=*pRX|D1}p0@IlQ-ma0QEfl4)L6B~e$L^k4V_D$rZesnc%@9c z6a4EYrJSCPc$a$fP@^$~5d4xuV;}Bo)fCS?oPC@xMOJx6Q#~4U;RG8S+XTM4m1n5n zIw#b(y1HGTZ#3CdVyw&g+&X9Is&PA`7tLy`9MF9huD(dC_9xKRwm4x3-FNx?oPp?S zJ8_8p|LncZjwU;k-&fz&-RI1_k~a3nmY^{(1|(o$z$|8@kS|SFVDGfDHeSdA z#tQ*6m=`k}uV&;|b53`Cb@})E2P2cu^JG<5f1Pu>tFk(iWH1;E27^K7(~M-%?lx3D z%xqkZvyt`Lpr+m77QwB&mSyLqAD6^u>s1h*D$s^_Xu`{I<-IRwAG%jITW<4 z*9xrk@)Th=GZ2|{EfG9vS1d}{@#GD-Fr$zPq`{=tb}zt`eC*NsE>6quG5n|G$%nLl zOINq8LwRg@32S~0Gfp$FmlL{<==EEfb4ATDbNTAmwY^REsqOz*{I5n=wNb~t5_X%0 zKJ~n`pZBqFHCNIhYors0@ERE+a_`^+3gEDeIk|!_b^pX3)EskMRkP1EF9{w?lSY{Z zo;*hBGG;3$R7QOg?&uQ9IG^CsvFJdomBXObg&jlw2tXLl)mhg|37HdE$Yt)~z4*O4 zTog;Mr2&sF<-3mEHB4$xg_c{Y42?=PhAWVcTcG2qA{6Qo`b}uUs}h##do2_joaCH0 zQx-m_wJjFNW8ozvgo0YV+>pJn^}ES-Hs#XFLA!p(HUTu%SAEO)80D-hwnH4Rg)Wg4 zDcLPNHC|zVj)|{mwX3x3WFos&OORCEO-NF zdo(u2_ZcdOov~MdHl|tJ43PzI$I?(xgS=Dz17d1gPm%S4`vICGyBC2A_Ez~F*iK?8 zwi)>d)Xnl6z^#pc&tU`UmV?0u9o*Eh?t>{5TsDE2y1C?yxcV8m6a7SEqd%%_6WBud zZNj$n=zE_}wlqR{h-bG$}Rd5FwR`KcfZ>!kCW? zdp_A&+^sgJ?98YxhKIN$y4=W0AgpPBg5A0JZ~y0+TH?>)Lv%Lu^VS$cZ2L0rZXVs@ ze3L744Lue7E>vNDKLDxU_tNFv*P`sAlODQKlt+FeHSpUEw?2yST=ema_t>YZMBAJv zF!Ws4uz&p*haXUS`uoYYV7J6xwm^KxyzA99Uh_f2D-2AVGwjFmVI_-MwtrJfTEHl0 z+AH+)sPr%N+|r}(eLcAKA%h2hhkj~5>~|gAdoX`UtMQh320*|2nrVK&H;F&5F_IVH zz!~xVK)jdhMFKApc#*)fkicy4G`vr_<}!^x8*O3xIQ!`?_bK7Zr<S4qI)v!6#8zptan87}`;M5^m);?h)wx{4`{Y3ui#5=ZNg@0Srw=%1y3!x@CBDP-sKIq2J zb)zP~Jw`s@(f<4V4gfyMj#``eI!y>uPS#R;464qxhNs24{tlm3>O)w?*JD`4vv^(= zR(niEGtw?&w$=AwolHD+@EQloGn~_OV3zONbYJbq4FZRp+8lqZgD0Eg%Q4zh!fd!0M-H#i+QR2{z6XljXmh)2=rRh+4CSdr)*v~%+A zyV6^HA~{90oX{^kffvWD3mi&y9`)8?%Top82!xK?9#2&E?%$Qu?&0j|SaBnNIilMb z{&9QREcbhQ`eJ_DR-R}h5NO2E!Y+uk!i4se2# zQyjJwDrMqiPGgmlz6RlyEKiKr5kV&{9T*%7wyk&p!OFPBsm2sN<*w7ziMpRofHDpA z0|aiUK0Uw0|NJ#LIH*J64yU7APT=D(NZQXwDsW8o#7nB1%1_R?Ez*aV$orK_!p6O{Hw=Nx1exI7}iI4U7bn_Hx5Bf`*$2h$b4 zBTwxRm(J3b)3NpnXG~8gRaQD#X-9P^(Lp^5W%gn}_4=zet z9|EwQMN{3>VfPe=!;3dKf4|WQ-ILHgaU3UKTB_XdJry%7G_>1e6r+m$D;j@y%1b#}9_m9V^Mx3n47|K`VwqaXk1;^+s$ zH$S|N6T0kr|keMURQgXLk0oj-2FqSf295?*G zoc(8GmCHFN)%)=g2hH(&%r?Ojcsi)*EPIwy*l&-{w8apqb)odMlh`oyJxAt9Ir#`4p>f$T5Q=jUDj@RTF`siGt1Nt3${~p_`b7*)>TVY(AYCDjZ zQpb{p=W3Ku(}iQI?TlsY&tTh*ftD~?EqhC?SLd(c=w|`x=oNQ2W`$hVlW3(gjx5K& zdF+PILK*m7ZnTK{>Z2Pvv?3qmdUwZ1O9=g_lhRNx71}@b_Zl1JhXp(V{H;#co_23P>x8atMF%_y z{qEf{Hj1cM;w_9Dh2dJLa6j)6~%y_nc1soJ0nfm^$|rOuNplEL3toh~st$n^Vs1 z2uAv2x^yZ6?8=9as5jj z!lPSZv}mV3336AJhQ(XPBXxuaXk@I)h3L%^(XVrL1y=TjubE`xUDrlE3$BqN{3R6s zk+LCY?>S?NXCD=fkwWawv(EK3CXV<(3nI_Q8hn?iHJpTsk+MzI@0OEX=@$BzzIRUd zeYNIwZBNm#Ms?QTRZ6d`&#S*{odLvZ@=(8g*B&kG$|S;7)G~s?MClAr%0sb`cnbIO zIaFIkf0ViE5FQffb-S$z@q4@wAFct7BGLWgwvfPR!->;+=Tyu>)m z`yFBPC8_#-M0;0jve-N`q?JEuR?K(K^g;yg1Ej~U9hAnkN2H$$5?0&PN{yy?>upSX zk>9*j#`JdSNwS7Kj9adgEzg4QKUW!;vx}R??ZK*i_rX5352Yi&7WzP*Y3;{7{o2GFBt$Dwbve9K z_8vTW6WK)ROmTxR0#~CLZ(0qjclUxm>A=i|kK-yUM#P zHtNLp0p<-Yl1uukv$+}o>cBMaleaz^afD_-H24w^V)BZrc>{F{YKxIK4EdN{ z9MVUbE1N^0m9bz%IxZSs?w?|s8hOZ?Fd#)~e;6j20#1KrV|ypEGkI{(qb3u1?h zl@ymU!Lj0`9oQiKKKH)*ahRj zl5bqxp_qwVgZZ6jnrkUj$(^U=zL9H7kG}W$WP1bY?;q~5xj%<$WPv*733aA64j9%Q zVwO{Hyb`Z4KQaz*^7bFeo67CyzofsQ1eCL#x^-=I(uD4sASHo4L}82Sw*q_a>&CiMWh{qO>^Z|Vyj(b3=h zt(-7UJC?U^bAz+!3UCRh z-+Aya?*^SF>$zX*^_xHaH=FMkvus}f@CQyk|MMdq&|j_>3H+8M@Hc;X=wSFU&S9E} zT(Juk4eX(G-UG>8@+)GQekU+=A?{q69ezpd*VzP5i$_`^5cqQ_a^`Y7t> zIx|0B<0H9?fdR*bPS&~)<8Q5D{r&KJ|LsSKY<x5DDLWwloC~=eCUKZ=C^E$l~j&eJ&m&A(%UL^1$f#)Uxjpt-y_nW*Ylw zA46xq?8E8Wo4{Pbi%0!GyYmKh&2vB(&yQo3@cpBf9n3Pq!#W!si5T@6Y4B*6M4f)> z-G!~H%tJa5+ke)6oMuI*;l^L6}YWI)SjW##-4X z&tEyDd1_#7l0IRhn`P^S%@a;tmnqxn2DtNUPFy_48As=YG^W}l&8(4ZfUPl1`)E}% zOn3V$FQ>VXXWR0ZvOc&IFFf-ED4pJbFKMqrldjU^Z}3#IF09B7+{#O_JQ}`2*4RF- zIgjD5B_E45*N@>)k2{|Rst#ygN$NmB+}`u0SFws}CVLd)2;%94USlg_%2~FZIdqtb z^N&gaCHwS-j|!ae5rb14sC3%B<<^PO3gW~jK`uqBjMclmkEh?H!e5~WPnJr*;A6nW zVLxS@s%~(ClUAII&|CF2Itxqcta_}|xQB(A_>A-taz0fC6V-ufw?&N2Www(uH^0q;~mH1f9!B1n$$E0Q~)-Vnd%HAb?98 z$AYIzG9qh?!pE78)p#rE)U-I6AzM!X)d|IZaC|}u*_5M!9|LexytL~SPo2cNB6+th z;;Qo%H}py$jIk|O$|dzuzYe%(oKAgucBu%kA;SS(C(k&cJEn0$!hVzkD>YE9W3=u; z9~mmgmbJVCpt|O95lhOOa@?r1!BBNA!_#_Dzt}S!p>B1EVqpK787HBu`^s5|=JZwG6Xj1Gl1DGo!%0X11Tr3# z1jT+Rok{zN_n&gF%dp~2#Tr@`;o0;K{wUf(lj`)Rgj8-b?DIqh4by_k&dL4Wl>(Ko zzAbm!Dz5$D#Nt>z!SS2Z$2oc26USe361vWRiud>mT|u93=z}}x#L)wO&WuZi{_*tR z%U5+;`72ICzlg)M_5nM=iK_OM4!Bo1ym=bBHqJiOK~tSjrtQ@D>Xi=Yua18BBX6A0 z|L77Y^h+GuFLTZpKDq2GR>!!g_T z3R6V7>}kPkyg6Rg<2X1_{*+Ttb@;B+U2$BD^xzv#%5kZjkxDnr9SAZyu9V>s69#?% zmP?kkZ`vPZvN#(en^W3SAM210+C`lw*ixO59h1t>@!_fPn1ncC9V3Hl<^vs^udZ+k zWX`|g_M;X$1UlANzU966FK0mqc(@IRGoNKk7p_5TJn2<2*F}9hMraiUDuheY{3!&l zAp+|NsR3)T*|s<+Vq0kX5mQa1A zQ&84zvMQte%c*ZR)FPtki-#GGm{h=~$_@bg2%T z&PpZhT4wO#dP+HaXN(mKT|@C`N>beRVC)?-h!eW=q7LZD_MUOFI-q;1IZA~cRF~>Q z6s)LOyLiXhT<2%{G4spNzu1hx;mI|CbC2VSyLHEPjCRkDZM?-kz2(CLKmGJBr+>eD z_l^%y;i`*1#QFCM>EML^o>1rNoR*%C5eyp{(TsX*t?gI$(Sw9TE{Y)fi|#3dmWQEp zr+&1H?ZX!4q;sch-W6QObiVDrR0e$PsMmE=K|8%&mkgV!3|RZ6>5d%`SS=yz(~JXk z(lw$dpzDDCHm9HeJe<&RK!1lrKC-ys)MEP!QPK~tVatik520LMFc~WnHT?e0Y3j1mJrq9ww+DV?vWqG|le}8oDx`g|bdb-UjxV?8GJ@7ERFk^dN zKPg$o8L3vn_VUM|@|jDtvL7bNm6j+AhT6f^_K!OKgS%ijbn{**4he0U?{Yeh+um{{ z*I|3e75O@k-WcL&d+WN0ymk(+F@P@6R&8G#(1WYqSq56e0sS7k=)6hK(rk2PeDo}* zcQRy_^&@+yBRmxv{!>&-;z-pc`qzrZ0@1+CkmG-tu^ju_d`zT|9Fuc{UZ^~7Mj4Hd- zSotIa8Fa&!L9{;x*RVgosm+K__X-4*bv8pKeCAt(j_e%pI&7YvhF1{ZRcP zbiU21OSxMEb8*Hy@;>K-A^xtxoaApC_M=9?Hf#H}YsX`qm-P?!DQh5TmK_T^*wyDt zO|V*gFO_%Jb~vGx^CE!!vtONWd5874hm3DS3&A}uo({7~LH_bTjW=vJD^JwH84Sc@I(ICG}9GX%#vX`FA%?Nzd;A*f(V&PL<)A ze^WThuRIjJu4RQA8?{LcG=sVdZvD#@<7m2INiLaP#{wX-1vB4@I&qMDZgEhRE*cGQP@k1sXtuhQLLuAurT--dA4JcvLT#4+%D)Xu1u=y+eCj4aPw%gZao)GTV`-B2t5v66}EAB6QO?`OwyMKC6_xJ92 zIyZQ8^VscQttip?d7zcD2GXO-5ovr0DQ-G1h$AomiBWI|`eKiY+*%*8D8>eS2kAK^ zl)(^vHFpSQ7~Gi3k-rS=&yZUW$VIdd(b|FUB$lF|6#0UrKDPd?jepYk-Zzjw$-!;v zm#ACP#>T>D$p^M?2Znww{n6>A%*cr?DVhuJHyGJEN$)w#CtDjxcQzsK_xIA(WXyTS za}RjylfcByr*QN|1sQ~va>B+X>NVx?i@IuZH z4>Os(JU5}Ihg;*q)1aTrwdLq(+jr*BhlDZ7KPXA_c3{|fgq7CzlHlDeeQfC8$X~-Q z2!7tyf^`N(->%rli9u+EeIGUtrSCYnq2i9DFGi}nqqwmypR4Y2(z zSv>#UpB%LNIhjTD4*;VBhE230;zXxHYv@qo zV{J0vrBfz|ed4=tm411?NZ>^RFB143B%lplEb(u=LGsF-^@bZowc~C=>P@2ojmN+X zk}b52%nf-7C1!TNkV;mX2Hc57BzdzfU_t9Oh>f#sxhIY@0edHIaR6}h{n;%}F>bci z*@zPw4HJi|4rKmigFgEXbNVJ5bv+R?r_13@kxkNUc0F}H>b;)VS zS2{o5Tu_dU;?@V!IgURUY%DLQDC$3p5Wd+A zUrzMYlk#OxPWhmO4sQbE*s24>*a!{U*pO;JTh98l19ihqu_^!lIp(?JK%p}Po2qr3 z@=6;RxcBdIUK=;t^~rJESKavBAwy&fJCUohjWg3|x6(w$*4mE!4*aM@IM5-eYJ&^l zVXRmO&r>_K9n#jJNvHi&WTssa$v#z{anL|KJszZ^BcCUsBP{Bdw(^4)`vH09V{tIU5&a?#gX$^FQZBO`-KwLd zC$D;1xgQpYlclHp;)E$r>azNvExV-t*Kc0oc#aeLkC#V3;DG-62b>XgLT3!>fM&bw ze|01$OXJiY$9A0D{lH6oT%~kC=S4LPcNU!9;1T=iY3s3-+RNeGQe%}eI+eC;l+SQb zwcgy(RRyfgp`GC7FFId(zv{|<$A}I#{1Yr__K6=?(1|7|xiSyjvKF{S-kz$BMU5@- z^jA-9J^}y4G0#wo10tu$da|;$Wo~CIo#Oa;g(Lb!9MDVFci5(K;zu27_XJ@b&|MF{ z*I63+kj}VrP3P@AYrAx&Wf7onX2zoq=(mhl^+r9aays85X}|J8fr6UjJucgkXd1f2 z(%|6P@WK_dqUwj&phK{hCT2=vUJ2{?q9XDG!Dtj=^~qjHF5+0^ z>Ka5ReDz1iW7pMwEJufF*DQXxM8{Z9H^0Cx)J+<_>QlI@es!>HmGe>hk_@?4KWc4F z=WwZa+Pk+u;eh`0yEviii0(<}SM7w36R^(bM|?2io-j_qLtoQ}X@vZ_-WZajJ~TNz zuj7UaBMaIcZp1BKvtGm|`)*Mmx}r_A{gkvPpZme9+$zrkn>e+Tgf`>VA}2aGiz{otUgedCFa5C z2%sSynfISKgJ*ejP2>7roopK%Cv^0}_SAcLjfJoxOPQmj1u;oW*k=VJ7040~TCkq} zs4@Q(d@KHx8lFO19B6mr9xSp~f0FiR!6T|Yluc4QINH7yEAs=qNGoex>}S2lq3e9$ zpllRc0;X$_#}ct@kvHVHWSJD{Krggju++P#J2X8G0=&G3-?Q#T6V>> zCZ}YZH68}r_4*0+`SQ|r`vr3sA7aC%JN9g(<&~{p3BkXUTifRv3OnV!N^HnpMl3s| z*WrY>uk6`t@M+R9m{9 zk7?OMWgddhw4Z`AY=-Lg7zq5}F$=ME zU8EJhYcE!Kk?JyrYkaj?;_sKKl3Vc7)+)2gLj0z&&ZpK^P}c{QNZLf(V~Yk$c_p#= z=nvmiTMN+{II*sJPznKH=1o}}bit5dk(=@#D|vuCYT1HIQVzEBta_H=u3fY2U~jv4 zAv~AoB!l{e=vQet3&vOf=%D83u;Ir>Z*3ksIQ__uQs;->WB2Mp+w(ppd>NKW6db6- z8IwDP3wdT2iiOsAtZ{)>_>4H^J-29!VHz;2;1I?zyu);^(%HdZ!HG4^J@8K7Jc_tq z@QLY%>V0DIuZgy`+4s3`K7UMw79C8VP8~ZJ!ayAJa#fr6+FrChpf}N7?Soe@D3VAT zLKE4B|JK8o4Ww%iZEiwd`qW4(i+xQxAYqL(bfEJlaB4B#c>#=vu+S##GIRc?#+%(w zE&mOd+d2*M?D!viMvJD78FF33OV$#fYnY16;BP6u<>d5S)|=l_KmC??-CCz_+SV|#F5gI%DV5M6b55k`NxY+9BzrFtV z56WxKSo(D78v9FV0rKOW1-hS8c=Vq?e*2%;qAsCv`a1VmuO)x1*Bn;$B?`>RjmaJCO^qVfZB%|Y6XcJ^9-U!p3@ zwcT0fCvZgy%_g?TR`T{>4H8?>p~N1c&ER^@O%DU>gT`zf*utcdeM?t*9SD_&rRhj-p%fH%#0uQ4Spg8gd? zwvY3L($7(koPV~~8J|4|9rTGhQb5-ki;e(_F63dNkn*4ZtrLPM(Gq}{QH@)&+G5PhjEWWgsz&_YjFpZ1!E}>s{Vb;=I{pr9pv4J=_?b5w z(Ab8S``pdaZlLCNhO+KBrvleGU5E5IkBrT^dV%P4rxVf{mB*=>-~Zg4>*j2EbCdlU z8}{quJli2%HiQwz!Jr)taHdF_FdOJ7Ka0vTz40y^v~7Q6JLF)H_8U@mh@IN<$0K!8 zh)BtlNXi&UsyAMV(X_iS1Z`biro#{6Z_#)%%#MV)YC%11K=nC!idz~XQ&^R^zl*dJgboopZ|Ycii-YBRo$}}_H$zEaCrIk-^zPls(W@(XVq>@k zRZUMl*U`(3!*Sr&=?ngkFR23=gwNDtTH>@jf5%5x_jpvxfcmJDjXd!P+kj8tL#GwD zG^RylKmoR;V}vmOd~kbZ@X{U-YFq}9lx^emT|W%LruOsR*lrodT00TyfLKlm_SZ$~ z)A8_DhbCy47sqkNn&VYQ+?1{pb(%u_Zk&9Z6I*fiiQ^2Ji+(h()(v~rD%o(%OCRT+ zcyqI1pY|itIM9{Tk{k2AIZYEsU{9kir=_%*a@40ykXvLK$Dv^d)H9uu+&J63WV>iY zr<}~w(Yzc0beK&YoT?gKpzLjQ+4f3*s$=MHeORMTI+q>e8( zN97T`1mOcw04hz64O6S(KRnVlsDwNKuIl48OoJ*bd7uLF_NqWYr-(=*fXX$sbv2Ex zyhE6}5yF)3=dZM4`rVf{{-*D>qkDNk|1Ni0T zE5_$JG8Z4Gzc{X;NH8D`9bnbh#Hm_YpYYX`Q*Rx&6cHbea17T63dSkuI-%o?=&88Z zoPMj*oF8nE|L}rCq$lvoOF5zIjE++}j_H>=#-rSE+Qp`|j$Cp2xen-8Z*_L86V}TS zy^O=D_6CNIbtdy<-Pbs^d)oVJPHxAs{c^{-Mc$RNqs&IASnAQZJB|$d3La(hXE@^a zN?%g+syxRwad{dU_MziL=fOIyR;OuP`5aS>owNJ%;Kmu&nO)L2?TM>H{}m3bo&>BM zbK*DUPH^r#_cV0me0_5XE_y&6al*E3%`1Ip|J5x+^Bt~tV3{!nzI|4A*@C4_&IQ`g zxTD6pz?phyyyTKGDS6V7%iza+<>5oybW0fLY;27mT}gRxwZVRrKxb0>=L}C$9hK~d zQ{uLV=Ei~Z0td}&PK5U)G02^>@fIQW7sFQWV(`8iq=)P z@-Xg0aReTKF#F4dav8E2M3qlnDM4=GS!wu5DNC+ci7my5RMCJ#-#HW6mdunLw>mH`^Svaz~?#{;qB0J>ATI!OuRLOrhqLWt_+``lB z^d#IlP6zUs2Ay}|=bAD2^Ed(hCmqqbfBu;h!8sW{_K!jCiPZO35YQ2wC5|U_w-a%R z5jgVI0>LDz4zJ1^*%p+zEsabKYrNTJbrZ8kn{~M7V$j~OM zgUtVJAGKxmzUf%CY47oOLmDT)nBUS<{05o<5Tn)W{*vD&$2wEsV5tHB_jJ zH89Ahe3<3TwF>XGXYi-69fyv|^g;OLk;a>-p6PZ-IqjgLa$F{KJu~JxxxC~3rrIOF z^3-99@+i9DQ-MHJUb~LiQUOo6dY$$J)JIU#=qdkcbx#GhqYK#&(-Wi7=eUGxjxcwd zjy+U4jtx@we`K7}J|f>P?|K!(9w)vdKID@w<-wA)~DoH8pN|M_!IQ2Gy}+ zANzaHoaNl)8mHz0KTI;-18g61*Z$|@FRYhi2eE;9_l=DzI|UQeq%rBXbvU?HcDAqi z!X|dylKB^aab={C{U^{{+SszJHC%g;jvkSl^%sUBHW4(g^ixGgTAISO6?I1rQ+kzg zm#_Xv!ABoOdtVni-C@_?;XWTTe5<;d&Q|`%z?xQvmhE`+l`+iN>vdSoD`l%&R+jND zd6yjQFF;ZxRMy7p>N-08q3|*M$MTx)Q*au$!{}W2FPSW+FnZWnUoMo#me)s9mCs4O zDL;7MfdjLSwJlZdgYs!w5+3zDTE==lRZdSTD??eN$dQ%hmNnakg~P_dK_v=RxNB2C zz=QpB9h~1)ux<5Q%^)PL(Z-Ac6C*7~HA^I~M(zR)>%IgY)CoVRJ}Qqq>IeQw1$=S^Gso#=Qsq zs-z$zzMz1Wc!J85)Q+BtlZ%XEVkFbDs&ZgFJ!n23D`6YFIvlAacVU&|bCun$-CN7r zH*G)Ha^GIeK1yG~`mgAOj@rHYhX>Mo4jad8^yVqZx4$oW^k7>Cr9DR0!Hq%!%f?|R zYzNtbGvXM_wyBlp;1kK=Vd#J;+$eWtnO(Fh_Pn1hlLOn)8GGUhcpv!z+`Xcol>CaM zKZ&1@EVN|@o`ZAXlQ*BYK8=1}C)pnVZ39M#{2y~>tKly(babAWKeFF`Yv@}aGk6a3 z$<_wa`4pplOJu(V7JVwEs`OAWR|h?ptcW#_xClzecSypyXUr+dnjQyp%9QsK{beLD zb>YKv`sn%{j<#Ss2;WxXx!Q+stCxOT++Z6(AjIa9+4+Xr zVy~X3lh+8slUt8DK7l_J-5M9)v$NH{JpSe{|Jgq1=;wYHJII*;`|>tTccsUY*L`Xk z;{^O?-1vX@%$du(n;7Kp%%kE9>$x~IoOc1vb2MnB2Nrm~*N=bpza5?ZgMa#{0RHsP z{=<>?%k?6G-?{|0-Vf#Z$=0L;Lunr~I@wPJw2l4_NwoH8OP;5{I>)a%c`5V_m z@PWGJg#KLSppRN(Y=}0Xeff?qI_MDb#$9vIn0J63{ANJkOUAp(tnYd2Z-|ucsO3hV#5TFA{i>z;`5pnveGz)ON6QJKcB#z7Ou~C43Cwu{>;g zSG~~E^lz0B=BClU2G07Qo}IBnv*=ePOMD^UG%VUc-e8(1X*ZZiHf8ZUouCH0CCYew zZ{2P0W?VL;9lKc{c2CJjd%6I$Mbq&jp^m_8rk0}B`Wv99|9QgPDfc-JH}^QRkrM|1 zPYu;6H4b|?D!5T!2C}g?&I&q|cxt-RDyOB|6gM0YK$n&qZ|`A1nUn~1Cx95RW>tpD zQOU5OmX(H$5Jng#gb}q+7#b;QSf9y9u9f~HNM9lgmUnou32Bf#`u6I&qkFYEA0-G1 z)s2SA7kS~h6Q>JhuWmHG>SWXbC*xywT4ysIPC_`@=f@}WVf2K%%=PZ{;(L>ioxUW1@ad4P&c^cA)7RJ$aX8WG8=gsqP8_muWb;^W zH<;QNr#P=$Mm}VhX9pQ0b;`ym2r zcAOf!)~8Mjg#)xJ4(GHpW?Uh=m4jM(GQ)5qXlV8q@<4? zmzn`r{!>@fE2~G?@3Ad+ID1&njVFt8*NJfQj&?+^)opIdSWH5Q1hP`)PunJOF7b3M zxJg}=))DI*m`=EH0J0A=E-a&tsKnYJ?4`WQqx7gudWA(8eMvRb10)eFL6}>BbtE*q zxLyk#Le{)bjEySxYucw4Rs&pP3yvTNsH;6rqYFdlGH!@Rmu($#b2Gmu&FbjwspD_B zFFDa%eY(Cb$F(b+cyExsavgdbXG8mxTM3sFo$@ccw0xW(y5|Yd=w2MAuL#Qt{gxxH zr9&a=)28VtdB$n%=iwvH;y8?()-hCG?Lyn3W8O6P#m>JtLJe_&QV`Gv@AfEXCPa`=ZljbRPOT z8X{DjF^|$`*Ei>J%83Ia?Q)5*a4S#9C^pvknj?p))4r`Zp>`Raah#px9!^6H=Db^* zt!?nmyr5mJyKHFd9wZ~65>jdiU3nO~Ym99BF^;Eks;0cON59@)V#jEcrPK`_w#$)8 z`>JWtd38S|&D@X~;k8$Fh$$gtpe?#Zf znkStr)tC1=4^y#a6b#D_#t8Cp%;122$p=mDa9Cw@k%ugpTO9l9kn1Wx%HhW_{IHLX z=x0o4c6$^bz;5>u~x_di;Z$V@>B2g~9C@NoYE~6FlV=Nix>_LsFE39=hV3sRR1u>r?au z=lVA~mUClMF9?-KosLdz$R{7Eh`j0}587Q%psf?hYpoUsIXJMP+Af!V@^F_A-5eiX zv&Qh_AdB-kA0GMn=eIctJr3x67(*MazNaO~_B?V|-(MfSepR<0L(u_owarQa`_3BZ z++DVnebZuY|jF9oa8$8tiiP)BrO?W}K~_#v43xPf#X^ESc0E<2tNLODm$Zl)G&nfBy{{z2N5 z7uWS>ttqJ=Ctus;6|P~~wy2ZRE}M`1fM+ePJTtzW!~Kv9ZN`REICVmqGK^CIv8CE_ z*Ref@t%Z0EgO)AD>6`R7Os8+r1;?vxFFS-?aQ#W2xDG|4X@_lzy`mAYFl3&VSktJ> zu=s9AbU9QXY?JH$*cHYK%~@_Fv&tqsTdzGlt~4S|ABf=~nQc28bq+f(b`4p@?#u|A z@v6JBI^N-Cfy^V@Ifqaw)wQ;vAv+=2Vl@5`K~M^|mUs9BS$yL5#{$x(1z~}Z4w>K@ zOTSVu@-novbU=(97&hLI);K3v@Ys1f?=4_MuQZ-{lX2kO;_0+~Nw;J#taPN-rPe9d zE_@k_LnpO)dB=+jI9Rv%TPUvNA>VqQAtgDGdMeVhaQ7|8{{S)Ju$ zSK2LY4ey-JpRNU1E4!vDo5%+_vZhkjb%&qGGz|&sQ6O?Hd?NJTTvtl}#fA zPfTy~oO3$_rmmVR9^n4{3s7K&eML7lB3vEUUiK}l?=I6}%dgXi;bDJvnfnS)7k$7s^r{e9_ zV*}Jh70bRbUcoNj=ck@$=WRz0Yi_VPQqg*L;){^cTAYn$i{c!rtfNgEGt+rh!pOrdmn zP@D-@ZF$b%ZjTvb=9n<&XqW2HJnh?z)A0sY8y}m4EMYU$%bAy^G>5bOpW(l?c=j8@ zl*_zd?K-(UbV7eF8&CiGFaG&~+;89i-2p6W8XYxO>R*FNAh3*NLKe#zuSpx9@}vqp zrYp2UO6bPb2pd_?9W;x^?s)W?ksn2TIP(jTc$oNaBmkyBS-%H4Zf*Q~uFk`MxZxC| z{eD;18aBrp)?L}QV6g!sL%g1)wFAz;zK@Bwbp<@~9zfYD_G`noHjRJp^U2mGs!l2}M4J{FlbvvJcj=HHN-)-QOF%=lYQt`|&~BdeF%|$Ow)0 zt&v>jU*f`jbbjK)kOmfnfj8)+$b@5hzDAK5^iX_jTs#+>x^`>1~3tTCJBU)}AS=bBbYWr2Gz2_*m2mMfT zYYuu@V|++zYyQ%&_E)aa*GX4j=Up#-Z6A5J4vcX54RMA;%=f+i%|O~q9jRz~JeV=z zd>hf{_)&x{d-L?Q?pNy9C#uwz{df***e~({i^saoai8A~_d0@P>`6lG1MAoP_o(aE z@q3Z^+5FsL*JFMg5$(`C7l|Wi$v6JXUC&XEe*XL49SHc7-hXB)>B9-Yl(O{eoVUb@ z81H$>MB>86d=zqnP;i^(2!(B355c!^Utlj1c#*)11iniNJY)!kw}CeNH7|JSg+wO2M3)L*r=^%Zm3NG6LOJfPqzWm2kS{h@+BSdxT+yUgpJa`Z2rewAKFay zF|_t^H(3eaB?F?@XAJP9plI5R@3HMVzQ!@h)1uGc>Z~Igb#YSVtxiVD z5@!prGlrp-_DI7XRew<}E@d`0%A52;-k$WPZAib2`r;gz_V^f0#Oi5j!S%b zN;x8(u59rwWmtGq(LrGiY}1#$iZOMm_^OcSR85WV=pX%Ss9v=LS&d!LC6$f*b>i|R z&W5jWN_@?I!Kt^nYf=X%ZC`r=u8x=`Ya5=vO22A{yzOV@UAO(L-Z{VA;K;61tWKAn zjO%^F3Aj4vI2P2qawx!N-~@TW$-HrrgpXG^#=r6;UL4cydzscr)B0{g8@f84>x4cW z(Bp){n9?rBS($d}e00VMikCXHztIW(4G#A>qH`*`4lZ$U&zwr@pq;S@|Ctvv-f6SC zi#($*k+&nX{^U!&ThrYgdkSE`mFU5*m>3x+j(?pE8LKzf$J{tW;#hOe9Y-b|C&y$G zCpg-aQ$5F8KyvD3HnGEOPSAy395iji=p#^I7i4+iUL4Q^lketm#KWM(yM2aZoMpVN zt(Jtvmob(BQ1gR^#6P(lNLIi_+A_JTWX7-pCo8fc?nhMG0sZt88C}>m+J!!(8hAO? zF&?={&_2|$RlP0y2+w52HaR~k%NU@taWzrPo7KWhMZ?#4gzjLJKzh{4t+VUBapeg! zo`QZ(cnuM)w(hcb^{;;5!Yu^Pvw+Vw`OvXgPK6 zL`cwTI(_Nr~%m_UPYjNE#($IWC`8Oy|mFk+pMS- zyJOY4C~G%`PQyAZ{Yq-^3tFxYN3sIw&sX7$=g=K(MgEwpGmlcY?Z|kIerp@hHw_UC zr!-~h8siJuou-|ebx+kE&eal>Yr2{c`k+Ge%47kVM^p{L$L`7NEM0K@b zK9itN#Ib#fEo(HJ!cqF;PCo^^HD*E}EPUx;lbnd4v`*N%y^YIzwfFSa*8*PT& zO5Y#zA)pgZ#6IEUDLENEZ82_J7^6C%#~Gayw26C?@j38woYd{>I;C4#mi&=SiQX=N zhX}p{2U6q@e^Wf2X^spYT%|j4L{g7NpDI_I5qph3s9SkQi=8N&7f1ArarB6J)cN7o zj~WaI^rVOFzg5S$>;0~IDY>8X(HBo0K0RiRSI)>4e_TGEVgKOdNpJ&QiSsyR(GXK6g=vt52R;KiO-6h zOJE8t*Y4zi!>z9Ch)$XM&;ad7q3SlkQNMl5n2 zALy5M!w-Tu#!me36roQ08ddu*xF-2a+(!rrDOfT-@)5a5E*~mrW!6*N7xoN07Fs4 z{CFl6PsM`;z4_;dC#KHN8U&i!E!V2vng3CS(lhAfca`P^eFR6FnYA0`EGs{|q3wYm z?LL>Z`W}GrlQ@rXoAwW>8I0%>Hu4t@t)zHd$yUX?eX!4eTY-MN4Xs@8^%HElk(AKbvc`Hr!^XOvCM2jqq>XpihuISJ>9 zwD9DGxP6{$jD6yQ77eO|fLv?a1b92}x#=S*7jz(#7SOtA(raISkp;RFTjo5p=-bR| zmGwExa)lPWXji$po-SiJ$HnRTyWXvEuIDix_VJ#Qp81(+3-2~O#HVoadR@|ZU9Rg- zH>T|kc{pr-9>#b)=PBi%g0s}pYLMS8NG!i!m@($rlEmhV))H`edw;1F+`3+|vH z{rziKue(tlifwyGn}=?!ePZR{9!&bSk(-jarwKdn%#t-nveQ+3SE-Im-gu42nv=U- zg`>Thh>YlNy9BOs-=$sjt4&q6ZA#3CmoE%lw#cfM>Z(xk@@V`8B4A)EqrLnox9}!< z2mJ4qwTc5Afsb58e&HU>HU42*jl03fBXvHMWgmRF&WB3>Qt)mwznT0}Ri>V`-Xx#n zhM$)A2RDyJ9}(El%hd^RZ?J}7D{mQ3$7B!c5HLF^b^wvYjY_{0jYH@)feY~R+>yXT z^4ZhzCzaVjdVYy5JMiEA$)A7Mg4l9=)OmROQrV`lYce2UzE}Ciw=8za=zJqtq%^F< zy;{1g<-XBtYty(*`QA4oZ*3gk*+9DAr$dd>&2zl_9elx=LoD03`OXzdgORw4oq=Qi z)+K%lZDqc|zJ>(sl9y{&0uR}T|M-9Yue*pZsV7O`^$&kI`j`L9zx%H3>$YwE{e$vZ z#$9Y)p#_$78Ok*ca9)4!*vb5yQm?W7BgCMQ9~z8j-M-XVFP*`8kiE@}t#R?af4F59 z#MV0OZ?FITjPhW7k^Un0djHCN*L|D5>iLv>(w2*JPQp$XY^kivWeqo=DQkul2u(Wy z3#!Kw2zd^d`upZj|INY2Y38=;eEz+^Pt5IoiGLpw_+00(L;E!35_?hc;I#eNvsrCS zv@w2Aqi{!Paq#QsaN`nLYMktnh#$V5>xbyJ))-Hne|Duhb!sx`iw31{0Op--l-lWQ z5gXb3juUp)1PXJlZ!fa7F8cOb@%bFz-XDG#_4BE2Q1}d;+3Kt3u!h}Q zyZX_9&-rk`V!@~WD7$Z;X;W$qn;(AM%odwc@Z~qiwln!{l48=zJ^|rRV5VHbU*wo6CRA zcw}y~ltBHf>TK`QW<;6}m-=8a3=yBe)HaYjX+r?mYH2REIh&Ty=9gL*zLkq=A=1BcD~ zI+*KtX?~hRUt5)pBp{m&&1Ldpq|I%(pF=zOCNIak=H#@i7y zC&Yp;ZQEW>v^vS*1dcB0V1AFYmm22Dz*nAr9_Mo%oY|>tx0n;uB>(G0F!})6*&%J+K zPD01I#nXTtGb#(svaECA4QeEr5F?ogq^63?hl9xJ0;#jOBA6E5D2f>UfHCH=gJyl!lW}G#Ltc-UW z0S;_=pnm!8prU|c$wO1*;H9k~7{)H!b}ep&AtE91iyc#3BSDy&cQY5{#uDFhb=MP? zqi6E&iMDrqfM*`84(cY3qa>`l3QtMS?!L7Ia4DzB&OE^!sdHbQj*d;CEvNP?RPFOPpkvqG-{X8v+tqPTy4I1@4__!7FXgTy zy3YLNfIimX+6N63`mTmHTag_P>&|Jc=%6;}8k^yqtL^jSFR_Co%qjKsDbqgd8ff7x z?MHU}K9GbnM25`NyS5v~{WnX>WOzvrAQuaBtJ^xoo?*|=aWKE+1oT%woU@na^5};@ zdW8+t7WzSsIu-o_$9zBj;X4Ati=5Nt$|Lu1hSS#ZS9ZVJoeyWAyV|a6>{U6Sqx)&^ z)zP~voX@YWkjE8m_LB^LbObh$sq&Q1k~be6;RN(oIHBhR^b4F0(Q6$Kv)+NB6ULDa zqJAjAGIyM4e|D-avv#80%A(d#M#`z*e!jbbZ=KNX^Q@s5lfG}|_Tx5;71mX*t!k}m zyOra(m9N6)F1b|LX*IJtZu9}D4I`(n5mCDSYhtttG7!$3La1%$cHI=mdf(&F58J5S zTW-_Bol}*6`e2Ha7WbBQqaO!woJiMZq)*WeGz7b$jQK#z`77G)%c1izc0ygJjPu|% z@;(IcOeVQC3*-d&9?WoSW<9dFhmpq>~Q~ zKy#hbWQ{-QYIjDhvRmcUdvqydosUdJo~$2z2l1l}h>`Xe{ZwO`k`5WA(&J1WN}b@! zf8ErZ0jkZU9t!n`VDhfc+UXW~W=(@#^<>k`E7TA^Q&DkjZI!pCi{_dR&L=!fU?gZkDoQFo3RSn5~ay|BG? ziDUl>*FK$Oycs&Kv_Hn{HyCY$Y1hbQ9|w`h+>gRozhfzO33`Uoc__>~-)Lm`m0%Qc zZF{nLNPE6T?Q|D2eEJpT{2;n@$kHrxVTac$@58);*dN^|O~bX6veOCO55-*kz#8h0 z?~g9tFn{rpor|+e);p|m7+SGw%&|J4yMFQ`RON);A@T<9n!4X%<$b+Io0X?PA9H?? zhwa(bzL1Ds2FF6vEo{)TAG>WSWQNA33KwX4iW|GEo}pXnT;C0EdM&#SCo+ld!b@Ko zM;e`P*mvi5T^+8vU;GH)=cCIu-Og|LQ^PJVe#hrQdjE)vKE-PjN? zN#ddn^d)^z8idAMh>geuw;hYBRO9xsr*zf7QwB!ThK0yV&uWIiCK^wOhTwQp;Ed*$85=3`qPXI`T%RX_7a4w({GRIu(yh(sJI=#Oanhw_43AlMO@q|q zzRP45F(K9%koTAuh(4%15NyR(6?8;wB2IHczl-H#zRC>lvy*l{1jOC_op&;^g!?6Cf0g0Knaq<3 zi3=OL5ob@!11ef$tyaJ0R5@w&HOCwFOhR?{$3OeG54KhZB0rSza=l34Ta>`o??jKU z?LXAYVV^*2`xF~v+M;%I`$lnB4VyQZi?|Lwx?xH~Am=*!mFNb8rCMT4Q`( zACJCj`O~NKtAExL_7@#$cP}CDZHK=nnm;b{TUox7-we8GPYu8Kbo>&zdix&_Wbw&>bPN4r$hFQH!UoWO>-U<#fOl3EtEaI1 zHZ=0P{QmDnDZ52rCiZpWTXWEJ(4!B25cw0t?QF5B2vgUJvFOpP8>z1l#mT%ha8oAn zt@$VgU;Zzv#PITbk-&=tUL^1l62RuRfz=*=^$Qz*P>noj`}3n%@sp{b5EBhwrroID zQ%KS3B}h=nUGWRj0Jlfgwy45z)4CgN*=d^1@;XB-r>L=k-BSql0niCg$CxvmN6v5z z(Fw1d1BtWQ-BZ_^mQE*Zac2LO&YJK-ld?&dJ(zJ|$jPm?L8n#dxS1Nq1JiEg=OAV_ z^y)ZMPBS-G*Eo`JQmyrz;&gx$h7L=&IQej(T~5i%VQ&aE&Pr*T4_jKYP^uIga%W?A z#KMNK?g#({Q?fEV`iP`$h`h;h3t6W+45{hy*k-4#jZ#~%iQE^7hNnfhM~jx1Vq0E! z;Z@x1cB~Lmzuh7o`zFp0H`&0K4cFIaIP|bdJEvE&F_^w`bG&WKiH~mPR@Wrs#y=D` z8vu1!!4oC@-oFc5*L0=sk+!V*UsusHuH&1X8)rr9apOL?Xhe0oP9OH2_3CJc6O5-~ zs`~1wPBS`=UcFP-*fjp*-O=mUoP5b?XKp}u1NJ34j|S!s;%qeU6O<`)4IHPMC>iu3 zL3J7WYFpA|mp&?mzN&R=8`nIY3F251yFi=CYCV^W0H~v!9apD<6CC2LadA#iF4?4w z!y7~tm3`rz_9K%!23?8YO_}9D6=wzdCl0p2lxJ_&v?bPmdVGz;&ck{Ow88C@lgTU*6}BQWDWa62^t6_^~-}g7oCnQ)hm)IVA~Q4tRC`b8E);ERpCMO zURyNo^g~qB@?6o!i5n}=WgmExkcGgg58`zL7$nRJg_Pxye?mm)!lRse7o8`Mf1xkV zxI)qfaHSms{-mIY?VuJ7D*~MMs88r@bPWx)%&6X4YrMM=-@ZVv)GapVCmv#Am251V z8@-SWBs0GV(=l6z*K>5q)91@60t9G!MUSAX1G^s5~(b;{HMU5B*v z9XvQ5)J5lFogXvjIqz|w_6>ZoZ{XIPCT*O{q={|uZU^-GFa)~o_<^otAP(q^37ya1 za02=p9MFHvN$8xoi(~o)r>t9tdZK-bv#!qb%!S@JoZjmgk+yP-nM+s5yX;46BF_NQ z5hdR!hjR80`u=&%A*{v;wVerd6h6JeZs5>)!hL$>3BA`x=NP@?dron-pWrhenOXW& zeN;E?lTN7pyBdh91KK6ItVNLeJ;)>aAijAaa(H7`Q*Kw-Mh>kkCu9y!=+2+(? z2$J!@tDdD;cpv!!&9?0LE89W)A}d1e!E{F!Lnku7ff(mOLa?DGZQ6_dDE$UvGvFVR z=~Ho9b_K1qi~P#9?XGp#7$jz>BQx zba^Sjz!-%FtV)1oT3UoV_^Dv@ANf_R_#K>u2mARhPUyC)#{MZ~?Y*{ZI=S9+vU$(( z+9_?0>u(*kbr8&Yj9PTE(iu*8J|Ht}P&r@WI32w&8EX%$b2)*PzOui(opb$gfOl+_ zAIQ)Ka%yWn)PtQo!eR9o;k#BkLGCB&s14;22gebTYH=;n4g_A(2X;C+RTjHC+NA@! z4(6|3pU3H32lPMsoi-3hbR5!OzhSLFo*&}S>0X&=H%l+7Q;>)Z<BoADyAr#w8l+K-&LW_iULu$<6wjP}C{ zI-sN5nd`v4XI%Kf5*^du<1l`IA+O$w2ZFB0STy9L9;`hb46)hDm1bn@BX&iv1NsRc zdN|ETefY3XJ~ZIRk{C-Fe-x)K%SGQJaA8F!vNliIlUHk|s?&7t!InXkoNWH_kY{-{ z3UY4Y#`(vh$D!$5>DY{IK<*WybJ_}TbT1+EKt8@gpTrp*d6%v^H+tJ|#gt=$g=9J9 z^Dd|Z`UMW?mlv-hbL&6z-Omw39XD6>#o1Nn|62secN0d_(h|@sjD{c_WfdJ#Z^%+# z{4fKZLt7mKjt%El>WwY)0|?kMZ7BK|J8kW}2RMoqMAi(pRl9{uy1#w*C>erg^R(}@ z?TmR+1@l7!C%iwM-~fLOV#zAKIk=__b@g16mdSH$O367+jmWj%5o&Cx1GP5MPUOQq z^nowPI-uKU@?1;}2`MIdOi0AgYrxfG`S{%HOVD^S58Z~10K%_-T7URjMW2sxJ4 z%oQhSK8CP%gjYpGPMsvvpXq<}hpTLR!NoG2KK8>~*a`de{d*nI(O=4WXRb$|+Znf= zZ)<*VEsVbAu61$aWo~i4Q8BqPM$ujM-gRV$T~G0Xj#|I%k0Q@nfVcv+i9Spz|xz5d>X<=-x2D4K|>1f)(Jza23ATQm1X6U z0BlZ%YmsOG06+jqL_t)wB@7paQDTZqTDgel>SW~7^RSt=nGjpydqQk|Q5glnAis30 zbaK)$|`h`RuHx)G_0zN<-D6PhlYT!i>H4v@I2B&I{V0 zGQ-7N%1jl_@D!Wt0bp_qzH%yIsc?;Y6E;kp9eIPCsf?GPEF&{i9IJ)B!X-CQY3d4J zYOtOBOEm+yG!CPWKKr1|1 zJXb}QA=Hs(xzml_VV74=JDxlWn(p$AKLM=2P(-S`(1BK@DUt3R@+|bowaUGyKU84F zdxRKSTXNc>7p=uGUF|LC9<&tcQ4jo#Wkww!`IDP+1UX<`nt=pb?5 z8CfjG0y|P1P=1h!5E4~Vo8Y$>KGzMTTaGHTqohlr&Ueb#q zkXrO(6q?F5jfDH~=;UF2J7r!HA1#45KmLE4WpqTpx%)4hc@NL=+vpEN_dEGv%$JqTy zv`=F`(zIxA%xZfTb7MK@A+N!ByFcRxlK7Tr<*rh;>P z0w=nk7n`Ia@}FC;4nJu79M-UZ{TKf{?Vlf?)nEHEp2vB|r#YkMi7{u;k)mYN_n1>M zpFp$cmGFtZS^jfz=|hnA>lS$n79wBgvGrT@_pV1h_B#*l7@&HuA;G`s zjQB>pP^wvvQpR%XR1l1Jq~gm@mlOCU`XYfB3A{+)MFO8H0gdcpaX*#FN0hU!Htd9K z7Ed}L@|O3_i6`cSF5z!sSx#7^?=%bV6TW#}A_XnayfVl9bP0HP$()y6V_>%5o!oP5|Z@P>Qp8)D5j}^wr7YoaVae z8V3iQVswOZbE&-h7g-pxq2rFt(Rc542*8mYr}a2UAc#15@jGHR7p1Lk1tVe}e6jyO z6iQAqYVNW!BkukZ8)D%x_f~^Q2UK~HwK(;QX!1Vdk>4;s0{?SSqa(7*W!=>z+ONK) zt+aMQQb>iIt{gfQo@uu-)`3HO9Z%wneRE2PbCEZWRqe>3vuGSe>1;PPD(yQQa->n4 z?Aesfo}Obi-p7$>{03~5i;Un;ZU*}+Z?gYd-MAhcg_xUcb?yh_6~e^A?%w5U0~(Z6L2Uv@?V{r9SDzWba2!!;n&>e@0&_ zkFKv2As?T@Y9KN4+J*RWvHcw0fQV)K?fmZCchvANw7KsH<=yRj$ig zZd~YZIfGFyGB-_qRypj9ZOq@OyW-?aJ7h%Ox(Z<_k6esbsl75qD10{6Es9Tq4O)$B zd(w~C>vpnS^$U*ZZ=J2w8~KY9RdlHANZB5#wOmGMM?@ywrm9iRx4t-}qu)LB03hQZ*zk|#8ZLe6sO~ygBHG=Zan$i53~586+fzR`KrQvU?U%4 z(HR4$cE^=hShP*r5uHDAJi+1f=BgZab@ohqv3tto82aaqlh-)4FeZM$0sRLZ&^-zL z4X2>v1gFD?!#?fM#zM0kD$B7lA7ue%8)G+>6BpE@R#Y@n0gp$?W+-0rQzEzpO*sV# zy|M#3&gewwcx1N4c`zrTzvEP4KRj`QXZm}c&?%3Toi?+bn(Xg5y&<5gqI9I3H`}p_ za#T0iB`p3|PHESepGq}G`?{q%{1LbLSzAzlK43$r18vqHvE%9>r2MlEZ~9mrvd`_r zs=M;KPIcAlm99MH4v*mR=jL@4FV36L_X8E}jDAPImt!~1-r5KL)Ia5_&6TiqtF!8o zI&4L*Q}~8Y>Wp1Utm-Hppd>?blXlPs?0jzZQb!@~Gi>b{_Ekr81`2Z{>n!T@V*&5& zQ_5bUuO&Nl7k;(L?YyimtJCHyn;PGg*MSme^wV=rK*#awocUgzy?mv!F8Hr#7at+H zpiUij^O2!3hv|fVf-W6XPx0i63msE;m{;E4!7J@mE;@0`XO0UdzSsymgucCpW=R#E zQmW)4zpD)|i4C)$xTZ^d1!;k5qnYAVn^#op5lL>_R;1EVvt?{z!l{mwl6==Q=qt}G zfLRASH&`|wws4&^IiV~6x=Q}-_@%x?U!fCP5(p|BQlHLl+PAkqUmd-DdyNw|_jKyH zV+8YC<3qj84Nu6~xlNr$;;b-SgZt?kF6yX{KA6|nka^@Iv~Qy@V%q@OCY^cDv8kRY z`%2r#spmSL|MJVi?MBza`{BV%r7+EwWAGMoXdTUtsF59bpDgI&75rq8iww|pe#756#w zo3fAmcpy?PopXI}F-}|Wv0czKw|?iXI?V-0-|%oww|vHgV+tJg!8x3EpZM+@#h?i2 z#d#_qztsW>#@3bh2Vx~NY z6H#|)?$ZfE9@1G;+jtYgC}Sbdig>walDKK&5NJ3fNqoTSZ5ziNKGy}+{t-NL}~ zsL@vfGwxqs)#hqz=|JaHn1(>p5=9-XiW3BG=kZ~R+}Nvbdl;IC%{rx?^sOv9E+4bp zmAU0w_MO-14&|itW*X)<&K#F!^V4-VABLUZWv6C*@#xIgOuXTaE8FC68Nz(`GwyBN zd~e6LviB-kD3h}31zmf+((Ch=Xy;nnZtO#KFZvet)iw2xD}g|76Xp?kX|or&(Js>n zqs#)g&|GHFO0f;N=t~_*Om&#vE+f=VvP}zVL8-3nqU{Lj0hgBL8diyd20r13=)(GT z9gwobN^jI%w5)X81`A(%w5D;@<82Om{B-E*q(8Fcd$M|IUX_6#>Iqkg)cjZaEHj^f zJ)~nwyro`ar|K(-Pl6aM6di5KI+qFami3lFkJ-+fTOdwd%E8#0mWPy~vvoYb_dgim z&_5T1{vK`J+WbH*8WieJ0}6itC7$f6!aT1j?5c}MWl!<&qvCaufsypmjs`vD>?A7%Ecn@nNCG=4Rw#ocz&>@e#`hO)SblyXL_fPj&IeIpm#KV*0&5&vP~~>^8qZdMwU#+JAW4>#<0>msDUSMiM>Q#B=A)w@LV^LZf*YC zbbiWE9=1Rm-b0(Hb8OwU9O?Kvwg>WJC%|7O<~?HF%PBHd((vDS_#8Ko+9g|#D!Us< zALz@upN;ptl70rT6jQhOBR|Vt;1v%dc;*&z)^gg{{ky+CI@((|y#48S zko}v_CHg8c^Zpa_IM9&+1XAnw1i7wTywl( z4}@Fu=5zTN*x&r+p%c#^ZvO8FB()Dd6mohi;Oak@TFio@Ur9Ju@AT8t24woXapN4ZY|^(TcU-aQ%gvTB zn2q+pjNh52L2PFef*%rmSiepxI@XxOW?p&C26}jk(?#RJ-+MOe>lol^=$;CA$H|Q( z*x2dqW?!BD+~`R=g;8hy?x z;e36^QI5Iq&XAwm7`<^arksuv_OYk)UA#Xhzw1-?PU2*#<5c<$hnC8xuIP)n>XgjT zclM_YdO^G5)IoceW&)+>$p>Ft@GO;6zu+xS<~pL=5>5fv;jT_AgGPRzM)BGq21jZg zW8$0&9rZM)WX3_2&FMMm+mqlqaS&(M@D49J=oCZNE8FUBG+d6R%ar`_BtOj@7}=DJ zj_aTzg0wSkvoUnEUATI#-uID^c8<8`c2YTVIp#xzHq=#eX2?V~tq*1|8%}BKDRQ{Q zK@eZ_oY04;a3XhOJ>!Hrj~Q#H3|buyZ*Lfv^zShaIv0Gr;_g;mpk4H_o5e5K$e)vW zfn8kQ2A1(aosM_w@y@BZ*{FTb#{#e=I<~`y!ikd;{iY*X#x)LL%0)*lrKo+d&I(O) z58I&hBL&ii%H8%xrpnxc%SC$hL*OE&u%R_o+9ClPCQ))NTFHS9A@Y~v;Z^u)m%K_x z{*cEGJqwz*5RoRZ^y|np2sep*wFfm*i_f|uKjagKS~n{@smL2B_G{T>bOwDnv7d)C zuKg07mJH>aPR6ul$=4?6z;=VJ@$}H7qZ=rqGNs(&gg)#{b(QmtI;A}ivDAB&lX7u3 z8_rxg4HtUqf6Zm+JkFpOIG?}%L1)RjUvWaOr}tj!n1+*@`fNYl#>rX7Pe0B9pPo2O z9oIO0>YQ?|L%I&O@S@ZFiE}LT=LIMIO7qQd#(eVwPMA3I>eMU$;=k7>a^J|ar~2Yd z>MiYCoJ_5+9m|w;+RY;>vFHiiK;EK!#!ZN)J>{}59ZQ`Rm}%rvc11@DoojGD<`iWe z(N8%V{q*<>o505+bWFx^5L1dByoRrvl!E|uP>Xh$`c4_6?L<;G23?V9u?D~LASaB3 zJWbg*(pmLGzI!Z}>?0>ndDhwX!uqib+ID!p^P?%6LIzJejGpGiVd|-j)Ec=^Ple{m z$L>{p$hY=JqF`$exMOGhfC=^WhXb@r&U=#e11ye(=sZoxx`#YJaw5OB?=GEJx+`AB z2y>F&=~^A-#+M92qgaI(*GW3#_~8+q&#<%SxjhLTeZq;zlh75yDP!q`evOn#%TJu; z{qP4mrSr0m=<0QS-~ih6-+R|;lCUpyOa|vhhsO)cOGonZh$iq6621`xn*jpn6 zPN`ET_8M#8yY|yTnGZ8So^e&DfX7ij&O7K=9C++QdLtcD`r#T-Sk@{UMzhCVc);Mj zw8v~?O`;G36X-RR&{lYv=U>Jmb6%aEhmuRyJ|`00?7oEeoH~oFqU-8A{grV~s6)pf zEnAk31N5;cYa@e}iTcuQE*lwkC}0U?T4~#w2FoK|s%^9{a@slLKy&(?h*gT!h z-{5rq!ykL|5e`Gr+QQ2>+E`CPSC=Cs^w2e$wUiDN|D+si+FjaojkDjC4(LD8=D)u> z`su&EJNoITx7BuSGWxoVJ$SR9@~)$kp<{438=m9T97oh5z61p%B5 z1yU3+0yF?Iz|3a2*F3_!!f309&@<>2_yp2UI{{kF3@}<~K^+2I%|awkUn=ie`u+aq z7UAEQk#(=r-B}rx5$@q`ZfqndTL!&k2gXXQryf#Rmubd39LZlW2oMC)a z*c`XhM^fL4$Jf{e%YswcXVV70`A)fmYk{e9!e{<4(uGmiEn7Cg{-bWxExcoQr=>+} z^Tunn#pI%&2>j^!)1;}48b)qt7qTzmcCVFY)6Rc&7)c=g>{8?`|rr`z4D-mv?>b@#|F!bPh|B+U_EVsv@;zC%hE3#%eX-}*u#6( zlWM_Nn~6DJFWr&{ZnUEQcw1-d(ja{&Xdatry!D~q(-LWxc8T{n;AYrPH?Qxxe$)Eh zxR>QSo`mVKk9*2`uc&xu9Q|%ur?>qzw14%+T>FmbYlYpmA|MCJ z&bfwnm6>7Ht0B?o*pe6hQ{%{O1vhg^&&!(kSo%(x$8br3c>;4SxJ>8j`tvH#pli_9 zy{=%z#6Li)*qWPKOynipjizL(*P~a^g zE^~ySD` z>LuQycg7{n)-7eWuWAu4^zb@En=9SPr;X}w-8Dxn9t@mmVBy<(Ry%`nWLJ$#dgO2I z_06L*y7O-5Zf;a}#Cy*>L#3c&tnqi3qI^(q+pQ&4?By*jthQLL$EgYn6{%w2YC4JT zCMEa5Bg)u-Ywp98!n;CLOt>Xyuh9A`&0Ug6{Uu>3D-TG|`@SzRwczK=)%MH6ytTmi_I`3E+tshd$f$T4oZQA~mfbX=#u+VI z@)x+#cJo_tqivR|ief7EH_pTR{c!E3-Ce7|0!PHSeL4eY z#|5MC(${@&Sfdsi*A(rfgpZ}~0R1vvw#Jh``^%^8B)K(?T-^QL;{u61)rkUetf*@q zH!5)+R~B7K)>3HT8ShhaV{P2K@6T@^!vRl)+*)-0F}U7d=21buz1S~2_x|!94}Sbl z|Lei4fBx@sa^CG9{@=G#+}%X`+t-{nX6iAy4YTAE=`_l=S#C6&;RL^#|1&4PJpS_s z6g=fq;{6=%yc1m80Pjd%JsY9{uQMnV=HeT z_AT#Q7)UbDLHUY&(bjeg$~n*kTvnG9E31 z$gp8OR&`mDm#g!ZgpLSAfiBa@?-6c|LI3qX`}_AO`RfPSn$y2?j`5(`6Vr_jQ3#eF z_P1oVe*6Jdx>3I$5)V4l`tm<0NHkqsT>;Uj`rF}_PBu55YWR6{apKOThg0erRh9Oy zqzxTg6>uMFc~?$bvU=)s*sZy%O!;uzv9OZJmOwhW@*j!jS=h-P1eLlq+6InQ`*9!WPGdSZfU%e%FR zh-=ZKdVQcrBl$Ua=D;%to;mP!aG-2sE#w(?^3jG@eoXEHvtMTaeE}r$w!9hcL*n8b z6!ERWa(=3DB^SShv2ZGvW5MRN{?eMUpkac}@||F~)?uq+Chz#7vy19=j< zC!gyya;oDww&@il-R7uM27 zD)$t*avH9*e0;G`%P~$EmTvM>A!R9lNBD*X^p-#Lv_LB2OhV#k2PZhdxsX4L_*vwu z!=a~8x!BW%z&fVoM81rjF7R|wuAz6i1+Z*Pj(|hXc=_E2l;jW=m(7!h4YM1i01008 zP1y@JJ2Yomu;vR_zt1q0^X_MAlRohXZse_(Q0tvCT}a;eotyU&3IJQOkTJ!2aV#%@ z#5s+KlJe%zMiyhU02>E%9ku$KTG()qVlHA#lj&3;h_!=ik@q$6;T~D)ogHw^4%%0%va*NZsPW|nK zuG85KCs(>KpTy9i1CjK&89|?7=7M@@%1P_UBaY)H(86Psi^FrHU#J33uf)?f0J2el zwnRsJ8iGC@(q79e+ftjd51uwzEk`aC?S9w%l$E)ycDYQHkSZB@1gLDr(6*go!E4?2 zjR;f>mTc`5c?g(jlqU71tWne7Y1NKFNpOo}T&f8A0eiWm^$T4R!DY*eeuC6p`luto zaz{UbqD&kkwaW#Y>$Ki-l6-;VxTlq$r%vh988^1Y@rSslNyiadr*UrI?JM@-jJ3lFT}Mlu&~qZN zj_BZhffKrpjOlYY1jH4aYARH*BR#qM|4m9Rz5nj zoWN7fn5Ww2lwlm_+M#9Xf1yWxu-}wS!hk{eV)MYKvybm)ITpzW9c<OaQjq+e^EZJhQQd+)ds zM_}l=;3Q^R!&spsxPUcBu`c-%U4zauHZ?Hqka>>u$b&et=%lDU)Z~tFDW|VHA7V_h z0H#1$zka<>4|U?y`5dRrIH0>xf;aDuvGyxb&;_8uerNyFUaLdMi%>@y?R>_^z^;Gni*hA+G&JpEjL`DT|hvZ34-F6Ou&S_S4@YW_>{&ahA`6CYK zKj?sdb@0RYmpG&2fR9bMRJYNo>SNd|#|P&O^d0N+kvVPFYUqW1KL{ zn<{;XFT8aS8SiFgOhNy0nm9Nrq`!6?c^I$nG};YpXp4R;Kj?#)jMLDj4&BIS_?me> zHXt&NoXf_kdg_p2?$%5BlLUk6=k17B&h5(24LYzzIH7AZAP->e^3Zka_uK9EwEBO( z8w3I`)W;EBd+LTpnL|V<@`tfUyCdDu6DEb6($!%ElJ0md4@w5W`mMp(6&leK=L$Aj zQ96d*4SiH!j@56lkdr%dfv0{inv*igmt+xCx)nxO0%c3E-|{WAQb0sb6Jm5NRCH%N zK`uw|<%lum=!nTMu+`@oTjYyMz@=U@$_ABnX>+cDXGEkv%7OOA`Xb9wH?RD0$rI8D z$6vx^ees7B6z`gvkE^m;QiKyPK`p2p2RHGiKKt*33aZ#0gz>Xt^}*&k^p^`#Eqv#4+TG&aX12 zK@N_6J(l+Y>5KNfqfFI0)&Cg0`WWB^^CAL!tK?U9^(32O`shAsYUcz)>Jf_6N~jV` zdR_-#Qp1F+F$FE@gQkqpg)_B}bfK$+6z<^)-J!hfj{Vv`P2bM#w+H`(IBpqP9WdG? zf8UFfIj?4$Y<6GPtIY}prMR8-8A7SDHbUF#qk&K!XgWVFB;0W ziD%F>ibN0OtEP3J_uIGon-)1Z%oD(Im2hTvf!Ico4kX1@6S!<~l@qVyQ+}W6@xri+ z1ztI)SfRAC{H20jP~R#!CG!X_5`)9KXMj1r7EO^A!xFP}S2)KP03uZ#c!{LSky*So zcel>D?1#luQ;LK?A69sR{y=&b#bB;JVfopkPuZzuX71Kvza8g?bdaHXts{oZO(bzc z;1pqxwRz5ekx`$wj6y zVj@vz6jfbTp;7+KnD~J!JGS{DmAD}{KeDeOasE@I(kLff;Q5i0^qA}5t+FAFI?^qA zj%WU_;QI6L{%8J!{dYT*K4lxbWn2H@`v2Hz|NN4V!;$?W{=V1OI&R7zoiE<`9rg$#moB^TTsfIa!oFOEL0)$E z+`wdEq9v4b(nn~Xb*4;*zKm;%<_DnZy+NLG_$~+Zzy7y>^OT$a+_tZX16$5vAL3xR z*rR=(#b%V@C|fg($ZWS#X21}Xy&_RN*7$(`T;tK!Ms#1pZ-HBmq#q*V7R64OVQ5Gx zadI<1=&@$Vv*ntx2|(u*xy3fiIj<(=7FFT|PxacG7rp%E`zP&?x@B)ZSib7WW7mG( z{`*|cyrIHNIP&-P@BB~y^;|OXr~F;gjotb_`TpiVcJ`--k&kwvP&rS{6?@U*SrU__ z$d-BT0yocTx5f-`G@7cRo?B^i{%4MF_4%(skG{wl)UsJ*s18gWy2qKF&OCI@Jg+X> z#gay1rw2$l69n4GYnbQonFG%pc;>(p=Rldt*J{i9piNN(JArf%#&9=sS;-i92j##aU2JcIZkNee}iL*b-EbR|7;R>iB9DC zO9g!zx{_5cUYTzBo>D^x!R6)!{GlE^SS)x4jrZsqy?_P%m!M~HHx6}iLZ@x_FW;mu zh)>y;Lp_e@^a~fIy8%Kei`sz82TnD&oJ=lXyyc8UMaw3dO|}Ki+j_4wlKHc}Y5bD4 zIC=?STaZP%4|M6Aca&4ZT@>m{zH_mvRCaqK1_4SMN>2sUG{}-O!pEQtoR`IF56RFhfU_PB>TW^OeiSmkuQk&FfV%ie93h zYI8|h2xA~fSz_9k@Q^VU{>2IX;EMi+vn&pUI-wuFp@HZLfgrE-2le=s0(>!4g}A2;8Vl2&zVp8 zhBJRTeM*YFy^SuMv2n=x$u0A=S`cz<2ay*bjl(;0yK*3<-B71)IG@z9-?>NnD|SOV zofBMMd!nf0fO7=qgp37nsrsLM)5dxayIeX=h0%BE7ODL8QCFq;th@mlIvd8Un!n;m zrV~1MWQXXj%}X0ob7maFbV8?ZpPd~ZoTGo|=(_TCo}<%Ph5(D%uY#QN%5YQ;2QoHh zZk6_zZY!5;(xYAAR?pmcMxE7>H;%HrtLIlZs$O2yDd>j1X~os0wrO>Uv&v|&Vd_tF z28)WOqOHIlS>~pfXj?DBCod9nx?>O-t=kxEc$_btDr|&%x9lMF{ zTsi*ujRB3)6lY*woa>y?N8J45g^uXGVE{J5F|70nE}>i6zuNqUcGC9DWz|{np}#sO zA0cDu&MiLxjMMosZLmYbC+T&w5=g2(3{3itI8+fjc{V37jK0F(VV~0g>7p(#>(LBF z|LEu1HF?+fJ+yV6r;Ug16y%}3LWrmkeUZM&W)j-1o`3sZn|J|Ae#u%Kmb3nfi&JHY z4j4C~(@4~ow?4?rYTsWm@`tNP&{E?Q=XVoHi_uKFlezp$D`a!*r?x zKp8+Ef)Ks>68^}qK^NGQV*{d76x1g31U0)=P`{UWyl-JnzFVo+qSBPjZjj9(Iu!cD z?=cq!s5VZ}85MoD-whfQsxMYy8NYe>CIJ)gqkQrtz}tuXBot&M6MXT@|8M_1SJA$W zYrBq0-Ok&>W51cNux%tn-Xr@Vocxu|aCj67jJyVC#&qQ*e)3g~vH=4*5)mfjuW*r& zddkL5=)L7E>Lq?{RM%sg{Ye)$6(JwzpxG8Vbg7~PBWa-%}f18xEikFNyG^zGq-jK zV$z1L9QpP)RyG^}QBJ*QPHEp?zS*%Kjt6jnDyl8^b6U2;lWb(I7sU4S?eNlCmvuF)dgdi>jZ0VG(YGq{LC+7N`7y-JpAH-6I?h$Uuy6G3 zw`TVh;ku1c1teDA%&U$+R`;i*H=Un? zoMp@#(H&e{>A(fvaz?xEyqK_o%e--=7_u1^w%x7VP8W-SqmBWc`9Sx@#%G>1gp7ta zvvG9|o!9A89i#2$qz-HEdQxnZ87K@&*9^(yKJxb#?mhwCTKP34Ex_D`zA9ceF*Acv zR$Qh3WpCB#*6VIb+ULusTroZ%NWIRl(h?G&H8x1(>zL--dX2^hR{AWn(%F4b_>Mo` zN2=b+AIvWST0K`J5jq_|eV=hPuT0Ot?pVX0A`35rLZ+suZ{sSd2%PcQaPZoBVrum} zP&|}y3R6qVA6*HArO!fW$e~a-(dDj<{(v*g&w6w4$cKjCzhwK&4Lu(GEVU z9NeA3!(Vy#Vb>1k4=egPphxw8JSX&pLE++8djj&-q95g%#ix@_gk5ct6PlaJq6>mh`avnNR|8b0xs3+rYkbVz#^3guwvr!Dd%$-S zhCqxyxJgKwA#h7vSl@Tk^IL)A(_@$AKSlVD;r$uYx8_Cv&42fwe@2SFeAQc4LwaSYlgAQ|XL}$_M3mlw03Cfevb-3{^XFnD@v+&AA zQg=Fz<7{|yU1<_nqnM+Y3zeZIPS3zy-aOWdX6F?_ds3MVqT5W84mo%5gzjEkqljpCc&Z-ib+ls>!FJ)DQZci#Xw! zF0uz~B9#M7pyUT-%2~s(7WA2kZv$0;7v~w*;4O=wqgzQ}0e79?b;#xh2Rdrul^iD{ z7Ha3T-^;6m6CEy(u2P>4UwEewSBBu`^@z~LmeLWPHoc=Ap}(lkGi?t{B%idL>I2PL zkcjMa+PWTo)N_IZ-32EvA47u;oQ>(ZFgW<4DKnKw>YMnq#osj%|o2o zaSAxZVM-@$7pjATHn{j0DEhYDJM%axk`{vH-88R_#0ADO-u9aS!XMsqx+KI@s`cH~ zBK3Aer;i=${39K#G_~K_M)_p_I?-v)Q$cagI^p)x3G9e=t2?*!*VF4u^yxV__T&6zIoY1Yc6d^wj#4_N>V&T27W9Cj92~_p_z3@xaVoty zz5zyOVeo4&WIvDShfa8L(&8N%h9}J%+MW}G!*>?3TM2a=1h8VhrESp_@}*CiCp)+l zmPLGU^HR3KuuuhFJbCBQg{c&D+y!WkdHrt`SipmK)R9VsuA(WK7h`Emg@xZbtf*=z z;LrSQ)Use)))^f_hKc)5erQHF%Ta|*8E&z=oPL@G&+4o)0Y{a`rlVfU3N1{UHm00; zdk*a6tdvw+FxZ-8To{efJ7S$(L-%!U>2tE9w}0m`xGTTlzpb zU*q)YMjF_#+&a!Oe%+(HwhxVGU}bzw-+)%^$qQ_Uj_uZ~(<&TUM6B~l@j$0i9nk4_ z;?8N~wx4!EM;b@D$lvlqHbJB?NXy+B$|o?Udo_&l0i*yEH>ATM8|_}&iY#!0XWYIz zxV1gTaq!M&8)GbXQx4&k&8ZH`$i@@o&*7h;C)s+UZ8;xH(C86{(G6`^+1ZRMv;!5R z4)sGlp`G-7dE~|dmpJCfc@pQ~Oaf!%OG}(NnZE!O9oMPw^cI^MNAz-16}LEaraVPo z&Rqf@f};0FwDp8>=rm5~@D?7+-(%V-9dTBs{kPz? z?_4oY(taDpvDY~k^C#Ob%Ttf-9(Noj)Lk!l9(5>@0c%^p%c#4$81tS z`BU&Z&RF~ohP%1YH9V0ojwSNK4N%S*bJTz5cQRa_*jJ_V?DF*B;)0D~4lk(>9Yns) z`NA7`E}d8q@=L~6>?z-=G`+uM=SE1HR_T)NjFH6iu3SS+g(9td^?2OSZ=K>vrPd=p z$58G#UF&>)j;=dbR^M@!b@K(=n0bp>?XxLJU~4VUdmNG2_y&;Cet2+Fj=6`2H_*(6 zOz5mMxEY5!-6w+t7DxkbJOBkpppL?rG+pfPT&<8?U}SJNO1?^KXBHP5mwQ^taemH~9GN zOVZ(!4(Rj|`>+C0XSE^P&r9Zo7q2gJn!9bey`tUNAXF*$&DA|8p>zU<==8fqJMe;j@)BA3ZKIrW3cB9~V~*r_Q+zAi=Vn*7H*+QC zFpe{B)^)_(R_Em7;4XcTWw(qGSIjAut$izUvJHNh;MnXX-;U`&+AD@D^g;Xp)34OQ z6pV0&PHX>UQ{dD)c+LFmj@x;ce0S3d>*GTy`i+W3CP&!mmUQ$`TcT6A_5sK9TO9hY z)tTsvZARbVfw;7{ZKnzcNbL*O6UDGK)Wco#r=q?3DY&Rh+jh^#!oBl)?YDLTcr0)n z$d9ofJs-6W`?=$~{I(AJXf_W(ZeDGR9p{fZ_1ldwu5s4xz7e|Z>*=#8VySTxkRDsm z`kp#!(*QRXu+EHo@a>dMS5Oc?j`^!GNXK4vU4bAyL}r`dCGF8!$o0syIQ1CV($GwM z9QzL6)Ohr<9ULx*e5c5EM)@7`Ni*ID=* zrr`}O#badk_f0k%q2Js0b2~=RKmC3UPK!{ucvSt(G0Y8Pv<>&#b8JJ=HINU*D0;~s z0+PFmQdew`bs3jHqtHF(M=LK%zTrulZ#$|j z-5%$+#`~^z2)tg6Z%wIKwXfRTFxI7B^}B6uoYv9txopQfEZVeZ?{_uM_k-SsYI+U5 zn-)X+g?-9<%K_c|xoy@^_~gyXi|v{}E%kG>{oNFlj0+9{&*QEW?I_@5>1L}GQH+93 z8KS0r5H8_Ixdby^Bs8v6HPN+GkLgVdvJnqg`cXym?3~BarDM)(_|Ui1Z-ksts$9C`u`<81Zdl$%buCCYlQ(slrZo#$wQ82)8lfIcbQ^K-irll`x=hDu(OxH4p zT~~*l7b%pCGB=k`jtND#JSzH&_Q-`gHO-bxYGeYw6l%QIqb3jz`gA-mo97&nxgzDo z;+1hG?aLdFApnD+3z;hn@(sKORK55Hj=oxjqE;l1q7@lHEA&#TBWnmy!99dtfi_}- z?-DoxrD42X_-{+)+HtIpg% zs%Czc{?52X7_jkPc7QY!tJoh=>Yu-=Ej{{LPtuzgB0DBTz>|y@Js5fGJ3!8|deUX+ z9RME#Nl*Oq`aG_Der?{yxTVi0hYuIvZ@X<=vawTymWy$(EBQH<>{6Y0k-AU||oB(GLM&_{3!8SIPIQczK&)6*d za*x8xyG3~!$qxi!Lh=T{+u-&JpZ)2>&0DtY%wDl4@wz47?{EIYhqtZeYsoo|tXJ8# zPzV#PQ1BfKU>>*Wi>?^oyYiz~@a$zg(bs1O^d~CV=Vm?1fi35-$G;IDMN=8Gt(a|8 z7t`kK*ef!+%ra(CFmdeK7LLYcn6d*wT zb;52m#h0ullH|FF30wB#Yna1sZ=&@3;?YMuAKH=nKZW9V+5Y_WqwM@o!;A%$zRXp} z_^|wrr0B*C{^zjCVADxDA;|N^%CiiSS zbyAXB{ug(9ft&owP%Y2M^(h?{t57LeOvzMfQC2L!Wfs_{J*~5mzYP;`gS;wW+gK2t z!iiVmqN^aIi|Q4xsY2R?Wgc7$`g~8zgNHS8Ssa&>u}#UMIr$eL0Clusku=UaXF8kV z$blrH3vrCY(dZ0Ev)8AbWXt~_DK8z>WIq~%1Dg&VBIW@u|IZ)Htfy8!Qv*Xj+&42h z)t-tAsbhvXxv~&DF%pnAjxo}IjU&zl4lRd=7pdb0Cq_3ocyWz$9}B*9IJ12^j+}9N z@tapJICNtKPDg9!^^|7;I?Dvh2}j@j z{DDZdwOonHh357(xXi-mIHb6E9-5Pevyu+M_9x5f1m_9mIxp%xr*rHX4sA7(PMpW0z1_!1%qvMe3h7b0eLr%3kqL0yS4!94Cueo+9LMfBIuA!&`cv^|w>V1v=1;hP_Z^Pt-{Cy@Eq&r8^_&8)gO3hB zZi-Njg^pEmKe*2)O z=GxD1*rM^0G562PR%_xwc_Csg6>aP{2 z!a)Pvkafs1ZgswB#i>g^Tyx6v#fcl2Xd4{&uW|m>ITXj7bPQ}THp)}qw9#kSzWW#0 z8_FLcn`7oHx7f5>=O-|==Os%o0i4cq|ViB3p5(PnN2 zU*YX3w%fdRB+?n`iZ)&_&c1e>WkUlUyVX_uL-dI{v^(|Bh7p6;j+qH1)n-culu29u zLbJMxEN}w7IJ|<_^i%qT4zY?$)mJ{$lrhAzVgNBb!3mvl=NtN09JZ0OI;1S!bm8@D z+N2{lusUeQ@hxLc&Fe&y#q}_jlA$*=7I`*A0RF0FB=ZghRswuBzfgv0ds;=@d>8zW zJ8{#bSy@pJfu?J{>Ek*!!~a*=SmI^I(o^O;j-zQY0(MrQ485el9PMB`06N#L_QNln z(2tl)DF0j994xUB@>_=|9hxiNr=WZK`6Z{u8`kOP(5)S&N*(C4UQ%A9z0_}$lxh09 zw1lV-R~#xrr73ou`dhD@du!9>xAaS&UDLVViE}-i&|l(&9tU)s&VR$E9>4vQ?-G9Z zJDt*B

zjC*pukc^ziYv8~L_ZZ6@Aj_5jaGp^`_e$6;> ziPQN7&gkBkKeE}u1-y~pI{vZhUnj!wn110#J8lr5to)|Y5#6+$SkJu3&3v4G)@k$3 zdC)t)OVnxYvUrsBIo~^P7;}z~>3=u|RvVbZs1)$`8i(^^He}Fl$)?y&(yno$&va0B zgEhW2~t@Vn0&W5J5dbCc^Y55rW#pyi0r8 ztlH!PSqumCo|idK(8jr00N5bMI(2llue&*bw!9oIGhbj1>-XphVLM;eiNI_`a(qp5 z(S2;Wyi-={Y>_r-g)yN$wGofP+qd9G>Sj!*UtJ3x+V5~cM<<}^4KhAt{K%#g@SEX; zcjvESq=0$@ANA~y(vnFf9wk)1yq`MO*eZD$J7FEAKM)E*z{wb8=a{769$ePKl}BZk-FyJJ zlTbXRSTTMl?*)8;7z(9w z;&}j@-vOUlS%y2b6wKw=>-Wec|pFHGx|`exkvTQ6rEUEgEjOv7U^?@G76 zz8hA&O{0CI9nlS?weNkNS-#s{@d&%o{-VRjdD(5))$?lTMqU`Okv{3{Zl3|RmeVpy_R-JqKc%h5eiy@*lx_W^L}(~N{HE~|cI#g022N0`dE{E!Hh{|R^q4B2q|dow zjhTMOm5o~SoHsAuVDN7FSItZLRNu-4Q8hl)9NPcaa9(^1?n8Is;*u`gc261g+<9!% zI>fWKYbe3YyW)_TBw~*V;x=o!l*oMTGpuq~{JYR|$&Lp@bg5GY6^$AcVi9EbDp{9| zA7t)=e$bn^-c9!hp={vM&Y#T*ee2|juVLZomILa=?eAMGtL~f9@{h3SQ0PM4WzuWm zu;~C+?;HA3`9Z4wOm~#m_i@E|Oz)lk96od4OLJh`27LdetNq#WZ!P|d^YDH@oZmNW zf&Dr*fEKlFNaHNGjJ(F{&HxphjV#dXBYAnOX}^NlmL5HICrQVVcRQ-QO_n3C^+Y+V z7SolFOD5FOEz(=Z7BEo^G=Ge7<^J*?Ch97>HJ&~IdQRBpz#_;;>_JMsEpHp`uQ@f{ zVd2aBv_#-z?_cwsjKFD$Yu5 zbx`7Sp7GqBZU}JnVWchZq8f-&W|TF#-;IC5^)-&0&90Ai09<;jGRWn(R?BI~GI_=U z;K!>PH-Y0n_85i@U#jkoIUo+ldAk<|ziG^9+}pNYV|54Ku+s-+`5Ap?9%MZyJagcg1J4}zDmd^~6R5@I(ysOxorIAdi@cAZ^8)w< z9$laHc>7zra|y30Edu~wC;=bo1X%+S*nS9*41%7Mn8r<;%gH2J#K{+Q=U!o0PZbA; zIHJ3tokgxX3Z2KfhDESA0W7D@vUpy6I_=!Bi2j7t`&T$dtS7{Q47%cUfD4XJGjTL< zA$@Ka?7Q$E#}k}quDNwScQL1q3obm<;asP2PyEvf{aa6_#Sy(6lc*PGs?#{iy1c#? zYF%C7*m4COtksHxTLxAZb4d+);6ML-RKQ{}UuytPxGtt5wdaA!AEl~573p}e)LdT2 zry;mt4)z7llJrF*k|h}uX&YQ*STv~@Ne)0jgymnTuR?)MIlwJl5Rp{6P*%SM6J`MI`4_6Iy`$iHUhazTjjQUt{S^|tbDaWI0oNzpO)Uqxbt_9e2YOJ~! zy?28J^zu|a$D#4)5C7uOd!FX9k(iZhlcw6e(BzDs2|6WJD=C_M=t869yd z;|V9jp5lxaXG!HACvQK>j>fZ&g60p~ca$`F4hU_jexM#D0oX_;A=$E)i>H6wqBgXJDMCC7Za7^b zMYc-PbeFza-n#2w6yc=w)TLO?o&ws;7y}!*V=-9MHY9L5p?AAvH^y0WV(KR{y-@b6)c9qcaS{2~>C3p<;fQAipkkSM#8a zaGZ@UVpHOX9;VVpLZdn{Dm%3?Izd}nHacF{<`(7j`QpWk%q4R2FnRtCn^qg@*y26< zNIEMth_h^!^xIPEqFx=pb;@@Kia4)gt8N(guW)L8f&9;yyI}gz7kCe!JPG{`Cx^fB zR9mrRfxS4nXd6E5Vuw-1aR`4d8XRAsFX({jA!8>s4%*>rJ zHBNe^n>v8$6sumJ(GJJbI5yixP={w?!uCm@nq^IWg>D#@d5|#Zv>i*?9I>ej?$(i! zc1P#)Yj|CoHW8-}wBxy<@5(|O;vCwxDGFLZdrHo>wr*vpT{brm&?pC_RfuL7(Q!$u zh26|3eQUgf1RIAvWombNjlE~39DA*& zPJw3jg#C20gdcx=eelB{emeO64?iBf=A`q>*YxiT^o%*$9XjBt+MbH8WBQG!c;k3} z1TVNm2uESrQ+~IG{z2GA> zGQ7kA-MN!a;?AYAF^BEQ{LwiPGJ;*!>-X6kc}Je=uVmFNlFQF9eOi6vyNo&2DPGgs zQO8g>4{9Fp2yY`a(RV_1$r~Co z#}k&iwJq`_wnLkrIC6#Gcg+27vE{e${T4oFOh;Zio7asUchdmhk=Y51a~=t0!7aVo zQVA=((gSr8NV3(JU9*gNR!$IJlr4ATfjEC7<2%L+<8mwYv8#dQvysvnwPFXX+qT@& zCo&eGC$S_D?ZBPR@X%v^##eFS z4DTi<5D9nkQ%9jR-h0R~!7ls&~lV#t)UFwN`c(SBGMDGttkUYoaDuPLPmGyIHGKD3T? zS;HB(@y-+Dr(84M#xv71*KHr@ejsn=+w{k9iC2~0%I`WqSNJ9QQZx=-+a$R2X5AF9 z)AtL#ExpUVUm5x9yy@XJ*CAbA1=r9{aVqES>1#t}cys4~pKB zE`bzSA*(b!yD~I7qZI`xR&ID=e?IUxLFO9j;IZ>UH;`mb=v-mZ_d$H=(7-o{wA`-# zn*X`4wo~?ZagWM(s_8VtjuWc_foxj$!$#BX-7i!5-L7iCY|Zak4#-^fUiP{+adGTr zY=w{Q$e#_N;CF4fXq!?pVZ}w%w3po|+v51jcMUdB;It6Y`TMW)>m%ojeyc8R6=Kkv zsAY(lpNdTM8Zg1w@i{#}?|97RX3#l3L?pBNV&zFnNS;8aDDXvRk-eK^UZtA@zk+Mai9xcT`i3UkCt>~F;}VNJ)rkUG=5@RpuiL4(al+Oq;S--iVH%Mo z%-_5oOWy>3PJ9aoUVih32f3$5(>zMSeJZ=f@s(Zk$JIwSJ$=M}00wO8A_~4J@}J87 z&`n;mH;b_rVUc0EzUFwR{mkLkN$5}cw81SWkN0u{Tx6#mZD}j}A>;jzUE$kycG)hb z%)8ty|G*10aL#!j5dT`oOUa7qWB4|d{-%#<(axNFRG>>M(;mX0r>X#n#h z$!=by=X*_j>JM-`Dr0JXr7-{NxW4_X|7o+Kekc6(zx|udyywKH!2z9gb(GQ`w@ubT z`CtC^|MoN*_(&_ZoRhOj*(30eCdZDLJI15guB6ZyM`CLNR<@~%1Y&sz>b2mk*BZYD zp8dQ5^0r{h`R^&uEoNS{PS0td6kp^E7DTds<2fePd%o?=c5F$=Gjj@FQhpNR{BHRn zW+0#J^|j86wrtJ^&xhWs75mDga!tBtS(SABRxPww!subW&(E#j56yq-zx`}E55L#< z$;`aB#l!C*0fMYM4g6$BGNve>)sDobSeARSH{^5OvX ziMKAuvTpv`@xTT3aYocpiv{_WUW@g0B=go`M&}b3|KEAzMBx8b#4*H^j-kl~_d4$B zEa<7K=DA4!ng!E3WVtw4XEYaUc@p|9PP5XbLs%T7amvsk!*aDKtQKdJI(W|e^smNa zn$q8ka+7w|wM4?yVtph*HvC9bm`*D^nfnb49~y`$jGb`44Ttcx;Hy3|>bF*xqDdF` zLLQJ&2C>Njvv{tJ<;@iJW|U1l;7ON9$~g`v@Z36FG~6d!pP##k&P8MD2#zB-ksRYZ za>8k!^2EiEI-2Rod8uRFHF84UM>z7`>%d79B(bWOc5W8;B1kC-4r%vVo)(B8Op$Ib z(majE1(@YzMO+wA;olL*Bis>5gB}vvVb#UYI+oT+%Q`KUF1c`@l^xtVi0D8oS%zSt zy*g&wHu1U8Go34qkfK3TLg|pd;@i6#Y&0XUz)H5L(zg@(PK3X(Mp|%Gxu+j{0=f?9 zDPaHBfyFZXvoG|b$#U{RJ~;TqVF|`~qHj)}#HllVUgyp@E>MAVWC3=19|(~Z@;b%= zUFWh>Y=urlCoG&l<~}(+Cts%-9QHV!Gp7Pm*V#EIB-7{Z_pu2u@`kt@Q|JH|r@G;Y z9(mFDXew=h6!5aRuWy|xj2qfcZrh@sgg0sT;D2P3W<&~UkLWTcU*8)PWVKK=g zi%9V?`l(E~{V&iCQ+^;3UzIDJEk^(sr4E~`ZK7Y4jhY=ugl|mJH0~%s^-Ej$r7uYc z-xK;Mx+;CGomS?*d=Al#^c^-qhy#?mXBlj+x~^QpEXP0jQ-6xp4_cNX^k)TZQG(v(5ctc$?KGI93(kqTBi-24_q|tmBq(83ag8Zou2sH zx9@JkpzcaX(OAwMZo&~eNF8;u@o*T%vDCf+>F|U8Ou6V;YE$+kco{^={l*z60 zAoB)!ukDbc*hk7~MSS-TEr!yS8v;o6pTz5ouT$q6PxWQokK+d?o1b5w9Ne8FhdAdr z-di4Civ%a0H|U876XaJw&!)s3e32o-&9_T5_6p(_;NXRW>@yc)>f^^dDs) zq3t-yMi-*D7L+~I%War(kfFmmoS&Rv-{MRZ{>US0fIpZViqZ}oNu&4j&Ko;?#EH{K zj+5Mn=#!!mUYs|yryS5ZB^o<=abY-4%GQaQ4Go-^EhnvyKAAB-{DD?NXGQ)g5tYDv z+ga;6NS91(^^FauUN#+GEg_ zEkS*t?KJiV$1iP-GC;v{N4CV}osHs({`gKjH*xdRe~Y67u3*!r;GuJo%##T*gOO7? zG2`Hm&U^Ct74l4&F%ll}?j}xgz(yYOQ0HRVFE1dEN{TL1(!Xr3eL7(zEKvSJF$TEW zVw;@PNhkK$TU{F!C|CCt%D9UI`Wc(^oWDFic%c(I_cz}%XU7@+)psv>=LB@-@U>Y8 z%mt5j^_Kag&h>7P@Z*n{2S5C9aqtJ7(Eo|k(24g6=Qo$|$J4~^U+j@{hg0`0@1BsZ zGrD*5T3Q|dX{q|*oW_j-%89Qx#VAKU^-7sIPr9X@epe_k_3Vc4l1uvy8#p)*vG1BL zUo5AblxfYKz=0EwPUEMiSLikRL7ptNkNBO#X9@8zlDm5n_~IhlzwO)b%ZC7k~uqLg5z)I zz?8GeK5cXHLH?8IwX$mKHEAF6kj|p{LrQ%DY~;Duh@l3J zHyv7T=8ycUYqt%pp=P6o3a}+O;JaAqthUrL(IaL3huaHkyoH_qaj zn`xu46Y!uclePkUjs@P}En9}U&}hl9K!Qx&sV{iQu0r1$k4d?eaowko40>GR>wZvhrG`?=ep;(Fy7MVZQtm-(;BAhwtQbx zw3EI-C?7@J^_D)oe?^w-GL2&{9i^QUHsW2b!v^X14o#oq4cF;S$DBULAC}#1d&542 zGpAe9p=m<)dxxEF*l8VhUcYy^m2TRnU)E69+N!R#@|^ap?Ql+pPMCrTTI?Ej;mCO& z_AG};31J}~26+iO-ZGCOXd|$2A%H$U@oM@-Lw$pWrMlF&Uxj6=y}LQXUX!)<#kM&G zUwu-Wm6k5;TH9CY(xUB@ag0tAK9WMF`2|xpy!feIK4y&dCKi~&HOfhFjK7g;hrb>B z+t*!$K#kfN=Is6^?tN^i9p)R(O&gs?l3Su_az!i3-~ew8Fs4k`1iY78+?f&+6~4UYDTg1vu%y1ABX*yU24lQ=j3p5LZ8T243o5` zoW1Cfm!Bi=1;1B(N00XNp7Q&S-L;{E<9s~GN4}H%<(rRIINM>{W3sX^CV;$5SH$sA zf|!tef#}J+*}=g7lsAjncJlcBqnv$IWD#f{)xV-QpvfGZbhA=;z!0=^n6`Jp3p}6p zsXuB|(eVz{^q$1`cx;WEPg$ljOSZ<0A8!A{46+^H=d(Xvj2~zsEb|qRvLl|mg)`(? zX^OpM>iaUzChq%4BY5z=W?tho`h>4n|NXx^IQiXw`=Eu-4(Jb9@~cm1hjUNlwSR8e zU>oV?OMmv4|M}o4pO(F4pFYGPbQw1`wIT*&n2-?<+-^MfDK@KZ*_RoQwj4>nmT_oH zJUicTTL5#~EHcv-e5ZA2`VuE%9J#J@kU7n~F`rQO4o=QxAG^f0B7Y3(SCzgsFZ$ewF?jaN?AxNeM{j`aaIaA2M5aBG-|45n-JTolp?h+#@BHoOLtDJh zX9bXXt>J{sc-}|)TkXhzjUDN76RuxSbgR#Q4Zk67%`-k|{MnG9^M)wNq&KS#sJwa~ zaKg76ru19C+ry6X(FNsr{Q&Y@wG+ z!hMf2=H(_`P~fXb3XwUodDY3JPJPtDLZ{cj* z1A2j4Bf{0fU16&&@^#e=+nj}6wtcOda-$9lrC^oQ(WOrJ0tEP(5}i%zbj+gQ$dhJ| zZ;)L%OI_pKa(?cjIqRoh^adZHgA+~~bTM^1k-EXb+06;3p&z3k$O(A+%MngT?s1Td z|8z1n^b>l5rL3<-YQ)Xss*;KFFbxC%h6jeU8xB=E^~A{&J&N;)`iN8G9XhQ3#Hkz@ z@jywOJmLVysYN~FYxw?J*PMDSq5+&cV zxl9vb@Ie!yNiYLKXp4?~=;u8SEBBnDm~z7Cn8mx7{OX009TpEqEkpL!u@q1%C&ftaKoL{hGR|H;#HZpC4iy4ml!K2Xy(Zlg&#`T-TvUa&<;`as4UI z=;tr*IC1^<;H8d}FE4XSu8v@C3J_jO3%3sTvckppZjg~1yL8U2ee(S@4)YSJ^A%24 z@+3}(GLklWLyI)1mza~pBQn|S?G-oz7J8~{wl{g?%dhZ}I^tj`pSUH@_d+r13P0tU zWq{WiNvAbvlegu_#zIwirF}DBxs*(blzJ9%E;v=UB*uSY{-T%YxkHZ%*Vl+Hbf(d^ z8R}qW9AnT$7vg$ZG(3}0q`UA}p2uNL$5{H#8+1uI$7y#shv>Mi1GhYhLm>{u=Pz+; zWITA06VP?kP^Wd2ss4ATV}LjBhSKBZm~zJLX@WYN>vZ0?o1BPK=MMXj)aaK$_!F?KM6CyZcp1kr@U{3vgC9Q0|z{$Cer#jdxzdJnt z?r6W`)Fr1?i%SQ0ogNL{h{4m1Z?Qvhbdy)IF+7LC@{-r(iW;b4T#*qm${4r*&;wGh zJlC9+-e{XraL7f+nX+-7D1O9!=&h5aIF*%wWmqIF)?5jI;hpR;!h#DPu1-Dbf%;!X zq#!r4E(1BF89u>J#vcd@@3t~eMj`N_5kF-VmXG;#F3)GEL zrY~C$dT0C9EuE8e81$6p+tWMfJv+Ez(-zx!th1>OT{wvz9bz9(&`EUu6#I5O9MJL0 zL?6&3Y!fpbc!6B8bC>AMYn*3aR8!mnjBb_$$nYROA=KYK`b<8Zb`eO zH++jcnDYgoSf=gtlxUPWj;x0ns7p5TBb!O+u&FH0aL~2R^ihW@Xoq*s-}J=Q=4l&G zu(R549WC7`!p$JuY{GtQ)wgl>l}E!)QVCee^OpBOsie7#(=KmwOjkI0t1w_Z+AbY> z@~HDf>CdfwmY$lcxFL<>o6ew)slgImbZ(1Y(FS!RbHqW0bj9Wa99jlVwLuRKUdTkb z`+f~S?fW_9obc)r$MX^DaPD32Bjo)CnP&qhSef=IH|vb>Oe;cYP5>Oc&9_7Ad{U_p z0Nfk+nbPV;bQe~juks>uY&U%3j`wsx?rb?b!A`_+%wK~6irU7XM%lRmHG^u3|_j5GS3 zHUa(}@V(-OL(+c2czSw5-E4w$PXBt*C&^-uU)Z0tZRm}CN1@%^s3Xe34He`&n~uD` zOk8}*NL_G)9s9m~act5lJvNp$x$%Y$z_E4U%7z`_z2gpjq-hG2Kr~uoEH28rnU8uZ zEj$UNOZjkHMmY#B|41?`PU*^>j+v7dfz2uCI-t|`GumFxrsa5ky!U<;i3E4}-UY)edHueZ{F|?S z7l-k|${A#d&oG@>X*1F_-nd&@>S1UdAYE7IXa09&v$su4^;s5$MQorfjUGNP0LsSe4MJWfhTu-BU6@HSIpCq{g*L1b0YgnCJK~? z_P*z~$B9n6FlY<4k}w*-A6hm%And$;?>yhDxa~D{z3P4Chd6ALp|)lD27@Odbii*=_F4XFLkRGRi@AdM6k+T)_3BuN8P@!Rx@14|X2Z-wdFiz;n^{Jk7eF43$M+eo^BcayPTzv@|GsuyHlK?`ynxU%J|mUnX4Z3Ux#n_fYT+=?tMPO=rwf^B z3~;SW{XSJw2uICL2bx;6I`@RZen-XuJr!uPQ+*I83IRYCK_hDogy;fm>ZixbUgo=G zr$IC3=3Iw+d6E^{^B7zx7_r+ww29rYwig|)?GjFQ02`z2d)t7y&fPdrDU*$G87+PH zFIPv0(5lXoi}_OJJoyOO=#(ywY|eP({FvFczY}IM4)xgh^*8#J7pY1*McDPO*`_|c zfZGPKEg~sI!CIX&IE*fOGhphyu+6rp+S>f9aeGB&y49$0WA0Nm4t8dg4}r(u3DZSLDh(T9T*+6 z;7b{Wty{G*ZCp%dea}_eOSEuCN%wJ&V$Z=d2fic+&i?dYKS2LAEIxnx0@CiMd#suI zX!>>DdE|#AL*P4&sQp?8$g%+)3bSPks;*zFE!c8mc*+hcC9UnvOg0@=q8FAjqDBxJ z?c1433EB#yR4vnsf%5%(>`-}fB6re8RM@T-OfqqZ)r+Pa0f(j&DZ|B zc|)W#*w-DOIB7Kc4wFXQ4^^Gcy_}*2+8@ug$M!F?hzC4S)5H0julC1Z(Dm~4FE*)p z%JcQDF?!RsZgD0;M}|Yl%T!(Y4i#830ZN+Va_lb%E!Vmr{(;BnPCN-8|Jd39=xZ1^ z<=B?<#rw#2>6w#Ek`~!1b25@Q$9IY&cH2@@>w!t!e z&GAlqBB72!XMg&yHe2(20{SLfe({OgRh=C#eyY%ZXxh7f6WMLr-~a8uc+yVjTm1NF z=djp_)ov`~5r(A3p0ZDJICd)GWUEqsGBiC-Szt=`^<%EBap)<}EoL5V$@zW8p&7?+ zT!D=7!VF)NL|TtI=0|QkI^W3r{nj!L+`tihLobZ)Uk$sT{wsUm`aNN51F&CNv))PW z)|~gFZO%Ju+XGN{RNFPEm-)G&Ut62mehqWjt@-Qg+rOK+^+9oe6^t>McpP!$z50BO z8#A2g7UPj{#AjSn`2|MGv^B}|g)8xqFsE1U7k_SzL0^L&eg5%h5#~dgDc7BGWfJr{ zDRB}~|KSg;-#;}+F+Q&O`)H_RXFd2n>Wt^`nFG%pc;>)U=RizqTS~)@+wiuxTj?{L z@fp^#RYqH!&jgb`!_~C}RY<6Me6NK#H8WY;Apw@^k|~tp^%&{Pc+zkU!h7d+m@M!t z^*5~Hz;E`rT~EsTrre$`i(d|lVLbs*hjbS0>R8180l4tVMYAqs_T)AlcW+K_vT#;M z#2Vm&N%xF-YfLoyc6^?}={1`2tACw=As9LVp+IdXix|CFz)?)0TXT699D9 zX?iZi2N#R-b?wlJP$v>k^os+5bm|dBt|x%X-g59*<2rr4;WR!+;VPp9@g2=|&dY)7?Ccg9a&j8C zi>STS3l~+pnLr%QaO~0v{p{v6i=As_HoD+aJY*w!>&hSekJCjkk)OrY5?Dm8)CYi6 z{v~^~k(Yu&?`TLluHp!T6Qd4cI#qX?&MDUE$&%jm1Zbuv7X;#1qI2DuPBhS@PUi%~ zFvo&8Psux?ku40C$6jd*p^kACA2cR6RVHtNq+E)MU}-9G|1Zw}fAb%Gz>0KyiDRU& zEOd_pI!*&!cRNP*B7Cdt{wc0oJd*CX%OObO`JiKzPHq$VS;x zt{z69L1ls;hRR!7>gsU3e&!%;PQRHDhqkKMd~JhQD3!l7T%Lt*=7m=(Coc>#>09b6 zFzqy+v4Av9(=n&TR$qfh>Inmp&blvfK!3rhx;m2Uq^b=mhfujf-^Ce6M@cs@@b(1s zY;Y1em9BUS`b!kq#s3_6_=~Gw6>$T^)S?!)u%*e?0j94?i6IK=>05 z={k?!;1s6wTAZ!XJqDTc+|NB(9o^PmghD(h-wdZoPd0x2`XcuQ zVLPDT;GC%)a$JRRi~T6>%1pRC_#KA#OVWUcff3J;kv6?Y1@*CHGGrrubs3rD)Msrn zZ8>4AKf=Cg_R@FkFUpm^4~Gx#u=V8t_h=hyzn} z$oJHCH=IFi<0(W}Atf?ogGNbPcuZ%5e#1dNVxxs_ARL!FCCi?T4D zF$^3!oZfH>yyjY7oZz^s{2dE~@rtuQ@r<+DB>R!H#5U2JX{nRHnXitqx@`g)s+39o1KZOyA5&{9dSHS2oH9w%u&d-pI zb8V+>IeE{`GqhhgMZ9sYhi&K+(Bp*ul5zRvDfSti_9lKdHVTm^J?`Hy5xBy}Y43je z@dBHQ1NuL`KKLh`&;P*vJ@MB%q5lMLU!&*42_1E(|082>=Ia=twB2`Gk<;mkd4jdy z3TJV|MrO=8l$-O43hh_*O~AfM(GmNkJaF!JgMMCJ$@}YU29oiZdCVCbMa1DB!0N-r zCmq1=k$ZF*JnEFlVjsb$v%KQA>*!3bnaI?nB$RafmxZF2rbm})BQgmXn1gVHADRZ=!p?m2{N%u9MIuw0N3BM!+3= zDTV3GeshIf9^-;!n{BCNNq@3POalyQT@nrHmoPXS^h9c2b zie_G9o53XsrxZFpW5X+KxnrwyDQV1R7$KnOGAe7!h7~PoS@S!5M(!ZZW&prSx&7a9 zCH5m}q}#VhZ#{ENO&LN3J&vwfX?dLid za(#2=ErfnsxMAA~_VNZN^s=4W4IM+(x1g>E?HXu#w=LsoWWcp#s1@Fj$e;CxFM&e&yk}5NR zM-Uq_B$6?AkXhpL#RdpmdgnZ}6~Sb2diVED*(+=u{Z6{w?CtLEWatpKh4R@H!buDW z796R8cbkOn=45Z3qZxD@)5VpI87Qvrp-Np;Xu-a~HH!hCm zS;Ls-)nQ&j{dSu9maXrs`j&L8@Vn9P%K8SQ&(hYWv1pesmA2S^=59KmpP#?Xm~@NX zbu%PQOT%bK`nwPW(CK$6yizvc%WqLwVoF#jbR9!aol4)|gV;E21_Viy{g%=xo8P|V zc@bO9w~avf1Srt;Zv6{LBB4WD15JJV-5m#X?6Kcc+L;QilVaS~XB)jOw_pFRJa+30 zUHeJX`(tHIe=Kh*z7zdW_?^Pj%iO-kIfusk-C{F*$4f+`Lj-rn_k=Rl`*G3MT}yWz zABU+*HGMO#j_>E0-f4ynx1TQN&NJ@SXU(O|Yk2b-)_BoXDfA2? zU2{V=^nMG>@9S-48b&;Y%YJBoubee~eK%%$UsY~Ro6Cr%Xr>=WLk&*DXQ^R5w=ocnEnH5r-TnYXrif=vI! zg2h(bKj;3t_G6J%Uy@&#x{Pmb0N3z_&foG4O}x?_EBv15x(iQie25tBF`j5a+P?V? zsP#i<%_F77IKOW~!!f|p?ssLby*3AHS6vYAd^W``R^N-I*1aNY9(5?gCZf&+lUijF-VHxYG#JTQG2H&*>ffF=$r3>14I{s8_!DA<7rkzjt zv9*A7UNr91Y**+*U7|*n7ahtwj~++lS*ICCQFb;oNTFmMS8Y*L8pS z@FestK76kFeo1(A%yE;B{S!i8p8UnZ5KGN@-d}}*D^`}j^KT@LlwSPVJ&0@C3 z=-IaJ@T2vk6H6uPZH{+7@Fr(2Z%lbNj*ho}@&3+nI%OL{SMu)gYj(EU8Z*AXd5@FO zXSRmr%C8b@-i5q;Z*eSg9C9ANg^Uub>_?f5r%Tznvac`x?O*--%|`#5|M71&)1MQ6 z8641=vF9j#s`fO{dNAP8)EsTl09- z`G(89XN*TXcC0HJVakodjbjg_U$;)6ed=?IZu{p56=$xA&q>RB3447fvBx6|1*Qu+ zgYDVrNJ~JvieWtS(oy38N!?HE@%($jD$w_~IipCvgH)D%{n&qR0M)5)Y)UQ87{Cek z3;J$>NFkox``snw`kj>kJiO;(%u5VWh2c+SULF_?et#sw@JXi@?AYrn$ z>{G?Vfif9Ak52c9|bN9RC|!0%VBu-Z(+F4tkFzaOSggX%i5@-gB5cwQDi zIX;qMX5Vv-DYTbIS_*IF0ZN|p55I9TcM0qkPSZx6!g_mB6C}nVh*dN?qUXjL-89x+ zQ359n92ibmczcW!aXAXGNJ@u*g)SG{dXGh}W8qN^j?m}AW^3m`$vC~l0Vd9Ho&W_d z@^mzeV-qjdFO6Bm#A4ih!$}PUpL4SEO`OnmFct*O zh4SPpcPJmQ<107|A<4{MoyB%$w()j zlT#d%v$$KQI#`EpG*8M2S7$RFyl`5&#R(l@I7hM#GCI5mw>o6GRA=3u5EEg@*U>x) z>?`p>+~7gAP2vkkY;gcl5x{qd9(yVysllmirO^|+bsEfO1}*}mFUV7!;o_WTyI<%S zsdG;8-%6zK9>*TMHdE)IepFs>6UD(nl!OypFR=oYCb6uyGiNANaHv&f=4>c>n-F z07*naRMA$Qhi>5A5zcrgaYTPzHULMjH}pwQt<=fKzW4G4&NMplrEdC~0+v?b?Dw8J ztFxF6zV~`3QHOTu20Ng$l;uu_L-M&!ia4L^h^`YsI}#`t7x=rWfc?R`<5b|QIs{Kd z9s59@>JT0ktA!#VRj04}YQ4F6WI-shjq@>KIsL&`^3)ldlt)EUvK)OyE1ZDZR@?06 z8sz|CyX3J8`vr*;5~VHGGE_I^iB#ABvm%U87QlBu!24lW(V8+6#g%3m$!q1KhU1bl z=~eGq_X<>yIF1gKus-dIBddKcvOw3ctM<$A>Ad<`oON;d(0N=(bamteo8e|BjvdR1 zeK<_m550AckZBl;^GG0$=6)@fCF zf>(Q>ZsKry$!XO;{e%;`C#&OldI`=OHeV;-@~50;&`0{L`e?s8=7i&8#{0-2YDXW&+42^LI@=ze>#(9uh7RRc<9tp| z)OiItlv9g(ZEeyN0>GXXv1WdisVen7~PphUuoZl>e{s_uZLfVG&dv7yQ+Rp~7TV!yx%ypZClTgWZohDh6Z(sckJvEU zr>FZpj+;686kB~nrOsb;G}lr2rwbjJuW&%eiIy=q4(PPIHUTNyq`it>5LTO@8@g;- zwG_F?hU8a^M~afJYifV&5e+EECG`Pl?CUWdQ}~o6;TYXbV zXX(i9d_#Nf1{3G>P3bz(!Rric;LN5l@{+i+v3&Y~Izn9Ap&qJxXkbo^bMu6rSCmbt zwr2jR9j30@6sH`E-TcUYy=*8zJhocLGd9D}R;Y(J*E%cHX6iaQWy75~kYUfYCzc1! zzLkqPW6@^+@zx<9o+P|SR^^25yy@cg74n2n?R1VTa|(Jk6(~Mt!yLv$nH)|Gd6vV1 ze3CDfub$d&^|a)bV&NaX0`@VEKqp*B59=h>(~PaQg>?9*1Nz;YYj9xq4pBOsnNP8K zI-8%rzybXQW3jj6v75qZL$Du+4&2TG$|hgIvkNxy(DwbHefbJG4f|rCT<%XQ*DPQg1cdf&gjsY z4QsUZPGX`x*fZ@P$mNc6IX6jAPwhY4&i&j-sW&H5-DGWa6!{vDu7ET14CD~DGv9H} zLbJ^9{9s@$ztF^QaayJva+1l4 zz7d9I+a$$iE>|+sOv@u#V7bw)a&mH`DmJsiKIF7-ow*@Q6!KMkI^Q3&8AUmfmfUS` z*(zWF7G8b{?=3z4Gp_9og(Ebc_R%f0Tb1WGE%qqRhQi&!diaP@{f>kfYF2TyItL;tog_)=@zGsaxU-P>&7!8`#c;$!vS zrU9N!KrAzEBK{@MeCKG9jW1pXmi>_Hz2Qe4AA(jy zeDydwr@e37vW2d2?&FH?Hs1qSFNq>+6(D*tXo}VlZ=I7?`_G$fY-3+iw}&n{UncG* z2bmW^^!*sGdJMhO;auF*BW>k$&OD2M89$weNe6$~yh6LoReer1D_R_ntF7{#@fci7 z-x{>19O)hI^VxLMW*$}1SsoQ%%dUBqroH#uzQ*)AdHk%#Zsug#dwCQS0V(P4qOb$m ze#<%^$Xvj02#hgy^!A8ei~Y2Y*kH&0z{~Scp1jNfKxJRdDd_2h+A!;^IY<5G;CN!1 zL{z&Y6O7A|pyKqJ!#V!2#N#Pl?s13@&v_TlwSC2>xrW`m%Gq-8oy&Y^ zyu_;>e6H(#aW$NwGMwL~rQeNv?Y~#*Zu*R8F0)^@-XFGohBMyy+B&Ud)SRJdSg9y& ztn|jVpVsL@?bb5z1v#Z=!2h4Ux9O2ASZ3#8rbv>gqgSqvbGgt(P-Z+BN!erL|}yykY|MC8fptm^9S>a6t06aHmx zZf9q;9D zq35N{`*7D}1QU_&p`x#I=XiJ19G68GFd&)2vm#r%NffaM$c>J-e2?vIou%7ctEsnz z7rEp3(BF)CyrEqEXxKcpZ`8doOo}V%8aa5ZO`C50dZ?MYDG#i3X=o9hkJaYmeBzw2 zr876!2MD;}o#VDH(|Mlgy0m3FT*bT1l~MFI#4hqB9oGmUa0(@OS9n< zxQu(FGo`TIXlf8 zmc&i#f&%g%5?L-99Zx`J@suN;dB*gg!QFiOUk>&+pUaOgVg0bg-lN0c9$YMCByFiv zy2C~xbwm`%h);6xDAK(Q3oO;M2li`@+PpbmWSK&5IzO(Z^}P7B9?m~Ii1_kW8zBlN zVfnDfGP~fR`#gBMS^f3_Y0rJEEg5<2>edG=nI*Ka;2V7KjC3>Xc=Hq18-meg6qMG} zfg3O#w&>hFmOh6W;iW@>p1a|NP5;Ng`_~V+@g(Dmecq0)3bGDICzKvg7i9hfAnP>! zgpH518BEE6KV*=YA6D?Y{|UvJRDczKrr%%v_5ZQhvfuyBzyD(A`9+5OLWyl?t}(b_ z{Wd)RIY|0v|Kk7sIjZ?Z>R96All0T0)DAt8Ic7GWsH>u9_{uy4oS=C|U}ruuZRGFx z;!Ms5e=WVgJ@>UmGs$tY@1DNK*e^MYcVA@PK&0b>I03~~;$!c40TX2Q?7!np>)(O# zIe1I*XuH1D-xEH(Pv^q6UwY*AZ&RI}u@(Jze5OMA6k-+@IUrX;o&wb1ffo+^`Z%DHZTHl$3Um(J z<@Gzg{dwNUhW(AJmIY#+tM4sF4OrZ!1gtQ8)%pVH%meuTPL|T`Te`#gI{X3O8=H(~ z4MOg~PKdgaXhoyk4A-;~h!^JEsEqG=RAa!{rf8&r;jQ)z+HQEGrD!z7j`5BL=xoY$ zb27W6Qb&wqse|oV;2fK(i2Gi}K3kl(GrNJ-4P5>&q4G55*k~w%@sYP=$7mTG8YgQO z;qA&AoUK+TJknE!oNI18Jvly&A)f|*8u-L$M1u{CaqEV|y5+EMyKaMY?F9hO?*-W$ zg<(YgqXA9wll@XS8B5sa5vEa#Xm|E?{ay zUdRj;gP5YVT2$%PxREk`xwc6>!A*@Z)B!pNFhu4t=DBG3LTG7-qTq$Bw##)GjpqzR~C&14a$JG-A>C{QQzznJ>=iLyT>&>c;2v z7q`zIF+T4x(6b^9K{X?CKxmkzk%8skog-)Hir9$JpBgx5RDQ+t`t|D=ggMSdqS21| z;#YU5PrN<#^u`--HCpgiN(_6E6OEPSheom|7?tIw+!$zL@D`&%jjp5(qaWCDh5pj= zD#oXd1DuAeIV@q^{Aso292bUcSpF(G!Y@~=U)+qU{MzsG(s0Q*VaJeV1;>dlV=ZaJ zpg_r{tS*KOv?V$ZU*m@|t`Y$}wcmMy$8yo5!*I(uy;?FLHp`5Phu2YaKgx}U$n&XD zAhNfcZPngwzEyYi1;h796gFw_G&o>*#y9<{0i1@ie(G=!5NW_jU&fG-+icHHPY+(7 z4a07X9uLV^#vET7etAQvx<`4^2)&Hj?H>*D>UPx=aO5_14bZ*C5W~bAl0jceop>&h z(|72v?=cSl7URf2as&Dw{`f8aABn#?ILCNW<3wdwW0M$dYv}ljdFwaKLw|aV5&GG| zS6_dXx=PmXGd4ht(IDf5Ta3@SW&Qi_F~;O(_49Y{NW-ul`eQ^bAK_OSm=KVMaz}P1 zY}{n?iqq;1I7VFEL>SvikKdGrDK8q!~3XnJ{pzTh_O>njYgPpcp7dFie7MTR{6O;Xwfk&$bl zs!252_L;eT+<#!QKz`O3R(eCR`XM*aI&1*&qcP?^Mzxt$SUWg=EvhrW{82?Bat_x_ zCQ?D5!~2qx7zh$~{7I^m+dnIfSmjY3Wf9pUT)Kj~K~_4%!wWFv8iko@&|S=P=;LY} z`r?p-7Fg#TqbpBNFls+~$NB@DSmds5Fjx;0Fh=xtV+^6sH9~)Tk=xL7i!+9_F>po~ zLKZq!66GzA>eur58=SCeVdCtgZN73tEIhDK!2v9e@rak9m>v{`a<0*=huIuppye&9 zm)MkRwEd1-j1O0r;JN0;V+?)i8+En@{2D=lG`jEzJx?z(=YWTPSH5F}?!uJ%q(j** z8t{mhe)k4=jKZV`1NhOxL3hE~4rN|L-54l)AjKJS?|KBi;95l;T}EKQq9fGN&M{6i zcQ9TH{5+PTf%o1J6e2OX|)lo@rEeH26UjHfa}k0H7@p699C z&eat@m#k|YcgmK!&HfzHN>fU%B3rb{o8znd077sXxq*+NY-oVlt`FeBJv3@~D4&^K z>5DSRJEc9v57+j9V;ZXwx<1rW4w*bY1O|Np?>sO-1N04JK8IJZ2C$oAfX)FNu19KZ zcyaIs-TK{|I@II4?>yk+JB-oaV3hurLpeB*1HN8gqED`9r#h48;2JrK5jwPj%jr?0 z?yN(Z2oAAOK18q`OIt&9=U4iMzN}~TEA5R=gbnH+6MHbNmBf@i!XW?JRv#;+w$rL-KL@SEqcKU6V zPD=lRuXL!mtfT7L`c+5MYmfOEsiB)LBL#w0=+s~NBoej%({GM3*AVny8)CZ7j*bIP z9JV2b-_RH%P5TjhL=vq zt!sP_g7A8IrzK zvf$FNJq#Zjo!2nOSGDGwuj1>6;)Y5X_x`fNY+IQ1Hr2G>HI__nVLLw*j|wVG3D6bY zTP{`aS6rM+b#`6lOSck(Yy2Z_s(CSHbCSU69U>PVverh%j(FekoYrkS*vr74>+x}iq@zj>D!mBP;XM1Q&A5wD6JI3+RN!Gs}xab_8cM*RF z1Ml?i=lsv4;QQwZbNw~%&mM?!%WqJ3tY3V`wEt>u5Jx>Wq537w2BUKYnT6dNQity= zE7u+V;AUZ5y|?V?I zs`i6*ROLK9cO9Kk!br~WoxnP;(^H1TPM-irAZxZ+X5jNRhg6T?kmDUkbe>W$LV&RI zl=q?Ecd3|F8tUels+NZ-JGCbw7HTLybe&V>4L|5lw6Qwp4T?Y#YV?P2=vQ;M@Z07O z-8R(+V!=cbpD#Mk_gO2EI^Gt*&HTvvuGfan7c2CwhUulbeb4gU%U-cm;gM{x4dmSx zD{4J!nFcZw_L;PY>#s)D?DQ2u)KQ7+;a7Q7{ar(&Z~xPV;vI9KD@O%|7|m< zEr4^{M=k`Ux`jF&%lS=;Jo_qP$WJ)U5MRL*wp5Z}e$Q&eChy}Pppw6J^F#{N-!}Wn zp{GH6w~krA)s zVjqY`_B8f8F37$!?^ocKHjV$;@1C!LN_WZf2GW1%?1z5(Q#rs5ambv4%(&>U$GK-4 z4q#+t2*Yf?@rS9Bqed3Y+piQE<+iCmE+o3qgdd0K*IQcStNp(Brt0r^Zt;B|lNS2R zBp0{1k^sI*`%>6lFJ4<&P21D>;#KitFa#c%>RPNy5c8!A=Sye)S3pvR2k#@EcI}&1 z(8=QrH2l1v;_C*y!NO$d^KqxgfAN5r=X_Yq|M&m=+XwA^pOHtc*Xg-g=h3CZ8LCm0 zjxI?kSm9fzHhBAuV1_eUnd1xRyuL2>RDWsSwCr;?ys(W+#(z(_Me<=k+ppah_)}jR zrXrQW)W@Aq!uTO!{4P>3fcx`H!CRL2Kuv7K2i_!9S_hw#fBNTtd+^w8w14)y|MZ-i z`q{T-ZfxGhZ~N?GeGsZFoA z1foyROw5&pPrWAwE%C;y+~Dbzd89;Y#G)y$4+4t zem$l7xACL{pY*AW0a74Hnvy)_nd3&Eeux{+MpHIEwI7V)sC7>8Bn)a?ZLY12P%)Mp z6>V`huBt~O+<5QCWjD{Rz;(yw{JP<^-rY2S^JZ)fMq<1NoW{%7-1_M)d3CcS8>KNi zU9C>DAGRfxJ%S&&1#*k+i^W-*4+Nr_7VRk~% zC*dWV#_|sjTRP|uKV+gXKIZK@bITcOfRVb*Y@n?_2j8a7LO%!~l~n1%!v`wu4`{95 zEezKtGO4nSHb7C#8aD7%#?59an*Z%ozlfXOOc_9jJPrIwAPh2%qcj4Q&#TorIr7eJ za&Caf;0yyV`!<{BsDDMDw$Y;o9oE7Y?=r@~kRt{e;OxFAnUzj)+BS)-c1WJizNtWY zAvS3Pz;b4pHkxGnl1p70o?K&uveuZA&EU4h4eN435!vw7x21_l<1~#m@9$57SsBj* z6E8K|Vza+SRHFvkQuG-QpQVL>{gjXV7qR5oPiCjI>&g6)GBzm6TicJX(Xw(3bGHUB zz+0QRV{|%ji&w*ddyKIiBW`%q0Qc&eTXZim0>cnJ54WOg@N$RYR5tBnY#Zw*H|E19 zZ~5_-S^Le+>KbnyVodA*4W&zC;4)Zs413e-RSa`A@(WL;gLGv^n%uard?+Ux(D-__ zVbkR;mjAuEU&8^7&|@g55jsY18lkrlw=D)8G`NwIn>C53H{^jlFT-c$QN95Uzs#|m zeQcfa(@o)?sWP$nsQv<d7il6ANq3bkc44n~a+irPX z;gY}dsQTNMRDP-~z)$|&xZ96BLPPmgop~BIg?mO49;_JQ337`lIGk(jXZyk@ZS@;D zhO050)422W`&kkPkXz+N*4e8Dun6v|&VZ0SG=Gc;Fl$zpPF^Z;-1Pmueu2 zA-XrBd(cU4ET%7YkzaJwg}3x#6#hLo^nQy0`XB%3^KEWJH|+{z-+Sg3Z{S@kugIeY z@@HT3{Py(V)t}&hgAc9J?L6lA(!lf%S=502f}4)d-(DTO`OX`Gy}g>l9x&W4V@KOw zvNq`i;&LavCIDM;onqJ$dVZ($kPf1Qt@fEB)ur?UOOZ9@9DB;2^lFe}n@%vwRW8d& z)3M4JM=xG8jxW&@9$0aCxjMMIxy(3@fjzet=kN}0P~KpqdV&%9F$Sb7jKQ-`fPzUM zQKuy538_tE`9+~*R=cFv$5CZlU@SbVF&!g6^`GfH;erE=XEMAD9`XYMkg)AUZCrZt zDc`ya7jg3}CSHYL#1=z&4|RlbOWajAYqUJkr98zXdYiC3TK( zC4N7oN3Y3OMb8$?f5#B~$UFvpwSH53rfR{$tJEI_pw8785IG|8UO~cO-u1QPM5FE8 z9M1TE^~ys#$V>Oo=KD+JNn>=zJBH|X&B@sYerde#T;@CsJ=r&e47l!NU7>1s9`{?G*t#lFx zU74rpr>2qGfVWqW2@L~*Q|9B(P^#O4BV_|bl=kv?M#UlPHP;{NV#mtO23`@4!7+U4 zv5*my`jkK0T85z2hH6LFqM-rA$`nkAv9dg+Z4>Pp!Hl@{Mdt}0zTg|QdH6t*FWuFS zv7kyDgtN@#ssieC9+&^>EZc+r)c1*;S{X%wFT<7LCl12+qkj8D8b;fjs;ytOm#$K$ z_yF$%sxm}ZZ}5bk;iGGfyT-N&kR1BdF?lHLJm1Dcgeu+q(H8q?t>8jq9TZU%`2{NSWmOg$yl@FKzYO3#dVNI#==Qc@rz9QQyD7P{)m3op8RQN zc#rmE(|h2tj=AHdr}6Rc{Nsmu0K)?Sh|8CxMFs7T9u}l zW@CmMB@){Si_X->Nz9;qY($X?=p4rp{~~CyvN+#%0N}fVQoF z!_l~I9XYcqWtz2O^jpcByvi8z9nW>Zj0WEytF21@pcy|v5?+7AyAjFUwymQ3+2G`-7)E+K4jsAe#E!;)Da1 zoQu#6E9O*mv3g7=yv1d4AFMU$cG{$YcdBfiZLl4tTc737Fd)?ET;uZ`9_2fK-l5bZ>X6bSsbrK3g7GB| zaW>x0Gkw=}ykMh$2Y_W8UcdX!a6h}aetfL!6O-X?L#IFHcg^!Mrgi!D=WvD_U#I{2 z{py>aw;e4*Ppx$szEWp-25VSkEG}W6LGNTgmM+kPyivzu-VE!5awG43=^5=~Y|pAm zv*zV$SVj}6If8jtHn*hUMnG?idMMWi;1`VVQx{9VsPrka>m0=ME9IJZPkAhd5G5Gv+=>qUf^^Lq= zk$3!0{>=kwzl59gmNt;?Y((DgQ{^FPd;M+NJUw&E5T1n1CqBuG>>fB$Hib+{}JSm=>9i^AII~boFE}?0iOx!EYvS0 zW`9O*vwnCJ`g1nS`9ahHV%CjCeu;3;<3wb|L($c%om+)KCCtYWZzL`e*tRIt>c*({#PGY|sSw8bj z7cAI_TnS&v5((3x2J`uyV9 zCEhXsq9p$haPyUh54(?P+h^>P8YD5#Ws??V#`}9{f@lr;Hdwr9w37|E(4!Hnw@SM? z+Ku}w4BN6%8XRr}_IG0AhBYqNfFTA1+)Cw5pc?dO z3HYSkq|XjvG_d{yDaJ@_azDM+Sc%)l$^tKVW02=f!pa`tD{fP}#&9Vc<&{Zl#J(a} z0-&shIw_%y5L&=ko7IJ&P)`KkxDAVwet)di@I=hzyGgtFLOuRl3%Sw2M`N+eHMh9N zh>Q~8IwJoFgFy|OGzOAiZr(@yQh#JaY|`%7iSa1|rS{+#8KH4Ys?>FxLbG)1lEGJ^ ziKI}9^QCV+w!6jA!^8>@Y#4yjr?y$9){O$M8}Bt;`)LE8#L&3EP)JM zF|zXD2W~oz$<8(74nvq5jB;+Mkgb@7)A6I6Xn3Y!>NU6DUD1Es*m=iu!|kTmuP}hn zxD^9{7=yu7jS-a_d#8BG&A1o``2R+4Grr4hd(h!H&w&%(I*Y-;vBrNI&M~$%pvcC3 z_yoNFs?d%Q@bC>}cgVGA?h2EZ|4((J$dU`Hr1p1W9Da$jv&(V_JGg z-oj5e?zb_LC4<}XrQUvrr$<9usF9sCK@+}4wHyB3WaGT$a#@s4)WYaD@f~Uve z(Ma-yvd7$_?z2H|))-1#kL^&_H4yiP@^{=&{^nZ@(f{x^&znEKJ9zgU2Qy$ie#5QA z+S6<_xDhwF&b~U~_VZT+eD}tOdcNa%gF!cj z?HZtZBfQ2bj)Q!Z89s8ge1ghEQuafpI`|9T+&nUrq)xT5(&XboFiy2Tj*T@pKsuI` z#qi5pjxo^nwrJ~jj5^2WV2*Qi=39Fa9l-6#&X?El{EoSGh2i<;lD^});`;0Bt1{^I zMtTjWBO4mdjwgdk`N=cr&Ip!j;q^(Ec0xL#Y;GGwB0^@@BUBnWJlg3vudz-!c@ift z>$6R>tT6c+aC(&fbrlZsC`xs_f9@?FAJY3{3e3EB8$_u`F#eVO@E37KyQg)4es;|8{m|A(46aY$3kQ5` z&S=kTc=8noY`kXtqbCnEvSV$AmZY5+-Kogg1LW=J>Oggd2PPn@(1&!ui(C5QntoLd zJp}3;-S-yb`?t3HTb@5+$p0-m@q6^&TVUTYZZ6<4>xetXP7KdoqewG+)UUQfw+$>I zW&JMCdD5pe2j70pgA6&3#6W~KLI_M@C6@=J(;So(Xdjh0{M!Bu1-TQEYos5zZ#Wi@y`K&j{!7mhYqiW&+#_>}ELlpUQ>+s{Ll$PqH)xN@Bn znZaj_$@8Maf<=q{VP|^~i%$+5p-$_|<9uUZdP6&Ha!nX&O?ONaFC7;$DARPS0R}&Y zUvaCGV9QB`PFgyYX!3;#R^|ka-?Q#f$EBULPZ`QuRb%XqD~CR`6#+?|;Vbw-XPpU* zcH7d@yG2j(1PM6-HijcV^bo`^UXMEAxAU5ZNy%B;;#l?&3<$B>JdVT6G0;Ov9`rDL zyuIhZ4QRgQ00#|L!*~0DM;&t{^zHG%H3xEd$Vm7_p6zt85h|M}>IC)0n)hOI>~rz> z&ZQB%d~uL4bub=ylozS?omM*G3*dQ(l2-H^Cbhf~3X7%`V3ltRCw~iD@xosi=a>Ti z5nsFb3&AWc!CCwxYIsVKof=d?arj~8Hn&6i+bl!y1+UY)JYBTygi6eJ<1sQXGY=<^ z@}h{F0>nl*{WRoxbXn-gAzbK+YX#$wbsM#qwq`z7U*)%zQNSLpCU1S~+sZHL`SC>E zhOTptc7B!&=x{BxVr;8JlL$WNooD$MUujC7)y55)q#c=;;3mHfRfX!xW^| zkZZ%2J+rPF)z?WY~8y#;nVLgN^~H@3zVJ%aq?oTS0z)~#!g zf)=edq%QDB&ziQ!B>7l67TuBcDtaXcc^OBSWR8=Vrmez>r*1qw%oSoMC%_=&r{r6QHo`=V~Og8-~<==<H$sKQGj{I<3p^Q`zZihtBh@t($c%cZ=+#o$_Ye> z$0%x`;2iT~Fb=fay3|GL%%Z~d;uSSnr*lEle9uPvGpDUwD>KiyZg#Hmy>ZQgedP;{ z6sg~4A6I5^WK}hT2w>{BicyDU^4>7G!xJA4UDeoHJrM#&@i^|x~~0n%2ku6 zjLi4WSF~wV6Wq2ZwTwEZVl-w7TY!kX%SZm?oq{>dc@3PbKmrrwz$TvB#WLD8{PN^+ zkyp9(T`BMTmRqCXqhR~E_T!Hgoa5~?| z$&mS`b)I2e7n|o*PJIg>Ap9;f&;P{Xn?L^D!T#X`OXKPjSNmC@TaxX~;X@% zM_ENIJi~sZ>?Kvf!zkPU6Ve{<=+o{^vcb(RHD!(z6{!x!i`e}qq z?|>vaX!znVzx7k=rp3AhewtgFho1A=;%QuYBGjX7Cd0gb?C<yAA>4)I{|Fb>^ zS(3~b_rie}4!m&Sm*9X)^l8ri2?)q5z>maRo0e4MkGS3-p3O(o1QFnA9pOZCO% zL}zwAwYg`iaU{DjDf0*!Lg=zYNx(=|Q~NBy$#4H|bd7<7w=P~_#J#zRAst()$e;-M zZd4Xu47j=3@|s&XH7>owxW=26*>_)$n`Yf?6rVKzw?M<3%iPqI&CcE&LRdOY&&FOX zVGcER0nfD?uiS|5p|?ENuhE4Dz|!G{FO9iQ*ln*ds+(r(_PW|c>y1v>Bw`H5#%FAz z521HWc!R;v5e?8leMK4+_ZUR<1oO85gW!f8ewVFU8EG9(kK{P=tD#qa7v3Z zCQQql*%FCMn+Czg2^JpH*!XCB*f_8J-Dog|@zvoWhOLylMTXlz=KdH%bPS=}z;NZp zb}-$608=X9H4MT>)?$!_!Iqo-r8oN*Woc-Un~pH+uWv*4K)VhBzIBGpNFovB(y`1I z`+5>DgT0LLbRqP?!`#|tImVX;=vRyrZ#=*9Nf?8fY^J9_+)#NiilNI5d3RTgMQ%vGV@%znM-J)xLvBvK$8h!TEBJsRx(2};#;}dZ z?Qs~;-4+D5b zYr4e~dDqY)&**P^Q#tVlJ#XJTrd%Fw+trT|J#BFOJKk>SBM_#4NEttIL5p;v8zVO@ z&m{yy~quMfWFIXS}sojFRrX+(0VF(z~5#XE3+f5{E#7-HgIyx~@K>bznO^00LY%^)3B{9NBLG1fRM`JQ}pbcoW^aW-P32iw40b z?K4&gA7hYu!a)FaFpN_q2fAQfe)tBOIUI(2t;0l(Yhws!Js6>{F+M+H>|fHhQ;dxn zF7R7@L_U3Ye|OBo7{5n{@T?AbyfGW&C=E504FG71o6(genc+CJuo@G&*61mr$_+%1 zTm0~gu`ULvg8*=QvutFoMs<&EkOO5IzrpT?YjHW?apDg9@lgu{E+tY<*s>9#ccS;1 zD=sk(6pdx&IsO6jhJ48S#X}G%Hz24B8K5HTa-MJ|am~9zAFQ}heRTz|&|mhn`q@Kv zt~BnRJ+5h7BRS`+D4RRDsX`-KaaMIDK_yFMcAcqNBCCGs>W^HIt_Fz?N~HK>J2Y~= zgKzE&@y0kArv7nFsMd9Pcux2ZzFZ!2dpky#$Be@x47utc5!%R{ppm{$bUQlVwSu}o z{X*ZQZqUPKbh|vGo*~~Ft=~e2OU65W$h+fUArDEAx%nNBj>>@x%K914EAYIczSq$5 z)%xJ{>mv-&(KqNCs-qY@W#C_9pcNzkiwk(q_`UV877j>)vLo zOB7Om?;Lr-K`ifpLzi5BdwX#42Mn`0gyI6D^lOdS={Kh1h`q+fIg}!ODeaI#8>7#O zq`AfVbc!-vOoHOA+guNnW} zU}XN4`jdKTSEL4BUn5)UOXbHtI@aJ@190e(zw-K)b@mlw?hYQ<4sSR=e|LqBrM=&A zSi*PYe@7l$#P7a?rZ-bpSe6EBK)|aUp+V@haRaX&<`vWV!XRK5i#?R^E~WQZ`fWtC%3~o z4q+K7b?^!6CJCmN9DH)Zyne>PCyv2-e*ibG3<<%_pYvG8zkNa;tti<}X21n1xd)Hc zTj9Mjk31>kH_&y%Sh`c^C_F4&Jp>3QQjR`RM=JlQX?3uDq&%*{>w!-z<{WQ$Jk(ee zUm0_e<9IW_#vF7@o#RKodY1Ws zMIWerm*W5rs&QN#IxkSfLyw$)u5X#^@0s&eEP22mUV>lLslobF1yNIx6%xe`Pp;@A$`e!l zC<|1YN7HlHL{27I8!`^Pky)cq^@;QCEdu7j4{(9;08Yln4Ru_z_E?M6KaF5vP8;hZ zovcgHWvA#Q(p@{>aWIJoLbzt~V3R!5AuSnOG9!J~^HIga!2v^xiTxMwl3kK1U+*^6 z&p{=q_1oSZ7pyxek%uXz^V<*lS#GCyDcfLnryJg{)3gtBXOIu0AU8KfP_28xp%6v3lH!>F}^*#<&bfu zZed6vpz3weYEQ zdJ0p%-XY{$oahK=`DH@Tkw>EnX4}4PAEi*0r%m}Cz#%x{EV}Z05#ws(!6eREr#|Gd z4md=I-fu9y@BAY#(ZzX}4JGo3 zrrQ?DOFL|tjy!d|jkL8S_B@yMpKZ+pKHp7H9_rA{JMd3m{ab!mGi$22MVoKZ!%b5v zJ)r?k9lpnTh>Gh;b;vDqNFU_bzgN^8R0T&bP3XH_l{@O}R+4Gk=B~*yZLS+S-2S$2 z=cRwz@Vw5)!jI9{=}Q#8FK-F{*OS)${mW52m#2@S0{IM%Jvsd-T)$wU^u!a}plmCX zTNy8%+Pw7rs>6>pNv*PVkA+Lml$1OUv&Cbm^ks#{w+JjAM1{?q&_H@+&GA=m-v1jM ze+yA1x}?yBdkax%1=n^C`Hc#|`W=q<1=q~ZaV&o2<@QmgcyC~RyW(*zS(OjHPQ=gT zpiQk^Jg5DV6MW^53?h_Cdj_m9&Lc@|d;yqHZ|mv&DeDOV%Zxf$tJa*=O(|NOmy$A7 zD}1@~tKMpHefyh><;u^@Mk{=GVs0I8s#Wu-OI~FnDO^cS5PYeGNc*YUOcO@ydlD*w zk|(c6Zs4q<+!UzvsZ8T+xKNQuwb*`89!9`c{pTn|dfH2xIUyjL_uzH{d zeEFplQ1d;J1b!;C1s%miy`4}@{PnrxfBNtibX)Ny7%?nuAf0bSt_gqE|L`R;Gn0#o z511?l2v0>SAWZ%tA!Er*nS`xj0!~VQWzoKz)gUkaM{;0Ew!I16XVN2c-rHgO@ogt= zsYn`MbJ%U}qoKT{&7|T{z|S^=2G{m4Bo;HYmm~VvdYS zr{;afjDaVZi3fl*llK{&v;clqU#G`^@qnJ^Zj|#wx^;&kk(XJD{n%;qaHlnB;s9i= zPNehoTOOY{mp94}@s|z(diB-wH@sN7-TAxg|M+y)bwAaJx3j9h(vPCCPff0WG*$o3 z`Z>Qv05E=&C|FC0$8rTb=@L=mzJxi|8!4BJ0KMJxOL#Y^p8jdReeY2A+y7~5DXgwT zywSP*y4BpA`TF$H`W*wqF%tL(@t<^b@}IMwT6#a!VE?0?Ol+REFS032z;N`Jz#TSF zx+uO;0RwB$z;Lr*C&hm(eE==JzdiT0#SVWyRb=9GR-prHb$ohs%C|p9cF$J<$X9jq z_J;-lUDHy)GU@LDXHFKx1J^V0mgLc zm`U~?*1h@7Up`2D?)qbCevk>Dtgq&Lcer<)K>fzZ*hd$b<(f7AShGq;!B_e=o>21k z*R><(7xk9jg}y{N`cdnQr5>DQhfD+|Q%mNJ1z7^Ne!B+Ha=pIjc}f&L7m2L%AJjXq z^g-rJ!V3ppIPk)OXUu`#aPoOK@lo|1vd8+t#l_@yBBZM4P3!dW->8me8Zc6DwULH! zqRE^vRBV7kSf-r(K1X>oS>I&t@+UnXHr;Djaial=8{NHyO=AJ_G$hf`OrvjUb94S> zjLb05;|9(fjLpo~NKbifQK!n>CdCF{4Ul6@hJlD1@7;)WkLAz} zhVbl&1t0aY$v3w`V#s$D!=*At^rp0HHeZ|KaZO!Fi+MLfaA;cO0TTJ&DnCncUm?7PD>C!=-AB{M_|qe-KNTVky7Kw8EEZScl8jj5>94f`6O!~jKBP%-x9 zFmgrgFgLc54xXZ*#?f?JPUu#S*<>Cg0m{f*_^0vtB{#ZifPPKB#$~tegm)9A2L;fk zk}C>e%;Z>HV=%kAB5%>Dh-m--KmbWZK~!9Jf5*mrak~LugBuMJG;Y&~{18L*L&n}A zhA>#7)nN}9xZR&&B<%Q&Q5if{mNc{hfpq5vbgI-&$F6k*te__cP+;U2!*~qfvdb?< zX4DB|B9Cb!ZGi6@qOZY?c~gwGvY`}1vm1>y8P_&dVh?B0+aM?9U2f&((2N_5z+#ln#{P_PaJdon5NkFM%Q(T<@APcLZMzzzdrLI! zW8lQFTmB;;>K)rsV~-jf%g{$sWX6`3v#$(0E~g;vrbeFF&f#ZV;h zk<9Rsek4|z)S!fL8D_#m+GyXbBg3>2+4ot+M@BScqZ4!Jh+_mo>}Czw?lnZ`Mo;y8 z>J`5S3eZ^^iEq5I96oI@Lf?330Y>}YVtA*49pVI|(d&%ObBw*XrTms#b~Q*hZH?Yp zX>5vo9b1N-uAlJ>M3DluC9Ek>)qJ;4C|?BMIKU!mVl(%!mVU3OuN%p7#iO}}rs_4j*j z|MfQDOXA)J9V0j9OO5jElXNXIO9F(-Gx(=W!yjk38fCx(S!!{;Qd~}%yhn#6BEN#xMq-ijZ!On&K?5PwO z72ndYSJtlr;DCw^`tFD^cIlkWu-8CYqeeeD@PR(Rzda#Le{l-T;i`<#SF20v5s5;= zJRIYC@Y*8U0hdTK3O;eNW{5GLI^0~+V}wh0#SmKEO*xJe0S+0cXaB zh*df~_|6MD=h5^9_)U?8COMC)am64#{FOncZBwji+BFQH=(Zl>8Sm(p+|gARaI<}@lQgHre|P4Gz( zpmrWV(l38As`&;edDS-K!iFcdi;vFsRbjmFw0cQ?smA3Oy;~pJ*>%BY`WXq8HVyON zDtj0UA0Z=*q2uEl#wf62RXP9nNTG%y>w{Ep4FxXyXlIY{iA#{a9^8?6n%)K35pDLM6mOzdwvLo#^|okTA2IH4 zd=a3p<&lT^T=I?(BXkempv_m`f#Z9?zo!#1KwrJ%#&r&^Ik;y2K#s$+F%F!(9IviF ztRlA!Di2iX`oOtjjj{ZO18$CAL$A*(4bkD728Ph)fihq%nT8qO8huGSF6o#n2I@8a zcgLLMp(PripWHI}Gp zb?UwIFCXVXeU}c9m2pZ?DS(~KD-06HPZ^{;wt%M6tignWSK+yQhoF^mXU(+5{?|cf z#t0B8oA(RbM>^C-|0#nRd_qS+p+Y|nSl0b`5-7pItF7)ZG9u))^-rl*EL2 z!wZ3P-n|1Go$f&^w$-ufcx=4wOHytPJ)#^`cikgeSZTNMkl?BT^w6YT&YHTw!dy)(~*=W)dApv8RRxnAazS>C~xQYk02w=YShzUgL9h zdH;qG-HHs_$F5DxcWl&QU##MUB`&-$1asJhW3ZY+T`DORB62KUjPA%>(|Cx!T|-RC zV3kwH*oydBqsU)%PD)h+L0L`?h#&3@T@on56*n*0HCI53ZS*$>-@`V%hpmSZ`u!2> z#0~EdwC9e))$T8jQCH|9)|b%(%y+k})$cu3h!;ZVP~WmkXV5+xCogIYjCzYYNhmj( zU^j$sGL77{IAcs+I40FCj%R(=1wI{?U&Q5E_~IHtN1J=x2ETYyapmWG`&Hbu&b=pV z<=~bM`%B~^5`W<`MQ1&gZeE4`eu}f+eK${=#$X{1o%`?~8*kh{RQ^NZ=JYr2{j{gz z?DC(l-+li1Quq|gW<%sD;GZ9i4VZ?KEu`8}4~4_DrU=!unpq13`t;JZ#? zYdtY*SR)F(i-ZmRzs<``Nx4yXqi+D(p0)R2US}`T+d9bB$Tx6g9h3m$pmB~sQV27< zWPUInDy-8-`1Z*wouFUMG@jqfoLlqzhQ3(%=#%avv>#pkARa^{)oPO1u`NpnSm(~L z42%{r*Bt7~1Xp#MT&*y_AA&Pysb+nk1q0Uk#ouJZhw#AFBOm5^fBW}$BvVA|Z?(lI zYpZ&k^OBjA5gu?wxRBOfzMN0hej8{e8yrx{0LPYBRiX+ANL*lDXH5y#P`w}+9Rv(% z&c{5id45{ggw1MMy2a$|^|v#K=NVkuL^f^CuHL5M^ip@C{`7RZKEN3`Bh7f16JDp^ zPvVv_u;3m`UjThcdf~v2;s9c}wDIpb8y7UcmNxrNQDCi zf1#mvpnYi%*GjN}rjD&>zD>(QlmaO;@=R+SI~lXGzlNY~*l%~zDkIHo7=0gHrBl#x zLR$VA;X4OEa`_LOuyq0^Jvj1Jk)1k82jNZ^tpQ^c@EOyr9*(8yWxdxU+UjS*I&&n{GBl> zpF1HuFUBXye$`EVN$R|>lPM%{f)4O0+~or|`wCnG&(fb!@bsVm?P6^c-{W$9w*K=jG5OC~PuXw#?kW1I z%S`;7SlU1RnK08unU6>doatoG^;GnbddhU4hE2-$OFbumFdyia-rt_{+G3wQ=WlSH_pv8Cnq>{EYa(!jTNvjsf#aVvClB9Ep8J6T`|tg-;-mV?g1UXa z=B0LY+&pgz6EK|lz`ip+Pcv`yDe2je;d{?xPFxR&cntWbNnd&wdhT-cW7iprJ(0eg zJrBrZ&o6C*BxRxaczTs8?37bD)@Zu(1#2yO|&=>E811}tS;lK+AerXQO74;G= z2o#~ksX$bP!!1JhR7Ovd6}oWVyM9ZAAV&>+kv0mnmmj%M=Rt_T(0H zZXvwJh~|ph=h*5*Ch7bSKqGK(U2_AkIJ})n!#!`|^fu0Go?DG{Ft*chybQMiW$(PV zImNeP+Mgyvi|=`UvH{wS@jPzubu)cv!SE!TpD|Rt##rkL1D|7W62=(uL<3F?6EzgV ze1*L~Z1|#hY-Ox~&H4V{;+}+iEwM0&Jz#VGicQ4F7!u_6TQ=pdF_iIU+%*O(M;b_J zI5r-Q?P9D-du}xn^npJAHJUZqT4x|h>tV zL>eXy^m3{ptADqpiiD}o32qDDp?d#N5WptF@uW|PCLyGdA&m*!Pyy?G+Qv%_Tp2#; zz-3J=yoN~16C0=Hn?^X-7;xscPd0OF&~?IdhmpiR#^@TF9b%d1ZEU(SqG9_M20-^3 zbSS)TZV%bW7g7J-~b5> zs=WP7IxXi0NZYi=C?*F(fX`c>y>V27*=nzCKmr&WF|d(ej(Zs8kif>JZiu694QAFD zMy-Ltj*QWYaTv0Jr3@SY!A%=T4u4BmfE!Xu=J07#tj%cOeGD4#Nh>KVIJ+eKGL0%H ziVtZTV$%w`(?0Uh3))QZ##<%PX*n1G8NKDEVIK|B+kheX#08?T)PB+UO~W6JBQ?^E z5xP1H-jXj@HO{)#s4em-rPaO|(W8S-PPuItov*zl1}`5_%cM9AhcQhQ{j}YGK1X8ED!y@-Czh)@abf4_4l;i;UD` zTs=Xadf2+*)h+YRjYfdnf}Y1)(J{`IehnkN$@WB^bF2C>eBa=E3t|~2BCFzo@0Z+0 z{O;{FM&ei8hK`Z=w-}-Gcx&(VTjT^AF0WONLAb`-;%020V6=Jm^~u4jZ%#SX@$BIB z>oevkZ}`RllRovMsC%f&6?JHc`SuO?-*WK7Ie5MKSDDK}67rAs$xJ(oKVrnKm%MDy zrRO5x3Q0SA(~o^c8nAw%_dpgSA>sw2F}Vik3}=ihHO4uDAM)x@LrcboM(*CnddH?< z^{-qGLm(;Z_ZhBAr?{NL~#qi@uo8E5pjbFq_j&57V< zzR!c4L|)8`5P(E25iNvyo+Zd>4HI-_Ri%TDykX;2Rf5 z4A9Y+R~nNdm)f!u(jUkdZ&&LoQzYh>`j#3PCNI=N9NiohR%NE$f z`abn#V@wm$$0+qx!SqR`CTr(wN+*!M8@Xch11B<8ohz*O7Nqh^V^+>Fq3zrjjuG(* zx00%V@41;-obn@+1!a_bUGZD2MRpnR5>Y2u3fzjA2As#rk_RU+7FQUAAJG{abHxxC z81=UYh&TpZ|9L3I0Y>A8JS)Pc9dY==8rmQip2K_e&kgd)c?}2ab86Juoj#H0s_q;7 z^bK>KYaI<}T^qX=z40)HcN`Y*mWRVGu6Qmr#{Ql#dO@Q=jc#3cqz`C|YmRASJXOU2 z0bgg|SU^pWMz?tY$0cpQK)-qu`aAUBIbr#5 zqi%f%-WZ@mAu_O{uN<@WKb-U|@=~&-F^208*H<0@w4r~Gdc7wNfVA__^H-k5wt*(+Xacva8M3ZYHr0XlFJr?sk8*p&I&0;@a7V0(fVpMv;I;l>?G5hW z)4X3M_))feUrvU+kcT?6Dl~lyl|hK@-uPl%gQyWE;6>%xH##|Jh&%50f7VeCL~ zj5-V7z&gB!lPe}|DR=;os+_DmRIAqRj2-zpo{WL?D`^Np8K)~}%yYgIa+(DmLL}LY zl+kQOTLUpasnzyS-mtP+hg6kbRZt_R$}>;#QXa?#{TkEyP#U~3xK{CX>x5NKqDP4+ zzob-mmHbq`1n>w7P0k-4sFHQBdY!eehh80U=;9rN;}#us!@Kacho>=CrN?>0wS&I} z`P*0>US*uF(-2|933tM9DI#U0)ZZD8!#70ZH$D${zWx?> zg&%{Pm-(f`zXlqAX&!x!^=~EibENJ)G}X`!zwOq`*Sd;a$2Z&`yVn zuLNauPQ>5U^5QhoniueITE&H*U`$>R4D=+JR$n9S^Yl(m|MCH0&-t*J{c&aE-)H2J zZWM^_ZI`sf4Ji%r>ML{g09L4`f(0Ak9)knNq;CTsY};bu{y?{6Y~|YPxf@={rse+q z`}cEjbk_uwuHwsdJpKnzj{eDz4}2G@{-0C<$(d{QcVvT~H3YI)_p`q7eYcdh5A!RF zdkgL6w-0sA@BilC|H@kO%aHr-&2Ofg&j0c6{`Ds#!grI_e;S#;yZMKw-Dutv%w%X~OS*PZDX4?~M5&OMxlSDV^v8 zIWw)DIJ;b@z4$L2c;Ubc2VOYvOgJzmlcDOjjWVI#fa&P_{1oGhxhH#dS={6odr0e-EbIC1N z8r5s4d!6=Rpn`G96}P>|kPbr|sft0I`Bqqn)d{y|;cckdt2tI(bsa!r5MPktng!q-BZ<;ai;aOK? zM8xen7`iL5O7N|0Sc`dqt2R{sLT7N2F}l-BEXx2)%C>C?R(S-do7r=-8u|9~;fc2i z1MUB!+=S<$2;7`}g+Yf#O&V-zgHkk5r*u%UG%7E(xTm}g8_wFRpRCM0Ws8k$7AEee zxFHx8&WQASse$h8ydF(Ez~x z4u1S21Fz^8LHAM0C7YBWFk%6bRbBE;T$L|B3%?uIuQ52uEjk(v@Hx_m0OPqBpfOGW zg$HH00thU$9`YG88hB}Z;H__mj$e(bHtGfpBq^7hD!J*?dX2|0SUAGk(&d(S>ZJS1 zFh&C}4OqQ_R-<-!yVkH*aRP^W=!SM^$QMHZ41nZQwGxB37(vkY0N!{bvqm`JTw!3N zVS&EJNGSn6jf3tn2(xdMi6i1SSQp-M^X`pf9bKm}T@1uHaN?Q+DjeJDHrt@1811d{ zIS>P0YxuK~79RUJ#u)O6JmD!#nosdUxhUfYbrD8w7%ZJ|_`)f-r5Y~dHVhRb&((J6 z4X)r5C(eO2`aMyT5*Wl9))5WQOdeV3pU~8F1!&L-n9=bh5G36WZNC~o-65ASG|iLt7&f9Ll<^x3XRkF*LWgCY zrJkh9kge>qS>5?_5a{$!cuX-F*G(+06=y3)o>pjTunApm&O?w*prc4cbcCn6EW=-T zL&X}I=8z6bBbgcNb<66QE1eU7yG766z?+gWl3d~s4z48`1u^)$$srf$6?Douy5<7Ca_MdAS#MG&>*yRp;#@nm3-pH5 zpT)nlf2<*@CDF`Nr%;?@Y-b)QDFtfMbtgwCNJ@3;w_ zgE->D1NqSJ4v}LGB$egJb2>V;M)%OKc@JO>@4TrVKRQ04j~Pc_tq)#(eZaFic>NWJ zf}opE&lp>bF~^%^x~{;IIXd4lB>yj2>pPC@Q)Nt?^4PTl%M%VY;6MWwEv8F@>ynG} zi;Tr!FTE7fml zquwTZ0&du@z%fZUZK9kF3zAV|+9pZkW8879iyTo09VSnmpB<<7%%!)k8^$>9@hK0| z9!coxM|CEdR!#z@NW#tI(k+gW%1_@om$4Ak0Mz%})y0LR9UR@T)<^yksl2o(TSr*2 z_8Qc=|N7=AI$XJmz5`VK?I8j8=nD_walNV`@CL&hVl<)J?`_j$T7WbM77zXnZFi)L z&$R^7hRpbmkyTFglroz!J>G+bKjIxZv@uU#YiP@%F}!#~i$wXyC$eN!u32cLM(*Ga zKbmF353IBqw_oL{-lOilnU9nzyxWpZgjFdk+nk38LfFIPk4b61Sl?7-;cr=)&^Cz* z39h4^Z}H+;p&FtV$Q-5;K-)Il6d*q6NS#eb=O=D2T4(uNzvg{bRVg?86M(1Vb9}ilHzEdd=nx)+$ zrJIJ_=iM8b?X_FRgN?CV(OQL4kmR(2yow70A%$}U#V>t1M2h!d-^Uz_shbhzVPaQT zR~XA(FmI__ToXI)&;^V;-v!LCxtle-Yq99`@vaJKU<-X63iBn|wwh0N_?r2cmfOEP z^*%&m?yfYfeJOB zzvjBCFl_M*r8L!V&d_@!Z69I+9f7K?BU{Xs(sj2#;E2vsw)5zN)D)Ph-YTb_H)jqW zoht3shWh=L^%rxc>e^*tm15l{LZ>8g@a1pYYHQ=A+6nDcAOeBQ70fK5GvXJX%)=u+ z^$@Q5{T+PYk&?(V?u{!g8SBafwc87V3zl4G(&gY+5}db;6Z(h%g?uIi>#O0f~8W`y6<% z$+-Izz;LXkz{R8CDZc{Bz+~7!{={VflR#|0;J2c*TWbdMgl9MJCHBIBpOOPhMo`xG zoQ(@?-|}YPe=uw7W@EC(zFCIGxSsOW1xeo13BnK8ieM@;(xXobr#OC%y1+vWmJTxb zv)}!vZvC_MKX@bZ=;{c}To(4_Cv%H(H8SFLTy}0{retp*%e?dmy9dZ4xgV8$ckzb@ zTwVTpG2^jeSM<-=*KftD95rFFUPBSTsP7;2CzWo70f4b~> zTZVr0<)wbG>uOm~jkK_XAGUn?ksaj|@OM4e`K4H^V1TV)FfshAtk3;>ee}=>@HubN z|6twvpaW;)BKlO}m;ntD=tMV5vf5=fgXuEE4<^GyZY(!YlNI-|{oLh$4gS(C=x49L zJNOdTb06D3>M9M+mFuNB8CR5siEi*>m&cd3X=(*AiVaMwRw2 zVO_Me=6&*-XsLr|@-XR+v0h3%`Fyp!O*l*|soYr;cYewd!9V)#?>cqzzl6S8TEqUR zzN$e!@o)-+*Ms4Dtt0G5!=`OJ6MeyZ4DZ9$-$Q={+3bH1!BG&8RXf%HGX=iXccD+Z z&KP}VPnTe9J8vc;N!-SjA!02{%F_^v2eLGwdqkGV{}SGjCN{hTUO4c=ffo*Z5gf>j zQ7!ew#W`bgnKVXho<0H00&`fy{_poX(liQNr z=&G?Cn{<7-%8kAl0bO%*co`AarsUuo8`)#TsnIqYbFVP!(13e><8^KvhZYS?yxFtb z>Bf6ED|<638|U4O%cd)Dnavz2#lENs&d0DDg=5Ufrs1PY47e^aEaWEE+=Lz@bPd$m zP^^KiMoaG53hM}S7xFP_>$Nx3Y6#$FCO7?RfXa=vry9*^RKMM5e930x%X187c=RtZ z9=zg;yBI-Z5TO}>%*56NM>ya59j;YFWm{JmA2^p2rk8;<@bEftl{mvuLFwJ{BX1DF zlTvnMO6zNEr(Zm)f7(>I@TYH-mEOofrz1y21KEWax0ApF?hL&zMve2<2M*& zYB=TL0x?bv-6bO$pou%T$bw(PF>eW_g3y=S#6w$+^^N12c5KGe214?BjkT?}G%HU> z7$x2vLE|y9fpMose;N;Xpuv?-4x!*63J#jEemDBdM|)EP3;VJSFj^NH*Qi3MGN7z@ z8c$jNLboy|tn#g_Xc*^hsmB=5O-AVOC4I*@h+gEt1@Y8JJVt}CB9StKBTY{>;U{JI z9Trl8TJI6c?`LITQ8HtmG|!*#AZepLJ1~V$nT$axV`9`!UzPl8Z0o@x%A4c4beUi^ zW*q{~5x3lODy?OFPpi{*5|V0;6jy!T7HuD?uQd?6VBWdF0R6%_2%%TidPsnBx8F(A>91cQ}p@M*YG5ACtvfwA{XqNC+xnoGgtpA3mS)5m4pLmkDW z@Ct~~ENZdm%c7Q}Kb#YiZk{eWsDk_7(jbi~k@D0fsqhkOrWgUX(WFS_m%1Q&fO$?s zbd3k)fyU+b1-8qPyF2DOQZrxNd1JH2=XpreAW5F5Bve5|_yG-aNH+Mt+BkK`2qsQg z9#(kP(h-a|^o_+@^%$1Ud?zu8g2pd%^3{39K^A<_BEPd12pBZNsJdy?LqL#Q<*tCU zX4L37^6&g)8To4^juB+A4yY-;Tl!euIv0}%4SrfTS+8Lq=fXPt!Zj##AhTKfy6*Fk zh!fTv&MAx!^_Te8tFss-uQ4vZ=YWOVYjoP`GV_SEty#YwXAJ{Bl#pv-7lw4#CSQSHm@T!NMH)2SLj1MjOugzU_^F*b-kFrwCR zrQ7mbjk7=u&s>8OX8aFMfJI%&I1z_38&0abQo}$)FgLl(FA6 z^uD;eV6LD&j47SzIY^3uHyu*w{4tGhMw4{;RAs4KD&&7$eDjqV>x6&s(XeT$!|~1I zoRGC$uMLRjV4O}@KM5E3Q33U)KB{aVtVEiCL5fKdl&>N!eIQuUEENou>9=L%F|PeY zKJts^ew6zZb-%A-`#|@h^?WG&`)GK{d*Q&(&4HHRmj55Bzu|uf?!)s-@02Q z{kYgk%aEm9vbQkX&|_3QEVG5Qbm@Y8SFp0TS&2W?W!sw7q{-)N!ZvW}ZCgui=xO8k zV?N98BCN$ctgRpiUgu@;37>aFOV!6pw)2nVS<9?K;3_&xx6V<6*%h1B$h^Yr$K(9U z3&kKq?7L3-bzmD+8f$R-r^0?ukrAx49Drn@IC(c75LOs|Fwc7!m3*mn0l=d^pol=@ zPW{$Z34W(P2FNb2Qfa=n#VJdH$X0L+UP*3RQ*>9@^}6u(#i$2FKcf+PX#ZlrmNxs{ zaMRe1>z-8#^2~D6slY-m&Sh^ z81&Fy?u{{UGeO)7c@6Uu_&4AFmxIl3{&EMiMjc;* z5&Dvxdt>Mk&-LwWJu&MRgF4rLc6cZUjAbz8+{}Q;%%KsMRWymt}X86T$6G$iRF&ExHmihCiAHUumUroVt z9%D0N*Zp8U|9urMRBJ7Mx4OP>H91eT2d zmJYUfQ1Dltuw`Xee-Do6184ald64rg z5;Oo@lF8ry&A)pV?faCCXv6dW`k()5(O~(hb$t?pb1RE%M&f_C>H)h5*m)U971=ph zXB2wQhK5UXkIjDa{Ik#4R!64K!_8}alY4F|JvER-M-iU%6>tJ}*#WlWCGULzTj(X@ zKXae++G5-K3N6jKo5MfZ2YXt4&h~j<@0Sjo064>4uKh8S)f}~#Kfze|dATKd{Fi_I zAD&kuA8E~!-s#_^9x1YzT)#0a2+4z_2MH6tNaj-ZUfSy9;W$|uPZQ#25?C@GUi|In zW7-=Y(*uLos{3-g?=QP)^!hYckVAOtL(4Y~{Jh?FoUHtlb`~$ZKkxU_Z_&?Pj+Vx| zT>t2G#?)mCojH<9Mu>FWGGiLb0>tR}Z6z?m@ifSkW;xT%7`RM&aW5Qr;lK+AUO4a^ zIPf0j{%Dqy!8BX^022lh)++UzKHG*U?$7z&Jf7tYb5VQ^6sD&($ZN#lhFq&-yH7U4 zUVGc{l^bR?Xkksn1m(tGZ%VsobF!QDui2b?tpOk#b2TdQ=I}D;9tIKKK8Qg|ZkBcf zknqk|aFNPih3U2u!xIff+X>&qA$t-03{lXVKpAT~vh zjmKpqTR7bVm3h1;b1EsrirPeNTPV#J{Xl^4&|@!~CT*k`!zDGLGTf^4N^O` z%R>2G!;RYF3z)uvekjsa;HTz}r`AFl8iUW0(pZ^>V+5_A&MJH(2rw8^RT)&>(BB6b z)m8lip=IIY9oOsXYaAJ)Vh#+z5(Bxz_>0Zp>lmQJdo5B9uPCMA5cSy)1pFwq;x7IX zrfyS1u3BuP7sKZ*u{sRM~ckb+ezF>t%Au2Ss#})(*I$SujEw;3*;s@bc{j zbyzKKjqcpI=k4KcZjVvk0T!r?sWwp4SXjgJv_Ye@j3Etb;Ww~EZtZVUW45(3+hC3Qm z*<$$T#{Dv;g;vIQg*Emsov>j%*Vxwxn*KmuyrEY4Iyi{xKUlB_DhY$F(xYmba0OOM>#u<5&CHi9liZLw^>uihPv!K>sLpF&KL$dr;LaC z`esArTI1KeA7&6pmvdpUw&q$HV7&Wt99I2>s-Q`Hw>!G_qtQ@Ca=mIVR-sko`2>O08QxppI7GtM}ny zWWxFgx~D)HH9hUHZ;Senoz$v_Ag;pEchIFSEsagOS-Qx)r3VJ_CCFC=3e5a{)WZ3Y zE){vNZ@i;|LQ%6sUl!-g0Gpj;?flfDmuE zfwk(>09d}7UhBS0yUCT0=nr^TMxxwydc#fS#Fc%=W!AACK5>a|KEWuNM`KXsTtjRh zq@MLGD-dkVU7M&wEbllr&-JTw#}$VKU_PofD@NDX2k+i;$i=&HxP|(_^#k-|uAn2* z$IuB!sIz(^?FOH1lcVJ;DFrWMM{ifZL#N|%G8SZPYMi=r0Lsb9!70zl8G7I=jHzRU zjxIpArJd24#ls`lVB&LrlZ=oe4@zEHP0&2oF=!18&z(zcYa5zhdJA|Ty>pghIxND#OU~N#ZB70GdbtPz)%6F&hB%2mJS35y_|WkdNt`J z+56Ft@Pz=$^4xFau%K+W?G4Pr{xzZddD_zr5 z!L1R;Kx$#yn0gYX{8Y?9Aulqv6*XDnPyf3{<-OBBlUIkV*EEjKx)q)UDPHvG|IgmL z^jMZ8cYYb~$f{yDsfBtr-O~>OBR~X3ghm@_t@#S`74+$}6|~|?S_vQps22j%WMR0P z#gN19s>*nWe!suDo$L3;iF{UOWk%c+d5*u#&CSis&Hdaw=ALyIGH{s6Qkdr&y57m8 zGn`8m+wn3dQ6CCYnuE`IOR@L>S=N8z^sNCsXY+v5J2qCiX9EN_88~DDJ?0d2bDsG0%S<()@7ZpR~)W-cCPunRpG4@yDeFhSbhk&tEE7+=`3L>+cbfdx zgN|vF-Jrlz!Cmv+W=_CH4m~`?w&g^0*Mr(cbsGH?_SkuvJ7MM&$Ey&t=d)Yhq+<{K8VS?6SOXZ*rL=`3DC(`p@EwaS%{=qVu<} z50)M>*2^xqMxl(hwfnWv2XnV;1#X?zV{2W@Lsr>DI^sWg2&%dU&*itYWsY`Uc;I*+9fCa95gg3h7H$6g<$Zl`eM<{$2s2B z-X{!sq3`W$|5$CmeTyFl@~QOW<8Wt>^>gRIr_f(#)3HJu?9i_7UTxzvTwWbkYV&+7 z|8;e`wr%Vfs*zmg%?4cwt=e!M&R`qNo3%vQaGtCMo#zgjpLDpk7sAKZGv~QhR~GZ1 z0Gl;=pf_wuvUxGe7GlgFEf937-$A7gD$;%`Bqx_?li83{wepY?DXN ziPeU4(C+Q~GQjeK^#W6%ONE5hZ!>7wIWKYxLBSv5C|imG=)+5zy{#|RJoBjfg^NtC z^jpyMK%lzQ)6DmzmymZvK|l4i_R;xM|M+L^ z!bgz|3tm0Ri*l4efR!s{1K@RVD^>Sddk%aE2iC^^Q+6(BUafunxBB5<#-n4In%aVz zGf1r2g0=}g_NkQmd8Fyhgg0QCpf5N){l&j~K>e@&^7jw&o?^z@hsd)YA?5XJ(u=^X z%o|xNlmcz$9P2p`HCE=CjZSrDg`364cK$x~<8eSo9UakkohpwH|66BLqm9aUMyBmV;^`Mmsi zQc75geGdN!U4n&;@=o3+ZG7hn&`#dl@OQV$M@*l+{LKUGPu($R&9*-3kh3dRW!iZi z%E2X0#JCEjVt6HPgme^bC%@qZ@iN`lWAQG3BwTa6Slig*k?LP;_S*XEQQ3AcXC9lE zw6ZFIe1wtw${5mXzybo;88h!~iIW}s&|uaY2wMjhy%;Du7+{@z3gPK59&RY~%isO} zS)4-qw(|SOKm1iTAM)h$_qkjjzPkOE{~-S77doxY>&17%hyJO{i@vT^ev^(`o`Es} zW+H90)xyi;-#oznlz$`IUC-HhW%kzB#LlYIj3zYNx4fP{Qb>XGQEX4cDxi)J3c9|b zSL#&s!Lwl=6ja8V)9-VbgC1eW$ID*iKqPTPWo?srRC8#imRjxABrkVqc)Nc#*F~X>jfX7H~e$OsFe2#0__4VtwGVuMb z!P}+s7~)I1BRg=ec{4*ROUrV193;uF_vMyfquL6s_ga80=#P?E`z`vZkNXc^XS|Ih zBHN7e8t107#zQZRbC(_&R-ETXmuzFe1y&%_F&c@O*j&k4$=^WiV|xzlIk4xzo&%pf z2WC6o@iCXYhIao?Dl5rlE~q*_lfTpzz~B1KH*TcTiH@v_lh5z^af;w0{~Mfm4sgo! z@}pZndcD5JrRK^H-8UZz=l=uv$X|yGKe6!xDV+mf!A39Zk^8b0xxs=73-qXJ92Oky32^}ZfQ$E%|#sR%fs-v=Gd;9+((l*wE zI#tHW-~TU>$6kc}e=PogicR^kvIPO5L+K{@zP9pjRIY^S%?D)`at+)C>(jevDr*0fNjyM5bCrBNI@)6tBghv+<+5vAqESG2; zJVFT3!SRHNE3c3r3acDAd*O%YmdeNUkyt*4GnWSZFfbpaL$tbbb8}oDiF3mC$*s^hhCaA)QPK3m&HcuT;WZ-<$SBdlYLK5hU~Nq$-9UE06+jqL_t(@QwCl<0IC45p zq6_jB8sUd@>Z~GeH&e)sv%Kk?#w+gVG+d*RL*U~-Q_Y1(KK%0^2*Y3bCGNhuys|nU z*uYboEI(w{LG3VeVw^Ui`&5Thrtpkecz>(2i;i;4-FJ*ReaCO0?Fi@dvtv(yK7}M5 z;iReTxx}dpj)%<0wBcMDd6%iKJwvb!I{QS=by!W8R2*+ovB!ojoVz!ynYz1 zv_c7O*L+c7YXs>Nap@dc3(XVQDBl2z&Kgx`@ZHg$2x+-qkHVW=aLCyC%qE}zs5JY>df!y*e#i$meQ8G?K5>MCITyhQ%7|A=j~c8xGkno9s-oV z@R62mLw(7b4f)FJ*h%o)q-|NXl#)V`?B0b{Xf;Io9e1&d!x>b%t!5Ckl-fV9LD5uT zu6ELOkbPke*3nZZYw+sa8Xg3veNaZO@1Pc5JAsNC5u=OMhGsJkDc~$Ew$*CK-h>Ky zA>9_3$pXSWNXFZ@D zchf4*9ExbVD+WeP3SKLcR3Xuc!ZZnWD^iC^6i<*79~?-jp6VMsuaNgA#%f+eR{~Y_-1RZRMtZ=cw5o zPuIXY<6p|s+IA{g0(Z7!m4B%}BN-TNNu5obgNGTXK~#@|pmK}>u%aBdGEagrs(#-& ze$b%~ssQ0S_6Ij5uX+PK6I$MPGlt?`^T{#eVEznIrl!@?%}u{w?W^;D6kgR9l9 zK}r5qm|6o>B(}|*6o)>*T@7bVoIbe5^&a@fn!$J1HW3mz)!&e)N}0v(x>cP&}?4& z*f*ybIX35O4B=$WWpmuZq{rA_Y{AHnp*KD%CD=tE60W)I5s(7>e4fwo<59W)&L1Kx zPZtA2uC)*#54|w1s?CM2<6KnbhyJA~%a%Yko+PY4>uN#p9E~mMA3t(;@y{<_{Cpd) zAJ;yI%`EJ-8!qgAO#HKxzkc!R`X9ELlGH4u5pNNVuoslkhdd@UE7hmcQlhL$8tGQ) z7m|Q>^&W-ZdeLEo_#Y4bq0>^%!nyM^2A07D9{Cj>j(B*}dAsb`;n^Q~K=Z}Q!!WlG>v473aQEslwpO+a`)9QXxfL@UNzEe=w#OC9dEPC69`C!B$@ zUJ40!%L4HF$J!k9T-NTd=geD{?($qYC2x|4>BrU{He;b_ZmpA+bSe~d^o{7U-klY);_D}&|4Y?> zqR$^?{kq1Rg}rZjf8ix2YguK}_lPXA8u=O%<=AkxJJ&TmU&4LjYi%PGNqFk-LLa)$ zSQXQ*{MP0pD=zv#Byq!G9Tt5Vx|4YLf=OOI=+H+vffjtNI9Qa~6P^S}V z%zHklH{8vO%G9-C^Ru>`vW@ntrs7rD|KH)GRWuD*j#92lMS>WO%CaexQ zIOp*pzaIwkoqt}V`UBy5rGnlAKC-_*0WTlx#_<6swm9$U#K%YbIG@Mye0D^~Nn&wC z4^iOB9J%=*$q$p-3Ehuxb4x5&IZa5DAN~*L8XRomd>#jM9a8uRzwdMEpWnYYJD1P4 zfpd)xKYr#fJd;}goD0>R{&b-p_P@Ox&kw_=vI?-Ckj5s<9O(eSVd%P>9O2}Da*RXm84jntfdk{o)6nB^r$eo+ zIR>t*OTWBzLbrRlX@{>Q9i#m%Ty$JIa|4U)ayt&G=vY+V=`Up6;smQBv5sP%Oszw3 zPWY5=bp%9l2G;o;XNH`Fj+32E=%>iRlj&6?c_Y7Rjo(^zL@r0fw5`smhm2!SJU>3U zMot(hdgy7BcXynO8+#_Nl(>CDT-qBQ`f`Jtk0rH3aU65(gI2vSX9?{?*@)zWLpcTq zx*W$yoR`blU)!TDVwX4$U1uUM+l}*sa-8g-c~xqAGPcG(l%$Oi5^@nYQXSeB(eUdTSh0@omKjahE&_DL4Gz>XozE{kt(SN^prZoe?*YW>D11zx-Q461hsT#+ zald?tvm>XUqi3>6>!ALrS30Qbba@fScJ#QNFx{Mm^5}A@YUr_dc2RNWQeJjpB*R<# zzWiBG0D&>8UTfcmepE=7yobIxp`&v;p@04Ld34W{S9S7n+|^vGlO(+56v(?v`NLQ` zk-NWt|v^ zv8t9;?|^heMXR(}a2-!NyyDe>x5_z@nMXWt!n<-lqvFQU?ova0ihhFpdtE=%e2 zf}M1ILt$lk=TkvSdzD4EtYHR4@+)w{=CvIL%Un(+^h?`v$0j7!QT93p zyTOX{t~O9!>6j^9H|&z6Q?Q%K#Q6(*g`Y9Dg_F>)j=7n$uTOAlWu1$YyZGw#R-93B z+P-1DYag%Jut-OAH<-A-aE#YDx?v1?>i0GGmG(u4Y{S?Ad1LgX`7hJvQ(5#Sh=zy!hcySDgHQ z`Qn1xc{ZDG=ztDy#2k2X5PE$RGkK^oJ>nR=4b*?vzQ@SDc_yp(k z6X#hgX|pi6t|MGqxURUkxWLxC*}#xJoz>~QH-}MY;#5o?#KorHAm0ndqW19z?D{PZ z(r}uMs&3hc~)23>aSymStRvU1Rc+>l>v7-^#iADGTuvhvi=BObOk-KYq5jKyw+`|9kYWN z$pg2k5I5ROapx%?0Mv)*vUjAj@TJ%KyGLLG>H+b!}0PjEraq>h3XLtrF_>IrE zV4HGNyI0LMySZ;p+hTf7q{56SG8;j1i?{lE>Uqd&=gy; z-ZQd0mSR8N1J_!;UEDL;*Okq1`}m#%&zS>j<7(!|o5sWk}bVCFX8HC}crmTju4 zWowH@+~{oVCdi7W@ImgJQBz{!%siIDZexZtuHCJ0x8!x{=9O)2+SrcI^_SKITr-#R zhe@ZH{7w+U#~d>3f4Buqh4$S9_u$zwKMlOqrscwpdx95F{%F)UjhclAs3pZbGB(%7 zw@SR5D|mp<|F_UaUSpY}v*ON62oSDC^WMsDNCJRDUESQQAD9bi7-9y!&}P3}i?|U^ zCUeWXj{}V2q8C_EdxgTK`$onMeHG&wSlcwmq~So293J4zMd%-*dAj#6AMML+*7{b8P~$i`NalkqKLePOqR5K$bu1Jj)h|?5?9n6?{#EnSt3%^B6-R9!XIJE)CbdE z$sdI<#h03TK^bEJfDIo;ne3D?yEv0UehM48;pcsweDm7}1U}_?dfm2u`+x_XfmRT0 zh3VYwtc;U@@L23C}sKf*UZdOYsngs1WV?&ps_`@36ipX)|+Z^xbYfl?SPDZ;E58qXvsvpOkw=B>`?P@u%C}Im*oz%0Aci0t^9(vnrDfM{K-<>kxPk#QpuDyiMVGX+`t2eD*7Y40F z24^3ZOfm{g%VJr-QYCo}xUm(>?+HV$S>GBDyx6+V!)8Bq&f2(t4tlh<&iD{L+LGHM zUL&nxgvvizLk9w9K44vzys6q;Ha9ZsQrDTw_whXk_8i!AV9$XsmIM5vKGANx=8{cp zDqf-mvn017RpzrW8yj!4tZRaIgeLMco*&5Yv8^B3=0rlAL-PLs0QiCLAs^tz!4gz? zANKv3+fSmr^1Ci427CI1M_lCt`h1vMALuTp@_G7soYDCAPw1~5z;BfRnyp;D7@@x} z`2QzK=bJGa%?D+al_NR|;HlH_yG{|a zKGMKh>=yp&`w{0V8qdZ7I*^v&(0kJ~}`h`hgC{0X;Gm&fd@m9q4f+IUG)yI&0f^^?%C=o%;15x+V+Z3ps@x)jdmO0B`CJ)|8%JTCg>^JdnIG?$;KMijRt`^uaoE-YN~Z*F zncIng!ojzpz)4m%4+;IxJJQJHvc-n*)XMXSq zee~-zr|#JM^C^9*~q7CpK!s%BM$B8R^hY%4m`u@%HR~a zH1usbxQtlSjPuL64(ObM zt~2-9xhJe!j!eMkDW{Gn=eP?_JihqB`y$Tho`!CFal#DSi@(itQj2J`Ej2FeH@LYS zyDDxruu*&iK5`70v1ju{Jw z2d{8K|AElYX&DbZk8w8d6B?y64*Jf)IOXev7a3wh&N!X;^c*?5sQ}eIZ3-@-AMy)c zF+NJC)uIqv&(Xgc%_dZUC`_W`LH>he@+!Y{&Ukc49)Lz&^6q)czIzW=#jaYXt^yvC zi5%>&^6-+rvbTjHBC;qA7{Cjd+?zU(V2#cnorjq~}{#XS!8oPz#+IiY|5 zr|TErf2R{V&fYkoi_5iHO$YWMy+|jkwz(^`^p3N)an~RA&$-!K zdmSFZTWFM(+FUm%(FVIQ$SdxggwCSeG3a%wy`rABEbkI-FhX2Gxxv5%aO$28(r#Mf zdxm4`l+6-uPL&_Fj`%6`WK$pJ9ykU4to^}pMO(J1)1`LUHJtV+7c?3LI{n?s1j}Gr z=_pBcIC`vJD5vGlR7{_!GGrtVqBHUYTGDRx#krU^J-Ili7ZW;<;p`u#JcGIVR4-khm6e)zE2RJLqH^Q>z+7MDCvI$wbqmD2?;>_*) zhU3NlyTMsbe?}*dln=0$yD{6zF&mmO0kTa)_?vaP<4XRBGq-rsGy~T04jyzl`cJ66 zqL&nDJLGfmQknLeI_={OuS2}2qB~{|fmLVg^y#at+tlm4hwz?t2%(3OIej(EU)Yzv zH_y^w_+(6|3Imx$5XOs! zY%Al;v8Fwp?${F4Z>Fd5~&vglYgCG4W?Rb zsVMD-?N*m(l#j;C4zCw?S%*T|{+#ZbkL7pyyM*Ew-~QTjV9$Y%!+{SSV{10x$ECDv z*}4^&44f0tIuPVcaE}sao6f@y=bzTkJo=lBKBX}hAm9W_gN;BaEAs< z4!{^+D74NdUGz3#JwFO;-X?#;hfRLk3Few#oiV+=Y_@F zN?i;j1?|CM%0&Z>1;#KHEFi~pA|%8KU$UMUdBEEu@sJd56u=hby8wfzjN)9wzI8vn+{k!>JURnld zE60yKCe{Dg$g-jJpB4eD>>4~_Z(s`rKaRn>`j=2li_K1*W`0UGk4LgM0G}>)bh>sD z`qMpaaLu;<@%nFH!|*7`cKOK4Zf9*rfrwaJwqtJgKQ8j>YMpU6NT-9GvL2$_jlFwn zZT0KfvYT^r~mcClk1;@ zyw+s(-t)tTofT6idC#rn{QlB5B8I}^Ox6bV{?c=I%_-yy6B%#9-S?Nz;djTiIp{g) z(T}>$P}W>)YsW^bnE92j%O2cmHfnKAX8QvYkgFR)7n5kJ<=O&vaz@#FbBolM38Mu!PM?Ddo=9arKQ zGuA`(l3fcQKfG~H^!MHRjaSDMaq?j>ANb}pSU#}#l*l-p<9NHAh;G~ud-HKGnEbdu zL>5jF#eqbc_-DBv0p}xTKf=n_`bCF64}Qe!hxS)Ec6iF+6({5B$lFfvd{+!TI%epg z?2Y3s<@pGo521BhIl*z{7Uu;&`1hi@`Vbk|axk!SWB!IFKbH0*>bnzY4)6S^Nrxam zw5r>DK)LLrA2&lACu&~fh@$hJyn@;EogQ%;+E?3AHu(_OklW0Z!>8njUxhtv;t`(; z;Oc~9kmPeQ87Lh0pi^wslMa=j@X(v)N4(GrR9DJIR9dYgs8>SjFLjK_o0LL=xN%q_ zFOFf#QMvM`g9gVx11NAggkAGNx+jnTvOrD0USI2ks-q0f8}L;}s?)#$JNi@n86V7F z5?+;4c za7>>10cM?}bix$(F}hZ|M;f6m?JX}F(1V`F5nZz76GWD5REXlMX{64>7D5<-aWB)Q zEA}t?B%R<&iKj&}4t2K9IMEI`P73{kPD~l!iRhptpScz8s8}K6iCY}!t2j8^;!t1D(lh1Mb)A{d;TSq?gCynDOa25368?wqtQ9hqk6QC)ks|!+}Sh zG2H2IIbOE|k4`~4HEM$(OlIX0UM9|e=E#ePOSw<7FI!=uK+S$?BRp{hPvVspM$mdt zUJVA;>|AMV6p#`4QPim}UD}}B_~P6#+LvhQ#agB++Z4PwMPaUQPUGAZda}7j$y+&i z`lhFA-`*mFJ5CbT;h1_li-yOJXPn5y>#3ohhA#RW;4r0eEcDcIPdZlL900)!oH|>2 zp(COCQ95#k&gkUm<_~ckM?S~dOhHFSWv>a2EdoXK}-ei_74R z&dFS8JVeiQyv`};IG=xo6T0^~&fIQHQaH=WQG4&nzdwBc3Mb4T2z5dqrvWqO@F9cI z&J)E?71qDS7L-6r8W9{B8N^yp;zRh+C1n%d7*>%mWz`Fjqox#eGd|*A!4tm9({bCjC;+H&NnIG0zf?3fF6v|*ppzteqkg!VM;rrjgw7@yfa=&J z|I}senDa?^>bM!U)JCNtw$rbfnLiGkZ-8gv25Y>lmrY;wqI%*}mgY{p`cofjTe?u4 z{^h$`a_gG=L2xUoaPSD(gHBnjExr_;9HzYvli68 za%*SRAO2-bRtxrrG`C@4bnbD!sCAcXFV}04rM4e%oidj-Wf*7fxJjoz^=??NU0>-$ zefr`$&cm)zkO%thJfb5y6udZwup`G2qPfL^?b0;>ap%N1K4Yh@JRzNv(X**h*1t4q zZSZLaYaBuY^`xZMO3(s5{?*#j8f7c&aO*}+kV7XjpE;+wZsIO`3%f=-j_9mK+!!K` z=wGv;#y6zDVVuVaz3gJ0-VW`q&CtuNL(q5U+05(G=oqH>nm%3Pko}5N-d}xxjlJXk zuEJN}vsnn6d*}c@JD^8oKDZJr3qH|)rU#nThNsVPHDg@v0+4kIb+yaRWm)r}6VAoGX^4*KI<(jC<5aUT z7Ea2bF8kzV*myynf<1 z9`f&piEkh-eo`Mk+~L@M z;07}&y_?|Vc5TXi<4xPjI2+xFmNwkD#SN+A9DY=Dfa?xz6Fsdll+&J*49z85iDkWn z3yb8{I6yD4es^qy5!*nYYMm;6WC}mjx6BpF+`f%F^+8k{RXAqqmfm{5Zr&@5u2+&K zV_fphYrx2OLG$Q(Ri134TUY1qhFd@@iE8y>!JI$0{}A&1a-g&IcJO3wv2n_VwJX_q zbblwPWL19j(eb{5NL_gRsXzfN@!C-1QjcyO`}{@UgjWycp|^iwDWPM}cBasH*ev}39y*^&^00++D=S4d znQ`NUIHEH^?SI}K(=XXrG+9?#tnc_`@MhcqU<*66m?1Qr2yWj+UEdlqZ!=zNJwfR2 zLcs8ETvBH5G*mHGn|zy+gyis??BP-n*^erMOW(K?m#Sn{C@;M_OxzY4H*uR4fBzX@ zyJj3+-gzDF=68A@?m4jMz@7u2h682u9<-$`+%j#Cz&)tAo3Vvtt57>wc37KBbB}=T z+RH{V=f54U?gI^t?$xj7V?G42^b}qI>L0mP&zVS`RH}$wNS-1c?OIK*bB34s!Nt?O zyUdR%s(irazEw3ctv;Bv;hVoNxgH+aB~eiB%$Sr3oArpTjrz9cv?j_#6o9wXWg+6c zKBWOHnq1EeFv_#u2-U`Ii&h6fX3NBFMFi5A7Atgq_N6?j%C|UBp|hbuVm^1OK)B(j zcS28Rpm%>oAkBtF#v@}Z9W9GgM=5bG1*n1~M6wHOEiRDKzY$NR{bSF8pNIo%AN;O4 zRQ^QdeY9rR9Nab^`#wH6=Z`@b;ss}c!JBOCA?q%4ol zd8=^E@#3p*{`gjPzht1b_17C6cy`&l$OBFw7Jsv^Yg&6P1GwhdIO z8u|(6( z{>Dc;hUR+#lldsWF*ZOIEze|+vPVD~c5NdR%bvr!y?J2x^6Q5i`kb(l)canM)?>Wy z{0?a(W53rPbE6Z?oV~Ixi)(A5U;gfSY-;zTJESgZVPx2k%oC)OH5(@4 z=56U<*O{vCXwb>ef4h^T?RffY*fm*w(EPv4MP=dDA{y^6B_pz(2Y~6j^B4I=med#K zP46#hs2fb;lRwsei+<{Qw6@NW@gJ^7lPrxczY&$bL`T#MZyW4kPKMFECus+)o4fJ_ zO*)Z^i+U@s%l6@(1A7kaIk4xz^Wy+>f^$pHJJam(aH`1MTKpZ=63whCzKzymjgPrCS$tGJ&`=W}G5rI&mleRIr*!aCJ* zr%l>k+9Xeh!gCx0PcJy#8OKB&!^-&-=MbG5_=rCrSlcYW4R{K!4n#UR<^)@uRdisXo(Am4*~dB46<;&jbxZcoeAQ9sW5!&yq2{n$Ah0~m)d zae$XU-e6Kb)qgOD=SodE!a4X>vi64zPXe@tmsHd(v~m9u&BKqe%->v<6qFp~7vfwqK5B=>r3Zd2o<>3jgb1yUmA)o7og+VNh}acBjkwVb2(&78-4M#WcqcF^Y$$cN;-k$^wQ8Q zX7Jkvq}+J=YsO1ydB&7H=gv11K!>LZt!ux$=uGz~5%SGScEMzP$4RR`u)n@yoak_h z40Jx#vF2C@E}V){>+oOa08fF{kvL9CI0h+)IH2PMrc>BCb>#;YaD>r$&yV&UqeC|I zJdK6wdU>b-WOY0>d3xAZvm8<_&tsZc2=~~n-ia* z?TY&ZUVHlJola)xP)V{r>nu&Kr!p|6O$i?4cASqDX*2z0ykk3bP;;y+&&6?~*lX?3 zkdQbyvdL75dA@5o;Ym(u6X3M;1HrN=&a=luxuuQBl->Ux#gr-T*MJO4p8t; z2dj(;`sO@zc&6P&2}rA7=boaCjg2!Uve7YA8>~a)C61vw6J89b!Z?DVH(tlk;)&5m z$lTM*bwD@tWYBU(b?k}@=Sk)SPsY_z{Cn=%5&bGUtS%sD96RFxi9_}I*JpJS`s9S} zN3J?(USD27A9!)R)Cv8&@4n}|jaNAh-Fd_|9p>6EP_kJC1j8%&>R(wjwd<5StBg_u$n5{T6#~-OXv} z)z9==TOE6CZ?xBaoA#>EXU%(+vt3G|$Q(Nmo)4S}W3%alx~_w%wnvBBIBnWy{Ubyg zVQ94?GW##C*#6W}=eW^d+Y!DtKtQ{(`8wjenL?ketTVc{Up&tDSLm-}LEOijO6$}l zeL6ZyTSf`GqItuY5x+!a#N2WyEcXlKv(O)T7YEd%ilrNpmYiy-N@b0?$}6#@ewBp% z_Bo|KjT7?ET3!WHZPm%H#^D2hb8tv}%_BVV zA&<)*3#%=4qXy{HZUR;(Xcs@CYw#eB#qt89l8t+?pP9c|ODv~cqb|;iCpw`sFP^e_ z%-Ksfj=!ZRTx-YvvgyYe z_o+4=`{KqHmg{U^GKW`eMs%yagU2U0R;yQTP*u+7wVu}jz3g%IP1(9(O6h_6B_G{9 zf;@P5%etuM`m&dZC4IVe?F6lNIH4c2S;yJwB{aD)3F|1{Ng~wluevB+)(*i%^c;45o4z5q^4&N6f}H+rElTMz}yX|RzE1q zzs74m@kTYW4Kl7NtMOf-ovC&lXY|-P2AkkI+4ZvP73Vtbm78fq7d23}(YVmF*4rC& zNxcIZ|J>+-I=(A`w{$AC1`WVQ-b9&T_9oTVlimXQCp3ihP8vt9jU=xyJp|W+fc3CE0q4ym%1`2E=w? z`HZ(q*YYe2K0?bMtJ^r8q>o9JYHJ>{aN5}0~ro5@jhMu)XoENm`W#_A( zg`{Q-O9s0poJ%x71!-Q#HZ4`{Ilt!yvQchwmPqbaj3I@I=>= z4kVS-iZ`+$E;F8+#}W?U zo~L~&>$y8x)UejI(zvE*XC-_re+#G*YmOIBeRBbOzIM9w>iTPiXG@kgPgyC*=DA_t zBR~H!)-qjWJnA$oq4R59CI6ptJ{^R7(qK&&|C|5rX`gnz%V6INbUxqROlB9(^dGPP zE{^B#thc+!qI;d$I!Jnoc4>uOpq{F?jsdP@k1~TXG{#KGlu70&UhJ58QS_=GOo17I z29%$l{F=!2^_1<@n*85O&V!A`_!bCZ%R=7g_d)<;qvWyi3QA5BeAip>4w-b_-TYDf zx?s%-{VB`#b-dUuJ3e@SC-e=jStui}D)eo%=P@e+mkxMV+#dyc=i`vX$haY{aF9~ z@Y|*T?;P0{l?DM>$!)_5irwU84jB0EBOuk3BtACqMy1R zt*kjqEi=f_-O6?Y1Ng0KLE%pH$&^Rn#yMi{4wbFgwo&$Rh z>^ZRKz?0^Hlf<}QH(v~}0%1*=$)(qpx>4eP3IXWZ9(K-l4 z$^{y+nYEc4$_&-e&Oe!y)zP(x-TRr)vT zk{8EmP9vL^Kly(qKOVs1q;e#u{HX<%j)omZEJPPjv!mr@88fJ%JB2dxazW0k}#q!zsr$$H#8c z>K%Fpud9D{Mp=k!&`;yWq_~|dbr5z;s%JXx=%AHTLUByfr$?u?@Ev)0>Y}{0ZBMq= ziB;NkaBC+^PlJwgt=0I8&MwiXF}rPZ3HD{;x4`R!+vi!fLNT-8GkzQ zWn3Ujb=cD}qs#JNnQ@Cr+}GT0Dshct&NU96*O$jR!8WJZ#!1KiK|B@G2F^d8+O4xe zoOQxucuyOG5s6M6p>xML8tIhg7+03dv?q_+i6dh? zva(;c&jUQU3JZ(t7b6PXBY9bj`(0V~rjyGG>N%{<)>ilXmHO9{vABTr* z5ki}89+1r`unk{-Bd^#50C__~PQ7nu_&A}1Pn_O$sk?SS;_Ke@HysO@vD~s?x~<(A z(Q;{4Cx?Bh^#PfrYfK1W6(8e@PJ=z+$V+iB0y@S=#(?ve;{X7vXcx5aI^BCxwobFl z>CM;(EG7yMBA7$)EAXv=(fcM7ksad^Z5T}Sjo#@hih zD@M?GXp}3P%ar_6*J2lxV?cbEBah|3S3NuIywU3_kB!;DI{t;5WmIWlylA5u1c=sb zPQDVv4#oZzT-zv943CK92v`&4)CNCvM#Ubhe+jk4-i*Uu7idRxLJwBts(px)CXGka zX!GitH61(FXji1M<|gm}BX#PD4%e9*kFOv>!MJ9H?$i6UFD>fz9do7*#k~Ie+}TXd)Fhx)6Y(-y$~IRJPrbP;`cA*T=r7XK=)mY~Ptq95|ozGa-)ZtF}!`Rtlh zS^>9NI$f-gXzz#3?zSmk{7bzFUK)xnYNT&f(|*wk3GV#!L4VsWTZ=MTpY`Wl{J+Pw9fP5 z4<2;@y5q1LOjfN;GPF-6FWcbC!$@6&djjoT4Oo2I6gJ?S!cC2>SgJMdt&#_{?UX)9 z+V}Nqli#{p9VGuj9cWQ_TqBrJZtzs5I&7ZzrVq_u^NY(eUEM>=429*NRUfv|&28{m zuP!Uul-dd&<0kzZ`d>O`x=Ji95*;hp0qv7(7i_MZeW^zh`V`P6MBR^&W+;LXS4_#Xaxr62~Roeb2g{ z^}O!_*!9}HW!Q5DvcC~IbgF-}sZC(rTK2{HQr^>V-fiyaf6CKV^-p}>{zl`xV!P6y ztcq6G41GzjGNin14WWP9x{VGBQT?^1G5?9j+fuI$ylvIJvpb}w!D z$m{uA_>OObkN#vn$xN}CAeNb8%G$1+RN5MR5nFS}{H8;|E~|W>v`3J+-bFE&^IFxZ zXEJ@Ha?NSNS6_ODYPtdPpS1WQ?8FSVTPFKM-lYzBmvU2G^C&G?X&CtYUH^8hWvo81 z0xm7i!`5=1HOohZZbrvU&)j}OKEI)4!C{#+jD|+tG-}pJvkD+)V@*e`0a$JG8(OV1 z7CNf=QI$1C@O~-vp4y$}z%DrT-a7mD#w1p#WG>N2#!Wa)^ zA|`gOk^fln4)l{uE{uG~-aTiYy(6o(z~T3rfpAkOX_6jbtr-*PQvX8IHCXJAO7maQ+JkaF0Oqr zeauOUFOPro;?>PR0ER5GG9EQz8%BJi77=IGro(L1%9OfM5a20fJ-b^YlC0*5*#j z8-;I;i16EfW?(fCLO0rFQ+A_U%9e})--D{_%;oP8Uvs>8>YH|~$yS!V$=PZdyA#io zm$b61Idd88=C{3_Wt@1ym;8so0&ktEfhk%xbj}#{o27WB&?nL7VYFo@J?D zYf!72K0p0UqPvf$Y^VO?|MyqB-F>5dnk&@Y6J{>i3%`rW5m)oO>=f`;NwrZInH=z| zWyH35VE~AH(}DLP_NtQ~F)K>mo3FJw=(()fXYPE+dTLGHGw#H9Ws-EySwO6nleftE z0m7AxT^uVQpL5pQ?@`Z17k~Kf=MO~lr<=chkoR^OuTg|$J`mG@i*X`3Y)t)!4P_RO zyFt2&wNHNbJA((;Q=S{_tiNmQUhlO@$u5#14zoA&McMXEdXz4*nJVq#Gre0J`4`Ry zWoz|6LEhR%C^q}l^=NI(|0sI2B{LOZlJ}xFWGBwU&_~{vk~zvoy-^%Iy<9&?#zm&`z$@9|VCNJAJJs(bVJt<>U%F#$A6#;dc#;&r*OEL> z6=;B2B|HX6st1|U8`r2`X`hz$EllZ(vFHZ^zD0I9>G%StFHRKoLvdRvU8WsAhL1xB z&NE)2i+X7~?_0pb?>NDtYknAikFqOEnPV+wEyg&a6ASf&*W&cDn_f+Ir>dvqqPO+` z$;8v7eW}|I>x>UU6&}F+&^@{Z?MFD>M(=1>+LfwuHeO>7?-Ambgku?0(j{S5us;

<=OMLJ? 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 Dm(Bgel67uGR& zNm}`#hwg;IegI(TqrX-Wx}MBu1%>>^QWoM19=E4#k};jgfJ%wH?SaR_SEC?#N)V-1%yElkR(I zF*2l?$&jR~`yuQZ-ixfCz0(+;VRIY{a@wPAr~}5^UiSAI&7K)=o=m&Rn)3XPaG*0x zdAC+HKt9XirN!F8-u+LEopiG|MkAZ2 z0sJAuT~C>{5@naV_w^S&vO!NV5NKS~2A>t3QI9_-HRQ4 zg~u|;(^3_(0`aklshAe#Qe!=v1y?Q~H)Y-l0FR=%!;`94)cPO2|9NGP(NlFcWXM*g z#Cl*XH+ktXvZMZB7;Dj>Y>=`^qU~*$W(P@o)d0CG+;KAII##^`H~KHf(A=Qb66gNqkl0N9;w=m~&jl{?PYWp?W46fuZ+mf)DP5`QipG%i{2Xdhtor(DYirmWVn-A%=(6-a9IwT}b48HyF z>f~wHUYAIF=f`ruq8`O@X)1T2TBL}<;0keVe%Hyif?BT5l$2QLp@!zbtxf;b$%MdU z1sH@QtY-SRss6}Y@^$_I{mhvI`HkwgyBa|$!&e-TCT5tPj+|M+2_xn_Dr4&P%`~h3h_DM+>L@)Q0LaON_c21 z>Fx7T3FSdZl^k0RzY=och2|NuP^mP-cIByG>wX^6Vv(#=e*z76&&v^X8lW7a`meyw zW~1>*-k@>px7Z96sx5Sk-BJFdiy0}Ov z9QF&8ZEkbVV{_r|cWgKg!P!3-JO0?gO4j_b();~E0PCk+b+n@pA>@41nBAGg>RgXQ zL`Q`VOkb`5xfLk$`>eT<^*6x5x8QQHUr=}d4|{)7$L`9)4o3K+;9P9ww|4Gan5?N(6n%N}ON1NC)xNB~-o+w{s{e6H{%@i15s`WC zHLp-yc{}~cD@DYtZj!v9{itdtJJY#}dvvhrIVVmYBP5&$gd!(PWT6wBegELRAAt3v zIU$Z$n$pY-b6Sk}B|(r&o>C~%3Lo8I3YpY`cRM?}8N|vjwc>ZlX>>cV?DJA!zanZ}r;Y;26SBCZ-G3?nvFY=#?k&=3-M@Z6W0{g|$Ol z_j}_GsK0^dtG|Lzq`ZFqycI^ZUslshnf=_fpZyL4KB!=2ynn&uePjK%3#Z<>Q=pRK z;z5b_a9{E6HzK*5ggS^zKAI{BB33TN<@$ZObMyC2eg`_fUT*M&?i&kYuOxAp&;VqA zNw3@cMKwrD?0%W-nf?;_Ix)_9r9=6H(YI2ouZ?6R4f zRme-bAZ9KAYD-qqM6BxN{+uaTq6R;!6MMzRA20%Rg^`^7KrY@)4F9|INbtQ!%!^3z z(}0>ol;*zF>8aO7nM9;a-W&Wv_dxuH1i}Ag0MV$?pb_2SHjR=8rpmxt#stJhsWV5y zS>W?Y>RV;qMojhfoSKg%kDB|Mz5?iL35g+YKq+o-%+@uuU2Vmz# zb|T%HQcAAGZnx&nmszR|MK&QS6&g2u8r`4@Za%aq#vLD@Cq5TN{AF*AzA|`j#19XZ zBHR+o5@D2E=*A170z>iQHt5;;c)vtRge(S`XQWc3f-wWX1|Vc{37(ee=2p;{@fjH^ zq&9&CMxdD9-gowwmJNQ|!Z5lVT%YCPY{XDg=AZO+!n<*O*8UKQxf|N#V2t^Dd6DeABaf6IbT!B zrLZ=-<&0^>;-lu@?_AN;Ioy@`KfICGrJ-BJm?a#YrPxKY&byGKHk4)nA0 zXIl*u<8k*!y6&`d*(C%Z)LD6n=7+sdXj8NT*XOvzHne?ER8+ogjMH(W0gJpoY&BSR zH-|Z0v#I@~OQ^^aU zuX*XaBC@P;Nmn%KvUOXMW^V%VqF9{~zEMqR2n(g)m~FhaRQ0*6Id%*suaDCf_7eyL zP7wS06qi%{TfaZHkE+4=`^p(`Ff)tqhpKkiNTBl{bBbNu(&C*jxNIlS^%@EDV{s>n16DWpkzT=ZhY6nd% zKRCyg%Hk_#m%;CUKOK^n{PuZG2{eXd`)^ zy5wOO*GkS7irlHCj-eJ~Wi^4v`+y?_3zC=#WKxQ|FeF>^Vd6IL*1Lr5EC1xjAC9y`tQgcIDP$~0f>d9(Brjn*WUFQa=y29Z zMy03@Ms-ZcO<$$RP@7Bhx-gLTDRVffv2rR=gL%vJ8Iy&>W6^50bJac&rIc}g_S}@B zAZfAjw*ow{!>4DYH94odZVjVfc2`UF2plOshW$Ubdxx-iz?raBn%w8?w&115S`bMM z&v<|5Wkif+5rYsmu;(PUDku2$d?3^li-Ag%} zt1xaZC29Fj1AfuUA{Aip9j3kQWO4`pb=B;(`-;gPcm9{==Asujv4_R~BDG|I0m*2{ zya}|{F$z;~(s*=4n^A7mg(!HjsZHUH)QG4TrYD;&I!)B$Iw+Jel=Ta65rgcxZG9r#k^i0T&jKGM#@m6u>$<5xK>er`~;KQ{h$E|bw<^-N$dWwrzbo1CFP zvc0u&Te5nJnxmcC%%PUB@4H;{_beBuHr&1pElxe}J|;YRlAoiB^IL3=F&#btkf~va zKovmmJ*^^IFG&zLj#G zK9p?!Ew5I21kv}8#d=0_6m_9)l07%(l$^8HrBTWc2J%n@*zQ#eL<`yR|L}0kg?hyx zZOtdBtnL^t1{~RQDTXrCzJ)4B3W8a(}?YAGp!U_b{c4`*5;6fmBc90#x@%)(WhSLC-JDHi>wwJ`=( zmb;(H2nVQkT&IPeuBu2jlB&S^ix-RjG+q)_giAziYc-kh0XmwXz^`M2g*$~wj+N^b zRkx@U$1ItH^W*v@3u4_JlnBf5-DjIj$SXfG{elUmaw5m#kwVq5pwEX2*LJcO5e;ri>=O|0i+K;3o9yK1nbB&$BeVZw?M(aww8zVkzRun9wi>x0f% zlAM7hC)Hr|-E0uUbcJm70@QEobNY?DJ`vK>6H9r%C~#KE(%C~!bFdSQ?6!mdlqI)ne^uGDK)-|a zu;fB=N8&e%q-1jxtJQ$H)7XutGb^XV|3y(BzU+Zk7O|B7sl8Ap|HRNC!hBljLez+Bbt!u@c5R2E?9tkZsclg$d@iupLfn8H5 zX6%qa;3q;ZE=7kc)oF69Hur{s?!%{B&eum@*^YMe9@veL*ZM*$GHK5Lm43~)C z&ZG;X8XRqljbAB{1fHG}LK5D{sxDQO#2totI7g&%E~gB=Fz3WJu1@N>BL&&~XdEXM zeCt-fwxV|;*4dYWU9Q+(Q*9s_a|hI_3b<8WjT|L9Ywm15#Huz8m;A4pAcA->g>Pfm zUzN5y8}05b=l=u(Z=cXOF$MpZyzw#p^!H%r{E*`dtKu0l8rlJVBkUC_TIC6Fzs%lS zca4>BQEoxIFVX3Z0(o~&TCYhil?ZkL{_3++_&W1dt9<9=z)hXIFapvL1DfTq-8RV1 zkZ?r3EB5X)6BjJ#(>pI|B1-aFhjn0ibWIcg8U#n6wyxH#Z?hNX-iL2EwB-6d3|!$x zNgewQNG`-A5z+H$hWgDP(z1l^2g$`BVJ+7X@X92C2qS~uehQBx;BuU?IGh`DW*7FM ztlWKq!}R17uvoZo?NcoFE+3J)*>RK&MtWQ|hQ20Xc2$%~+dhXl0WfBswxK&<3gfd) zJ!`3VN#V8+bfq2HWix#wY^Rlt*y{4EbDg8SGWOjf?^D9i5$9h$$NJiR@Wu=?`OW+G_M^khL$|Rsc>y(> z_JhF|2vws2BJ1bX>yHd}m+2)DtI%z1+?(boDJoSzmYy3E#;P>Z*u-G`c1@Iv;C~i%|Hr^Ej)o)uMTRV5$7Ckh z;&68Vl+i2W=gH`|>nG`S(bFKiGY9&7SrD|fhRGeD$uGlRlcmrjr}`WN8JA;?852W& z$+_bI@UiZe8LP_g3CSMp7P&;`Y8h#%!kYt}9!#Z#BHJQu``vfEZa6^f0Wz#(Sy$Ao zW}}W-gL##&{C$NBlFFiU(iwN)`AbbC1ztb;`Kf5pl9DqvD(45mqEktr6d}saiX6RV zL1t{a61Td(ojy z$bqYV3m;*v^>{xvdUp`+S-ljW8I0Qp*grGG#UkG9czRo5%#9K))Z}~$yer$H!0K_^ zYd0gD!oSp_i;T7?=i1q(o=yL@9Q7>|%~PA`r^u=etKZuN)zDz7&Bi}h;lnOPOKrsj=5Am5b_9elK>|vz-9`0AiB!h$XFac_- z12kAy8VGyh(RCUA2(2vh*bM5BMiNQbG!DraYGMu)FS8DT{`+K?(pB{ZVU9ya8{qoV z=#6~bDlVfa*qln^6p+Jr@YTbczD=wKbq(iWHwJQ~2%$>*?Gzy=%0}d()}9*vlWeBG zI9%joG{fiDvVObIy)BKG#;Rb&#oL@s@VXW{f{zk*i?#}Kbs=S zN}h{Bf*@Q!+Dh4dtaAr`f}t{*b!5E?0iJXED*t-B<*%K1@m8)qEOU`83DHTJ#@Sa5 z%b4`;F!yHQ0$PGcHj*LZLtQG%U&_l#>FQ`V!PLI}o9aqFe$ikRK@ z@F{Uc5_l;Dz#;J|HOS6)WigFc2GzBsueX$WHE017lekeG)x=4mPjQUdi?dq2nl-~- zY+NV(9)p>R&hIc(ZILeUD}%*tnhkJn;&i*tGrNB!%@=6=~MmCa>h(Oh3_KxraG4Pu&^+_V@V@ZwCAR2lGW# z=aIQ6f~i|m5nTu-6&mIcKR($Or#an>O;7mS*H2(*Gi$$d3f#$=2XAld*5nf&_c7Z` zH=z}8(XJP;E&hj3P}(+AIf~Dg8BP!>BAGv1tA%o!BiT~>788IO=ShVK9{thJ;$sZY zsh=t6d3$jG-i*k^Yn$9e`hY-aa2kUTt?4ar`bP+zHqNfgUF5a!XQN8_BUDJ!94QxC=bkPr{5BP>}NxxdC z&c74ptdTM0ogiTQob|n&%N{dceZHiMq$h|JgLCl?t#@pg>rdmQRSXZWYH42R3imX0 zp!asVTfCKTY=@(0u+Xx!Ah$H~UUK0T1Nyq3-t@SkDj5IuxtI^JGGzkKBQJ$K{vV&b z)Ryx-^jHdA<+5fk$r-y93TRPTgXyxhW08ZED zPbebMU^vX>yF|ai zugL7D4KKVQ@3qs2!9-%5S@qU0Qes;{T6?mjmfKHc%S4(!k&hv`Y?wY?Ms>62UhwP_ z+OJLL+ZIzc=Nlw_@c7#TkuzczF40LyBV5iZBKH5h?*|+brguCK#czf@iRGn$pZqGBItBcoTWdJ-n%*mC5Mw*r z<-2k!u}_pX`^>pI;v{B5w#Dg1M2J9;-rg4Wl6I#XM5ed07ol zX?dTcK2aOUHmQ_Rht}@Fv!0aeqDOR9*LI4d<*Io~%qDBt$zQ_#_6%o zjboM=ML^GR9gB_nt{bE|O_w>8`#(NlL+kOE0F4&)=oZoYL~FGgK6GoS?&(2Wdvp|t8d8`T!={7V zm|D1q{WW6xM~Y)wNnz;=6sT7a=*FTcfqNbkIj059}Ckaw-P=I7_E?(S2E+f z(7^|A_hV1_#)|5y=eM=kQC>3e0p`zf1F}wfOGE66iT{nPLri-G&+z@lVJvf3i@4f} zrakg)h+??wN0V-Wd#S0$Z)%hLR@Pea#QNA#5; z*&U3VqpV`by=()G5B6V>I`x)YDu$8SvSY{aY zaheToF{(xg^Iw0ipRm{{(f5<|gEzf(oqBdGk`~W|UW*O8pulU?XwnlgR;j1n5!k@l!g2t{%+9UO6)-oTJ-}=9m}k7k6AW%a7M45lZ~KHI6O4 z=^|TNeZRUbtZB+?gLhC=Kjb5%zp2&PSkC?(3SsP9DcEXQ6 zxSf;4=NJ~H5)P+a)%8;WU_UB@Y$t}-S}t2Ie=n_xfmT&Bvh!KfOs9^U{_DufRfM(t z_&@XoloIh)at9@i0rtG&yx8R4HEBCf8o|Ig^`{pO%38NWa%9eSg2~m?$*?H0i$8TN z$ybRw8~^IjgT;m51g#8BaF*bWw|B(<&B=u#dt-8cOwk)d)Uz=2>shQ z*{3{Hdp}Cc67_70BP64N9t39@=Vg3LHde=#PYp$D+C6X% zse>Lwu;~NE^8_3!w`0>*?YEpbIVzI+5|}Wqev~u~Q&u#GR?P2EZJvP}K_k9hil<1y zri9P1I3tJJJ`scgBFm(FIlsx=1@?*!N7|mLpKXGHf}hB3z6HmY7h<>c?d2AD1ztt~ z9bU|WRa@STpyJ#K+!|`p<=YCl57c!|l_TMHGC3(&s@y7l`xRN69zJEY2|IhjQ3J!$ zOHciAq98a1i3lBS|LBn~RMvlWH-g){Sas!g>&o9-F4}{18HHI2bsKD>Dzf*Y=oUeA zfumu9ff+0p50gL^;_vrpArvv=W+S28GjmzedVlG~sUNz)$(hQPiLoV8rPtroU-mUZ zqyzN!{O)&y02kqXm}~3%VRGW}+FjjNfaOx1yv+}uc(C_G2grZl)x?Wf(aeCKYB1$l zti|2ok&p-3-7w(xcfUsoaz)9i2^ep^2jRN|7Nhm33t1N9CTI$57agsVou-|(`S2G> z!yPf_HN%@;v+c>$5u#D3vwn#?NA7!Lm*H*2bt?kgva{O#^_h)_yy19D_&=iTwO}}X z$Sn=1#BsUbd)$%omFM(edd_vOq!E{}4-gA}X~P1oK2ScGbt8*By^Y%A^t0jU*lk&k zhB=R1@R=nicx!t&8Ga$B0NS;Do>W;-Daw1dKUU<2Fa!%3p>rsHQqq-dJh#Lg;^I{# ziG5+j&>s9ygp)$9{<9h*X9DDUVq+)3l6{Ze*@QzUgJiJ-cYW9#kEPxgA@M1)pnI5J zGBpOu_&wj+2h&BJSdzU)esy!kT?da{y)UYU1IdI>x+*bhqf1vPLBDb5Smr~r$^(_6 z+7NzQ)WN-#E1i=T71C6`b@Vc~3ljH1UEj=vJm?o5io=BdmQmzik{pD0vc?p$cUJpZ z;8MkLXp(F$TbQ-PZav0G4ZF2-gLEYEt{t)_sGJ&2Yc_pd0cw6u7&g*W{v) zUrrn6t*w3=F@5}{P0Qx`fyadc?Xj`|J&B|UV`qgZU|rinI3@JgM36SSx`OW{jYZ?L zD{0aF?6~}Law~)(YaQ+zI|*)-9V5pH9O7o?duR?KR9pDk60Pyw242iR`&MS5NM;e{ ziXr8vmH|bGR?2jNXf*6FdRu8=mFx}{5&PG!)J24U?fKb}`M?RyE&P~?lo&A^HaA?W zcn{o_DW^q5dy9gwdPaN6G_8aw&WxReMygDzGlMB-!{O$ujCjP$_IS3TjeAn{PX3dW z*InaI{SU(v9gk&XQqd}BN(z79e@!btmBIJek{_D{w2LiY;}1&Rw} zm^Nl(Vh8<{&EP{TV@K&Gad1SE+*8IiOQ$?3mvMfPWA$6@*$pw|={tFQ12?S`Xgv3g zIeRScsrmef*Sv#+neMf(N!%OXi~ zTe;%L<)SBY#qw}04AkHR`rWu`gR%}4a$^RPrVY$~QeBPTBS62#CP1__PQAF1@?)#G ztr7=DV`H9ethtUT^QsVkY(8AS94Eq@J%n&tamA(mU!1>!m+m1jes@(zYV+IB3~$oy zAPuw9dM_cyyLgS({Bis`Gd2MA{aizKcC3G_8LYm=6Ekt($%$fl8v;^*)ZsnxDb7~6 zFsx7RnfrB4KV&5_>?%wCnq+J$#PY~CF5ucjJt^RmC52bPf1nW;xkIIXW!8j`ECGXe zk%*yP8q#a6o5qh3kuPtEmu*O?+C-M9RaeF~FficL-#7Noc$>EZXH$~X?oST{p^roKEh)w;N681$4GeDk&^ zB}6|HYUf>Z5uDUB2!~@%>*Nx@AENN?zuXHqqtU zfbn_BI7u|0;ar$v74jvxKGr(wAyuZig((=MOuwERgB%of?PfUUEw^_9%#uAOqjr~G zOija)a)cU-sf{rV5pRiHs(Ij^T@#K;Sx-+5x)TEPDzP6+i1LpJd$#_@&Hfz~19R-u zecub#I#_ySdHdt2&3=pJXOu|Y{G;v*i%?k{`uX)`rpZGMZzKb6F7z{1cqQ%ql7;lD zd3LYn-dXx@F{{ekgPtw9#RCR@;>u*={3oRy`ixjCt&vO*%wkv0<>Ge+2iWb)#pt z&ib!dK0HFn8=* z`xQ59aM%N$(mb|yw`@#%rF=c1A)b#X{IXMFvvGp=DE=C81Aa)LZ@JdMxvp*VpUV=p zE4G8N6rXE=@DJ`a{+DFYOuXci2L2n_#K@a(yNN4DeotSqc)* zQAlczM-4hwWGCyCF*35K@nUlPt?kn7XrJjo@lWGYN%#pbW+F&jP+sI{?RC;8MeefD znCxKi(!I zwC~f#L;@FQ;kdmIv~!ps`W~WzQp-sPKB}uSawN+B<5~+#t|1li5hTVqyJ8O&UQHX} z$@bg?F``ucRAIB>gRWw-*H(3 zqWs?oC|!R%A%M;=bzv9XE_RT0#kDbwvp+S#1gCk^X{l}nr z$}qE+4EE0~d>w>ez71@oD)H==|9Z>-Hr*2t?{oI#&Kdg?yhoYzkx-+`pmftCV2oV8 z9b#HC0Qikips~#=S%jfV(4K7#D}rlO-6^%*Xj_oB>ewBfT$|2*C(RViR)O6(EK|0c zIS1{+_Cq20yK`qbS$J0RZ_dTmA8!t2NS=ZMdexnZ7hlx+8Wq+?%1W|3fY9-FRbS znOS>J`Wy&y8nemkw8;pzNVn1U2{bwTyf8H2TiGex>`s?^UU)F-d&xi>!19{ekHB{e zIP5ly8uv|42T1Pi#?d_Fe4=w|FE(I#PVOT)zPS}LS;9(>JMm4+&*Gmx(BoB z{mMWeC0aQ*Z$zDY3Sy3AfsQ$0rgq%VNHFUP=R+_e!uc2= zwUcB^cp=~&h>e|5Xj-^c5Pv|ytE@Nf+hOi|DG@#XfKsIH6Fm`7)FrZJ#<}^KUL$}m z60zxJ$(#Uj$r8vZq= zdK-C~SlbJ=^H12H^>W_5T8SQNKBV(b32TgzfF7P|rnm*7G%HO<32810XzS7ht>H%* z8eJ;$y)E7yP1r>&H4f0Zj|}YR5ApS`E+Ip%%=l=(DO<}#0Mx&dJznXoEb`$7bvC85UWe)20JjlcvY`YaDii16MJyTsjwIZYtrI&xJ8=+tKqWDjtmqRw- zV9PIfwl`xhZu2##`ha21bsO$!+?;O%3On6|%SSFoLHY~#b5^gu8UL14D2g9%=co^L zW-n(q{+Di=7irWIMSm@ac1f-Y&5VNeipv5s-n-%fNuhj`AwlC1oJ%z!cp>&azv7rg zS^V4gxdb@voD_TQLbY^nLCr zjE2ql#6Dg*DKIpR|MHXD5bol6blxjbcxT#WZG1Mo%tN$L zd}QBdc|uPTb~)K1LLrJup*mjJhRC-7sXfuW45u#|@UbqA$+pf=1a`MIyyHmUh?S9vsa$M|1n-_Z(1bcjbCCr9~RBiZD+|!i4 zB#7R6fA|1s%&cJ>svZ)$7a5ComWw9U_4S&`_2UZN2j&(ZD2Z5+!e_kpSC4GE4yurUoN2{^!HL`gAd-)0_EWz(W~;T_a4!+kNkPm z(d$p840L;QTps|~yJK5C^LW;E^7HJI3k-sTkfO~$cGxpz*MX`&_K6q$Ce!~ETJC8! zaa&SFMvjzwLb^Eq$Jbi;i3~V${Yygs0tI6)vfHu)zd&%$Vp)Jleonqw@$I(%wror! zIHft=g|+sTP788*!fPhzt=^XC~-4sM$7w(QT$tD!1+Ybw$pt&jq3oshJ$G5nPA`ZJs59rxC$WP2NbMI}W0O zPGecc;_Ia~n4xTd6bm#kC(Y8s0ucCc%=q?DhM07%7%CoY@jRi?b7Z-N7SWIL+VGKM ze}n-F$mT?QWg{83)omE~e1cP*!Pl{1ho8l;lfV1il@ZEdUy1%ak8MumgAi8ek`Vq? zGu(5@)pfM0eq!G}6E%>MILvt%m*Y2Y?l~6>rH}&lv+oE;^H#i#nex&@K8Q+XhbxX9 zjZ`oN$pYGpxhhwy54tdK&P$!CGMhx}Y!hKl*#P8~pOw1VJUQ zmG$ti&$qJ=9+(t$HdV4iv&bRg>MW?wIr(|!7=#HS+l?d|8c$;~u1V_awt@@X14#=8 zfo*W;kIZ*XLU}v2u}rGg88IJAb?&5G#fB~!&8$uxTO}-A07s$S#{N$aS3O~nZ_*DL zo-q@-DWklJ+a2d3Sbd!bz|I%^=Wc|lu|!-p`>qZJl0@Se`;(>51J@1&{*yWw1Eoh3 zQ7x*Zv*BbU+pWp2h{M2zIwR8zb25$ep=&PDB%3k_P`YF|lIl1}1kdT;fMSa#gHv{W zB35Cpx1jh!bT+FL3zxlIf&SApN&XzsJ#N_gJfw-j@=0Ty6O1HUkad69CtcpyG)523 z{fRw_{0#fS-ax>nAeG1!*W~9*U5cfU0z>W+G_=%9KM!e1z}%o3{E-6QHqjPiPrxL% z{zq20BI0)MgUI3;E~x^j%_0aTdd$(`bR*=6#D8JcW}ld0+cs1Akpbgq*-DZl+F>Dz z+Wx{ke2fImyrbSR|ASo#G1h*Mo1UsqHJ=9*{RCUymaGU`tn^B-zkr%=7KjF+~)TSm;k3o6-a%5tcVLv~Cs#6pL<= zB!KQdH?snt<}Ux(ZT%~8L%H?VvpuLg(YC|7)UsxWI5**#O>z)rztu*Mn+36UIODR`x! z#iz%Oygxl?aHi|q#&Sv}oc7XylXJFGK@DLfe(2M;a{Sv0;u5PI10QuAZ(F%t*_P7o ztwE6%4CP&H#u^@HIbZDB|4^(5b~ex&YW_1xXtAhxno>cx`!LaQRFI6gs7vugjLIf1 zrgt`z2VOVp@T|M-tXpfXkUJu9vo^bJHzIajhuEZcD7EYqcT`v%vRQbtr8c}x3g?Nr zZFxOyy#Lm}TT_Wmr2*ggp4Eh8eke5UTdAdGJh2m9u7mUmbDpi6dn>eMRT9Q*{m}ic z^sznT8iMFyRB;~2(pyX4WJ_dpte0q?-)_IxblkjQ=5`123kP3dX0kir$5GN*eR72a38S%da zH7Ksqp3VP>zS-{K7Hr@pdZsgaj*q>+>=yhv`MA~^-2D5cP%o*T1g&pcBmgt3Vc8N@ z027u+dcKU~P4u}Y!Ncz5IJ`FqVsM!|1blV}>piyF>5e=i&`!WCCZQqTgV5f~i_ozE zR)tr5rXj?u1DPxSVg93aLu{}()&3ho8xFD~HPcn^f@*n|+!|NJJ(~Q4$0Zx`Z@!vj z5g^0I1A^DG1%g)?dJDVAr;$QDqL6{?>{C&%<68Nhd2ZW4WAXYbzkh>dykmA#Atdje zt`=ex`z%h}P)2U4=}%Tu9;~+p2iyGG?%Q-!au$~I$*8o5nhG*$$b3er zJf9P9nS}xJT6)E8a(n%SUh7Sr@Nm(X0>pQ5kkF_dYqmu9CJ~`W{od}Wt3~NrKtFw) z&3DMjtNLOn>+yJtDtGPxi9=xzf>Ss1v#J94WC%e99QATNaz9JNePh02d?S|r=RuPp z))i1KJy{{h`UsbQ+3;3+k33Vt`&bfxR~zyC-_+GR1uk#w%)1yPgnmdQ=$#)${I?HP zley{>DUClpyMId0vWvsqL3UO05n2_NcgL@`b!JIQ+k+`}b0YzL71nb#Lp4-YbMBao za-ptl<2>{jF!?(+kj2Zv49Alx#}NoEh)E_l)NGM8U zKBH(y!K22qVp;q_#>vU>0t7)^!j|RIX;XSVF6Fx47mzI=TP4~-zWe2g=jgVLPFOPA zog-*4l57hv8JmJF^r!~dJBkac6e{0e;AspLub<8@ruCmQeQ@vIU>UY|>QVNdeXeNV z-uSH5jQ*Y2t%kCACZ_T`p}d2aO1@BZ57KU9ve2_QM*VqEBxR3f*!zxG=u9>GyAxZ= z;NL!W2*^gKECn-ov<}CFwzF2=wi}WJNguskKRby~YAW`1HJ&~Af|_gC?JbKLgTahu z)@P`t-5O}v%}+nxw?PYhQuNTUZ$wMDP$U!{Qrg zN7{BZE-Ie3~gccZ5IDyElYXF;V znImh(&$#HhRl)VbFZW2EHos z+R3VNbns=3=|qcw%<2M(^FUXn4Vv$ z#dnA^$a;I5{E6mG-xlp904hC1zA$w54uUAjzpyZnIaM-esZvGmO*57ZU`b*?{YDZE(-T*n*Yh5_hHP<0+fXCXxvPGHeW1# ziF%G|6OKC;PPsr;sy#Y6G7_Q{qmN!D=tpG)24 zmsdJn=se8N_cmnR2oZktXE+e5_m=I1i*9CkMPDQIRGPk)XOS*9%fD(TDP!6z0~sDX zya(LG+&AVaWWo&}%iD!^=pjlxtwMD~>rNJ@t;d$H4Jwq#TDju3yLO0W{^9V0PTPO} zEqrh3qIww?{4%lom-p23>?MEqlbT>L?hyD*t= zQ&G^BF_^v} z$wKz1LXe^Pqx2RW0DOqbxzLhn<9=nlSYKvfZ^ZJob-@)6iK{GD?&TMBW$ZmQ_NmqKZ zO}t{UyGcfG_J_-fsp=X z`5`&q&_q1?ARmhO)VP2mWx~Nl$&A^G?l~z6buGPAX~gpl`>r^78212|KYE?Cn7`W7 z51qMC!C=$FPqMZSoQ7k*0Q2fTWwN3D@OW9)sGOD!mFkPB3vWiVOUQU+Y?{MxWN zw=B_BX{}*2Lk7Xgo!1WPXhU{GGX86dWvWC8Kv10U+3rLH!+zKC=K?I9OysH78H~2; zkx92*TOJXQb!6=82dAAy@cb%8dX+miUu%waj4lnEmDa>rBwV>mB^nqHfHNRBZ3J)Y!tlgN4I z@~px?*iaM@8!*jJ2?=>lDtXIDM8&|=CveAX`H3bCu@#LO3-_mBSlFx{UdVyJ{-~f; zRRAQaj>Es%aqDl)>AP4>vgL@tnyeNC3SF>Cl!DcbKu4&j5>f-2b1oDlOo`nIz{`(< z0h+(Y59rjY{8UXEZzI^q^3ZZI;^7ot^f1=BAJ1qmXS{b-9)k6v(+4A0`aPy{_B6J! zed~TUZ!DSYZk79FU4sahh)@XMgmKa5xF{f}QtI*yz?5Kh<9D9(yK+UM-T>sL;D(v`OXnnMc2Jb9$ z1Uo}HW07IN(_h^g6`}XxTEkZ8R~Ouw+!%$G#OZDQaq6){k_qRCHVNd{DK|aORd=y4 z(QE^2^YsjtDb@4l{!6)4=+){QjvFV8%wIDcn?K+)UKXU$_J5FE8J-nHOzF>U{tB%T z>_u@|ts+R}vs0_q-PAJdnzq~8f}Io$9Q6J~qjO|Joazt9KmSP?;h~Xk8*O8p0=mR{ zmCqNKXWp6^mbB@3?lk&t&|g=csy1+p>% z)g$|KIJYXXRt%GC2z2P)e2RYuWxQuk`+eQXB|d@_B`6vC$q*`G=Jn@7HHdEQ5a4n_e`K&npGR@oH(}XGACI#5XvSenrYwS?nsZdrn+dvr+AOD1ddpwPhqa2x}SzzaL0DLgypD? z(dsF87nl{JGL^lv0Y6J`l1P84rYgV!$2c3h%nU^Xo-~|o1#vO5kOYD+T36L zp1AG0UuN~&k^6bWhe-`zV=U)_gR*JQ=ghZ($4DW+KDcdr`&%K;sSHrCjTeDLl_P^2 z3xg3#{(bFT8MW!FnhUq}s7WtOju*>um@rlMU#w7%9=Z=(f=O!sn`V0%q~D62yCV7T zg?=*{hN{FrHns1_z|&>~C302DN+yPi+xyA8}HOG!-G^uXA+K3tC^z zf<@p?KoQc~>{O=OQ&RaOk+Bys;Z|8)_2s%q-*E?yOW)pTj5y`jXLm?24<`pf6d(sJ zlw$-+k=Bw#3rV*X&TuOoF7E}|67ICb+_^^+`gM)yoLX%yR}fVX;U7ko4;5O9l42q| z^jISDg4S0TBe^vf1aI1>pYd}GsSpksdu`mot$E?>fUjZ~cpd@Z$L^*_#T;;lf2=7`^+mG+kH#)?yT#N2~sc=t; z4wE|VaWs$(M4Ji;RQ@`DnS6}Nj9{sPD^NHzDZ)o3HIATYwvz@=?Rm(7YJ_o?+v=)f z`%eR(k3EqfI1$cDhmdIb;&z0pLHd$}WKkf6Ns+Lf+_V6XYy0t%DcLBaJ6O+ppNfIl z43+JX)A@1`@p4Z=5a}2k3c-)ue0!2kNlTD##hLnT{NTORC4?hYbW7GPN@Vp#wwM+J zSn?&;*EYy^(bD*&R@a}P2Kg`9tNi}oY~JpBxbG=n(bN5QLSaw`wz&O)H>76%^p{vi z{}s&tcj?Oow{EX6o2P2iX=fV&b ze{FgR0}czv2%#JMpA3(*~5%1u(W@W?&;&Mty6 z*}S^YXfRGMWmqPk-3URFZt{F2p!HK<;2Uj0kO#m%``SX=-rpv)ff^Gl5{+%f_hdxu z(;qBL#NX`WQzBl8b<)lw8KB906$oq>s8LJ&?avZ*&{*7z3BLDK=M@DiJM$_-Qg9{m z9nj%w%wz4d02a!M3paH;rorG(d)nD^ljfKw=9><$sA)~s>`>2k%#k~&JA&4V z^M7pl8P2+^4&Spre4atgy+NF}sI}l>s&&WFu|N51AJz1-2=voE2u-gN$rfr5K5(03 zn6ucpSoRD7!XcK9%h+nC-H%cWRMHz+EFdJQV>w+!PiD=78j3=R^8&Ty&IH zdcZdl>k6JLo|9)L=ZPvL^S^l0&?Vc>5K53vBvE);*^Fhi_#M)Ugn1y6+`0Jqa@=x~ zPAGyJvVa=frxRK|ywB-7wJ4`6iJG7I?uoT~S_V*|E7%8!F4rGq4{&%q zhX6#W;NL9Eg10%ua#tBsW>b$o=1LZT@efK-KOkUyP``fO4kt$0&o_UMd;Y@1tR0V@ z{@N7uG7MemsF2Wr6P3#gD+Y~HtmmK=%WiNK*U(M8 zu@B(68Zc@RWOTq=ltD*!V?v(#-;spyu2xL1Qg{1QHvoWa>}aJ!x7Qw#Hp4#t*k7)fGT`IV&6R$;4l7Ri z%=b(X8prncg%*}Rnt_*75#scVz_AOm9&gm=hc{q(&*e55MZ=W^OpUW*uW1W@qtAuc?I zWfB1EIb1>RMr#RL^7E_=HHf_n$)4`OcDQw*uzBL^J>-HOL)%LCUHer1mvF>0UA4Vb z-t*>1eX1cc(m&NwdchxXGdhK|83zX!;`V5RBdug^ze-*h<^Ae4PNe+uX%*x|E{kUZ zhaS*l5olM3^C39(-z7lo2WFov=G%8}?rpqaKk>#$2Jf}v)P>l*~d8_OjHNq%3MzY-PF zAT$@ubozDHHgrY7I_dJ3nhO>J7C9Cc9v=)jLDK6Odn+8Jp7#Da$vks#V9+HR4#O~} zKOz*Dm=X?mYiPNh#ujcWxAh~kmgW*s8ZyQt=X@8SNdCQZgv#l=%{@$TEwUg5c^Fyd z+2t7iM2ge{X8YNY7GWmSaeBt?_mqB8Sx#SI=^HGXFYCt`c{pS-6nmGeO#A^PPQ*;I zaEMdroqSVnaqGx%!b zloCNQULl>ZvxV-P>GT62o=6ZzX#lrxx8XzySe*pNX6I&Kraw>T1H}OL9f&o~(I=no zx>ZCG6CaO*FjA|J#xP4La$?z-ZCX6@?o%E(uAMs zzGHN?w)m#934UGf|Ew;YVVUy7oC@+!VaPCGH!-hvWR$)N=qT0!NiIbL` zYC`8~!Nw7 z%QzgB%`rAF`{@$+R@IJOYS?q|A^*tx?v*%1C99CJRi;;>xi#XNIYaseD!{PxQ}Kiw zgC5zVc^$)8+`}!G~7A6KJ zHz&GSaeSJDbAj7p7C>cJS+IHAmepOKi(viBS|B*+VCnMbi%qAdihz7GC72kyAJ zXVI-pQHfo?xPK1Mjd1x1%UY&i4Er}8i$hlAL$Fa?mV>%T=b>y5*`@MbiM+}=3h>m0EY6!`X%iR@}(^NX!MCp8xiL zNmsST@M<(9V40&!_=YszOhB55w&m#m5g^NiOA!;ViXq{w!<{{&$`N!y!`)>XPrdy+ z34itju-n@9DF0&Pd`tmf^94*cd!yFkT!7FTc?fID4@SJIAGKb2V6HU?OnAKBnwW5! zXBm%34~m;d-RlWqN`=oh@Ag-Ox3cQ4d-kE|)aN z(cgq0SQ4Z;yy)5CF9q!XF#Qm~D;-fsD6aODME`2INCo8dKFz}`W5%)A7+}2;wmL=5 zJMe=IGjF^wdI#uEYBt=I&u6WnL2qE<(tjXwg$l5@KR?o+MoInkB3jkPho9+a)9#y{^bZH1yXf` zWX%ReYNpdK1=^lped|^W&@Hzxf!2siGD~{87)H<&cODQ|1xUP~S@|XZE2QfAVal`( zs{C=-US|~1-HDuqFOjM>YPiVIluju|wN;od4*{#|;g4WDUBy*tdzks`0b8sWrUxa* z4{}m-=}u&fl1>yzVeM`^OKb%<<5Er!1efR#@3SZs{~JeKS;{AwOIYB zSOO0#BgJ=Rw3ikvx>=`MBkQp@w}qgd3v#g_0nvL9R(2YxDI@v)0L#awhYTi%q#Em4 z50nFmf_CAiS{*-<`;2n0fbpPT;h_;P?-e&+06TKZ<>vv<E#|LDvfoLc>wk8GVCc($NL4!y!LC7hxXr%dhIa)4z<;aU) z(4oQzyTZJ^o2!OC`}S;i2uFqbyeK*Wn}ZsUWlr?HP4)L4c%J|dXkVRHPl1>$BL1xL zo<9>@5Xuk5x;^Hso?h;N6dMOuove~pj}X+92RcK`Kq1&~UOWhTU%#TJdlF^pkbcVd z3wC18S-{!hBeXEk9D2VAW%o|9^YYm7x@xI|nQ*NS_d;ylR!APjnLv{s91N<*3AYz^ zO8OFF*~dxK&u!3^;p4>pnNC@HHx+GHVJB1a-{*Q;L}4jCzk%NNs72`4-F8NCvE5Hl zp$PXwko7||VuH=k^*~d7BoNB;KU1>%I3Nbl zCt!qYj5*7_+@0|ZDOpwZQ8WHursuj_PB)WrgqaP zu?G?-!~gG*zqush~_Y2S0R_&`$ z8-hr^8~3Yk#5O%L+G9Wo%|Rd;54axqGY;BzVpOy(ds!>g^%}iw?65dhors(6SZ!*u z($j>R8tGyGz8NaKqahQV&NzPZ1yHkA?cr!hbP0d6m8~qtjdt{+E{mId%;md{KG;l; zKsXFt02b8kaq_&#RZoxTafjkY@MbYsPkvz@W7rdw??xY<4Bi$0dCzcQ*l+O>WzZZUmpk}?z7L9q77tS6J%H_D*Lc0F0_E>(u<@D|)gl)+Obdodm)8~R!^z;^> zUlYwKew&}(A@E2mYRk~kEl-`V2l#9VS2Tj^d&uOwl@(Z{7qr4>Be85J9H=eKWv5?ixvaO?W}XtJ?%dPjC|-($|r3>K^iCEYzx*(Gs>Z8Vgkj zO?%Wn2IBU=P&XJRvxmxaUO!p6%q*bl@?L_m!G~wf_&EXBu@dQvaO* zCrtGCySRyv}d-{G4gX&Y*XG+~o(EaGyh>fKF}! zkq8#GyGvCtzo6CdyuZq3ds1Tiu#Vm;#g`fiyO3XG33s}_f~@(8PW#Qsps^zrMxBFfzIzd3 z+63MR5sxWTU0a{%KECWc79qb~d?(xn`l+?hH@y8X6YHv?N{w~_bz5dNBHt%SknkVp zGs550Xc)35IddzS)bYm;Y@T?$u9(9)J%KJR6)1=VQ)f{9U8V=R&BWv?Amt&I!ZVI& zf~EXP)HIRS%NCIQ#Fm+k_B&O;F>qS~&j?hg*%m!rrK=mjJj3c5Z?*bN?;0hZ*K)D{ zvVEf9w`_F8T-6EOJf;$YbNBqu-SILS#^ImR%iXQ)`%h#(F3-vUPF1Jk`0{Fn0Cg{3 zXTLoyFq!$X{nT_GN#CqE^pH2WbqF|E53Ff0#fj!*1~Bvx2UrF7MeYWz5lV`!5{P z!0{S=?AkTT-?r8AlOFd%(%NPy!_-^b%NL|M>Z0A^^|vaeoT+zt%JDuH`C~D%&O|P$ z&B>Gf;A9~ud5&l><7%6KuW#!GW6umhQUtw!;ji-)KQ$Y@!u;o$vRbq}jFg_?xihWO z{i}hv>0{WD2Q*dpz zjt-ArF`7m)yf>|>Sh#%)@031I;m9=KgT^FXxIyQmmo05+U8mr1>Tl=C79b6~F_~;7 zm2g$GP`=XF3U3Q`A2Zl?Ma>&t^|W3QIV8A5ouv4<(d}SOF#0JPGuTvQYKJ8MZZQ~- zp=O*^8?LfEP-*%I9eZGg|FVQjJOp8ezvwzJ1*AKfxAYEpv(ihBekCcKm#E%X z_A-xlt4y0)m>taFuRHlston zNVP=AZDlXBuPqm3UTU175L+KCNL;Vs_a9hztPM5pntbyr?#0o{`pxZQQX38a3;~Eda>jo?vrfW75O>5N zL=qkt=xv8Hn1|o(JfgpBZ&x;XS;L5P=TOY)T;p`X-8WG!)Hg{iSU=9TzR}Ew)5g2~ zt#d=gJiQ*^Ad9lj{flH8qZZk27l1m^%)&7e^yS<{PmeC{eZ6J*y}~jE=`98>tkKIC z>Mr4BQ=H0kOMa4 zWEue5GE<`$s%eXuRxHRWr9J3B-c0_Syw5_q6iAv>N~yVz@A`zt~e!9FX8J>K3h*H^y6<}>JRbEusffpD@`pfu3sN5@T zm#?uZ@}{rWIbUKH=hRs_32K4WG^Zx%KI|2yA^@LCe`mSHGf;CZR$jkhWA{nw>1jmj zWDCZ#-(PiY(&kw8XOjaW%w?kmULLa{I3iwmn0_pC262_pfpj?QZhVLC^pfJ`lgc5tfd7Nb67^}0n) zie#x?ETF;Zt`}f6o{K=HbIA~n()@ki>sm*SUz@*$cZ$?`De$pAf?DH;z-Ai%S`Bz$ zT#o6uxDe%j0p54a;h*KhEFj@Xt!)Th^-~GLzA+Yhz_e1(or66~H$7?`NcN!e;Hi(t zm9ivUp+yGoqc3E0#v58Twjxg+{pn{Sg!U2hCDcXc|na*yL0zbZ;Xd9_0DOJ9kp;5mV%s^Kt zM0_~m0rZ|buv`O_&He)okRI{EM|}_Q&KPp)y}G|qC*aYSx0n3+>sHFO1YF^EhW{I` zLG~p+t!K<(bi=S*menDK)z0N`(L!Tcaz+^TV?hk^*l#YX5E?FXethS(*!zhx6erg# zR@0`cUoqJGIN}3z3|BO7t?URcY;$1)ujrX8ry^~%OQ{-)1Lc8ht$PT6*3ujBTL(5? z#A}8iCD{BYHdP!JK@2fjW50RZ+Xg%o;}g&XVwCroVu08ot^}QX>jx?Kl49nb){SdO z^I>saL72XuSY}TgJjX-JFH5D~Jhmre9MRIqoGnAoTvnVSBk}@XLN|0-x4MEY`BAkX zt?+Bw(c`1rb~`@rotqm%_4mfZXP*C~rZ1$S!6Vjme(x&JzurRw6j+`i?xX_m*7IlT zI};tB#}Wk5lbP)=)4;8z?-dHxYQX9LZ{tQUV)2F>^c%y!Q~VANj=Xm z%d0vG!gt2H*aBJY(SCL?ayI?Gd7!I}60(+xE_wRH-wfvk*7+}0wjF8irYLvH!So0( zS4nUV?OBLiGI!~18iX9};!Fi$)MNk8h#9|KeXunonS@s9C2@MxDEE@3UXLE0J|M@4gcRFTi$bPqHr47QI zln`>DS=e;A!a)g#BR$|aWpbc~rl+3~#vbCxO;|fAzazQUGw|tCpbJKm#E|~c7OWXz zKe}U*n`T$zwk!{K@_t#|1F^+y*ksAnnZ+*24VyehXktLd3(JK%GlGLEis*K^FK&0c zz30zK@)ke~{F2M8OIxRZ+$MJ`d(`G*ara8yo)4&IoK;*K*3W@TxVR zH%mKf-&RAg&yDN7re*b+hWvC|9_qG_xCfLp(LCywNdHzgX^g{5+|$9A{TB0->b($; zdcaichT~Z&!RPJic=ZP<${^?7fAvi1q@`B1IwPP|_3^&PZJOAD&HE(7KVS5UNoj;Q z!}hcqyWOvKf9Vm)=~#zpuulY#8AFzr9kJz+QtaG!U1mcGi68W`hV?qZ0zSf2j%D};S_pUa++ixGAE z+%B1i{$QDIG_VHRJ8*Oy)vF*rGd{c32b}5G=-C0iL|T1#qK?JL~{m_+_r_lj}DWP)8gJqemi1f_zrr!O3aRl8`xk(!eC} z0y|HYyj=H$xFg=Qh8ao!GL#42{Aa56mhig(l<+0_-=;k}2d%+-33KdJ9#?n3R=)%hlPsEmARTg>UeGW@`m5=2kK{RAbcmE3Y zv+1$v{RWm1doP=8TX$@fYo&Ra6C&D2NvB`5RvBFDSsp9Qx$7>fU%z=E3-4pE>hF08 z#RnNe2kA+YWKDoirio5|^30EZ3SGemB-bQ6;lY%Kc_lO`;VKY`iSRTMJt`6obA`*oYO>?$II(vd&U}^IPt+XRDtRgL%xIH``RCiU3?xNle;Uw1IK4qe$DLq z>s0tWbJWjOf$Hni(SD7a1ix*WTt2~Bn$R;9H?B8K^&r~3W!CMWf6}Vv87~%gVL%BZ zqIc||fJH`Tj=C;5aW=?SY9l-s%z4eNe^dzbk46(y&gbzvJTYFsAQI)+Abr~J8Q!tj zF)^YLNO}fHW<99Qg%m*ld()}^sd-pNuOz_^KGKVoD{An~WJPMK?%W8v$58N7zrh4x)C^N2+7lGWubPsNji2^DhkuwYJ#tXt#eeDI5srRMy|Ot zg8gcq*1mh1hi&AxxJ9`gZPN4hC_S*-r;@akE@&(}ed!(wNQ3;Q! zchm%adf|&|_EXxoO7*08uTo7g6JE|)f__4-7tOVWKYO~4>!mKg4B4=xN6Uu)#?_p| z`{cHMrtXkZGcn&+D1$oNk75&s&z&QRQ7C$aWtLo+A|ERPXod=)Um$d9kIwDC2@%D; zQsM8Obrk40(7_RE08&qXG>93I4}`N)D_$5Kf)aS41cCjB$8z6+dlW=>up7!%?CpQ2 zxi<>u;G4#lWmm^>QX||rd{FzPN>aGhKWXz=0l8KSwHMoa+3e_@_Uji6vC;!%+IKaL zq7gjJPv_YwA9?7Q1(l#WE`rzs!5vz+hF$guF9OXe3{u$Mr~ z_3fMMsK{z4p&p%Do=c^CI^8+HYoeBW|L?6M8w|c`z|zepg-Qtl({4?%Y>+ZvOb)D0 zYNuTYyY)f!wj~(#nbm7fNYNn9gx^@9-wnqc+MFEh@i?g+RN58tH=zvnJLGW=ISYqC-?+XZ7wh4GzK5qyYg{%9SqBWgJN3UB_V2z&&`*w4B&2?htxD)!{5H+Wfp zDp>OC`5g)>n1r{Fw0C$K3mFlP_g1)LWsc$)IkwJKwqT!S|1c4Ym-bfj@qu0I!4%0I z))XNd>bH`|OC)y(7Z$S7{HFIUZYSU8(0|b59+61?g(05vwQS6MYX16?UcIe_K4y*S zuSNbl?r1W+V;9N&jXy1}NSFQ3oUFkLT9;Kmn`OI#zb<5!S|FkrqU8Autm}Or(@2!} zCz>5K{zLHP76C6AhdUzMF^AxCr=h>C!!G}`noA3xGspYCx~x`>T$H3DzJP19J0=n) zCj)*iHBVquBVk~W+du%JE6}J!7@BXxQ)^(;m_ud&NenKCEmX-w9V}7Q4`!G zCp`*WJz_l~4jg`8ZXww8o*3AA$~+EfXUBg=8OV-GJQJaveO#~!Xv&!59CGHtgH*ii z$}SYWLDb*poP&oZ=?QoEXOF4VQnK@xj%`+0!@EU6uzMy@^4AkktTEioTYI|qW^BAu z%0}GQKk?phzC&;HM#8q*R++MH^d|HEE09O4SKEp9Y11bnK6e*bytj$QD93|I}zyjJ45 z(nTeLgyJkq>Lo0v(ba$T_^~63eTtk2uZl2;5z3WC5)9~k{AhH<(CrU~-r9L?1bvI5 zQ97svS+kd7yeNp$`G=QhB|=J!mGBR!U^Nf-KDT@tW{4&qMyzRb?b@aG@VJHO3}8(U`+oBZZ3xr1J?;`p_#PYY;>Y5bp(-R#4~C|-|@QgYTKyo zFJ4Z^(OG3wU*PtA-lgx6fo=iUoA9LR7_njVq<@&Erq~@l6eDi`f4I{2HNIr|{@We# zS%zyl4!_&jF`0gHU564`WlY2Uzw&vy+44WrwV;BD%gA@Ul(RNB%!e{IzoG`w{$F|< z64i)%*pmiVFQkO}{*sFnuLC$JoLdqcd+987ttKFRlK;guK)H!iz zxT5Fo9?^)exI5n{6A=ZC2<#_+Y9f;RQ9IX~j>6BtT+;DDmLozWh;U z{{;j1gLc>qGTPqZ2~o9Z+DJ>iNwL;@=~*JnRR>1BWmAr>rqNi=%?w%-i-`=XJ~;R)#yw!K;OLpO87X-b%){QKxqTZxY(vPxjIf?6%l@!A6U z7U^w^zvAs%aE~9?oF) zymk5S9^nWwRMU)WkXjR2c9OH3aB%M1J}%g6XKRi{LXIuoZY^APU&vzPXre7 z`Sad3#Dn}4L*U!rksG?nDNkInTe0xzPe%6N%f!AADh4Rc2+Hp zP0Tj9g(O>EWqgJZ9fkqcEbAft3OyR`|K37ZvJ>yPwo`MlDy%z5B5uq2iS1keI)XVI z@D5J1KbzXhsx^hM#NM9zkbSFDmsq+P)tKX7-;xQa4vuNy*tXNOlSv6{%GwzZ_uYHk zk`&0%)o@vknmvEQh|Q?fxo+DOp-SujiAt z8#^go(9{ZIuIUkq>tw8@ANEuxf+jpn_~i(4+&w(~<=vyR zs{Arj*hv;u-v+xCRRi=s;+M1S+C%ukS4*E z&hkQA#T#ZU_BZdBk7)?z*EYBUQ>SX%0UuecU9R0}dNzK=-M}@Q)VFV#2YRLVx<-Qh z0R?mDPSpVMj-~+jDPefslfbcZn{{fx>c{&3!_!$dL;-GVn;v?kq+7ak$f3JM1*Bs@ z6d1Z;=#cJaKuSbXq`NznZWy{jI$qA+=e+;n`S7f@?)$oq2|7g#4D)I0lR1@A{z^$L zSNZqFOl{43i*AmOM2VQxo5Zxx+243%vq`}|lfEN+J&1;oPw8XS z3T_dJj5OC(dVdpG>72v^%9mQA1pK5*+IBY{qDrlxCPxeHA0ECx)q%c4XNGvvG|6gB zc?PxFEQR=@b8BrO$Uz5A!g}i;inu&c!0&D!eqEqXDf%O%0|3)w-tlhTixF%9pbcuh z68`UIi`?y}L@y>YsU@AtM4N;7==nR++zxJ7=6E!o1l8F-c<5)U!4*RnppGfS5V>DN zvwO6r7S|eIW`SZ1I@~SxeIwbI8JOXJGm%&~fceEITL=I0cu8iJQRyR`Gm-Rs&v!BD zvS5j`nc|?x4Y%o{Eg&s4t1isdo>qNYu&%$yt7a^#-kqyyOgG$CTs~!Aeq|rtDBt<& z44nczDJ)TS<#C-=Zw|v5mk2I!!{=7I0*~vDx*rVlz@;9)$&f#$nYI;NM}<+Y94FO8gQ%e!Zp;ELF~ zWW9aa?!(c69RAVWQG1);U5wLoIl!|K1J`r_2Cn0pAqVO^nF#1hpH{Hc#};w@0Z14Z zfL(A;WEduvg;#_hGrKX{@&caFR}#{|E<{bBZ{lsgM#qdO=YK=hi}EJGZ)7pG&9?eWnujUxLSUc7TN5Pe6LpK( z^ERPALSGM#8t~j(Gj}>y;J(tIN}UGPC#Li(2h| zkE)`MbVx(H#ng>V7hw}KU8dw(ng08)yW@~+@AX->a(Zo=c&2tEA5%~r?%KQ(^Nvx8 zzogGX?@QZa{?2hMao_0g*z3c~sqSxi9=URQphZbywTW{zzYP2tj_MmdDc`3T^%%l$ zQ+??lPtXkqliTnrm%Xi)TAc8K1F$ROa|=)7T=}-X)fGly3aiWGw9Nw4;^!uYgwlr^ ziQM=ihDr>XrNA^N6u0M*JFUB`a6_p`$wywlBKugsvysu??AWv3hJtVDd?O5VA+})G z$(i;)9uxIgge>0hu({rw*DPlOYT!}E|&V%f+#!t=OqKyx@ z6<12yyDjrG=f~^^!sOqW18Mjfr`&L|8X63l18vCi?;|)x62fgNC4m(i5%lt>F-8u} zlXV>S=#EpdSb@d=P#VP#gs7H?a?tOUBMN<&Yji6ZM+MWQi&wGkvs%c*@C~s-{EI4` z!~Jw#7Ty8e4qd|3{i32HqJD2gx2e4pGMFB0a(i3XP2#DGG$51b;l7NdG1$#pED zGyd{5za>HexogMtf}I&Pm+n4~SyG5rU1N2!0|EzWmF!}j3RV|=ZWalQ=T0H7{G~dH zpyu$}q3fbhDyx-HPO3-jU%rZw;Xr#*!tbnGdhLB>jI{n)B_s=i(IvwNB~|-roUh6q zD!W@_t{1Lt+BqwqOZ!`B{?iDDNnul|?!wynrJQ}W*Io*CQk+Y4s0!&9lCrY^=gBW# zk@{HSBBaA6YhHMCrcWa2iKDG`wWt{H0wMs?kYFn)^RhUbD=Re}}Ahb|}y_VD`&oB^`2ai_AH# zIj0PQwL&51Cg`rrPwiEhSE0RCoDq%6+YA9oAbf8$oTpyn{1zezNz*8>TMdQnRQ%O< zGJ6v-<-x)lR40sK&9^Izzs2XXi7Yc-HTej{zs^OS!R4oxHqW-hVZZ^#uk#fRR^eMT zFb2v{IL;H=igMkb!_z~Ry)#;<<|3c=S=IqP9B}ykjngYBnzy)#z;E9pR0sd3CI0HzT9XA2 zv4l!C~hfGt^V+~@8Qn~(&}XtYPS}D$|?5%pVx)@3iX;871+b62U$Gc z0xA7e9$&Ifa71_dl|#q4xYuNnS7N{e3rs|q=AXpnbyBEmX{r3g@sBG%_-+jwnnjP-e)6x+3;`mW~5^o4lj5v=jYxl_}Q`InaMc*i|(lj({%t3{?A98$P$x_{iN(s7R`(-%VjEk$&x3+|Byq2C?L z8`)ie4ByNd>}E48mh(~*4zqJ2^h=$={(kk0Y*-;Qy;a4|tZzIZ-63@YuW23=sdYI$ zA%$&?B-U#Zj`#inoeRN38fzpU?g zp>Cv$*)oCUwQ(|2v-Dfh4;eL<9Q{@eknwo0cT+|;S$a+aP6XYF?II#|n^8Ud4pCXi zc$3Obgki7w$vhrs3S9KVPx|@`U5}zRB)vO# z!_Ej`3u#?^t|J9KSvT|2I`F6S!I;y?CZ)ekgrKtsX zpxepT7hQ~?{6?oYSqhlYmO=vYi^3a)&Zyh>Bt;c%4YtXsGUE<4=G>}gBMLb=zs{&c zdt(&!4iqn%m+V4*o<=Z`T__*o?J+x~;QUHxrW7d)h|L)dgYz{$hBcaiwDbn^DYa@E z<&6M?-`Hg=HmDxd1Q67NX)%iQ>U$SmG&)e$&8b|wH5Y+Px$OjadZmAKqvfv)Y7mpL z^Kf_0FY}ObMc@`iwvJVJDg`K8yDibgc3x?rj_xr6M=Gk0P6hv5Z|`L}65Wxoo@_i2 z?qV_)3IRvIuzB?*m3fW@0DGNCBcj$Wv4|pmG04=d3TVHfvHCLFg2l_N0Fq1`U_KuO zB`IrGvD+q2nXgfy_Nx5q=wQA7;OiM7Bj+$gH-3>5BRBA34c+s#dYt=$9hdoB!PUdO ztSo+M=+e)p-Ug)_cgLe_C&6~jJOyZvCr2N6V5Z>PE4{V@rKQL8y5jYRT%r?M2cz>nu8+ls0V$nuEry-BJzTK^I4A`_u!* zGhM``zp|)*)7Np>EJJWbS`v~7WMsWNO>3+gb)8p0K zgPKOlQhKDCd7Ri#<+&Fx?+)^2IUvvsNDL( z0tP9L#3K=}7so-o{H^v=JoHmRj#9>N=@c`Q-LzJ>lPN>}qwx2F(%`rAvb`ykqc&FT z8p*E?n?va(Y?$jeI{IkO9-P}#ZQRj-ALKTM%*@QYjiy! zp|SiTx0T;z`dku}_eJ8A5yV|~*tE6I8a5%a7;63V2Nm2sM?wVuTWc4S59d^$67qaq ztZ$^tlPcMfZCldg)SA%fLfV{(B65!K-zV`Q>bkN^YMTLE9rvDbKiUSQ?0ai#`@s<}GM`;c4 zM=0;YNQoPCr`DMLxWauLYWxHIheYP%HuC5T2>bJ5f2WrK`Xp~4*+UIr)ebFo-zgiu zz66Gqcgv6Z$9mNdhVCoTergNsq2Bu0x6wg#ki1&!Egy_FcD5v{ICHB0K=cOBGRb7d zRhkeE<08^qWv@x80@vO2pW$uu{W=wE9QNumWPE#;MfEQ+1_D-|(}voXqaE^gDHk7~ zz6>?nzy7>me{>`O3Wy6B){6xZD}ulsh1^sPkpr4H81qcDin_*Kgl>Jcup##$MsM<}x;Fb&dtWzw=x!P1ySRVxx$**o@ ztr(XK@@_-5ef+H+1uwv*b$@}#g?$AxLk?}Q278x%Kx!^FPuX8-$tMOon~L``AZUA2 z7wF3h)Ulq~?C2hE^(8h*PUUQNTU%p_-kLMLK5ae1za3uTzEI50Jii-ZEJVvQUcj0& z5_4(egfe`Y_hDqfiwZ18o7$@Ie=kE^?yKR48FU* zat?o)?Wf}yB>V#iKTm7VQ2I2;C4a=|Bry5PP|;$1#T0PFm9BHBvqfZjb2!+D?;z9e z@(7$afb5@AVX2aO&8Y>wqk4MJbZOe|d)F^T1Ae@VD~h0ba#$!f_kDif_>G!#fq$ta;- z@zzEI1gMG!rDPgE*BU#)4@Hk;lgUQ`4P)@lRI>e05%1<>(OV5l+i;tk!{PFua5ocv zcHe7MlZmkjCRkHJyEE;V_Y=_armA}?LsZ0 z9CF~ST#NXsZ@pHch2GyvoYy>5_(4RBkWA&RL)s;7q;-4O&Rq$L^r{^2;On1^)?I<| z|8jVTCWMnEilHz&pD)ih0TSia{{P}J4v_6Jw9XPJBbuw_-ir7MzoA$Da^^TEX1k^; zwz8jl**VRLttyecY7s9tb*wEZpTx`*$HwB)aU>C+{>FVx-9%35qV?%DNkHh&wLlgQ zsXCD0yN+)fX)7Ac)eonJ50_XpI8kO)@4ipp0C3FEh|c5fb^uM2n#wL&Q9m;#`tEMT z!g+@(hSrs(^F=G>juvlNqK}eK*31?^Q`&#D6*)_Sy~e{u*bM!7n*A_$Msrzbm&8mS zxs(Z#Omwb$1jm%Q^Uy_0gu?nI2SO*l*Tbht<(i`(dr&VT9{l_@Rm(JhqX^KNytQG? zqM^bi8|tEh*Jz0^rWDVU8YTQG%2ye{Z<7!zypR@&8?fn%qbBwy4`GuPCU?UifdLFE zaLqXX`}R3C%`Zdb_=4VV`Z)|^D&NUf>vN@JO)Ke1TYY!L_M&(8CEGM_L)8|G8Dg>K zmjsI`;&RZZE%R{bi?+V!A5C>mdinEGt^BrTSD9k}0sF6vJ#{^XHR(k1M<$?3R!zF`jy5w*`)Oxjh90 zH?PFf;!6Sy=znaKcfAAP;}5Zm#I1>|P_6)0anlv&sOa6%lo9*@;39*sa6&^uPoxO| zAA9A+>dFe(UPII-1S9!6S#@EV3ngV~zT*!lHFqY^&!il&VU}Fx+_o~)d}ZZWGZ=QBHYl_G*nmo^+R_FJbg{uhi)Mjho|^Y+zDzuDthf;macR_*;O}P6%g` z7W-_33!B^me(t~~2h(L@tb|7C&FjcB-WS4UW4Q1wRee3Q6w&ck;PkpGHRqGuP6MWO z{+Z_PtY$mvm0JhwSk3tjsa#3RY5|D@eqQ0awR-b$CqwODSgv38d(x}DHH}F(vp{2S zW;{=ZC?Ogvx)bdg1RS5t#-#++UP~Hs#1!C=u0PIz+p;(sPaUB|KwZCq-OlAtyv+up zi(g6*esF8V*wj1(N0g|1iir1EO2;%q{)4W|R={R=@ay#FrXx1i^L&x#HYxM0kit%g$!chiGT~M zNFAXG!AH|pyC&jp0&|iH$Er5g3}1Ipw1|m*VBOYhuLW3L{k7KGgaQLmY+WLXcn_HU zN8z?Phn1GT4@`HRx0`g~|5+#eZyGor zS|;0CGFt&Nx<=)-ZlKKUDC0;MN%4A(POAS0^h*Hq;|0cXFfZm2VdOAcTMjK;!M-I6 z3?2edvE%%>T5s6Q2sMEqwK1~%ydsNLsNCzZ}`MV!O!1Mx{sHy-H|!z zC{KN|H7=En+`%&=cTf9LMs&Pzd-gb9_eQ|HN| zV8ZU-0{k~Sh356g!eoSJnoFB05x)h(dme09>^k_IvBu`3O9{pJErp!~SVsCZQpQ2( zu7@C2>qg|WutjLg`@zx2u9fYI^j|qGnn2bz!r;OShjy~r%kp_Uf{=)_AGC#3@sUew z44P@5imt?@#ZLWJlV&zA-<%B(2s!E@GK`wuUyc1V4siGe~qn%AkN{zKg zGD^LcTM;9ZR+&E?u6XFLza6wD>7s}ueDSz)@twbP5}nckX_^k_I`|`Dyeli#X`_d2 z|6GXTZ>{7?C|#Ei_Y`wS`jgUYGJ+bM^CPq3>^7{A7Z-7%^8rhv$2ml49!py<5JS2C z0ay7G4D!cp7>nr>kIRsQNV3t*j-2mPolt~bUnkJ04=uh!aPv5uz9(iX=Q6<5dOE2d zS4Hk1kXOE|T_TZ|hzSV9%}ilb3F^MN z)J`t-`0Z%&ZA?dNdiDdq!;D#X^xt=-ZZ)=ilS^-ahewM z%o>ueKrE>BT1DPRk8*ht#Qj-+uXO20a)m1U5R+XpvN~Mj-_3LH=yTsQZNIc5>Eu_x z3?I!Nz&-`yfqnm83HzmgM7EPSiVVNxWray7kSF3zvgaw$6KcBN^}1*I8)Mc4a_yCV zLd1ajcsCNjB?n9FUsilSutisOG)ZI0&p9+5A@j1Eaw%LgUHE>eki}{p;yUVYHkbzg z&T1(5l>YRmF=wjx!T!LJa8#v1!-SWXVyT0X8q;1%$J2Ef2HPkWk8|(jEYobix64UF ziV+$|shzZ2io5<#TUHKvvEakxlY^5U3FU09;h&{HvI|8Iw{<3j$_9E0!6Sby;*47E zJ}g#8lit>mJ0-PPMp2Crqr39>>4WkZX*nzxk2BKkwXOVCRs#IP`?9v_zwWXRGO%y} z7I~&~=N-^(HiR|q{GH@vo`Rf0Kq#h|Np%WKovik)K3{5}N>m=6ICggzf-+l(R+iGt zy05HP>wEo+TjVSHfx%RoWq6%=td<|bg0}QR`BJtT^f$xQFg{gMS1zPBSE?9b6zZ>< z@n9T1W*oIsKIcsL5A%6IZEK$mOV5sx9b-pt%vK}tYBv+35qT=t>3I?ni5rrSlv@yW zWQi@Bi=4B@nj}lAjU)IJn#Z1UZW3_KD6-iyJK2&^L57A&0g9qGc(2YR6PER!!c|@2 zS+TwJ?RP)Jeu89FkvdLHN)*+1Lz>>4n;*lfMs$fMsej38Iq_##4G|56OvOCsDa?P- zD&BG+S@IG%S@UH7`ew-Ybf$GEfR(YGljfk|9U)2jW&;PYPj^{tb!Yz>y?*DHb81@| zf5D8k=J7enp1v%<+Yf8OVOEU3@JH+X991Qe#ItCu6wv}!&6S>{pS!u$)b8CX!KV@s zFZ%Xwag*Aq>YL1yS3Hp_nK~950m!RyEq;g39Ki1nyU4&5RnA;_WcXpbCH^Kp}?nc?LiG7dn-?jc=`{ac2~g2&h4QnAC{*BU&&*4aV?V2UW&NUSki zQ2IGo*3NKDG&hAt6aCG#pE_1$t1_AAU)P##Tm&J%+78*q9m``mY9GBiWT7rJpNse1 z($}{nB(e+QER4gFoi^t$GYZ~Pa5rkWoW#pV5L(1o0j8_@eRBGmPAlxvr|k2p_R1lo z^}VqABay9bX-%1GBP;8w6W#A{3uQe4h@^Jg<5j3+VmrTS2+A)2V9rG1;g0HQ7p%RA zi-eQqv0b|sH^DxDGIo{LB9X|F5+Heaay$k`;G)u8vScs?zkVv33}aqr+iu7S8xG&7Dp^&oNvc?fdio)>y&yZRAh;^}>hVWwAwnX)j5a^jh?c za5Zo?ZHV~#VscqGa6bX?dMlBPL_$S;Q#b20KCTHZzw({@PTYQg(l5xW%+@9SotMi! z`d2StEXTkv%c!N#&B+r?Z{|DjmTsdM#9V!c)(zKQd8_K;9Wh(xHB&BN;d?04uqoZ{ zQ3CLMANxkH+2PM&)E6;X^-w;KWcyiv8GI{Uo;8h~<8SHsZ%v-IWC$H8j!yxQqfRbS zP62yUuUCmhTy>t`zAN7v?>Lg~tQD3$z)|Q-w*W!^I?!EMhzyt~dK!Z``0-U|%Exl% zV_)Gf)W;Cooe|Y`#Dc+R4tvqbCcUr`B?6 zE-6n78AV!j>xshn6P}B?>S&6klH<1MAy~HZt=EGOeP5G;AJ(Z!rXGNh|$X9J7QUhB>!cK20l086kj}5CtFq+ z<@x2~ka)-v!!gRE%M7Sc1bH!jo|C0rs@7g~Idp0L$wvB^&d)}O=oadwZaWCQC_D-e zo6$O$^5s!vBCEzUE&AXEzf)_bnMda>C4G(g53=E|^%Vl8MwRuaU~zeUTJFZmpXuT{ z1RS#Nss1pHJ%vM+% z)^nxGHzJfS7M-Klq|ee4o14^# zw{-mJ2!2>Yzk{M|c-OC(gyQqC)7)v{MPZEcwUSmpz8ZS|K;|!@b>kAG0|w_7+)&i| zz7$Z=YKuL9XYJ+9XVGZQ^k9~>Nc1^cCZ~iDJ@NajL4CmI=fZl~t!j6F(@P}UEMGHK zl{ly)wG(wr_@+axzPAi24-@yF&@BH9-md1)8M^2O4Vf+2-wS}_I47NRFa4q_d2>@n z5tRd-p^_?J_bs}e7;;^?pjl%bf;jmEDZGGJAt#Y_=ZmDGnj-!b<~kiG7|YHr1{*?V zcTNxg=e1HtwxNpM`EMHVd4^cKV^QkDjnL%GIF{&=7!Wtj%3x;F%V>dUX|1)v+{AK4 zo~di2Z1-=nKTGSVTszntlwpjbz0 z%}RP!q?5@p;5UX^ZZtbq{BO}l(iSRL&P|J`p_)L2^ts0m4Li$$=~X#DKD^8-oVos@ z8+?pmfBj+tn4xEkoRI<*khPyEeFE6Kaqu?&vpW(5j)rE^NS^?JSA%0Oz!FMmRCQK^ zi95+c9PMVuB9*NH!)f(B&(yBm)2N8#XHG!9zlW1RJA7%^p8Fu$7?trfoK*4fHViIm zA;a&!AiYTK_EHdXjSI*G9HaAOPi=VW*(oL@o#^4ab%y`exP^fmV(;~FW}dg#c0^Cz z|9ij~6>G`eTMG56r}L2ST^+NO`X-?{TjWDu?vs`(x$nPHPMUN`WTFB6?+&k3)V2G7W0+-qu*ccd~3c5ZW( z$Pqb>qVc7&<`Tf#{;3(#FOR-?=Gy$WbF<7wuZ_dKA?hY7vdPkCkuGNGi;To^gy}#3 z-QNAt@b;ompEi8&ohrs*9ox;O15s+{!_aj`5)X$j!)TlXPOc};ezQ_N^S_)~TG1B3 zaoh&g)BlzHPOtMiE^SV(wBpSkU=b8oFtpm{A1ZMR4)*`d?|xZjA*HY-dkLztUtQb? zn2$Y?za|i)*92ugE#mW=kw>E3;IJUM(Nlk) zTkd#&=mZ287vdrzJu56-Dq#>OHIQqnfpL4L8=R(_j5OEX=pVK{hT6d=T?YMFw1P98vy(!0ZlS%xRCg6A>(Xf_Y>k%$WQrB5iQFf=G<}7@M@64v2 z5w?SvEeWKC{%5N3TWVs;NeW8SvkL9A=VgRnN=dy{OG1cq&HXM01i=UIJGfq;P!jv1)+}ImgBqu%B0WJ zQhrB4)HDBEm8H}*`|TxcdcS>Q8sgL(h_ZSYi+2UE-(KTex%K~7EkhPnex}NFn+}z# zC4EC5AG}byTT%9w0624*ggTMO;pr_eR%Z7qvfcJKaNlSskvf2{CaG$XPCRdHXB&$0avYth#_&`4z z4bs2s)V{s_iWkP7?Q`E)A9OdvwQ47OIpeky>(dZ>Qz^k}dP+R4e_s%6Et#LgANuAJ zxpRQ9x{`KpomqbSkelF>i#EG>KF=U@yK=O(M@durX_T1;kuxVZ(iPW!31T(RH{DE+ zRjC|hDkLJuTS$0uNj+h#aeY(a#QMrDN@4>O;)e0-)(wd|CSc`2B@7*tq}D}C!}emj z2aY>gt8vMl@Ez$V#viooy4%?)X*s}HjeH^BBcpD5`+S>O}X0TPJ zFw2)C2dSLOS=?o44EGw9w?u@AC z#->5}!MZnMw(C%`luN%#!ARAOuoD5uXOm_E*W~ep@a5{3ks7w_8o6(++PpGJPiAv$ zviVJH`eK zr48VMf3nV5^v4upzQNN!{r**;>tg%jK+S==C5&f}F#$>6lBICSS4Zr$1D*J{AghoG z>=?At@ftXjcVxIOexG|ec*XZ8aHN3KUmeD#d1-JfP%(M?RRyWtBP?#<>o5-i1)G=8 zGOAtMFvAo?3_ z`gNO<`RUWKp_K&*ll`wI|1}ZaP{Ay8N(@-%f?}}ntguMr(Z(& zmhB#!OGF1kkCnqOq3z$Ol%?_+w!0-yslQB!zF|h9u2ftP#_!3hl$0NB*>cA3J5K9p zaW%sGdB79<+h-yx{7L_*s~P3#FCL=?$s`^vlbR=;r5kkO{h8Z8CwKK?O{>j&*8U6@ zr~0@u*=C6%QN*mofiNVw<|6$05HYuFWk58O#~gJ2Rb{@?>JZCxp2u5cK~&WXuFdX; z+;8Jh>Sin}8s8-^I*5A8s#L*z#XP1a_DL~i9oc~03yBsw_2}xakvS2hwnW=vpCc68 zf#qC7>l$eh6MoX!N>}*8_2it;K#dw(?LXwy4WCkN6L>zSYhOcxt_h#Z_cd+QL}Ypa zNK9Kv65FGn^ZkhP2Gg+18~>6~C=>A`aqjR)n8mhq;rM&Bu;Gz9(|sZ=-dSmyKw3$G z@-u5n5@>B5{KV5~*8qB#dD`dd3a9?Q5KiYg{lmD8K0n0ni)HFfu5_W;RO&eu${+aR ze6bNSx)%r%<7d}ETq&-wf8#(Em2z?L?8<=k&n$C(TIPptrQK2j!Bfe!p;KHeTor$10MgxGPsO;fAI^Rfih)9!@jZs^ z2R!PR%p?KtFxAw10$qhR{&;cUlA;0^R#>4wkP!Csq60x9GoXz5tGGP8@J;l~6139- z%#O|6TVTI=BS8*p9cZnUa(jF^9=- z=qfOuYh?ir-tCTz_d8kRYcRYO)^OBWF^1=b zR0E!maWh7*{Y6_)R<5K`ZwcPYN_pi>;f$+td}4lN22NBT{EF4J2XY!RPAOhoW4238 zZTHOOc5n!QxfE_g>nd(lfjhm**wK6q2H`&oW=Bf>n`H;#U=_p}VCWHxqU?0#ZbIT~ ztyc;b)pUz?_)jHK-p*69%@x?DwLS4rC0WTFc&^S;CF_plx^l_$X%V{Q-$p#yO1@lE zi>GWTN4Oz%lxAPRd?dtLWFHKBJD#85GH|8Do@(m2s~YVWctroy4Mb>lb!w%S3#$9H z?K>c_w1p%WKs#6!l`m{zj{UDg8>LZySTJ8f^!J69y@xJ)Z$F=^Wu<$|v=6+XjTr9< zlOE>lhJ4ak^IYQlMg7zzNx3_UsJ%9jaQ3{Bk4(PH<%`FJ^j50(sHvSLhz8JC>Ngeh z@F4QY@6>>{%D2Hu8Z@B@0)OB%X7X(iK9D*SC;x^ zM8A-GJ=RTeWJV&Gj|x(~7Ai(bktrNp(j&x@X*Mk=!s?A5JpAO(;;PjY-3@sAR(AjO zYr7-)&wc!O-mJa*#V?(GFgD|>fv_+xGlXl^Vi&gMa}ayA)gdhPFB&Y@n26sk2I3Eu z$ZfLN>ABP9&Y0UqWLM@bN7+87H>n|%rP_ueH*shPtfS>JHAy4`KCXXvhPC!Y-=~M@ z>tN>+_7P!WGN~&T29@@@&iXMXz8u${NEbwSJ&A56u7xT! z&#vDcHZzzA7KVKd__n#!WdYfu2jr~pmwm0>LWPgq9o2D`pQ7-OZPtgmr+jaw?P^zg z>D+S+dl8Wq8>%#H0sh&c<_48&37Pj|>DD?@tp_uCa|oMnct^iMhinyQA@*4(;7T?0 zEnoP#t`P-DP+e*gY763(J&_fd#EU(}u)eEX3&9aADvX5QZ5()})U%#`I$2HjlN`A> z6cEJWM|oA+vDUvus=j=_<>PhA{+wcRSGkMl^R?=J#V~a9LO7x6&Tv7iA?e;+{J$YE zDK+FBj{iT~y~U2sW1GNElmhtEJ{P2uoL{wJd#YjT&QVsL6xQUdVqdgIKn@{SXO{5f ztaQ`9aeb~Nx}1jryv_NW^}^VaV6i!)pPgk*cgzbxKPgVL4ZlFzbK>+YU zIs7W}Pwvtf`Uj&#!FD%@uYo_Ychf>(W_&$zL{GFtG~?OvI|*R<;?KJb@n7sCK0s)<1@YO0&ucW0LaY+DtA15{p1LWf2*CKR$htk^W6 zHTwf;DwfZp)FQUnN|~4;^>Xhuh$nOY)USxxMm3gzo9L1ZwRE3*;1g?^;qq!O4X+(D ztojwWXTVv-;u&1WcNsqhgyAyT(5O|3``1G2%932(e)Haag09}hseO=<}zI&MDx2(`dFTYk&pNNyw?d-9xnI!6{UX9DV5#|-P$(| z(hr#xtjH*n0QK3};4RYn0d?=Y2wyZbPN%8PlBXeKKjGo+`k)@4P!n^%oGup0Dz)^J zsZS${620>Zm820NHTgWkXX7wik51V%93ltYz#kD2#+Sn}jt<@yXFS;I4QVs71rk9i zB8O)k`0RH|hYLfMH;wr_SV7GO{pejduYJbmoxT|zY{a-;EoZ%+d-v^|Mza0V=}RfU zU)kaGc$*qBK;cqat)1Nex~8*I;ltC^uvvHan^DHN`(w+8hrn?=*MVZRE-OB($qCQU z>4#ft;yZyg6b{;sq$e4A+!{xeii-S7I^0(+nm3;W$`k)or7|JAt=T7|MD)_q7;h$3 zu0GEbti6|BUcKY0f8T~-+W6I8Rw>B3^Q_mn%!Hw98Om~Mro`0JhRgy zYgt_6%0HSoY}T&lcZaM}fz*OZDmxTzgPwAKuA%Jm_kG1) zJFu|J)vxV$znqnY9~Yb{pZ~_O{J~bq>p9mT3;y>)eU4vbs39k%N~mnHXXQ`tAo0wP zyl`wp&Z%F`@R*kJAK^k6dkHnC00aoptc1(0Q7Z%oFMW4w-JQLn&$A@@+onSjbqyRSX1FMqQ&mB zDL)*0m+#>8@BBb3TYgdizu5eXMeS$a#lU3#{Z+;Dsq+IKE>GCWXn5AQb&yxbZ%oLl zgMTo~ngB!wp>WIT%t+2t{^484#dytCzZAh&JGEO;VJsn%YRvY;@^kvX&kc9c z#ypSVxpNd|&m0nwiS_gk4lNT_wC=wS7(YF2Z8B+vDc;|lyr~bo8LRp9kaNF&>hVqb zMkNq*kMTv-BEx8o79e>HfM-gVTNjH zDN{J|s%c%b#S~2QV|euqg?_OyaUbYp{lbe#c$cW*q%TXPNn^sv=YC}~zHZGvpndmx zd>di!*?wI+pJd)O=0mI5Y%Q!jZvDsZPtkQbi*E`%~!!J%T0a+k$fjqNltC1$Bn z67-mrRge&=?HM&0%m03xSh^b`HJOn`DfwW;Bz9j7USs1fE_Vt~md*5w`X@~`tk3e6 zXQtV`t9Y9F(}6wN#nSln37f&vw&f02LUb9C+rqd+TRX5#A}&Vgf&c$l0JHydw}Z(C z{+T6FJ{#Gu{jqrWz2rSt7`)cN7ZE2c!*rRDv1yauL64RiyOo z#&(@11?OVsp{+E*RJ8^1tL@Rm@M>9NKZ>-O|>bHij%>2K{rJfy(H#>QZ zfK7Pc(Ctp0kKM6MSRD_~6Wy$TGbW465zsZBE`;m7YQ)3Fx9b|{3VmJ!Qkd{!^^f{0 zC4yw9%6rM&F%ie8y2lJk3&}S}v#=#jm&d&{n3=yq0_96oG)a~lDRcro6x(^IzTW*8 zOcsJFaYV_Tzs!2R#Y_0f_lPXKzFsS8RcKEt|KZ`ZH@HJPz=f)qP;C1+mC`_{L5YCZ zQg?4NJ>u*QsvVRw4n-_HFMS66>kg?Y8O-7xnAT-eMvCm7;f2^T{+IeuCwZdtZ*_yE z3qI}51A=)ohc&-U|A%Y=#FCcLzB_x4i~~z8l(xT#dKfk8%z}F>?D}Sne@%({^?~lyVvtIU>vuxWEV^rS*nX zIqR>^JWP<&tP*0MQNF6`$haRHpli-=M;`HO9t&<1&t2QiOv- z=rDuUGSC{*Y}6PG`~O+R7t+z*=4pD-dUoKKhJs3f(Kr7(W_0N)Q}#z6RqC&{P9j$>Nbc^)`iQVfQ^ zHW@(7?DyFbG6Y?KMOMAN!W89}X+iYu*oRg6lk0TxEn7mIWHe0k=7^4=&4HSGQ{e1WDZ2xSX?}(caQ}$F_$%OTJ6l055>Gt%4 zBh=1=*6^O+F8#Z6(K(&ydd!0i4KzMuA?!Do+HJ9Ovf@`2g#;jDd1&4@J}^#N%iRx4{T|Yn`hkXypX)q5z85A z03sv>Gq}5#Y(+i{>u!CLN}hl}l{^0va1{gSWK9R-&)sJ}Ry3Evp6|H6?yf{vZ1~{M z*W{|UUMkcjGoHt{MLQR9jd|D|_*o5m;T;7!TaoVs5DsTK3sf$IAWm`Cyy*h}MM$vi zl`;+)3|HJcAeztg%@vWCTN*LhjZ^JZ$IffV5`K7g zs=IZ|6TsHGle%AZ^(Ow1j2FY-+os!U;DC3j8kDP4J^ZM1d>t;`dXMCf$j5XYBSBy!oHMF~5hdLkfl7!ly zRf>E0wbLWp!`xZNR&8+ErmrN>l4*ORabczrZ!C;YAlua*?iR2baF|(5^uI0hDzK752$N# zgwtoQa0W|`YSwDmY+(bz#$WA;-Js3+4$a*1;{F|nwzEg(cC3fA=K!7xYU!!O7Cud< z!^FO1?Y)T3%ofVzbq$gBg_)F=Fte5mGBC}r!pJnK%wk5ojdUI3@eQd{z`gXYSLm@` zA1}>@3ns%|QiZ5%&5Yt;}qsPx_D_eRCD&QNpD0hf5 zuWgt!yL^@@(HKlFKQKKbsyCIFW|_gIe6nWxSpPlEy@QuwMQGhQ$od;C?DhDT)f?Qy z4XF*5zQ`Sd{tYpp6fDgO;uq=YWF+Fa-VurlqX0bgID2tZH8`$y1xm%BQj0wMyGl{E zcNcF$F?`;%<8I3iQ5nSqF7HLeoUM>ekOPzWW+T3d`!mLVn4U0KmKA8op`9`gIJ4|7 zV4-nYs6JkiUkk^@xHtGC+(g>}x6C@hzY^QX@|~EmnxJY%oxfe_&FGR=SLrg*>ghSK zV!J{cT5E~&^kJs;vF2_T&9IO@4A0`M3qv0L0vM#`w5fIQ=JTJhn9RhA#&KU~t`&3I zJA(1(t?JL|%|B#ijou6pc-WD`4Q|F|6}U;6&mv$T;dt(h42XU7t;pX) zxoi=&bzG^1gVQ}6#Dsmth#dU7bfEthO@b%2^YU@YVTnP1^H!p7JVd>Vc~szJ8n!TP zx^(x54C6mCy-^5z-G+`Z8_3qiqV!}jY~rZA%^Qk>Q_h2eMrEtddz9cxuXQBLy>}Se zh+NzoJqfdM7X1J0D&-H!GN-KV$pd}fK(Abzl;eAqLq;M$gTFRnK{)p>dt~*}gmPlO z8Vu@kvXn5_H?dC1pATzwt;xM+*$R&$AtUX$$QgS$b1+Q6l6q1MV)WHc0F#Cb{lR&i z>mZ8tcsB|RRDV~5yg1s7bCGHBnnwNCo>_4F*PU*Y6+>BDEEVWb^NnkLst~Qc05+Um z`HsD*G{C<@eoxc;^mP}lwr4hFG!+@{n1p99q+O!r(lk5Lu4P>`I}qtp zpdl5yu|5&mMWGzss|d!Yu9?+Ws6qaPd>w=}9l?w(J#wxTw>yCfomZjxr>9vsA>yc>fpaLTnQ)~__N(NDj_ z9jie-?Y9|5umc_oTpHMHVF|~rw%zOnBDe%UZ8NqvtR>&(@=sPL*{07rN3MZU&N(;B z8j6y`X{*V#c*F99A{l!G{~rLZKvBN}^9)>l`QTUZz<^UX&#+Ek@Lc?b_vJ&}({lwB z=h|P?zE9)(_nw*9Lcsv`efmw?Alx%ExYr@|*T?dflk?Ga0yzUN_Jso%4qQ0!_u;^? z(K@B+Kh}h;37Br}wOi6=dpiB+`zhJ;ZQTvJ5j(NcR2R2>sGjLBwQb`k%D@XZJZ5}+ z*8S+DG5!0Ju*moaeQ6MfeFJ^R#6v(f#k$EYeLM6KEs9TU)W2c#;w@tV6{&Z-`>SVf zt91j3yL=>z$8m`X883N)4`( zT{rIyt8KaluL6DRI07{5%~&P3xEY8-Bl&YfpW`L}*|7Y|c*pg&!=5!f?A)-EaMt_a zPZS>=2Wc3)IZjF9AyUfpGw)UE@HVL40Pj0%>q8Np;TOOpYQiC&e9&*l>4wr6@(-Qa zkYw7F6nQv{(AUT_G$S=a=ZyxbyE`3o*b+a`5iJaW^21dVA z%mWXySroozGcq}F?3*z##JE9bZjvb#<&C_RC$R^Gt{@USGJ9aH)=fb#+&U*uZoN7Q zIO~d=m|WjyY@}WY|sG-nNwQD`PGi z@m22mrx%HRLZAStMJK*}NxR)PQIS7?TJNkP(nM&JIS2dy;=OIo@64+5FrtcQ@^xaH zxV6vS^W8Y7#&1+&RYp7nL?si}F;J>_RSBJq$7qD0ghY!^f0zx&w$2k`8^&+$&M8x7qG@~abGxa;lLROKC4oxZlfAnQ@a6vL>_%#S}(`bsm;{b zjF;PP*mmT>3b<_v;kKJ;-&kwT;ZL?}8rPo3JWo81qgAkr@+4zp_sw{7-Qfj=HniM& zS%n+c`BpI2v{h8g+B0(*8mK65p;vhI;@UBdFs(J=;+8hQ+1qB)tfa3c7o=NawT2tW zv~OvzWC+H#O?z6lrv~NGArJT!U%2B7W@!^`Ketgre^Z$n1@Nz5AFh7+Ma9Mkl+f8L z#f|9qW3F1hiI1_0cROEms)z^YGM`a_(zL5I0Uv=;QQVtXw09RAooiGPm%KP<5Ji^~E>_>Uz zW_6W7qX_CeR7G*)$a9qf?VH*^d8@GHes}lMyYUAYz3F!-WOMq!0Kf*^7+d9>l60F}q1 zyzQ=>Av1gD)9@zL!PD&RqW=_E`dGrHd&Axx_qeE#=p_;x#+|PmB9I$C+27(G7t3V! zp+p|?H_A%QPRh!0iF5MIyR{=}KtsO@TRkX1{2q{lB6<{1OowkftRK803*ED(l9%!h zol-BNVKN?aOaMKuPgwsP6DSkz!*QLU%9zaU`Id7--tv7%n*D>HmXUmRto`<&@;!2i z;*Ux}IS_#`*8?J}#%DhdETHULANTI{QRy8TLiwBrP6o|c}l*RWMA8Eu+Pd1Kx|`Fytix|Wr@yp zEWcshqQH60p%Tv5ch2!uM0ZaTkK-=o*s2$?AT8gOEtS<=4~T-SA_Ghna>{pkp2O#c zd{{=VS$U|Thf1`rcR!PP==VAwU3Eulyz=x1=g{whje*> zf;>|RUGvQP!!ruP&#cW^M$&HnjjpU$S=Y6uuBl8V^wd%7Cp=IAiekq!-D~_++S}9S zdqL5?dqgSEd+NrPo1v{U*S6ub1u8VlXVctq3D1sq$GcOBKJ}E$TK^uHt39wEZvTox zwY%q3euvPFWh^Zo(5QkVey@f>kLxmx3+*5aG+m!jN`B|RMDZ}@f2IZf2%kt4sX38$cwI*+)Mr<jA0Fy za(HIX;f{Ksg7!g0?4(V(*hcbES%opzH9Z_FV}$g(m8BQLXN@y;o&L=}&OF#YSSJG0IJPmJJK?WFI&zt| z9{3#E0-u##%eQNJ%F`8c&Or+pX|=UxuhJSI8V1_WqIZ=vVp4tOBq`|>^nO4Pkl}uq5Y>X$b5hfT`aa~ zmc62Fd)LERs)X*^Tn#b{Ze`HYjC{`Q;?-|lc(mdJG6M%}Qs5_#bT*FPA`-=QcPhaR zZHVjdH?+sT^Gp4uwmSIWam;*B3EjRO^FqJ(;#l2%+SWI}_sEekHo3Na*9Vx;E@R9> zp6Sp}0~~c*fMnBP`5J4&BqDT;deqRyNxZ_*n-JADuJD-yk!Qb?824bio^cfU$Xr7} z!b~1pw9*isr%dZ_J-7XmXGIN=G4dkamLq@UrEM>KAy55|QU8>5+OhH}^A*~<@+<*q zPt1ApXkw5@ZD&uBC>i*wE`wGZ^!j>pq?4uT_}gKpt{%&(d6 zzI4aL^jn#mIzLqwJD#synvEA;%RgUMPs%*L;%#W424J`+qj{QNbDWppH2y3HnXa7s zE)K_LlOC1gZyejE0E;~RH*n@HeMxxSa?Q8Ll#%76v9GLYnoUPiWWHi+8HC?SefS%O zN&T}6EAQ1y z0~ZclIPkL^VAgPXE*!XU;KG65;=mlB{PsY^f!P>C25APPdj80>j`&^pLC-{+S<{im zW#A_ca5`Aapf2yz1-wyKK?eI2ZDZ9ZXzBzXL)Z)|Ga(Q@5gdHFh~omWi*}Ek?)#M! zB(WbkLGtloD5ATFr}8W~S!{oOeZsJC1yKg`Zf>D_GGaujC8PoGW30gn@g7c?<}n-(no~?`EbrZuq{*j3 zoMKqNEpUnW9^ZQ(`67miW!e*R{r+dTkAzKECYzB(sUVFd86C%Ro8HcKEobH6)`bmZ z{uui;B%0=pdL#WHi^0EkbCOA^b-;yyVO(^z&fmH4J>2^BBOq9T7r8f~`{Bz| z4^>q9mAc|%UA3;8P!?caG&RxrggB_8KIFrkixR^~LzK}Mdq@-UGl329>(w;H6{cHDnh#0Rw)f2i*A+(0Dg2$*jN@=i(5jt>KO{7a&ks6@-qxJy4)F!ZukX_j zJF+z1BeyHinP_ZbLgcW8D-F8h4w~U-yfovonLwVh_ijcfJ`$v+sa`>NNMr?EbLtZgipdJ=zy7h{{(QYbE314 zD*0~7r~e!qS|*H?+h-NXJ&@zN;w0XI*Np zV;TZph6h>WaxM3U<=+jrrgc2x+A!hd!_A_uzqk=82PQhkfJW1(vXn}OY_?-vEH~?+ zXy}H~-h9jEcyC9Vo6p@)1W7J6cpa!xCHxk+kn z2r{l~nr=9EgWWR+GeuFKbx*$uWW0@?&Fv_#tMKnFj&6F6QVm>LWCNPb@BAB#8nR0q zTx1IP+H1EgTl@((`}r00gD2i2OsntwifFt`mx!!A!3X~v9M6Oufk&ixeE%nSKLS|- zq~jxii{JkS4k$x4)+y_r@54rRb*J$_>Xd2gL=K|mJA<3SRnAfo-Fj%_VSQ1t!9&v0 zSJB=$_w>7n^)82j@a`k5?YaF&>w3IHPhG$)a=7@!VJ)yO_(wkz(hq%SZf>P*w|)1Tyf+Q~>6=O$c1+5bNnw%@-Yj3>M;{YPTIMNtI;bcb zsboQ4@dd3t~uxMD%vK;!Ef#j zVlHA^7R6Bd^W4l#dt8NBCRm=hYWs^(v2uqp$Oa_?`*!a!R zv?jA)9QF@cht@NoogYi9ZQ=06uB+|1d?0`1IXBRRpf4RHb-P7(fPqrU%Zl-a6g=`jg8hd*x_B2&Num_>2e93tzS62@JenFA* z@rHR6-xg5Lbl$zpx0!E=2=Y+kasF#wN}u!Idz3VH+|2HcyA(HbA(G7cKzZ+2E((8c zeu{Do<2k#cRo`YG*TV=%Ym|e9=vd1v!x-Qx3(}qkwRsqweC%A^F_(L1Fyz&FmHnXm zPd1x7!pLj#-*Agav|@t-8O#lj&?L><7i5BbxV|H=lxOA8eD<4~ z2QrvH$8@t@88WN_I)!XG$_w{c8rM3Ko4l>J(7k7^;6;3_Ehsl)ah*YYqLk;3WnUT0 z`Mu2>yj?$Y?C1@shup3$4YUs^31__q7{|NHLE3c7+cw;No{m|gsO{L5ch;V^Q4U5z zQeTk^`5@n|nXvX1J{-7ddVdf9xiNnCfFdw$!Z*gZ8`{Vl_PSX}uiQebOgMf`yA8ci z5V^9y*aP!IneC`=I}TllBKq4sP1pU^HFe>Zc8b#L5SBw4<~a45vbS917gWGQ%1x6$ z!L1%a5<%K+DZOgrC8rxU$GbEODlt;@( zx$&E;>zv?%*Zw(1M~fbywoQN%f2`3Vug~P&{jm8KD_+WXt%QEht=q4*wa9NSezJ~I zQQbqhp>E#Mmfyjo?JY{^9CET{5q8VL3e+^XMmfNbooy9#rVmh7AzI#1{FULfpS%F% z!B(#Ec|e2p@Rt3qGRHMB@>)P^~HuPO6a6@&BCqoHI-6Zj?_!a zNU8Bg1$FB95QxyOFPXi z@;B^>zJ9}IVtMW4()s{@57eVMHqp-IRQM@No3vNRCo<~+4-d{It=qP}5(k_&HCw;z z>vEt5I6Z7&Zy}2Z+gc_(ua!~D+^}IH5WPVf_ZL4D%;`AgF>)_JPD zPw(GE`aOVS*x(3h9sQ1!={Nn??jwZZhS>h(nItD#U#6Gg6I+2B^VC;puxTMj)CB6P z?U8-qID{kz!d6zZCm6WQ2Sy*Zqfy+^AMLn|s{2tGt5WIg8*C%(b3?Uq%AZL&mgzxC zgEEDgXTp`nK|RFTMkQatWjhCibsagU$`G!xEi;bolIw|%y^(anT`$Z{=qjP3vCa`Y z3^Mqo3EVqorS&mS)){=@f~|;s5XMFjTPEo=h1pl7uHzXHX%sZZpFV}hJ^k(XZMVg3 znzYxWl2QZ!2?wuq2u^YnlciR$R#AihKYaTmZv0R!LG1$-A)-uF_R$3RviPaaeeWk6ZAIn?| zYBK+!KT)0>BP)60Tq}<|emtcAq%2IIzx?BN?^0gi1<58X<5R|Q{U&6;s|;$}2f%Il zF1_JLenw2_3~GOp3jRL$`x~A)mTyXyg|Ve~Brb6M)?osz-c4H?q{Xpq4_Si8Ah8?| z9Aa!)nrirKo;8CeZQ&ww%1_?Q|CEDpm{MZe(Ca_+XPzr&^Flrrlfhr9oN1QE*5Bq? zT*(ahrQ5p3d;Tql5iEOnr2pheYY{6ISgC5zv1%v754`}7?+#diSK8ot4cPwW-u^?M@c2LNDC z@T}rEKyo08SH_6`@CcrPs0%nOu5{tf6C*k0_siF-U;gzAA6vd~;_nx3MpqI2zKbIa zR^M1)c|H8f;FsANm@`ObfvhAsD0V>Yg810H#==$<7)+D_YZl;K5ORSJyMtuHc$SZ0 z(4Fypy)(ciD)27EurTAoEt3^ZJ{*t_7g_tUVnotPe#eAvP8#*rut@yMiS<=NcY(M= zy44&caySBo}*y5+*?BMYMs`H;dw6tw4KZR(nf1upuq@J*et zo@`M-moF|f?ffuJ{gJy-LItnuF|B+cA-#T>wI0`G!CJSlke94Lw94l$esqyF zB|x~#7NZS;uDIh{zdbnoz?-G#fls~)mvRVRaYHZRGRMFC3m>O7S^B4c)8;L3v52=k z1sks`|CP^@uO-sQ4r2?Oj|v}y{4IS$^Pntw9q#ws_*;^HA?m_`3kNP7IOD+gTCW+l zZtc#Z|G`)L&36x8(|2@VfJ|Bjv;b#b$5`hk$EV-&`QZm4qeN)82M zKKAvO&dF3nzh?uPn=1T>FBa!hdp70dqaT~Ao_8LW#m(jL#Elv%9r>YPet?-{!;Ja( z5T6eWjLr5Gj;E{NvZ-X=CgGUVjR$USc^bE&KQq>Q@dF+omxu;#>2MU~7>ThvWwYd) zOiSY|FWJ*VI`vC9krLC$Vvt7zQMd&y9ukz$Vb>k6h(^7EPDD}YHryYHAwFZkfd#+1 zgz(AR@MRo9F>7nJjFEpLW^!pXsLfbS`H9#Cyz~}u!}pjXuN@7#)%z- z$1oF^bs>q(@QiW?+(fV>aB*BXaN)p(1OF@z%%<&(>b7pSgJ%It`+M%+_hTP5BTjn- zzw~W{CH*zb%6(3oDQk!mipOIGAdF;nPB3}EnCSE)5GY|A_q+#D> zx8vr7JQGF~8F1)Zf&!nkOp|_sh~l3sD30QE*EGIkiW}Cro_E)b3+@@Suu-4KjUZcY z3wLah4J?d5vdP1dAj!G$!8N4ZI>Q*ojo&qHh-b_bIQvt**<^ST*6q(jr4B* zXK9`EfzZ#5qHLVbRK(cv& zn5M9{j98?da$jhme1mk9jpgr}go@FaY({nyKJuYCZlo>#BCT5tNMQlNDJIKVxy;5* zgw%dM{kdZp75Thrk-o?@?N;tLD7Ii!LcjCILKL5#u%EC!yuMd+w_@0@f#z3*zn8z#j&a5cly`vsp+@)#xE^V!h<%~o~vN$ULS8tRXNnQ%^Q?e zD(^TxN-WF~Z_$RnMTt&jVQ*#iJAxNTtJwO?!5!aJKxf^WfPk9;%=LK1g+cR1OVu&7HaN4)gL@WkBa=J$MH==NXx65Rdtc3b;4yZQ6w2 z?mdAwD6zkikJ|?hNr7$^&{fp*#_`TQ<&0%ce5O5B5^?P1;cHbswVa%X9@u|nnmNQJ zY_&X``#9f1;oi9n3bHCc+P|rIOYar`+$2m&mIQ@XJLP0KIPOE)l0#h{9+?|sRFrK$ z*%4U|GgvEnf=C69Epv0%A*P8kI(&HMdqx!YnJX(J?oD%^CSMPG=JLqv0fpFCl+oW% z1UIdXyrVx)pN@y+8hKN>5gf{6FJAEAU&;ua%D(f#D7-iqL>8TMT765uwlZLSvR)u7 zC_piUNLje|?(qTH;_{aV?FYsV?z>~2D_?xdUwKwJFVD;O$O&{vmt&AB8LA+nyvuEH zq^zW&V*`1u+}^`K#|CqKX3nn(isJE>ag6e0nMV=yY0Qn0X%#+S!T$z-Rig1A32Dgs z0rYe&0ba9y;Jo^F)2~IZ9*l=fsQmguSwuk@eyNZ>538Po8uJq+bd*r{&+A)O{q_Y8 z=h}?(+*_6Pr29wY0p(EYGm;EF915fo`t7gqo^pQuH)Z$@B~kV=Dz}y|<>(E|%B^b& zFB~*@M?4QfkWXEk0iAh`Jj}HM<;OKLd8+(&EzCK}mbUI5I$YbbuGuaes2@9(vD_O* z7c<`{FuQ*W9piM>*L1q5La-%0}8STULbw<*_#u)erZW2lie*q6~~4|MTPP z)x-Usy`jiHT7S3JcX{a=5oMb88a_#HJd+M-?_tqc7{pR*Yy^E|{ z2k~Fx(EfYaM)swW2kURsP$|s%rhMb%c|#!OFXBP!{H0AGJPO{7#Zz9SW&cK)?f)~f z_T+b3+ndO&l@z{F`Y9J^-XpJ3d{+@2dhV6INPFGv2s?XT}jQhwgdM17$`m{E@57Yt2{N4uJ(9Q6lF_#Bg92 z1zC|&<|pg5q>*nB@Z7PWe~pVZp5ksArUC`|6LLPf4I!}-QjcH@T?=5b6=-^{{nl7# zc3D&Cm(3&Is3U4elYKZqV0fZG7pEY`YyB+KvvIL=N%<$c()qyn&hVeD3X%-dSFT9v`7vMjboDYYNE& zkyS?D(VljmLf;lT>@O%Q(#k$?`aMbR{OARnejHj~?CUv{Bm0>$-yj_dJlW$XV>xa7 z0z-e8vA%p7X8BID@I$JHybV98&ws3XhW#>V@J(97-ISd;gbS1}3%?7+dmR4Ae^cO( zxRchD389wjtz#m`EaW%-(qRGjA3GGrFL@>`e2y(Y46l5GGB`pyb;xnPywsj^l56{B z#s@tF1{z5xV`KQ(v5@@Z@K^fX(jb1*w2Vn0borz2cyJ`k^PLypKYon0#QYfK17Bl& zhBGpM7Uv__pH0;qp6}l}OXhF>`Vki-`DZ!tquE@i_gAIy87=Fyg*ksekN2OZ?>zm> z``?`dj>Ip|g##B3{16BJQw{2GHS9k`>~8@5NE$x^^FEnO%73E+&cI{UfvtvG?+{L| zXK+U7@;+RNJ3O6;%*R3EoX9usd4A5{XUy6Tqn(a8Xbt4R?V^PPTXE~}6wk#VPr$rm z;CaWQ%ftOGi{M*M`MpB{{r>7-Y?9MFx+w1Cjmf72SC!EBoY3^jX|2*b2g*{=K`@I} zoSx_{;!m7BDlHEFT_8>VGU#?;$%VuYmXm8~9s^?+qg(*ZI#3Y@w@x!4o-PcrsODtG zxcUXF2^uVm|@8rfW|6W^{Sd@{&JC1}zgo0F$K=W_+O#m;Nwe%Lgeu{>?;r z%cOh9DSm;75We4-rNz7FGGBF1mp4;?q|)ea=%lbILIO$pG z)I;LB2<{1p&n)`vfZK?NVs*o+xbq|WCP6q0+qOlT`O%s>ZCjI%1o+z?;yowfvZ$agRWji{IrLb1~K7#C1M=@X_2cD%2#Nn|u6Nr_xR4KjSp~lS8u#!^g|;UGp)LIQcpwOVto>KX{gBhR3!)#2aRIt;;KG3m z2mTuz_^hRy>lNoTecrl#9x}U~Bd(9y$LUwwKH@{#KJ+|dUOJ!=+pejbgbU-U#j#)1 z4M6%Y#|Z;@th#5{me`QC^rHkz>AN^aFlwXYNQ~Gn9F0{P-W>zDF6y6jbWD(qm27f! zgW|>w!MV+Vo27{B+UVxabuM8`Z`Nda&1O``ID}>6onxFa&dFx}@$RPnZiJ8|s4@SO z^})Je=FO+MJlZ33E5;n7A6<#bYkl}7+F&z&rVGu`>!!+sn<$~{z~*>2uDek*iV@jt z?Ivf|tK7f=tY+ppHi+ElVZI&P%{))V>MyLddmH*4o9*2snwxUS^W2=04IplqLh;-i za5M)tqpFDRrXI)O8x9l7EfWi)8b8V+W4+LgN(XPFU=3`qVf^Su>B<^8Ci9N#nlHzd zZsHjx8{50lCo++8scf1@zn=^#VLrgRT`E+0n#IU47mlYWEgv3nX!#o+@j|{>lBfLT z)z}cqcZWNbKD<3%B@hl`@|%PQ7Pwi7tb}&ICs+pE+ymY0DBH8yi9JtjK!qQSiL;5H z!EO`;oQ&{zBikG0lg;$ZYvidn$6b57mvz880ch7=;m;UHyD4{W&ec!exvp9nGVjg5 zc}yIg>03n-Xqa;VDV65-&di7(XkIm2(ZTc83-`7SMtJ1ST&jg|+vQHuJxP zi0SyYc>kbdq2mnxk6}GSc6t5NIWWuTv+_%V3fmCqWxjPz33Yxxm4D=p3Q>hO+fLii zvF<|oH);w{t0Y5_(5CIau_gz^MrG=fZ%CE#WY<7GNd<# z6DMN|ZTwifsNtIs`Y!Q9*4ozgdluqk!;8WTsnPbg&st^0@X#PNmMHQz`XZGSs+@=H zfx|wuo3rVs@{O#D8TNfHcDa_aq5s%GOb%%TZw?AkaRwzCb8P>f9Fj%*4HYW1`}s3p z19|7W6$%qI_RaK9DmG|bw1HRNx@)}jRdOA zc~UatZSB@;zH1Nnf<}ja(?ff@abBJa2QD1=fCFuJW?iwpYptojzw0M8On`X%a~fVe zv+fmFeyRKIGix8K<`GBsJd8Qu9Lu_2A!BF9y0l;J>G9yF#jsDtjq10qCEHHY=x$W* zLlJYk83uO`Eo1$fCd_%v%QItc_D49Fw|y`k30`69ky;BP002?xeQUoR2A*$pSpLBq>-st0%Op?>p=y)t0aqpBcCa- z?bm=KU(UCZNiu4gLos~w+lLDBJ*e%Gxv_kfr*hY)2juFZl8WiO=Y+Kg4l(F0=+0@A zlL0SJNQ8ZB&ezUn8RgSjrPdUu8w@Z(K7nX-(hT$j6h?cn08909QQ)bPRO0M~ylWGH92#bo;9+sz^ zZd4k_PrN(Vd+t$=-*T{y3Y)~;GbT?u;8#h>a?M=YwS&khH0K)?`+`tTbDVZ#J3>5i z8s*Rz#zZQwy&%Xh5Wj~m#lv>TGFHa3-r<@DG|cu!MR(gH^JNvxdJXmVo_&n&S-iV@ zfZv;xm-WoYJWqO*1t>{9qPD>|*GLSPM*^2d4#9~CQNL>5Ri3Sb%23Gckcav~f& zsf*S_()Up5TttF=-8jy(+$qZ&l>2fa$&gp`(S4fo4nClWbn7~UvW+6D1!vj^mC!9e z+PgjS>uu;SC^*|5M{yc{y}Bke@Gj$3%GI)+My37G3~!oW^VdfiF}>ru6>CNhoX7Eq zqX-54!w%r9BX)pMZP^h zYtznhxFli@J@Dg-16KIUUP0PzBa#9N;M9p14$VN0(szQ}x>X**Tk>_AIUr*J;}9N& zdHd@aWhhjnd~})jSLb!|(m54}<2a5X$Iyb*bEt%Cw3(yX?zvZ(^~V?2AW2)f^EYe6 z;E*SqI}GEBEqkvw9z+UnbvxgLJn9PV1WNF?@Yc1>7`3ew!+3jGsIWjBS>% z(2h|C9GBgobgQi1kcX6qY(sXWkK!S${74^a-GK&6#Bw3IWJFlg)XuLjyyY+c!`;vc zn7U#)1d+%VL!$0q*1UU6G!*3bU9hQ2KQ#1D*waVq&ix9}5q0w+O!HCLVhh}YfRF$a zHkombc_bt*oy94Z;x4}F7N+jPPyY!&?X`asu-r>aFoH)6mFEZrthCI!H@@YUI02{a zh5qzo)b+VG;QM@(9V8)j%Wofd$mJbvbiP&O0He0EB%5~Q&32hS%>y^==Xa{qiJkxe zKmbWZK~(IQx83$o66usa$GaJC;I`gcry@tzMU8;?gE3^JB%9sw4yyScS{tYNpp~Xc zr8`cWcAyU3($^+!({-Ij`Wat}H+x&0d)SvcZvgQNV~fp}+qqK_nou}TTIrV|b{>!s znN#j!+IBkjbqthtmox}WxO~y%Feb)0uKn1j4C;^es7J}HKa#K*Dd9yLbcHbbQ*7&0 z`VG=^%x<|KBgz7S@X=qHkjJs>=lH-p$43eh9T)fi77ox&U7iaE zE*$ti2W)t!IfcxCX6?0%>Q8%Br$^s^#^7SYF*-SE?h}3ZoCKV_j3kENS&~V?0aViH z0LXmJ0a)^A)MZo$WN;KwupWPmxujRzzUn8=h8q7QY(^Q7#>fL)gBC~M@$5iZTS#fq zfytmfo(AMGGy_84cwsDGht_x|O@T9l6k-RHlef@5OHAr@7iK2y#7KG)MqKAvtTVG% zdxeteFr1S63yX{~EQ0@naZ$Vr;eK55L%9DgR38Z@#n-22P_?9KvZY+Pp@z{h6BATxh zmVN_z+JTGz`iDFYTGqZtH)%D|dIib!dlztwcJ3k*AE$o~*!N&wqyHFeb;3t*DaJWv68EgbCeeDAocQ{VK0{o{yLPh6jPMy5{=Y7`BO`az zfgAjtpSy`F3ok>iBg@J&#)SoCFFXioBRwpo+$ZqX_xO+l>WPcgiIa99V5LtSM$;a6 zU9`9UWVfLIR?CHmEEEuz_O4`X~XtJKW(7usJ@ai7JvN6i&E>x8sX|kQt(Arqt z$cuJOPWd#;-raF1Ae>zvQY3!Xtd18Yy&Ite459C-J9 z5Pykx0{&s_6Lg}=BKnE%kGyweLkk)x8k=={Vk}!e>lAl zC)8*BFdDzv+D*Q+bWY32F=J}i;yoO8&hMULFZxkpCt#*H{1bPi9GFx1(`fUpFs7m_ z1_DP+YotaKlB2FTkIS8wvG4>49a~>sX=;>J$9>5)oXB_izWl?rDUWKr>(1p?Qsa z`VBkdINc=Uyv+36i16TMjK^yXb2E(YyS(H7h43#N6!xgXlH)K2lpxr%K@B}H=wM#P z$BY*?JKVCFBb&9r?dE2cj}Ckc&kZJwZJcCge8+gm59p3l^Rb5yLSecTNgH3ds&vzh zS8mSnBcC6Y7^Njm$8mtd26-`fllMw!cx73Jhm&81O~wUydDmZzOL)Kv3yK13t>foi znx*9YF~9gFNOxhHMq%rNo3N%daqxZ&oSv~y;f`-d_z`hQ8xnBOk;7?9!Y&D&q_eyv z^Mz?#-N$t6mR@sqj?WSE8g`~N?Io3eh~uA1_dkTa7cws#_}g&6c5AenI!Dz{FR?Uf zc)n{APFvVyXMvx1@L2l=NK_x;1GF7b+EUm55_N*6-95&YoYVeF5)gdiD&47-)8fb8Svq6|QQgVg?2Z|y6Mr4y|3QX#9C1|x3Ec*HKUD+7j72BcvHYN8!sKZirUegc(UQp z+j`u17{dq(-c&K2P2X-nly^pOt&FuPH_^BO)X}!<-*dA~HjOckbDhfZmvR}!1Io#@ zJP!mg=ib=V4QAMR#~)aguCuW^YgJO|x*+fw?-AdPscxon9ZY`UpPR-2b?h^^g`eqKM3R-?HQ>)HTeEi(!z8fNnZ<=`;9J_sAdklz&qQ1p{?9dXvvV)`}kzsXv8Jda1n7-)Gd; zSsCI81^&uQ1Ft0P%wT_-B3ePnp#1d^S$~OvD8312yvxuUVgjkyanvC;kS^ zutJRAqo>h*q`_|o{Jo)n$q>)}LB%nZXtpX) z`7N2-A4xbHV_BE+8yTO;k$V+h%%e0wt2fTZz-Pu58s87z0;m!lX+`YFgY*7W06g?o;=vsJYwjar7kE*pmFBb4x?-s>VLp}}L_Hk2BkhJtsh zc@CCRA;NJi{?kkaR+Wp}QBmiejL^<_XgurM&<&L|L(W|=+8Ic| zm6>faYyQ$#NEeD#o$u&J7(Z!~WK2?|O{T$&$)rpg8FQfMycvpMDthOjF#K-mH*Qe8 zyj4MrK0x~KotKb@+?L7)g*jg}1!<9eIS_?;?hSczE+fwg;t_tP&s+^2%>ZRUn!o*1 z&O_$DrykII*=%3QVt|RvhbP?^3^dF z@Ai@QRnDE#zDu4=1%=}wZ_eGM4C_A7w{Fr`lAc`!ax)b<%(oHcbLPkNd+rTWA;}6YZV1%s(>6(=acNLg1iHMSiHkAfKR6e%(iQ zi=wVP%y^ExWWSjGn&}|;-a4&vWp71fy~2F>9nZtY=&R&Y=Htxs@3@Isc1n+9(<(5V zKe&ZruzSXCMd0|N``chx$3n!Fx1q5zU>#5fo+;aBl>DEgh>il+ zFVv^6e(!-_ehYH^!dOJv_J-m;_2sbRo$&yg%8*Z;dgE{ha?1>uUvn*Oq?6q_jw`6j z)&~Vfs;wW;gNzYO1DyH3*?imcBMiAUG`ID7BPxdkc!=K1GlD_5tgt+8kvk~q z!8`cbW~ekA1$}P(R^ixsOf^kA0@v7s1Fw+fD4|2!4rDKusW8fna^<>2aDh+WqAd2J zOu=v0CLCu)NhW2;+7k8d&O;+GDsFmkpY*vtpiwatvfvT^-hS>23~@anik_4!|KVWP z4j@KZ!vnCE_gtUJK@g^&Fyc|aRX)vLLF7Mm!5huFNxn)&;vu~pa_5IT*3#IaL=N$@ z)WG52Xs0Nm(4h>!*=_?S&-9mnIhX+1-t*lrO6c&=G89khI`%D$_ZDYw!QQxsOT&Jy z8?KMOhAw%d3`>LgP()lFNpnkB#@w`HTiU`qlx^?f*#k!?H=qB?rB>g(2nzW? z5#3wRiA=htWw{fbANjbY@4Dtj@>e>oH(>U-U)Zl~yT7c_(FS-p*qxb*%y<1zS|`18 z4wgREIixa1I;PRQS;n?0)Ct=9?K6BuE_cwzCF!I|9}0dbq2DDw{8WB&D?RPf%~$-` zUmb({<||>e_f*TI12Fu>APXDXFV1$Q-q~&u7g=>qnXwM(x8EcGwp;MQ{xU^HesUlX z^gA`ZMYdA!sB;_4Ee9GzreK4(Y)|OkD);n*%I#|!SJ>p(0(#&sq#^fsWzTEmok;b; z2Gl?3ui+hdIZX_jG3zcia_qiPCgmxw8*hcu&w7$|7~@%A zco&2h+&cQC?hg0S7Y`EbJMn_sc2Mv7F255Ofx#sZf|{I}mXE|IwwS`HVMm|?pYYI( z%+Zc3fAkiRYnyI)z;pWt_-R(;du`kcKM+Kpq-R<{^CPz;Q*x9A_mo5ylnZ4SxO_** zm_(Y|7WP6o>PP4yPr#=>Vf(G-9=!mD?=g3ev^DHWaSg`)Y988hU`=Bl?&8=-^GdtU zz>{`}x`Lb;G#vAuqO5wp-K_ zFP*YEaZ%`o@!N*J!FFkbZo`IyejIZtcf$HT##_zz^fkBiE!Vu;C8nO+uqdlE2Bbz3 z$bC$b3*84~!2bB^jXoUr4gE=Er_7F@bEN})#NK||OZC9p&A0Qm$z~ z%>#Lt1scj*c`HnYq zAis_yOjq`6D>BoTWc-DbYC()BHE;5_uf;$85mIS)^skH7GxDy?XD>KyxAQ;g4#nn8 zT5Y_DlRk>^T)%VbrL2s;EBuAe^sW6qM?Fd(W;pcO{)TZuZthK@PrYyhmwe%&4X-DN zVd1|%mH{-kh7%Hk3z^>_5ruq_^FFv_tWSZ6C;f=x7+Xwb^v)^CC-99k;tjk-GJc7l zI0F=* zKo0-e1}ADj#1c%gr>hhOfFC1cj+q;NBYcUpI0%^TX&dF}uIuFphcjVJH(ZQ3)?$e4~0 z>X{LX-VVkbGyy;~2YjZZJ-g=)`k}=*>A*xbQBEk+bl_-L3tfQBzEWIOjsRv1EIRlT z%Z#s697fb%95~{I8-$@rx)^MiKH+#oYr2f!j%9)$@y*)&_>jTpm=u(Z3=0Z_hl9k% zF$WUiz?}3PcuR-$g*@PR%>o0q{!X6C8*q|w2lr7zXL9*9%IB}xhpR7~Z29GWclCu2 z9x6q!UUhZP$8#60oWQtv#CfKOm$bNXP=8Z%VkT~K$bi58EjP)k0y>M0OhBKRpg!kg zJVV@3UY4;F5$7*aKzBlvatCiF5l#X-X?6iPlM3Ezn?=isg!-R6OH=uvU5>?rA2`Ak z$RRcn@&GAt;)6-sg&ETBVw@(S51c=k--`3y1b-~>qa<3NCqX}kCs34g zHi1wN5fDyzyT1BAB=DJrO#$T{nhSy;8Lc*IEm1C!)0m^&A07=rn+skgba z5HEHT^8=^1EA3eHdB(i3*nL0=-2*FJ6y8CnlUFJg5vb=F7f81((B9Ek@i_!VblM~c zz6T$B(b(W)Taa&Hq>Gy{TAQv{(>Anq5oe8gNp`WVz*+bu(@*+S7BA38QIv-8gW7DF z`K2@qW4y2UV*LoGOon~E|upS3%F z1BGrs0w@D>v3JJ#zC964`v@Jj=hd)D1N_1{-wORc?h(uHaeN^T3I3li;@vxUmv95b^7Kqs*AYFY%?pe%P3}NpJp1S0J%7t_H4cdHvnY z64*qWIHiv#`5f-N^8-j?nxuYt#*5LDUK~l+tOuSBUUZ3b{F$+fCNV4%MhJsXVrq`T z;&>zG=rsO=bAqm4xYT>zM8Xm6TC(pyeMhVaBFJFddaNzdcBsFskKD;Mn!e>0_pHGq< zay9+dbS{32W5zY+aIf&m|2(0k&O*(2^*jEq{YP8_*WMDJr=I1*3Lt5o!i)(2Kx>t+ ze<1Z2k}n*%aNtKdV4FpIZhysN&0pI7jJ0^DvCmjxHgAE_NB{LB!0^H99C$(ymNpvO z&cpu^WMAGirD60%05mz%81PG^;b&^2@9J0%xI}7P$6fYqNoMv5Qa#colt+3-&zb1H z3ve`{uaK;FG>MtFY%~e%AD6R+K7=dAb<_e878P$Mc6ZA zhy$BaU)Y4Yah;e={3=;{(@T`i-B{*&5lOgy%&@7Ogn>~J-P`0;#Lg|?+2BV~ltuVD ze$9gUl&;v|O18{J3P--^IUd~@^XL)llSa{+3R6 z$GE968;V%SQu(}wJhR4i%T0ARYzp^Aa`p&@Z*G(_=WYb!B0!WHFpS@B*bL|mrA@?y z_o-eRKihzMsn=#VC#qy{>99X{uW0Q`KCy72tXwa11C z4^s23C9jZ)?E|-+bL06ICG;(d=Nmpm+;Rhp3M08y$qlH)QC3vOm)CCMgZlE-6iv@@ zE92f*Hge{sA~v2yArCOu^D}SA>P`7>b~IJ+ATEr^_lDTM7hsl0#6`mKmQTsHWl5e8 zcZ!%e$*Am0YJvG*%EK7IL`~ccZ3$TRp|$X6ZK+ zz*OwrP^UK@tcC*8jeed!Yn7rdSods@ve}=F22Z4DkitU*q`?-EoYt#3{c_8%&`= zY_l&9zisQlXRP@%Q;dC*zCieH~R$lY@O}fa%`CfP}>F9)KKc2@vNEDW8>O4;B%~W;&bkRFAaoy zl5%4wN~|jFdSh-5(#ktn93#6KUL&k)$5KOu_6E>{`wWO4IW9eO#&%H_Z*j}d_lpG(D&t53!yiS!=d~uB(LTn>_q`#Z} zoln_U*_Sj|ww<|woU#=B#&{SN(VTjr8%=Xfd~wojXKn*rWnthTjeJ+94ZcCqRea7u z&HXEi%7jWpbX(g0C_$w!iaoMQS)wd%-9t!UredSF$42qp^c1bgUu84%H{PS5gY0vhcO?JN8<LLn>3f5m+;N-r7NhcH4z_bn zg=|%6R%XaoM3Vg*DmvTmhjemlj_l{&SgUwb`5d0H#5H%FQ@N^u+d z+AX)|=5R6lH48+%Fh6_gh?7|E8Npu`$lcCo%aha{(mLeEUX)^x@iz@|Q$p5{tcReK z41FpB?@*q7W)0+t;(6xMm)$QY^Zx7R>dP-|OZm$F8*WDT&;!fGdi%n9%K_zFmC&zI zwp0P#G02@lf)=4SiCcG_A9~}l;|qDE(pwd#2u+=V=6oZf%p4an&Y);wACO(={3L0y zuop+APnAu#SC1&6@0gRnay7P!Zr)7nwi9e=rcdM5YqSp_=}=rLF& z7nQiGgpRT)ZO{%`*HBcU*?J?pbJ&}0mCC&2-?_YNHr8(Qu0UrEfVN6DRN0d8pSOv- z_b9Z8%XHK|Cq8kmP-?wFejGz>p#Pq>YYT#TKt#viBtT)yGUp@8lg4$FDw+Eol?;(7 z?H3i_N#hy%JiLKrG4d=wSSzz^DE9;A)x!?dcy{-b^;HyJ}xwJLLI(caH)(=9zY#_Wwx(4Lj(g_H!7cHG^^^zr9VnWj5vO z71_=B)&n(=yMv18Pu!w=!&qLvqRFe0>i*mH)eZ&pr*E_!D51YR!NX^mk$q*zvF%j; zs7v%58oy7vUMIhEfC_ZaZ<^L^Wl|+cmFAxz==qgHANbpI2)DO>zD-4R=#YOZw_nr# zS%;OARGr8tG9hlmkgM)RB_Oi``9~S>Yx}=9bLE+AsCbIe_&$9SwB0NGQ9s zR{-lTx{DzjRHd z+C}#ly5@V!*kHqV`&+)*t1xbz-w@{9^A3K|=#n-#$(wCp=B+B8|BE#Lg$&0KM%`i~ zX43}VbzH{)8``})-oGdml*5jEdT2xB2|wvm!QaFCZV1~9qtdy`!rt6&dzf|wxLe$t z%$04W%^>lSOK*9W+#c3JTr zdw4`l*7eOJkAq&*Od7~L0%ZFfnqrl5`;LqQB^6q;R)RciG4|0L>s{h?Elt!>dUY&s zy{8?_ngr=&-N60+i~;T#d)#wa!o8Q#u@}Vyaa?=3MuuoTG9~aJ3lE{v*tTjM_t-{w zxWJuh`;CXb=7sh>2VD%9v^DV3+@?ChEBUOC#pt?IzS9hUeP9}4@?FMQ{>H&jPy8mM zw=J)A!fyujqO=jX(Qnu=p31i-$B*z`36q%HWqWJtMI@^77VV-c<_gK zE!CtBp*awSGPI39Ft!xh=EA44S^CWU!B261DX17npA>9XlF zZBZ+7HN$0LFZ@Y1a5q$)O z1k$!Z6GHaNJO>E`CpI1O{6?2~pmH(VA!18e@ago;%z$Os`k7DOF+dhZ+NVFI9}n(GL=hZR^>S*w>VgA%D( zXcE~JzvN>;1rIP*79ZZlIiOee@R@MJ@)R9$jl$Dh^)+>nx;{I$(5cy6T$FFUXE!7#@93uI!4$Mz5bx=NItL^LP9`OC@oE4 zASC7jIUQyy>0&2?jon~=+Zz(k4u(M$zrkj-6(!T1u;E|7^)CUYHGxPkgEYPIoExl6 zvdIca2rn)un10GeIx!9a9OOGmbdew@ak|*lU~C8D3;>yB=5#XPyZH(L(&&k}UFZT$ z9#a%=CKkFePSPYO3q#Q0q}7G(2R^=h;bY4qn*twL0J&pvV#7ysCoB%Dof*Ed65+-z zSCX=L%0m6CC)W`yZf8nzC<}co^7(N;ldj=*;V(CeGx78^$exbLgv<{cDK}D*4^BuR z)1;)YlTXQpui`XpWs-L$7)}6Ph&WC*IwX>|n(}$N9gO$~MvU?x^*%$E`;y-bYM1wc zaUzDFI(??yx8tw*`uXQ4;`{{YdpiD5XK7blpm#ymtu3s7^0A7K zIVz&_!D{aUBQUNvzw*Jvb|`J33p!MC+dbCTS*T*M3qwZEx&LC_U5;y7?EC*{~^8N`jJ zR>~AQ3)A2D3j$4sM24rQaq^sc%rqLK_(tsDUSpoHlOA%GHgQUq=!HpBIw&i#hKy$* zIpR77oKj{g8nMriW32d$SJOL&NE4y)Af|xpz>(ba&Z1Tco`n3M{Q=U%T>F3K{=p=E z8jM_#mvwpkG>ZRrgi$bBm*;Q90YrJWHa|wyQo}w3U)PLTL1&B)BfRrG%K0g-cYq&w z*rNOsCG3>ud}cLc8>AmRR!-s^oTq=J}E94ioQ3uNDF|j!p>zu1| zFg~XF!QBt}%+p-p4~=02*vWgwIL*eO*7RJ=uUS5t4L%v)4`V%bnwvNn1bOSgJgwZ@ zH~8933Vy6$(+GKW9wddHUfubR^P^!HzcKWw$);=PPs|b7)M1iQ3WIKW<9khM53V_< z!k*!?oGo)dQ>4h6@sY=sMOn)KW0|hPe$ctdjC}m`k*~)`T3d)mKV3&MrD+!QDNNoz zNh`FRCiOw&bFWV#EOP!4rRO-l_x=&F!|#)%i|xCC-%IckcHzK<0~Zeb$2ee1)t+Rw zb?Mbd`t2by%mJ!xf4ZgFRRwOwJZWw56#ji51D^I=i95$JfjuG`>6g0V#SM^u%R4Un z%HvpSh8Zx@*0v88cQcZ}B`+(#b{m6;02E(+OkbS*I4-dNUHZ8o$1^0xK6dK$E4t=A zS&%?ie;p$P5AR7rn{h8=i*6vsc0(@fLv9jQ@qEK3^DP_9wrt8{;fOUO)`Z+=8+^d< zo=w8Qx=!S{C!2y?^Ja}^CKnQj6`q>5{EewwjON(Uh)^ewuY-3|Q8@kT4sH_arY&vQ zHpWcy4>F7rdfZQX>-u)q&Y+de^=os>#&tJes-WhrcexSW1A##5rV{@=kX0py+!Ew2 zKlsH6jq#eBwt9QFw{y)j&9m!Q-Yl|XqwGr#Y;|Loe)adpsTVgOvzbl4C`Y6a9LX~> z@aB$H*0m0-dqrWx?8^kl#k%Cjcy%4V8*B$($g_M9YsR>4Y7Vb?Pi&(O1aT>I1SqJ+ zYvj0n7uHn!eZ=+p#ulLXhpZt7j;-&#{hW=nTQOY9)=cAwJ`}}=k{ImSU_h;jhdE)$ti68k1s~8c{gq+OrT{YB|Q#Ukpsb+W=b(l9-ZZI|s9ZPs`{DTfkA)DREB;|cBN zg?HPr)ngQCnyps)q5|4Njyww$$dz3rjb|wgys_44tlcAdQ7sSQp?BI#z|lc?%SkUH zpXcugc??Uk;xtNL29)t}8kF`#Zwa!UwlC=Vmy%^VC2g*67_B*7V-$mYYumpd4_Yy& zuSpuX@#scYY{vx{l?*l>T!Zp~2gBSk#!z99%Q^X$?YP44Q>umQU3 zA-HMx`3#MzV%iN0DG5WAqKso-FQLefibyJL$T%VVP2bB}&DgBpLsX~*j1l7J zS`K0MRX*~5@=FQxW~uBMa6bWY>>qpZ3h~Sz9la(4IR|qoLlJ4(51YfhCamt}+TZ>G z4Gd7Jhwvefd1}nzF)YVv8aa1)E*!XU;G6?~7mxOsBjr>f>x(tUHpx0S>JW9bwMd}x zr0uD@&9(KBaQ-v4r`=QUB?^IzZKtA0_v$#_1rAfCz1HC<5JJm~$}uWU&<4APJ(V7J zyQkcQt^$kc_&wj72WdHJe;BjVFmJd$+=Haxhe3~jonWzer(&MA3n#=TG*yIL3@vDGN z`KVYbgjoh(X1t{1jn~g8D;*fe$-fQscqoAT%6xMBxaEU!UBL3$(tkUL)~JN8F`evZ zq3^xDMS(-UL6l>{9I(JaEicT?Ur-Y4t@ZM|a&6v_p2V*r7P9R;850F`=*+vkA)k;( z9;|c3_nVJyWc^ z*>g^farP)kd%)kGx^Sn$rF?|O%>4;_^(PPdA)1FF+;d~= z*0ziCG(U%yS9Um8_MmL!-#HCsJ`(~BEH?(rY4W% zA%_Af|M2h@ZBf(0mpIRi=jm$48=NR#{(lG2H|(x19RA2sNdF!KxLZH-p_Xw}P-l2$&d4^Wa zZp+Q$?D=v{${LT%Syvzksx|UdL!Rv;P?CoG$SgF_W+?MJ6sWBmI}UL2(53yaDBb?b zVV6%Ri*r-@i}lHIFmwkG?EqB8RQcR6a4AR58KVr396(1E;*~`XdSG2m^NixYbIKPF ztML0O4eOhY7Ba08rnQTN;1l_gG=4N*4rF`8XuluYuB1>&RObk+?Q+c4A4$u*$Q}S|0eeL(+ z=E-_UhRqCgS{HOfkMe`F%F+bVhN@t%o%U2%;|W8)W*;t3Z3B1z9!lunTnjZ#@<#cn z)avO7H!#5i!067yMP>VN@b>d9hhM0GN`0nHQ$bZF^n}~)!M_~JMcI4Mjcc-&9o#J* zvSC?CJ4q&AsVn5in{fTBA}R5p!NWy8tic0Ex6aXQ6I>UD77uJCR?6D-;`^_Z%`fzE zzaYIR#@>7-`?O2f_EFXy+Pk#n@WAoN9rzBcE8CZEk&741o;3Fmc7TtsuA5-I9e$g& zb?ETmdgjF&=nB8cx@>P>Px~L1BRui~*+wL2bCG${VJdCcgg?~K0B5`)FXWlRqwOE% zWtdO;LJN^+_Qf5e36tEDFT%oR_#snCTTS=WUFx!^+eY9Yff64Ew|{Y)K&Ta`PWvi- zX1MlzjBEP5YX+2{fyz%ye2$5dPGF%S541{4=n;j91%MYQ5XUr4WO=Rk|6}h>6y&y% zZCj}(N$&Um|9NlQQjOAEYi|U}OjXJ9Y4>frPqGRL5D3HsATSUhi{Ba`ujZlD2;&Un zTjtaD^@BI?<|*xtwRS1X4&0i&cCX8ApI5jK<(5O}`5*%0VEa#BVnF4LJ=GRIVly7UU`O$_4QapVM?Lx#ax$cEy`%4W zzF`y++sn)OIDE^@fvfeyzH{n}jV+}Mok*NMME=nSaNBqpSscft>}cQL=nrW+%vGSv z7(OrOg@>H2@TG;k=pk!G;P*u+V!vUm>Q~UOI0Iq(m1X<7_q3(=j1%(Ai@J%CvXr2O zmiS5gw+vB-2ca!BZE|>17IYmbZ5q6Fe-3ttJo#fxZS@B+GPDPWLw!X_i_0Mng8(d~x_`Gi)Yad1Z!1?Km z?fC~ZwJ9OTuSJzHF216qYRdbfX+*~F^i@{5jPF)|eNCU@Snb*RYn_FS=rOtshtWgy zqYh+W+Txd|J|u_l+yLB@f_>e7Ig!J<#bx8(O)`OmVN@c#SWk@&V^W^?bn2k8vKl{)+R37n)$} zOkYq5KUBAt7ru$yD5171J_YKS#`1QIsQw+ti3F1M1D zf0!wL*@9NvS9O@YQKB;79CrvebO@Z#nfNG#M0`mL9~KsmXRJ=HLIv;;WiUw26XssO z;e!N{+2L4AHpjB%3V#8MW1P_VnK~5qMH_3Ro|D!?k?B zHOVus3)l|mm;f<|%iGGFG_sQ7q_JNEr#pmJ-fHH=fq}V$O7o;64O%CC;w~S?$3Y_t zkP0G?QHHW~AmZe}H|BkS<_F(6>jn^TgNO+!lgbAUqGQ2*Sn&9`CIk#VeT%1W7u0t} zC0{&;rqxCgF5df);r`40yU(A$y!(QGfByhJ@wo8E<~bIyJb>=Po(s5M^6W&~iBT5y zJ?tt@xrgO$4qFpBCd`@8ncsz_O!AOTKWfy@=e4aSvdR112x~`cncqOgxA9@ z-&m~6VwuF2EaOK`7sz=yo<(-#zQa~Y%LmtnE3oBC%H)!kp$3oa91sy4fDmdOJOapet`*R$lq;Tm$+~`x zZI+dB@-Og!8CNAu?7=VJhlnjWsD$t@|0RB)zJZ?w7T{T|x5LUNYhc18p`n_{Kl$fv zp8jM)7j*y2G?3bB^~y~8v<{!%ohxSQpSiPGUQgRr#+UrwngHxVj(~rqD z@&U=1m5|Bh+214Z#P5DG0wR>u=WjQ(#E;Pp@camwOyQ|D4|?AztMFTN>PGXGVtG7r zIdYh@VPWXaSuEe@T#I9M#>63;)SP?q;3xN){}iTp{OyC;vW&&CVts}Fgck2x>9 z$^4A531bvD%sXewTuv0gRVNF-?B()a9`NC59!?RFROva#^PykA!os`?TF%kr+4CGsoho94d%2Pim<1p3JwTAzRL! z;6GzT;rB6O+AW->EW_*yPh6xp`&oJf^ZcIy{BOTSFS)i#@+T?eEqp`oPa< z0~Zba+cluwa!jFJs|`Dju=dzL#>ml1?&BC@=>zTOq_mGW9ABN}C{K2rizlfEV*Lod z^_PiL2CraFC_lnw>;p~xbv5rwGcNQ=m*((NgeD5uoQ`??6HHoW(I-MVyP?Av$0J48 zbyu=`A1v}=$hA=iA^yXWQH&crxD^Z_$U2kh@TAKI7dFyey9p0@vOzbv$SRwRqg?k% zKE^y*=f=-k&KgU}bH+=0(hQ^Ccx%M*3R8Q;d9o=J7rebs)DlzlUP+D&O3hs&py-;B zn^LnehIJ>`;Wr2G%E9`U<3kTrI!062uF1Q0&NgumJOCrkY$Ru`jE|$jn(KsY#(HA? zoway%6WucYa$}gAw=<4o&Ac12*?71%kLK$|taG{9RvFC;GbtyrxLM!Lqt_W9yHdv= z^&?u0PYB^t{rIu`+-RDGJvRih)u>->Tbq2<3WL(A*OCiii5s?p#2LncNTp zS!)AaLS|1z-Gpq$6x?lqtRVnc~gzm<+hX=;LJs9)I zc=?HE4mrT@I5>_mEn{IfDJy>4eRX0RaE$CmwiOzZ|7xMo_xct9_76DY3!Zb<=BtKTZ z4X?N5{(A|tm(ox1O&MzA{!md0OrFu$^pV;MaT#CNx|Xm%$!B0)+#8ktlK`!jAL=@f z53ks)okL%K`I6nO9{BA+BibiKx-pi_v`b$i;WJ6w_!Dhg8)&KSV1{lU znjVtzS@+|+hcB+leC8QA>oT~9JO{T9TYszPe0`Qp|9QT>HspG6QQ7UIrq54X*!raQ zQ5|PM)3uw(;kZN@(M4eG({!$pQ6{+f_GOllWpVCQn#AYg)|kc_e{@M8W^`7_J-_!l zg(>Xi_o9J|27aJ{w3BHp+RAkqNVCk(+{Cy2?QM=AZQ3ij38sx>TQGUCC)ysz(m8NT zWDeYF9~c{JZ}Orh?6Zd)Glz+N@dY2+NbO4xuRT4{7c-A>KGS&tb{Jlq1KN-0>!{dF z`*jbl-qFv0@Srz`qM(xXZEcXY&AE{WQ?-X4FvwSp)LzM7z>w!uSIddq?uc>;_+&NfyfggQsdS0C7 zd~eNtR4?SB;i&$o?xJ>1nfG=S;%R?oNcnm@t*m?y6GyZt9g z49e1K4ehL^6;JW(9hVXB%jYin9F;}JYNGx*O@EYha$6w3`UQ`fhdC-9wJ8s zk%r?0Q^7Atc$P=YWz{e1+ss?nzKlHIfUbw$I@Ti(iUqw`_^-~>53l;<%MJQ9^d78! z@xurbB#(8~7qvazK2dg*Bjr8&I2h2e)^7~B4y)M4+>??;fpK&*S_1MFzIUqxu$-o&D@~QruhdKuE^dBXr z?mc{Medb6gFzlt_5akQUShjxv06+jqL_t(9tg)S0N1X@f01`U&Yw{TbBeQbc_@+M& zDCR4Ll(7e5Z6A~*Bt5`rt+1Y$RvtX;CNJvVbq2>JK0k5~qOf1@kb`|c z`bS<8r!2R;9Qm2eL4H-#dv%h-YqWD;A|P*}0a0I0kc0HrSH^3gWB-u02%X^^pLH*v z{6S6tu52ippz9DGT5Xz*YV@ey9Q)+}n|5oh6Udu322C;MY?)ey zwrAUJ>MJ@ytiXNb0J<(O=z>S}8g`H&V^|MFD?sX1`D_@bR7mt^xMFN$YSN%snG_1p z94PeVUcBV1wj4FPx6|=26<>#T!mILRy$f3@Z+Gk+{osLg+{b&y)qbFew(c`i%QEBV z*jU?0+ll>v`oCRY=Dwyq+Wu_6u4#3jEOm}Go~Il_r$ul;`stDW20pDX5f6p*52c|8 zZJYMLA30*58gD%i?uI&{V$;`1gK>M-)i!(NAFRWBeMKHoG zbI0BJ`mYDkQzrJA7*N`t&uDW9-7MkTlfLk>bZFhRj^3hQ_s8AQ#%CUE8%&+B+^rNA ze#*-7vd!bkqy3YIMQLvzKDqYzF?#k;?+v!{_L_AxPLh7)%?o#&j`je0M8Njg$ojDy z`BUH2O%H2tbL(h1I@U(Yz!ht1*UZ8FvT^!H4{Tz)d@IT~+KopPeQ9T+$PzxqfDE%{ znlHL5S7ak?WP?I%qjenqp5f)`foI?M>_Oy^>-}dBx}taL{jCS9p{GpF&y>mXlAa&P z%6NnNb(cEE%WybVUHc+EfZ)mcwvQV~w+{GmE!$OeZCmM|y6RjE{lJ6I*zdF6=<{~! zrEM9$?TbA0{*kXnUs0yF+Hlr)Q(sVu>dztc^%93q|KJ4yzfd-_)@gxKG*;&5%P|PL zw(Wd<_KV@v8Fb9bE(bN?1zkV$0*kNMZ5{4HzR?qK5!cv(kVR)sAUumgRg~aJbbUb z#xbN#-LoClcEb&_M;>1$<#^xzjPmeB7ddZXJ5$b#f0s|5yetEIkn)GBy3avyTQqeT z6%eQ1>{Ds;xV}UHzJy8rCPcjbp)cL(A4~$FoHWQ43^MSyJnh@Za~$cLl}nqZoz$%YNSY9oFl51x zIVETKF^}cibvSNnY9&o}%PLHzZaBU$!8rBgdhQGL@hRgbY6L#0NGDBQGgj75eF4q1 ztIiL~pM3eJoR&k!eDV*e=tzbPmk+(X#6UvP6}~~8*Z93V{WT4okrzP!yoz}vafnGW zKjzu#H+J2VJG$Pmm^w`8FDl`8!V}x{a6o!eDYt+wEb{8v1P`EFcZX`IptEl{Kb9$+ zx~cA~$JB+TH*Hz=zF}iL(7w;xPH~DiR|)N+oW)P_M2Gi?ZT;n<00RU4^eymcyD3i= zt}y6MZ^DCg^WoFf)mDPoG!=}R9dPeYFirygqVgJ=1Ju739Qr?lW%2Vfpcj}wRRc%O zUk;)Fsj9so`>)kNdP@g}O32UZ5MxjCW5S9bV>`-t)99WicxQK&-_V(K{Rxwrw24|b z<4qdL+qL4-S2}uhZ~?kT^J@Tk#9JzMaf=?_*7cfiL%Q=}zTw+syH^KanGg^b{eDSg z;W;8(RNTDeAm6J{LtS7o>FXFoxd0_>=(Ci-!hnNy7kIjm&BJvcj<5igi4lYFZqjo? z=fEBx<~va+S|>I$iO*ozL1bV$V90_)=mVdDue|D5ln_7emVu-TN=(OCOd;vsiRm{M zS)Sm-!KSojo5_oB8Dw?h<(m%!pyk3(6`C?~GV20g7mgls5dHISJ&f)mcNhO$NM%A6 z-k}FSqL2cynG7jc7RgP8-Av$!bI|D`r2g(^V5%;VEaj!ndsP#AxaGmGlSfy}pt_a1 z#qFJVET{a)oN&8uXfbVwi#&lPi(4?sGZn!4XZ-}IHlaXk-nrpOl_KegOFZ7EJ_aduf0^;1CoyfB1w@kr zzv#+Zg0$Ccs^o@XagmmZK73{I)kz&0rrvjo0m8-cBWd>t@GI{BxH!PW<{RF0>s!R% zyP$>L@NHvY!4|Ms%mTq9i@1+0#=4OcgN04wEv#VFzWGITzfhD14Dg^H5i5_D{nGgz zcEmR*-|^;F7vk?&SiWNeP~NJXc*8C_V;h0(n|-xN+6>$Ml1shAuM6ofkMPOj*bNUI zZ06DvT1%(9ra;t>OCdg7Fiv?arz}hmRuoNu`iyen71e_WXri`j!ek)Jex*S6+w)Mf z^>^~sw`7Xzh{6&ZxCR!zjqYM(_z6L7ya!RDy$up@DWI3$giQz5dN#C@%w6+wHwi%) z46S*d75^n!9!+Q9Kt8j%^2JVh8D>8F->?2(>dnX?l+tZ zNfrZFc+v*H{>16O6)zpr&fzD1{68C4@bNQz9XYVP6Ca?Rv$x^}7IbeLE6-c;!2SrA zGH=Qo&Af$c;P-_qT>>tJJYidD2nI{jJR!%cXF*B(Ly6;GkPM z0(4N|1Z(HCze`ce8^zfVd|lArAYB};NhrIhw@LZRKj?WRom?IBv$fq``mJ;RAD z9{7ZX+t3lG^y@GBax6CvK=a6h1022H!Lv6adgD5zokA6Z6h#p|hsNU1Snf0SBYoD{ zGena*>&^KJ9AiQkjLPfcLH>zz4CtD@ht1{jhQnYkb9F4q7?ClXn-Z1AFA3g1xEa;} zjX-k0uRGnp`@$x}jQJR2Mt0cgrX@FHx-rKwEajO^>a4f1@hh9i5)Z~~dS$!_FFqSk z#*8PG*Nuj*ZmlxU7oG38{k9`VPSvBaTsABN@eTJr zD8X)R%;ph8t4<1^O>>N8vmrIQ^qB(s@bDkH^s7ET2XMojUut?`v->0ZR+AZHf@|Bn z#9H4Y8&=&E>KOQ*u+JR)vRl4Z<)HvKs6}RFjo{(VjjcADjDZP6Uzs=9R^-2bvo6@0 zq~K?lIslJlA&0#ta$>mWUyOm6dYe20v$EnXsKTnFm9*KPzzlqdqyGB-P6qQ?4vi|7 zv~6O;G*K2?cyIQ=28YqP+kf7Q7vhk8&#>Z+_*q=PN8{&lsqJ2+edM5NZ^f6!_wYOI zJ#DD`dvU2>q>;#}P!*8hougG2M64=Nn8|knyK)5<3c0nt0;IEY^glQ@e5U0p5pU(s zHsT9$XH#XK7gB$M$gETo1M1JS&&wicE`MVqandACQ(Rpn) zeRD8Jn~)8Okgc7SzV<@-;UblpC>#8y5mGjOWqNM#4K5kISmRm~c=2dxvuQo{#yHEMd4=4<_Ccg=&spies3723{fvDp z{iX+a{3_%}4i0)aCx_5EfbOAm4`Ac~x?-6f!KR0pXI?#kU>`(Zf{@<(Wjr0#D}D8B zxR+C<-PV2RyW1D>?1}Xr3)K8>ytZ%hYpXXtpQo&S?&q3|{f>3j1L%4F{n?kY^E{k> z$(IItpg}x2Jiq8`i{rdkwH^$y?)&_Wy6)kl?U}cFTV?N85fe@v`Bq=n1L)v{r_n>l z6=>YN#K$c>DVG$DKj0z0f!u}PLj*yXTvtL?sMY2Yo%(EOhg;wm@1lW=2Hw;_ZEt!| z+t4&0+GA|6?QC7sU1d#t2V8AeJQ@@28{=E;$(lRl`M5R(+v4~+V`yxR_C^@(_cPBp zp0RBn;C%F1I<{4Ob0EN%ZFn$%NbE0OOrv&?F+6h_ZKZRf_}WkoOSzwjp?&)U$h>g) z;hF<%SA0pZeUA1jD043^ZL5cfokMw`-L-dx^6RC3jX7)l$PzoroahOB9=P&rxR^}R zJD<7XGl%lv*I{GN=}$cPx9_)FYx&SR(WiFV!&6XoexS{_2x2c&0LxwyV?HM*dc9I& z@T!egE@9!#K5yvI-{ruWzS;5Z!2)kbVA-J_56n0h^w6W_6zx+kGLV zCsrLcKRJPQU!6$Rhw>4dIcCS6&O4=LJ4v0V4;PQH$wzthi{`9R>t(t0J^v29S5|&Lg1MLk$zeF;feanILAGJX@elO z9XbYa?BrbEXH$-W?xdgcMvjaF7^^4`I_-W#+c@=zcCP+puLrl8FF(-6ezX0SwW9xI=obb>xuc*H6H?ABVE!E6N_YkDSqq;~I2^TJ+7M4z07kObYU;ddeyX;&snZ z>ZKYd-g$#pN)I`D*o!n0!Irw9K0PqnmRUJ{M)vTYPdk%leX>3%qiyDqIfORB1J$>D z75Y7gbU*NQ)RYxiZjncaCbu>fz5&VC*@FZ8)Qf9$B^mkurkz2C%vmJNdc!x%n>OZ4 z1fFx~{61q?HzIw-Zh0`(5Bz-n=1UGZFp8qG7t?XBV_d#8pF`+tFQ_)b@^T#blsEDD zq5|)=IXVw=H%wqDPDLy4T*_KKTNY2Gbq^+C4xrBqMTE{AjrMR$`QGtT1%l{t14 z`&65QPq<%_2N5vp%zAGHeh>1tG~z=D*#s~i8G8_&b`bk4T@tC>v;y>|EfS^e%@=-9 zcVqWEXYmDEANXRgFVk{P)47c=ZLyDXKFo1r4-nEG+Luv&POo6oI4|)jXH6NHtp9LO^*0$J})X>KeR=1{KQ_l#}sDbCJ z!F+}s!Z703&NA?Bnll`C7Ys5br*`d*p>0ju)t2 z3KW?jgHEN@K2@DuU;6?DQPl>tuUu{0L;9>iKHNXO`|=xexnJ4WjQ^nfOuq(iRt$B1 z?T5XbuTfakT!e=|?V1OBDf3%i7;v{<-td8P`-rWztyrHe2Wz17$@E#s^NoDG@q-ul zq2F(`v-cnJReHF6_e`0;Ag?b5NPSAX^o90h@oVVrE1(LAudNJx2%q|d{My#6YeG2t z52zns(eF3D44rz6UM+j;gZ85125`^@ZR<0)wCv_mZ?=u}F)A8J>uFz1%b|JO@H_iI z>LGreodtO|B<5Ow@|&=6z4YNQWx1kA1U>~ws(zp+M>D8fQ>3*5E%Ra%MF1C6!V93Z zRN{gO0+0ziwY11vGWKTWRj@VAcB~BbAIrK8# z2DdiUaioXOeb(T;c0oUFJi4_EaoT_TL+x$Km2!+wOAh)Q`)!zB^dQOA@&wj;!N2Ab~SzlUKTqk8F#woWt*Yh${75yuhjJgDZMr{IJEDhZL{W569~&wEuW3 z6%Wv$j-L!L7S5rrEkceL8hq%I(sob(j{UsCo@aceT^7HXrJefG{viFBcuCLrnV{vR zoYS7tzgd3*0|5Af&U{|n)b^wERX3&M`qmRKDv~+tLiz?U`xiT^&NJUr7Qrk(44bwM z5!FV7r8m@q?3e z@dw0gZ(c{elm~If2Byw)lmA36EQA;0mBS1;Vdk+O@=N zI2TpQS)vOt;kh07LTbFK8;A8{_48{&T@AdSWRPop{V^3$1K z0sOP@yn_0_Jj&I_%XQJfMFX#DU<{-N#?F8%QQqSwISKx(__>dvo|_-}gJ`JeV z97)hiLMy(QiajYZfHS_sal!*6?uAEWfg#RCIv>cmXy98;T}1xC!jo@Y&s%z3z{zCD z36X<#I)9u4zW$ZIvW~o+j5-(xUMHmVH(nwQ86BaO-!5Fj7wTk8$D$7lEiTmZP-y17 zkOY%?8Pqc3|H?shmixIaof$9+!5Fp+zC3(z(})|vd=T#!(OtaFA#^7Q%;08`LcH?G zFgR5t6M@tf8E4YupcCYoY(ilC%3fL8nFAg^0hXEmObnQ|DAD%DvI}kXk-$n@uz}md z3mh0>&0B!wiIk|xBuE%Z{N?B8H1MC+*bnJbwy%|`dG=E0#&WkYtZUNZ9|(wR(vGm! zMVmmXl4t-*btC9xFDP8tOxVOE1kQy%7m&oeO@K(3mJ5mcn*+woS({DJwgxb8UudWQ z^g)h`!Fd?97V~3^zzz-Ds?$E5%dJjU^FRs4eG~c}zKc$I;KqWN4?o2HUI!i4Q?Mk! z0M|q49zMspcx-;0FXqrq*e{~r_|Qgs7}1qSyP#chLD>faER4B;MEW&vYrbY-^$MGF z4b3=YRjpZP0?DoV0vHra7F4NDgTJs4;l2?w!Sr6R7Jy;y8j;xZA99DMiC#CO+IB;NPI}*W_2`BXIm>ZkA+fqnCOfT~Azm^Cn^BKS|wgn)g)<8_e5*;$B>) z9v?9sDNnf<7f@rn-bVh{OPTUOvG%=r$lxj`g_HUm4Imnv*Aftj!5&RCrGo8Ybyjs$ zd%MjquFbfoKnGW`EC>lLS_@g|9$^Oe#Od1}wf6)Ee*6VCeSz{+rk(Ev5*!oW{a_Bq zmc7(>dn{D>+2I=sF9bKb6ET2_>7P1x;eWa4A-vn0D-(R?Ual=8H_mj-;|7AtU0Kq$ zLdSb9X@Iv6NEqHQCo=3j%m?aj3UI@PzGE7lo91(Zo`#Cuk^qIy^&so^jxrU zg@lZeFM}lGU7se7%{%M`A#5BDg+!m`jT>C}QI^sRQO!ejmNUJ+btca7fl{BLX;c6|1g zug|!7Nw^uaIgXTwYhaITvhh1jBxl3_j5*~h4tZUN%hzNW-$e~1IPJ*#ch=GzOS%Te zrpNHNc$3KDmA6I$Bb+$8K9@~{(Hf$Z~&~nC~`0FpQ>KVRs@HF)Yyu@`3?l@d0DmEXnrq_+`=q$LQ=x(-SgRmRt)So+8(jHYbmpHul8AIyX!e6~vFUqwd0+D+FbEiI5 zfE0Mm4*>rJ@ZtBE_YnPEaA8UOG5jWCAWO2$OrEsa#jjF?$6+pv1250bRxFcQ z>P2d(*GdccAx+V}-iqH66If{9$TRBwX2*!Xi? zgZMNa8Q;Qn5?}mP3;0j?TFSy3{U+v^We52#bYQ<}xM*am=vXz_B6bN|0Ab_QCxVFz{xdmr@nVf8;Y$|xq{lz79d3%A|C^^ zr8lB%O^aXt${&dJ6VtPW4diB-l8vC)*R>-_os&@HguT&)kcUds9FZgPE~pYb4(&yXS$GUlL_b#=WE#lsW#Jp1l*a9@nVb19yw zNyiPi>I_u$OKt>(E>2(C_Vd|HYg^DUX2^ldrL!rk@SC`9CQbh?&d5X@f@O1e{Pvl1 zKYd>Nz3QuV#oEk$e$}Kpky69)k_J>G8+CFMExr=JqAuP=0~ZZ!8i@UNGdD4D3DdY} z$J#ZoX_Z@;cGP)*&oD|Ap1|p19g!=R0|J zu%09AUo52_EM9VGnE8tbF+Dis0dC99`A_SH`nP-_rW+Yne_`K}J~;@*D}Zv~J7B@d zFGYqPrvD{c-qgJ?!bW1{SbfNnr~L)>VBrz9hdN)_gOL{%P&W39>AO}vllQOS&m7J$ z`t=ZK4~Oboes!E}1hy>8yHd(a)XPuyfvob2&P|0~`%nA1oLHc4F)9F-lhHi4@p3#m z&vC2|bJ$a!oJaa4WobligmO(_!p3L+9^(?n6{E_kY3M>bYU4Q$8PrSBi3hf*qu)4; zoZ_%uIYzSG@uD@qF8D&5cxJEN6NlVycnO4u7xS_L+E3P)fKlFjk(l|qb9bGR`tmv1 z>fveSQBUvzO%KcVplO&(8F&!vikG*6j*E6fnLR$xR**0AbK)~*0H=Dm;b5x=pKonv z98$KuC`j5!>JZ~M$8e-iTgicEPQR1{I)Wc>(k>)Rm}0C1Fyi`)rR{jg!!mq;JoO-l zGd*zR&~k>Cz}Zfm@B0;b54%4;ly^}}J7cMcOq-8=Qjg3zY9!zv@~%9w5|yt3drHUh3sR zLhXu|c|EX~vIXTWw$qZynii#^U9kS=5IW=VPaLRy=4A=?4f6EB0aoiv_JFysm4mi9 zeE$D^;vo9R%{h+uz_R$9Ggw9*h|l=>3kQCGYz3h z;_so4T~$%Ou)(oK*D}#x){mh{x6?!D;CJ1Fmk#(<=38KG|JDop2>aLUy`s!%h2TS0 z$4uz)im^`)WO~?=HRcy!p0Qsq^l4YtC2)P+!L3F!257kM8TiGHmLMj;CDZUt_Sle zPYnAv4#1ARUlfuzI92Q6FL&U!(oBEfN)$?vlAgq+eSQ&d(&U#nuKwEJaGA!>#O0Qo zsiVn^JOTvIAy4y~WP1{axvl>?ja>0~Z+z$I>Q{L3#cv*QQwsx!!U!8-TK(eGc2a-b z4@(`&p>yiM974BF+3w^h^@jGJJ_L`V><%?7U9izh(>vBzDDlrG8$b zmc#sx$XXWmoe+zQEK45wC`QrtCUOayzom0@=wJThQD5>mc}OLX>D5)xe)m(^zKc$T zHX!3m%ZtDBOO|D^Y0@)P`o^AK-jj|_Tbn3*bP_n*vh4z=?j5UN(H^Y>wL2rz!m)G} z4-AUZ;tKGLQXl!N7MG-M)+I#2Fn3ZcGhiz2lfU^ zT@}cZ^BSPZJol4QcvWq_rY;;+wh6fgLuHUlV{E8ihUeZmQ^zNM7+g>zzfh$rtU$zw zhH(A{(3CJ~4)O>CxZ(QdIbH{XiS{0+m&Vqxv_tNgHQ_^S@{~@&7M>Hh1N?^TJWfPj z$M}i+QG7BTevbJM(LWVD<~`Cj^IK`h_@5&3Hv~G$c+|@ujo(EV%jE9r(Vlh*T{Ljf zz$+R^XW%33UwIIY?;$Z{at|D_s+D^&4|CK-@_M-XEjvGtDJz^hw;16lShHjttOo5zec; zF8V%L&fw0;jgAQ;6Eq*FxbWx!^ejN?GimDsZzf4DJdn?cWF`n8ZO2|-LdhSP$Z$|t z01_6~KYeFW;R#tX7|?fMhjW0+1kpu_uPppHq4VDO=vCdW0fb8#UzxyVbxFLT5!BU{ z3p-A5ot(0;leZbJO<@mg9LYlz=}5!L&1e+-4yIk4uq3-^=Oo?5QXQ})#5WAgl%e54 z(;%{12iE8&=_YJIjY+c6126?CQRMnFF7-2f{u%f#IR2U%NNussSa;TC+ndVOa1*9# zSugfU*D+tK@4!t>9DndTnez>O!D05`!zZJIeDg~;`AC;u+q{0=;Bs1OJFtDZ*ph_+ zCZzx5VH8H_-EY_x7io$VjM@_s>zs3FpQO7MwHwC9nTLgGUaiR@F*XH9!{>Q}yff*A z7VAK)^{`;dV#o(*jr)jg>;ZHZnR)p9(T5q@OPmMMbNDMlGO-1hn{@U!sQN(7MOqeB zbr_LrXdAx4Az^rTF-+U&f|D;B_2Gr~#s?1LVt=Zhm1oHfd~Aq1;1Va(My7TN#&BG z#7QcM0&I7cY`pcZLZ9ZFl+)OszdPnv7vby} z3987rP#QLQK#pk(B;LOgKQsp4AID`5uPN#JN+;iyvLQ=&=q>5f&sjIK{hJ#?@m zE*x%`Pcm)Wf4ggBsNedYQR^OFuD_?Xg z6XqQF@%Mbi%kS9P4P>WS=-#)KDd!Dta8X+mC!A$%yl@jPT*tr1%m+FIx4Q|CpX`%Q z|J*H6nK9&{rW+JWGP1F@4uISn5Y|+w;iewKbj!?o%paX==UekoYHbwY4diZuc8=Ds zw($U&hkkCdaARFI3#4NnJ?%9?4l+iAavwYmePCoKmjTBmjFEJVQQZ9UjST~jzAaon zTfxKzELfcnIbOl}5FuY5VQ$qqmUAyR=P{omO_B0>dMdgFNqd*N!N#y z1dRI|TvG;r16D6AUNmsgz(oTW4cIA7|6+Huu8A6d;(yLRUWb{wLQlktpo3>)XQ;(OCy;Y8?l0+$3MleGV=_(UULP!=ZAaxXtAd)LuP@{43H zVP&%~Yee}P8SD3vU3sDl_29;AgeK1wesn4UgjdR~K8cgWY|3XR@NCFP#>I~u#bx0M z&L(2UY8m&rKF`ROwRpc)25Z%6bnAFiUL`LK2&}(Z_j28xwSLyZx|vMLB>;%BXVW7a z8lwlswTS~cviN5?WrMbQQMQz+@zO7yjIE`YY{j30E9AAVM=ene6r)pcfiKTT^h2jO z`z}K^2f#7>f`q$<%crqR{*80*8`nI4&TlZv;HvZI6o$hJ_zFz{PBKk|{^Z6Ne+p^E zo5$cuUhch)X-Al!i7$rz_n2po{W%(M!5`xdZ!};wGA@9`6BpTqQy6{RQLaR;pSRLB zT;}B_xWxSiuD9YPb4)w(Vf>qnnf4PG_{v_8k%5}M>XzY>7RY~zykD?SwWo+KXC-Z0IwD1H5E5iv!U7p8FN6#^%uVqn6;Q3+9{0v zA6*gnsYL|RpAgP-A^HoSNoOOywjEvC2m5^I7MK3mvRh214Ujju#D|+r$vw0|esDg5 z^YK9e>ikNo^)hwUXWjEM6zZ%8De__z>hd!@*d9DoCJkRi@7H?s1=Ft{^zrMR@(yqG zLs{R6-*BGGM-IzCzLg0#am%0e9^)&czU&r0;p9zm?&X<~Wg;-%T!vdz8$|9CzY+#^ z^X2h!T{Ljf!0Q?~HY#nfV`MI(8}`1o2~%-)8KHB`tR)zLWe>D%j*)%j&KNldPd#w8 z#>nYIwTs#VTR+u>i*G?t+f;tMbKT{|Cp4|`b$$CqCZ+PD_3o$Nj*>}L*jXww}7 zG))_J&0e2;>>~Mm%B-9b9hqu1wCPT693QkF(Dw7vt>6+^zFp^bZ;^*jJMQmS9!W!X z?Yi?CzYKfBfjqx1>)wHU{V;p6RAY68S%3FFhso3nhk&`i<^V6|S|EScrCo!5qU06s z(NUit=qqbw-^*?3&v_W~XKxkxJz(#F-5fwyZx9ogm-@)sKJ%ZAQ&rM3U+o)+Y)3{>?flq@mMAx4BsvH9{jftZ5?$Ecl&h1mtHv_Z5i4}b4VrO9MDk&pM5x& zCEqyfF!ecmIS{#`LM!_roe!7mzGN+*K!6L|PzpJCa-a%8%ROaJp7i}G7t0zv=;zi0 zRUR&ep3V-|ae&WUqI>U+2YU``6Ji5h7?3abzsP&a2AK#^=g8wBVQ9J6juOw;g>N{( z<#@>Pvj@)Ntj8?AIJVB$uwlx&uB^)cM0?M3AlGQ9V>dWdN?r00s=BdG;5(k-%hNF1 zFk==Ep!?Nx+jpLES!S!;GJdhHvc7Q3fzXV@SNYnOz=+5mXu(4+(^C$XKk~H%@RE+^ zQAyek_#L~E$q1ks8v*X~t7mwB0nm2kx{QZDqe{P?36LC2`_}s6C`xSJ)UL8AUBZPnCv)!vl{nlOV z26E<*s$znR$mBkTS3!?Y92 z963@S%+q$KE^X^$Lt+0YrzrXfB9)R@aD((Z0TZg8%XUafkUWrUl2;W`DgRpCRR1fuG#r0(dVs=f?Fa6&7 zq7@EmdjQ?9a|4lev&RSPiFMktL?_lSaw0;?ATO>!7Hyc9=+onKYx(%Xg7@h5J!PGj zOn878{NE_+Z_s@2VK(i}i}M;76gy(fnrRM-ryUzNt~&KFr`PaeKkkbq9+)?M`9gib zxt4c_&7rkodytI})_nR%{|}sNlIlrK*pI5;=z$m0&iZ9^8C?bEN9KiyKt_-gvi zjR#_Nv}t6}j8*5>Kl@+iargJ{KL5r6^k08__s{?PTMnT=c_9B$B|#6pJ5IU6wxsRR zMqKMiTc_>2UVfzw_yDZ)KW%|B^KTnZ*`jCr3agK0uS}Lf?3T9HKkblkWPGCCe?_<} z*Hp0CpIB4LL3G;Y2M(fBrO<;U$U~-?qdxf(0XiD{fEUUz`^GI7fUo%yIwXl(^@Duc zJ=c@J@+IZYrL7OGGgg)GKJ5WN_8wo#+D&cUEj-;I`wfTWsWRxoIt(qxg5cKH!KVLl z6g(}o%?4GvIF^A+RZ2l999<#P@z0rvWNdG*$I&qmQ(VnOWaZ^WhLxTGmkgGta>u-Q9pH%dNt*1eVxp)7ZNZ( zSI5DQO}I%MX{66z?E^`qgXWQPNT; z%22{a1g~&&Rr>`oOQ{mr9hqFWJI$nS zV1YC$38ZPXO?r(zON3s4rY~KFzBqCnXie0)AG*R3 z2>9e#ap&+t26WN~egM6nxCxIhl@J`@<(qE&_>(;M<4^p*!#~n6>~)Nr`=kGwET;dR zuaO-YPQ;t|(aHED>)ek&@!$In3oh410~ZbaPy;li_H2iCXyf8KHp~^(1b(;sEwGY~ znI>xD>gP8Dm(%zS_ZBdn4(SzkHju^S5+TvYf$fA%NT*=0qI2e!5zfK{jgX0NNZR!b zO}2$)OFn@Kc(9zRXZ5#}NeCw@{i;GceFte-c#-}$CSM4Y1w$r3^9EKIdOdXRgY`UI zpF`*_kY>Tn2~h`yBlqZT^W`l64hrRd28Ye(BrJn=2aSa-64SXN{cI0 z_XG6Cj{{@j^)J_7Qv*|;Z*ljdvinh9)2v@k?((MJ)Y!(g;_VbuJGMGfn_;T?z2dK* zshZx3Ej)n0HauY)RC!xIz{wtL{77p#X^)JV*d>X4*o7+>C3#@tpXA6wbg?wv@XBfV8D9L2 zT{wRTGY`@71+Hv>c2kaHk3P5>-wg%M_vXr6FgN8%`svflekCIu)xd{iuxtonyz)Ya z;@3{Pv91pa-CXBZBX6%YD;E zlBl~k@=j=+0mJ<%NPiulLhL_ybsYR3WZ**jqJfJBE*iLK;9u52-}HXycfi;lzNpg& z{S=jR$cz9S1<)}rTxSmTBIP*z<+&jQv;*0Vo^W7USkI7+%DMzI0x*9-qyvZr%f`{0a?a zz_vpm(YvvgHyflg&SX5v*jeTl2I>aSDcpGIcCyqN@LB#k7rIE9&7>(uVXRE~_eX}8 zlT~=Dj=h0aDgo)-22XSgPS**ePvP8{$JjL+gnfq4113*7fa+#bo=q?w_SwRWd))x6 z^K0oD_mVang0mM`F(t2{Ua}aE011gUsM2w1*Th=uOzSU!CFjVD@ct9;PjPO3kR}?s z8?{FKq2JcWp&@@>p;)pqczO{pSmiiP>Ya(@shI=3@DRV`DG_p<)0umb4I2IPc+=;a zwDA|ul3Kx?CG!m2&xAJwi`e8nj~`sdr=BkOiO=oIG&sf|y>q{zHEqSe&M%OY|48FJ z&%m6=Pafli37pi2gYgCY(X|m+K-3JPRKaZ8-58}n{3~Vw4ABDgHZ*4Tenk{`$Gz@t zK`h48*jZ)IoSBW|KJUy+-PkP67s2wSG+P?&)AsC(YCRk<2haNjG!LND_SuZCU9dkL zyCkpj5mt;|{6>n{%s}k&Dw%9AxJ{n1+`$r=mw#y-~S<5BNOqd3Z~oBBcrs-Duv z6Cb_{0lz+LqF>@!(=&&6JiwI$o%UDsOCCPA?&eu~o~Qdv-TI6#AJDgNzD0+QC0?F+ z?&R|}U)0Pd?e{sj0qFFOaKA*F%Ye-f8Qc4%B6 zYqP6u>RqAIpQOz@*YFF$Jfq}*-ZNw497^TjRS%+fjo9(BkLI&yA3Kpl&mL6QR^f`z z_KXdR0dT#2%`e!a{ zujC^w2befy)p3J+rMf=~bl5fJ;UFc?{j+yT8|?f5`>9On__6l=;-Oz<^x&~{{jV0k zu?LE60@2s=bNPS2<|LixfJTP+DR2Cm)d=z6DrarF{Pw`)(nI7Go*c@TIUmd=AH9!# z9;XDNH(&)fQFUv(?QiTmwmFe~4lcN4Ouo{W4t^u*Or{0^``h}I002M$NklB>{)c9iEe?uswg&i*r}l-awMGH>~NuxjeJFP|~p z`rX5-)hX1WFJ0S~f;iJ--U+Ra8Q_`rQaR<*9M)~%!*}cdH|G4`5IhG^QdrWmUD0;* zK^Xm#$GK^WM5U`InfD-*Fu2?kmoNPV7h19n@@&h)==riRe7N7{hI{wFI1cg<>$T1? zw)&)9InLa4>49^;CBb7W>Nw*LUaIiOdIilT_uZzQ^bTF!T=fglcYH2bd&1*A z4!5GGb%4&cB0AgMH3xUCF18~Np1VKlg#(A~8Dcb-dh+a{Tgp5(GJ6XxSM8|vzlVOX zK8RG^did>wyg>L9Uq1iD7sB;>0Num29Oy*{wrg#%y3lVKqqD5h&?ZzWaAD7&GB75- z^^3@PW@-7YwnVzNCokrMw-*n~zpO*(e9cyVAp<(<7eS%+J9Q=LmLNvUH^2EI7+5{j%t6^FW{vY|u!oz_M7 z2O6&}^lRleoXFMpz;xTOk|Wdc?Dy>*cnS9oCay?C)f@BKo# zHU|9is@}Y;H>$z@L!NbpH)kAw9^_UQbNTGrF-s1i-#_ye@+S^`KV~fR=)vVVgnr{8 z^hfI0BXqz5F3Z+&)Eq>QPF#y5{e?k+YYZ>Epu?AlIoG`9kov7JP{>OZXz!GlJfwdG z);8=R^!xkI?>>M2?cJ~c{FOuKzrOoS9esX)7B69-#l?P5OKETP7L?^DUMBJB7s`M+ z>oslN!|1#V=N;8I2hekn6@A*SU$9xnwafGumI3Row)K{Ebwg}a>x)XX!CyVtj`8~V zQJU(5@Go6oxh2h)sJge)_U%0M;R@r=C+)v}^~F7O4yjLg;1hPAPm?c=UY`&60P4$m zdI+7ipZ3o^Wv%OYgnCL{)o!))$xG3SGap*}2YcRsnFHz60c^&54!yfpY!%>+pE~Qk zNQ^5k;%ygt)_)O)KigHanIymtpNNIqY+J7s7$HL@bcFS;SQ>6zct{%rUR~nSXJhNF zyUL#Nf`_z^htUyD6IJm@Bb>1aGN!$m9!GsA(R$ye15VgbF6(kQ(%0_s*Td|#7wZP~ zPuoF1-2>IOYjc!Wc^B^!b@}8FI?n-=)^^eHP}NzY&{fr8Tpd}iFeb#KLIENop=4xQ{zI6>n~Q~%l( zYVAsga%gv)NH}#~-i)uG`6e9uM>gtral|%3YhIv2Y|@2WDik{gM>~&gk}+SnL0n$; zq^v0_?k(K#L~;s6Uu_W|g{DR|(qCcCk|5!gC%h8x)%pj9+^@(ck=>l9aVrVWv_*S` z8z+3zOfL>YUVD7+&wq!O5>5N9|9sq9H7k&w@@_tn>$*bN5!4@*(#Ma;N9HWoS8lF7A7k^A2e3N(X zf5snv&3nvm{PBLm;~(=49n&U$;{J~Q$T70wzbe)teFHG=Jl^|r1XE{xmCoh5XyBrO z-_gKyPJ&GXYnIy8iL;@dH@3pmsj*H*sC)bz#{_Q71%M#F8^`c*UouuP?`@t;B*PF47S1 zBrF@toQ(13)d@6%bnv(+!@9(~dlqEwzp&Wz@W4Ve6H7OJ`JkNzm@LwvJL%+Ye&7<- zk*>N|U;0j>>RL1|t|XpvQrC|C9Y8t2F;DdA=!JjZD5f~_nn4!7<_(-gxsG%Vi!&(& zTCf4#ewvfK!R2H}{P4ry=;a*mWS;o(jX#D5ZhZNS5{~ z;i-SpIISO3vsWE{g^A%gXb+ggbFBLbul5i$guU{REFmj@=^c5{2G}0*MrYqj$s2q< zT+1WGcb{o6nSPLMWU$THBAtZhVHgY0+OCfuKQieD3fq-Mb{16f022)FMF@s0N~Ddm z6q3aq7Ck|C3ogHg?qT#>{5v1gu&Cx2(LZsp{S&x7klq(|)JAG&WlDSI!L1uM1-)kh z*+qzF9;#eFcA>rYw_icO1vdBc4qq?u`vL<_^;C`PzBz!7k;<@_RV_#nPjr%pbl|-h1(%(=hK`10%BNhTntr3Rp|3IkuVt(mj5QU2)~48fv~BwrR%hxsJH@_(Hyg zYmYCk((!kvwc~==A%3S^*d5odtHzmYu07Wg*G{95Z{gbG#W!>ezs9d|Ee9cf<`Ux( z>uY)BL74LL8pMy|20xq;v@L9yCP@U?ll?(%B7ThaZ;@(=C@pSOxXDb)7|J(fvF;po zwS|EnbQ3zJ9m7R^YRj9>@8YF)dYBjn%lX6#Tz>dH^1U;%KzHgh4l&^vmfmzrmbqc$ zW_+^KJVklR=inHA#beldof( zDa`<3$Jo&Uza*z0-IE%!1KiLSTBZkod@@hJY^J73uUFE7WDx1&0TjFVh*5;o+$ z9=^*V2`s!qxGo+R5v?0JI%dg60>>>YU9A4Os?kt=Q3MYuj_q9I5GwQ>C%Kt$9m2k0 z!<%jnqi54y#!_I&7|DD=D2BMKE;*6FRS(iBE$Jj2SaoO~BXJ4#KEKAT=m3d8cE9S^ z*cF+S^|PY-64H@XbkLVK^Ng3Vpn79WxVbfsW9;Wa1(vwt`-K||Amc~ZyL=$8(BftB zr-wT%G1scpFylPd-STpwY*=^IswEIkH+i7Pc+0VC*1Jv4Kn`~S&&4(VLyZodP zrOrsV?)WtlH?mef%b2w`HnjPb9uJ^@`|4L#?(_45Gx~h=SLBk$SVksk6}^!Cn9^{1fvt ze#;HWW8M`x6cumQc&7$!`8FaldSvPlPGNIzrqB^qw|mLDqE6y-bND&JNMi2C7xWnK z{YsPc3Eyw!`J?d%8s8Cr6PHY`>rH-bYX<~};E9ZH#{WKzVWGYXI*s{bcgM`F<);X( zQNSQm>lU@i0~M`ctM&|zf!R1q`mzFxEpcH>Xz1^mH|Ma7^Jotfuxa|vuY_TrEca}> zrr~BT?4hD;`sN@4U&TwC_bbvlFdzkOqMJ&!W8jtye<>!AO%*n#X_eBpA@}JYrp|kx z{7Vi2f+=b^gvG9#E7T4#D=l7mkX|okgu~F(mxsY)_~a%D#_Z8crY%dP*zUrYLTvP! zR80|%Oq-X)#Y&QrY?;St;6>%n$_U>4!IU`VOa3AcATx%jP1l}NK6wUYnucCqcwDHP zpTD)1(OAooG6g=*tk=fi?S<{WczD~iOug|L`8-GWkO}$hALmPZlCduN)jsiiAdQ&x z3)Tytr=%SGAw9B-W6=!{>CZ`4UU|j&#YOSj2R(f8IeA}-vK>125IS`=2O)hK3UudX z?1m-kAs*@ZqWOIJoE^{lp5%)!r}k;~XVQ*CU+YHSzg&jxp960mpF?qv%QJY>4*44v zr;+}z5?Sb477KN~!X3&k5$0Q22kaz*w9r_I-+}I#E}@GC{?}{3c8FzYyN>NuB{m}5 zvQg(s(uPd?HFvMJ{e10ATfkzn2lX5;_r*-wFygRH+xU6xL*PC9#k_<~L4hOx9H<2w zRBGQn=;+~156x+(v|X{^vEkZb=PNpGoubGgwmp_t$w<_BkP{i}xK)VtV?J%VcG5jj zzT7?sM)^vx2P}=(raxngA0J&K*Y3J!Ntw0R;*u_Z887qzI%39#D}N5*Dx(IMYvyd~ zb}^p#vV?B8KY=Touvy!u-%CFPUw$RqLzc6bYoDK?cNnW}BtIhLIfnXKBvn`GsTX*( zT;MNX`?f5nUqQyLY)H?%2Y5|;YlaBrvwfg~w4VS5oOVaz24(Iu&oeVu*9VY?g6w`Z zpM}uRI>%=xZk~H&ZmI0586GoMl)k$3h%og9`u52;;Q2-w%FYXYtbU)*7`7iIo$DvyT=4|6hF_Z6wYxIV5MaltUo{TELxyaSGr%%LX~IA1LD~;m_xZ7HuVrXWb5# zIjGB=gtO=z3TKZHG%Aa0HZaUVTMrH1u)g2{TkE~+TDRCgb`rs>_K-8Uq^Hage9AHF zAbt&vk9+UpSd9ebQ^+3(~be>K&%j*w!tZBsu-uW*74 z$VfR5sGJ;dPL>GXI&RZJ}P8ArQx`~;=ZYT71#QT(p0Nr*_O3AaVXit z_7I4ys3^Mpkc~XX`&`t+w(s*5bYHq;9aM%}>ZUSz`1~LL_{Y0nK5+p3AHQU;nB#X} z#%KvXV81PE+wSKtesTIUhtRRMQ${Z#X1+ z^`67^H}}Y_ZSuu5$Own_8#xq6c~Ka|fg5FUw9{7DGK8f$kbiZ>p==MK^Kz40`WVMa z-)?c18T_aI(>9(klzs{Q{`2Q|zy0e!d{_>z&U_r5sI7e#32K+GEjEc?`J?otxKTQBS* zY-g68Uo5x(@E~UoU2<<-puDvu&W$tgqz=veb5(UvftC7Z8F?uG<3D&Y#V2e7FRbvz z6`2ofR4hO)^X<-pNHiYd!00!ewmdpXTETmWuK5P9cR`hQR8&QsRyAajNmQndFNA*` zyDeF5?I_`3Cj5;6%o8rQIFJvKBu6N zY@5>6ro|3vi?AQw0}i+FCr#TmV<8CG&wCi%mr2-;BcnKE&T_YIQSNS%ZU03WW{Ruw zn)jKPm-J(GoD;x?+fSMQh8GI>XO6IJ+fE|~FCcymy=ynMSKC2!Y*~laZx&Y9 z+2L=aBVBJ|T?iqhyrcl-V#$JU+p=I$P4rka<0Oi!ijLFT3%ZJ#a@6f^_?_Us`w)*ES*U*fruTQxQfKVBg2)YMUeoBsP-R z{PB}39zS9;pTEby#Mmyhb-_LUvSrCBZqel@cuQ~vU-6`o3))jRj=7syPBuXD$fvfZ z(vxEZFo1qd8PxT~N}O;jxcNoA{R&b*L%1-sd~L!iKj#_kM;%j{UT%`zAM%BdAP@=8 zvK!lH*{Dxa>`SJ8CBpoBAZ#b<5bFK!{EB??NIUm$Y1OJfUm7;m6E=05>3p9r}`^?kE#+DV>`mzD1{F?WMU zi0R-aZg@#rXt!f)H%DxEBIDx6K54(qMFd_4pbk#W&%H_dp|~;`7u^(Q!zJ#db-t!P zvxwqAEen)P#2B20NGg`UqY^mq_`E$DAw9bwX%$u?+RM9Bh{B)k^U zp)FOzEjt0$0LCC!bRC!uqzVVJ1D;O&0=?i3SElgqEtk4C+psbL@3-<2a4ssUIWozG zk@C{S(}C|FiyQP8|Gz^6saMC!@dw1R-dW312a}KRRzZtst81zAr>*5RQUhN!{2qC; z9uFP-ndl^H!8KjJN^50UAd_ld7B|vHT>Hc}Wa7FOUH}Vy{49jeVkWpOKw_YL^q#kt5_j}(u!pgqE~xu3!-rvhY5nkEMH(*H zT=CXB`ym&|eE8eNQ+RNpLj23I4xG0j`gQl%|IrHfF7COc*hAHAw`x{5)o;|g{gAX>Q#O{E@!C|id4z!lqk_+$OafPjBeXn6rv+=?BLd=2 zFYs^a1|+aw(nCv$IHokumT~bUMB|RIov32TiImloG%+90c$>u_fI?$@bx7=l5^LpTo_) z{&}7gxfb9LO5}*@RFE@7(wV4T-ibLyle-;g&-e=1DbZ5-Z{l|}w97Ox=gJrjA4_y# zC+(Q$wYX#YkHV*Uk9UUW`aXX68Hm55uT;Cs8-%&tA2^t{U&lLbY|3#aOUFdAc&mmQ zy@z&ery+5jYP31e&~6xm`2?-wa%d7)TYrkboQUltQ-EeXGC59gY~bH>B7A6R=U4Jj zn6&gE{%n`yi0vl%(vOnSht_Td@nNc)-8{^4;~dO67h?b)#Ale=j8^EGIs!EJt$1WgP#*TGGOC z)BF{L^z-sHx7Q+Z@I^K_0`mXtM;>Q9btL*0??;$R_@aS}1}+-7XyC7|0eiD{SkueI zpPN_N-#WPDd<}H#19#Bx`n>5|gl+f$4BhlIA|H3)n0&8vYz}g@)_GgjpMQgYJ!+;-Hm{qS?$;-sPp@)OUO$&v6^`E5fux+%X(xOenaRR0}YMVdfcU}VJU`I6g8DqKDzMOJc{s9pTJ!GRE;ELD{Hy!KooAKI} z8=BBXUY5&PQT?iSb(Di@4C+#ENXdnc#h=mL_FTZ-VQ}tdDB!NyT!L6c=!;j8;n|wfZ6C4TZ1QE83-SFO9H^@RM zJSuNCvnj82jBJ(L@w#=yI^_7>4058AzF*N}gC<|=d3oU=`tyA@g8Ie79{gh*jEr-L zAYV~(Q?&B2DJ4s5_(7*GBR8m{gGQ_06^g&z8g2QigGd2SBqrT>(~rO4Ubl;00M<{T zO)zK5=$QOJ2>1EO;4Nvj=A}#tZ$E{b@+s8eH_4NGeA5k^*!&T3B^y3o19{AC`kV3O zPf-hV#ASGBh~RL&i3^w`iplbG{GM{$(zc9FFX#7NIuJxc(Qxf(l5x$l}4DeC@~f#)R6<(mm8`Td(sz2j{RkIef06#eR6nZ~9{xskT=DkTcE~ zbLF^P6Xh+i$*a**WUfXhcHK=l$@y8Jgjb0AAqsOF(;SnpF(DBhE?ANYkj$<86^iD~bf!t*x3n1YLJnBlADd((Xd8c7cpCLY7FJa3MeG4~+w`9P?=i#vF z_vd9a-sh#|+ZaID9c$(E-d@g5gY}hk5G~L4v7}i~C~MhMif)3JkwrV{)~nX1^mWmf zUwU$Z26ZNkfbL9zIW%0!O~WO8uOE+%oX6qAx*U&ur?e2W{?sN#Gai zr!Pift;T*S2he$zJ_ieWfUvZsD=n44`84pz7x(tV$epj7CA{Q}k~f7#-sm2=Wj~(c z!JjUNYFzh0*0iY=Kk3|Vu#=yNBH9X6+FnCfDqMO*3GQV%u3#ff{GEEa059G}0~ZZ^ zr-5lu_}gWhI^Iu$eZs_RD~+AOIwJUJY1Z9MhMv*1m}a+Wx^z`7s9kV=!bf*Mf`9&+MkWLbLm_YTwOQd{{;d zvvY;nSLFvgK5!6SJ1u1oHG7D}_3?4?C3*GWU;CKO#T-Xyzk$vi!b4Pf!HqI%Pt8U? z^F*%^qc&E}_s=AF6wIJJ0xqBSLmoQw(3LOS@gQ%;J3c>H@X`>sU-=G2Za)gU-BZUwLGrbpAr~Uqulp?JnS%_*dpX|7 z9MDAwAW|-j^B%w{u72g-ew7al!)R}bM2$+A07<6 zabJorXh6r(^ur6Xo^cjs!dXOgk8fK|eIwoR2zWi9l`lX0!Y00^Ykjib-;HB0%nfZp zU7H8awldDR2jqTG!uhw-W&MJ&xAl~&xR)Dg);$kVr!L75mvTE+zq_M;QQy)wsDH## zubi_ZyX};O9yS2RxqIBy=T*OLE5W*CvyCVpa^3hcDi2Gdw;V*zc+vHY&JDCd*B+QB z&of`Rd~&_Oy-M)HIVR+5eaiTb!<#AR=sSw0OtcU3V_mf_Q5QHUnl_WR;97!q5`VR6 zt7u-{fi65KrOOKm*cqj)mNQQT<6Ad-NEthcE_6^sT3qym{*mp2Unu6_`9}|l=AbBF zKKCo=`2xD-N?B58qDyc{-+9meGHPG4;0rfw?@+KTJXnW(t_9_}CkN1PKKi9{4}G@I zxQ9wR;5ab45x2H7a%tPuP0PD>40tMY>ON(hwqRYwaf{U01$3z`c%Q@5)J+gsq5g#{xDAX=MXvv(LZ_!9rxkGNAOX9ELXm!D-CUrFA%Zq`pna|{KS4#ZTKw( zV!LUbL|$m*1t;p*I%pgAZ|S~ka^SZ;YNOU6^x7L`N@Yym86OuF_$d4IYv@1qLi>oz zOAd&Ou9iLXr3Jo};T`Q9Alfta*1AHwqb7g{-?~M-hj?F*`2~J#YrCuc?MqRR#l}k6 z+F;-`et%jPw!7A4XlgTHS$pq%%a@=AHZaK z5+2Y?@Ywd!B&4pko%0#_daGxYxo|3qW#zT6Jz1kwG;tdb7;w3XN&B>z%h<)D@NfrY z|46-7?;k#MAO&T8bNx9+-s+de*LSqvUol>^E=sj+0p)<6_>w;T8hIkKZJpE;__)C^ z-vdr^U9hJHDd?k%ieZ z*P_gUFUZ1!2>QVu+gr0i9s$tMJ>?>4blJMv+dSId!Z!6SeGu?1ANEz)equnZ&TMTx z@|pH7E{xO|9o)zd61gl7@i-Rpz<`e&y72)c`S%#f-*Q-neL7-7r%7|XLrpvj7v&Y4 zBIHUw%(Txd%=mcy*>F4MTm}($u4Fdd;scayW!38UvylAS3q#6lv=a@~2&lEQ|;6 zc#CVE&FAjl!_)47GUIq~*}tB#o&18yIyV00Qfz@8d8JRcUZbD+h*CHvRz}c? zk~i08afhrXx4oXvdqG z7f^1>FYPwr)85O1S6M37=$LIgfzL}HlxcL7HkUxk-?qp@aoBmj^h!LAU7l^<$5?L7= zic4STO8abJ(T(74@9AgKnZevD|77YTa)?DfW4+tQ2Y~!J2 zneiWTu&!D^ysSH}TPhyGv&J8x4gb<=8<0E+6B@?*ux`SD49@+>=kl`Wzq}`{u@Lrl z%qtV}n06(20U$4FZ&KIAY-aPneo9l?)>Yf!)Y${i#YbHJ%xLS(@bHrBh69&tj}`MG z8fpvR0JZZnxHs4QBt7F^eLS8~m}s#D7B_&pc#J8C)v(Sa1ji|=J}7({X3^4Z&L&to96dr;K<-DnY@-}LwGSl-$ilp zT_o_91gu2dGnz`HyjR5bSHyP|0-PNa@h01@fj_zh*!nl3rce4d{Xzt>!O)QPbQXB_ zaDbOy;|9VLl+ulEc*Rd_p4UJd2E%Fccfb)ah8qRXghyF82nh-?Plm$m0yGjd{1)%> zI55uOj6rKp>RpSfEU5OB|DMj56HBu&I!|a5<(ovwYBQ+;{yd%lg z0ixM7vUxUVvsmrGaAVFq`deBe>0Lbd@g#@It8rdp6T;_jC=xvR zFT}ve%7pAb^DlMr_<}(GhK^8w{q=v1|F7rx-;c4n{qLvy7RBExv+we2y&l9z|AZ~K zF#f8GbDslkc)+;%!72UWVZy~Q{=t%JB<@5A(d%2d6&JkDMVz2@fyqf)n$w_JbU|P7RaEPi9atLHTb>-?=_P-jm}{lzw}Bo?ZR-LHrTxKr|h_GFY_WH z?WZp`Y)82Xw@JIU-Go66(A&P6&U}_v>pEZzAPGzwpSZ-IXBW;z#6kyQw7l7@^a#F3 zUy86O^1$MV_ZMCsJaBuOq#Nce1N#s#CEL@oNXpzv$t=gk4@dm?+rJe;%(^O)c@kPw znH##iB`(%tLT_a$e&sia(_hdZ8KTg_n_ydlfECvCa?@+~HUEpi z5E`($!G~88hWH~1gA#y$xeXQQ{E~`sN4n<_FP|2Hq}n-88H94-#TR+xabu#9hu@v< z3MAn7a`P6}x5B0HR@`3|J~Ynr)u&X$WYgLs#RZi-Cu05VsrNKS?csWd=ejHI5ME?^ znzwMBhnG&cLw5)Fe+R$Kg_*KizIHXgl|kYY;ExH8vEx4?TKJRjM}7v!!pY-c-^;u( z*|2?9V3YPo=>5%LlLd3kZCiQF68^mnJLmgMCwGBuK%$x8x(o3%jvM6e2`#`{Ls#lS zvVjp0RJ2rh!!JL184-82`y2dgo)GyqLcHQ!H0!PhB1Uhw_=REm%t@FO02f}Q^hSAJ ztup={HVatdJIpD(oVa>UI#e!amLvHhAuHz{X6Bp;BOF;vD;;xycA z^T>;reAM4GzOd)To{RCmsDik$=|7W+L7)tb?_4o2-WX>ztYjP`_2mm=3SJO*PR2OH zgTS~w5Nr+w^94-|Zx;HV>?}Sqzvz@t`B^U*!jGXQDlg_pfA9JOmiWrW(6bBhN}!?L zSYXKH^$7i1*fIXE@kic&4XO*4iv%tbxJck4fxn3a(%0-iXT6myzU*N#w%K?mc6C<5 zb%U>o^3>Ep(B9I%a}s1M5Ku>KMbbW>KrU0H-+|!t<4xZYUKw+Y(tf=(Nf>(h2_N&g zQf*)%GR&7+_{jQ1p5Z}$x!b49RDI>RJiA9J>(g!y%w}dc#=D_>?v9n*NazNz^ye9C zW|aY&>uq#Ng)PTB*+@y0`Rf>foVW>?4JFz5sZnt@Dzb5bjU#Ssa^vD`wvksaWvdK2 zmh(A*{x&XUWxVjj#!WP|miwv`OnVHEXW;8WsJRO~PfD?VB%@%G&y4XLyKOg!%uCk7 z9fyv*%&Tks%D-jcXe^sXS-5e0ddr6ETQ>C1+WKt3itb5zacnu8b{{u_Q0^S}Dt|UA z-CVurW)~mtd-Byo<0v~fiufguq(%O^vYRzMH~0jHu#;C)S*FF^aVfDzTfE^(e&*eU zyi|snSSTl+GZd8`I^||nH`G=ykOlbFOFT)a2J{bJ;m1HXqG`bA25G;x^2DcxdN2R_ zdWM@!$p*ZEr8{L&L6EB_K^1P4UYU|R^0LdgJcMZ?gf^?X{>CLw`MtB<(!Z#>lF^IN#S34aUMF+A9|oP>x^tHkl`9Mn&Y=_fq63e$dN zhZot7YfJGqjSbgpG>Yv=qXzKe9h#MZyq|}>l;gyP6@Er?^IW&JV2|5Usf_Dk%6&&s ze)3CWk@^L2A_{L>=*p7>$P8{q<_0%qp1dn>2tFo*1Q3``nTC=pLG>B`p2l!{!c>b z8sMMwIkXTXuZ&sXv+wlbTe$$s(hE4c8&t=b5kwVU-2(yN)l>1!uuxGzIP$m}Il9{dajzxq<7YTm0&` z9IXo$iaj3uOaEH|$|m5|4P#|urG3juJou7m@V*6n3BO3-e_aC9k+VA0I@2mhgjESA zZMz-=a@kh&uLp|QhIp^GAnilP*0vM2r)d+w6mp^wxAim)oJrgE+Ln!Bs%@rozA@1(h$`?eJ%S@xAP8j{5-Q!9!-{`I$!5 z{Q&(EF>Sl==H*p7xpC4aI)AM(8v&U=XgFg2JlraV%B#Og7q_*?K${=$B{N-dj4K&s z<5)*S77d2|Drt;{VmxhWudL`9w3#+b%=vugl6JFM;e zSG?qgd8zVDv9dmrG(1C$JQHWXElTSi^c9D+d~a1l;(qP-JztdW%{#3e!T`UHrzJ?9 zl)W;{{Mqt-LZ#%_VXX)1UD})I9{A9G+Y)(Gf7(YV zOLY!edN?dqHOuD{@?jeenw|$P|XWKLK zYnx2H70@_&P=8MFlR9a+T87|A{ld-HebqawlI?NUqR7_?{>fVp%m5F0O&dq~S%l$5 z-G%qu$T)`QwpY<5bOJZVYs@+Pvz@r2+&qxs2BY&^^z!EF5z?HD@bydTqy3rn!Fnh^ z)-!b{YouS`hk9^r+eQ)9@RQmFLzb8{U@g3w$aW!&uDmJ#;6%@Ad~ADT+hN%&FWWZj z9(=>&I}Mw0qnArZm6sn|$p=@Ey?fr&J=Oxh9*kjsoONIFV%w%(&tC$_in`?8d7-*xIYI|oSsx((O2M_$)JOQ&5Z!m%*H>Sl z@hLo7rlvhT!iVJ{-(*+VN9t}4n6VwCUC=0<5@p#?m;MI9dyvb1NC|l_!_bj&Wx;KI z0f+tUqxCQb5uyrbn->t;K2shx=b8h6?T3;;(%U{Jw-NiTh zXdUyw2iuiLzA*1W3HGV(E3gT+EUa@LFkv~geAGwt15SgdRFRqekGQKdJ{#{<+Rmx7 zH{QcI&=U>O?^!Fbj}(>iK+?fuo!;(-hEfKQ$qxxkL_W*Mdd3M)vbXp%B;LgMWL#+` z27JbsUBi7ER~@9R(ubo|08t&vOLSU37d||Gz=#&VvQQqIOgiq^^JH!4r7I5atb5>z zErK!#6cJ&GAOBa|rQ2|!q{Vnb9IlHKW&bZv|I`+4sZDtcYxEL(%$eVL;dKV<01P{`#8sbJo`0w z4_#nvW;&D0kFtooaS~WI@EhLojxfq(BN-|CO#QJ{U%D1V3RKz2PV_M?4#oxi?WlPWr}#<(y7DcX@NS#r^(0@>6T-lkVBsh*b-?kP;{cz-sd!av_1ZGA zo#OtQc0Uq<@BWD&r69d+D_(Eb3co&5CrKMWZVoCAAHaiBmMw=oSVs)ZZQ;lVml@$F z&U%jQwuY{$%#f*@F`YaaQrXnA<4MfKk#+}9%<*wyUMY?-1|6S|emUmu|L!g6fl>!6FgyESmVN zXRT@zCX5kY2Yop{El^_%vBNbf0Pt8!)yF0=Jpb!Qq=l23gq2cc)C3LQeu*!$CL?X2 ziwix@`=*4yh&u~g$dcBGC6Bac@?DW1(K5^p;q5m~J-=_YtkRPFOg#9rpe|zYq%jNT! z&)<^!5q5ID zxB%gc^iSNMS+wz`#1|L!xgS~dIe9vsgLnANA_VlWWB759?MNr|7ELEi4(8R9xP*rT z2leuyWRq+`{fJ!uf6MB7{HDaG+;Lyi0IEgw(zv!TE8s=O0{0^9xqn}+B;z|I#}r7OO)vUpZd>i_s@-aBde0xyJn;K4_H{s-x}2at5wD;!iCHq%9~UP&{_ z18)kiBbwE0S7OM8Td*72Gj5YYAGO507_HbOl+OQiuvyoY&Fvdl0dUbqs zG!kR^BTKT5qg(cV2=fiTQCY zJL}o5X&>?u9*|b#M*c;<@+zDgyCU1HcY_Cl&un;1X4QY4{z+4P`_pI?x3ySQeg&)GO7MQ{1#kK|@ui1`WlJQ6t1fO7de zx6etbr&oc3J78P-BizxirX8OL(E;-ot!EL~!M2bM><}04TXiFm-{LzauRa*Eum^k( z-C(zTZz1*kMz}!VaPS*xemDF9`R`7%DoS&Bkmyt(xNY0e?B(}OejD)?u5ZGXM3THl zV{q+s!xAhSBd2kxlZ7nY(Z8oWzq;J3Jq%*tZM$p_ z(q`Izr4331Ah^_3^VE(1)NVHp=5{{3-`V`nyf+!O(e@4o+BE;9Bff+!dB~EWx|@WM zbDj-qY4q1eB8npLdP8PUaKM9iT~bT3?Pn1V%?;sz;}YNhgr)slG~{KT#j^2c0x!@d zY#OM9C$RDP1$N>?o)oxx#nOH_z6~sq!mK`~&qx%o(2K0}+24Dc=l%-o+D5*F$QUDE zdHjiY-ak9m;EsWf8_nIQYFWvjj7m_q?Q?nr+H3p6`7+zQpLV0Xh7mQIX+PjQW&0L4 zNNTL*mA)x`Exf_Ao3XR5;(P79&bmwvasR}IYYgE|Z04`gN`%)e!kg;b42wX$Z=W>z-{Mw^=(KuG zetP2t>_daR(;5&W45ZDdrVOnHKm%h&ag<#kC#qT=0yq+*hcpyUlG#2X@h7ZQv znaVO#u^k1QY1)p8ZvG}uxPbx^avD3@?>*!z#~uO zT0{5;zP9RDmo4}jL~F3cL1P$hX|QOix$e%Qk zFYGzadt~k!BWTM7)smMV_)ZaYsriGXZ)Bx-hFCgh|ZUjKY#Lz(O>AB`$cpO zSsg=I_U2o?kbe3@@Pt?L;q-D0XYYAmu&q1KUHf5rv-?e$b@GpvE6yY7y)X+yga%>XgbBuoU%U9lI zA!DSGe6*f+PhX7BDciW{gfJTIW<5w_A%{yC7oE_j8WsmjUb&Ha-1KcQP5Smal6HAv(Z zR=sfCcjD{Xx9E}ggZqpeKPfl(eDtF@=vT*;sZo|f$BN1uB#0Z=vU>#9a^xAlg!97{ zr_&9(fxg|dKgo?bTr&?*Z$^(aBtB7JZL4CS8H2rbKtb?IqdFa3$eM-nUq{E~-!+1)Zi2Ii#)Nl^yqgzJTlsbe6B8$(uvR{bZBz^kve zPkHA`-FT+0v2HmoRu@y^6#R{RqI(*j`vvrS+Oin0(Kd9fFN`=%K|0%M_$t3OMninq zoZ(0Q8rp7iLkrf5C_j{&>0J9tpJVvaz0@gqoH`ssbY!=+E%-wn-fct0uVJ!ZsLoBh zgvcUy4}K)){AsXQ-a*ZQ1k44{FG9%&ZM(Q^+u_M}(K$gH80C$60}@@oww0NNSd0=T6Frn?KOwm#@k?AA|vqO~MVE?5@2rOm^M^RCLz1Y1Yzectsx3 z(cHG%ksr`jnstj^hNJK30>y`py~`mAA2B}n%l7X-pnK@FhUhs^fx3N7n;AVJFR7K_ z%Q!%nKSLL42_%o(JV$**AhtCLQ=ZXbc%y8T?dPip^yV{g_HV9vBkRX6)Bz7gfHvvv zZ#-b-iMB8X=#)ne>Li~U%bLdSQ zYPnH>^o{*O``x?G7@bg9~ z2%Uc6I)>-E$YZ-BR0_NGz*XSLIlwS;TvquuG&Ot2I*{tcJXX>Q4$b;K_g@$zCNttnb*)FCYqVJ9uY+rH! ziR~ZdjT$BmDZ@LM;NF|Okk-1K`kXqAU$oYR@G61)C(rc>&`UWa{|m9sPlf?!Uy}kNW|5POG9!j432-OxI3sh% zA#*2vX{6az@VTIbP`#f}rqp}*Dh_l>0#HzXS+7rfXgT*z8$#WXM?q~{uQSF`PLzpd zBNBB$g@I?=CJ{rCK1Kud2g;m!NWHKPOB$H8?@1p@n0=pzr&vPQj$didPPS?4(#lKZ zp&rSIZT-3uL|F1|7zJQEksH>dgG4$;^w5WhllYcp+nC)Deez8m-i*+}N&3=}K7Pe3 z3-ZEcvH2j6`Fg!_F>=KVk>?d4eC%zaxTHZ|$|kp{2zO>65&N37d&!e1eaBSpg-~bu zB-Y;kj^7~D_bL~nHm&W$$glD>%#>u1d5RwlQ~z${Px^*~s_%AMe>Fs(@v-G>n;{PS zb#caBet9sDQqQ3xV6S1jIVg|Hw(~&yOZc+@!IN}&)6Uw~(oQ)id3OAqq|jb}D|2aI zvkn&{bdq|OjmGDBX9hBGctt9D)ny&aLR!&f0~$^O{KEG#pYpcprl8@adGR6nT8}H4 zD9YjrF#Yi=j`Cr6Wfsx}+rVESJ|YQ~qz*CjSTap~?r)r7k^j;c`A~Q4hs!Yz^2<0R zw8Ujv%E7v|XpTHazXD?%;A+t(&fp+;f7;LdTQ3?1Mjtuq6bIN+2Mo-{5@#SJt`ZRu1 zK{B4|I&R0{|0izHG!{%{hqji`Lpg@^j{1At-SwPUjE(bU(2IB{Al=FX?A(JbaJ^a z61Ygw8r@157nn75y$l@18c zxZxn}jDwT_@RNWbl1_NU6aVUzgm5s`us{IV!Gd^p45C@_$i_Iwrp~!~${a7PS?sxE zV*Q>))Q`M$|H)G>S!i^@%{wpU3ID(%-5nDy2kWBj0vwCR8aTQT!Gb_e2xAoBV%r#{ zyI7vh84Spk=M3=WEQ^N@$T|{-zjB_O;Ezk31Gi4z4CzF{0eVjWuCc>sjUHUAXCnA$ z90SgL`3AW>GoW)o;t;|GnQdZ2K9+RkrZSW+G#vtF;399Nhk@|nvWS>=DeFI8KBt5a zxR3EoDczST1GVY!0K5T7;_`8h+o#v`c4TXu{?}k$VwyBB!!OVO%@T-WZ5fO@zEIRW z3brbb&uCSCw}g83%5@%`I`~H95%LV>4nIDlz0xca*l_DN?F_eTLEf2%t=f{8Ju5-r z`SFXSuF|`Rbj8WZRFL8z{D~JtPqa}qku^jIkA~O!lIeHaVyKj;U@p{Ooa0+MK-W55BmiR^I_UzSdNIe_O?l1pGIN> zKdCnc&e8=Qky}y%yD}#SgJ-cgQ48&)FtbNW_3ylI;Jf}0 z{J2BlTQrA|0VYWAD{0KqYu8yY=`0tMpM@V12K~Z+#Hl)0Cl3c-z6QLK2JwB`9- zt+EZ7Byt__9K#Qd|8y0Cr`4ZEaojEQD@O}=a>0*XH{ERd(bDWM-D|?*#*b9PcT%>G z3Q@)ey!5Z}IWXYz1+N>S@A#s^d*)gn*$Dm9&mVgFzMDSSpyB2n`+fWIeQZt+$Z}tv zcCO{iugtl8QOsN{2X(QjNq;sLaKdv9(}5G#4us1W;vn{ayM~tY&EC#~3X}dH;?C(@ z?*@HdOgkTRbF-(jySb`gbwFM$1P?7G+l|JP7X@m0c7Bx?T`51988?M2bHm{7zT`RM z0U?hOTdwpR^yDmjaGnKT9xf8NNZ=xYiv%tb_yGyDFUhYzZ*@7r_`OKtGJaX4?b8B2 zeP`(;NKo%ZEMfGQg_!iq)nRu5)nTu;J^L&1o8-yZLp#!u0;*7^1Tl;s$FLDcne0I-URP&jF$? z--6o%Ngvr9?l>ju(hOQOK6g{mNn>H}wLwW9=If_fe33!&1g!>RQI89f$fAp3?&x5BW-%h)#To$#HBp z`)4C!5xXAm`-+UYB>`;pGz1Nie9NBn#u{F||+xJC{^kg<3-wRJqs9^}YC zU2sGGtz%fi9H;v0U-^=j8;IQq!kC)PbBu~3)4aY=m)Az(XE$b=(nUS+xv?nk7~Hhw z<|5u*BwXns&+UDMsmN+`n;gsZ3Lt-Y^Q^;_$3U0Qh8})JWO9-I8?S6KDRZIvm(f4V z1`q1ufzH1%i~onDR-yf23P&ORSzJdZ-okYrKGapYKUXvWp+H{0htz`-@+Q1hf2Ws5kR7a9?!R~FdJOcB?v~d{s7lQD=7KN20t@0>GZC)E!~?U zw>%b1HZ;fR!M2_;2J0>#7*~A!nF9d-;d^k#8!=3%QHyIdp$Y!3D}*Buf@GCrjE*$I zVGZOnhB(SlLm?MPx_KG>u#JbIe%a6sv3!A%e#-YsVb?y!F$#L6;n~Ay48<`L|CNpY zpFV%){dPATs*~9S&Dh93$7hX$R66^ltSM<>;zgMKc=(5ox@kY_=KavXeb4|M9nOYV zyDtBfYXsUcmHC`CnoR~H?-DZMy7u(N(g?`sQh!E=rrz$)_VM0@u^+F0$tbcKOrXoS zW6GnvFDlC+j{l?pXLEXWD{1C#IdN>223)+)dBwgYA~?N?y+jPc4_OFm)Rr=<_xw`bS*q=6W1?)8W)J9aX#Z(x}v!DpnNX* zaXsJuKtt0QV$=7ywy#l?a|$=JdznsVB16#H?%BR}{U3PSTK+66jdC9CQ({OV-^^Y7 zVlaCl{OY1}v2-@3hrWG=Uq6-pXWlc|&jA3O{U&osEUz^xCvo!Y4ux>#eA_uW{f>Q4 z_%?r{hhe)e4Lp5s5Lxn@TmVxeIQuU0F5O?bF@Uo#Gq3K0=$Gy_w3Pl=_OOu^H~7CJ zFC5fTLv#&M+}CFAm(dgao6i%5aku2zTVr9r`a1jCG=%lb=-c5k$^m&e>pN*&%pBVH zCHBGY*T`A|UGp`D!u^7Q`GTLUMbOtD4bj=l_Q?1_Bf;#YgP$6gcHV4xsBh}IMo59v z_+EoP(VKVp*YGZ1$kYH=9-?5?%c)D9uR3Ry{~CKaHqr9=iUEmxKQg+p9Pq362u~vU z!YwLDIAL7jCv)5}KwlrVSs$~xzvpY$>m8O~C00kor!3{e_fzIuUCtMK*}I~CT1T?) zP?J`4!u>4PjX4-7{GwAH_;%9B7o)M~%*EkX9Thh;x?j+;#h4qJXsGVk>>A_nF+%S+ zqB2tFq~~m1eUf8UFb2=)jK*hKmvW5w5na~^U88E`W3|g3B>2}rw0m%@kB;roH0um& z3a|lg=y^M~9XG08xpo|68M$_)e2_V?mf0g#qz@0b@4kTR-Zcv7S{@@;zsl{`TurAe zjdM<;5#?RnmX*fjsRx!VxLv#GZA0SN5&RgP#{k{@#(+yBZU|BqUAKWB>f`567^MEf zSXDP)NR~(A$OGu$lD(wX1?nDnHAuhB7su5P=-eYOJ^M~@TGEM zAFOpuUaaGtBZ|0w>p|xtlsA6KNfXJp&P#{BBL>*iwXaq(U|cggS=T97Wp6pC6YA3~ ze7bIAIeev#-PpDx$FJ}q?(my-q4S2kcco3%P}ueeln`j&XuF#B0J)n#mFAZE?pI^G z9}PMCj+lC6N8;SVbvyfmy>B%tPWiU43L@o7exh&jfPcHBu|1?LERSikVu()0eHNE` z>%8uowa(Ai8o5%o$i)NMJ+$(Xy5*j}v`+}qvg=wVZRBbvJ%m#Yv5!o@!Q8FoOgLp^ z{jjVh**-#DryTrKCj5~+`NVf&R?B%uN6vqfXoWwrDv4%W)D<=;o(V+j9I&y`~{Tg)p3HMYm&*o0M;5!BD zkhi*760l*r;th+)cG{SfyQK&2YuCoXukPO=7yBTjthnh9qxq3f`XuDk_PnmT{p`3v zb%9;=z46vd{NBrq3&-uY`{q(j; z@Ew4>SznPpAhgE#rN?wUA!HBKFPj*+=|WwyHJSI|Fy`5b-d zAtppvFbWJN)fE}=zz7c|k)IPr?~c#Y)Bl^viq~Srqu3VZ% zUG(-#+P2{tGg6+x!LRLi`Z2~t@>2aqcD4hwGgJxETfami6)wEyQx41n5oE1#@~huJ z|J5BOGigmKZOg};1Wq*E{!G4XYt+ZsU4TP*r5(rbU%_|(N?s)VE>6KGjzvE1dfq4t zyP(6SxXYcv@|qDGc+6_Y(^-W;Z@MxU*GmC*np0^MMjrE5f|#0}qc|+P6F#ZzdwA zE3J9<;jDgz`67zW3?XC^Uik4Ka>1-mrga=teuGX53$Ob6f~k~Ik|)CSUg2TV1-YT6 zqsGJw?bcOX(m7`A7>t@iyA?lm-#&5k!} z=+C3Bov#_qP5X%qJ8o!Ox`zu>PD~cmdXgz4yvs}C0uzl`ag8@giRH)bG`Xo3Q_lI;j5bh(K*gdG#_ln($0?{CE)K>t?W$+LO=PtC)(0^M_R%->R@ zZ^8dWNGUOlzg!myTqN-OBw(dk71Pc>z0t7&YNZ-nb`|OHCt!0;+lQw1my3>L;5(LB zK+|Nv27}BA%Rp!) zHZ+r2+;Y13WmGIyMd%#I$>R-^Oc$d%7=On^>Y4?{+Zdt${Ev_Ce*Woaj39n`_Y)ic ze!?K)BPZErkrE_baP-9i6Ezm0#Ob2JmoF}u@KS&i#Ek1gvWqL;4(=V$D~FX?61uo+ zo_eqRO+GC+nNFt6odbl*DlgH&>*7mJ;dQZ+1OwJ{`$Z-ETxo?=q&?rV7q)j|F;@jO@g(cQrXWz+C%P=bXC|J2#~ zw%U>w7NCMSIpc)_&wf4p%0(m=x^1U?;rPhnj$cB*19)y*75%15TY&50uBY$aaNlyD zNbjP<$(wf1+m{Qz408g?QfA=mw|(>ljf=>2a~i0NnxxCZ75H6bvz74WN?tIsSbfI} zzFQW-VvMd4I&m%!nHRr;?rFd|m6dkWD+?^dJ+sLFOupue=T18#x6O@|LQfr#uJ^#D zv=MRKl`sn)%2h&KbpzA@3u!4vlHeDn$@$&l(+VfNItp#mNqj-?KHB?)71!d3kikkg zcugD$>-?}S9!t{cuXEV3%3b&-4^ckD>^T% z(Tksr!Pk>4xRK7z>Q4X2#6ofi+RzSDFXR4bxQPD*uJ8|vA;0k%&r8I|*tg<-G+dlN zn&wC0|4JHvC9c==VQ3e(UyqlA-*qMV+x1|_u}c3y?sa|GQyhIsHWhDhq>}OA^Z3=W zi9aIvJNl=Qpn|le+<9-ES3=2L>kf~Gtf1wY%;MT$P3AYi1Gs5}LD)R3A0C!$rzE%J z@rZhPOtI32+=hnugzMs=-`&P0TT5Y-OPfQ%eh#_CENUuJXL~5#;4%Q zi>|?aZdmaM^V)R|)AAXfg}40rtb2Y*>n*(Jke8>61TGS|NZ=xYiv+%vz_DlA{dPRP z{oRHt?U3lr(lM>y;@s2PE3Kz=Qux=S$>-Kn3XRN04r!$m6=&n)noj7XX{yYX?Q4CM zBVFg-LAMAthTi~*9RC7YzF{%x7Hy%<#(6|0&k{)_4#S0PZgNvCW1>68MEMFjW1<*{ z`86iLR?0ZZ@zO0{XLDSU4P9)U%7$Xb8q#5S5qz?F;%hDsd@+jU>8!)}wF@`rp4iCy zNZcdF!1;PL8yp=g%R_b^gTrC;b`!i!1s2cAax$>{0;70LYw*fq0`hFYA=0pKE(s*{ z=2p}l3$0ZR;(&|bDm)}Y82((A@X{<$IcCcS1~+-Fn^mf!aBS(O2sdJ~Y5x8_>rHOj zV;7XsH=R8T1FG-|e~~xOmS_CB$j7JAg~kc%Wv-ARuD(9&@T_1G*cfBQW0+;P zo*LleAG(6*+mw=)hqIXVq;p8Nd-3fLi+2$;U4HrLlc2lrjLhZX|3CuAf_WRgP?k!d zr+D55Uoo%a{w6v4v?)mZO|CAVYZ`EaQaJrOX)fEYroC}a40`KzZm(x9+bR6~OubHC z2s=W>zuJMO6kuw7GkAm-CePr7vnB~I%Zp>HGBHE#SZC+`&BRsC$}fq7Ej)*J@h*&s z_s0twGr4Pnq*4IR8;P2Dyh@{#xD41!GGKq7c( z8H}6kcSMr7jsS$&^T z1=h1r4?H2(*E~`q@mr@mo=qc_XX~!bT5izV?(Mhp2HR!;GyuZM`*S9YF)o324YMQ- zze8`eGp>zyF4491Yx{Cp%6=x`ZAZ5@i8%ZjtlN}}al<2A*V-K~1SfHsx5|U_E0|;I zm3CT@@E=;jYw+ycLSDr@bDZ69*Zo(_slG68(jXcBzzV4FO(R33%%g5_WR3+q1C|(y zC`*`d4koDdG~#}Le|qi4Mmi*D@n?Lcp^daPa5di= z!9U_!1n%*=%NUJ*7sKzTkET%`j=PjKdnWiA`rR$YhZv)1WTyp(hRM%#gc|mrFisZ# z-7PpUj6C6HzZr2F+sgkt4EL%7lxg0jIPl|k9m0ItzjM2maE1J?Fs@Y(bD@)vOJ36V zGH$FaH9FVeoV-{*>-ITDPUa7JoT4wRTRi$DYz+7qqr68~ocqV9HwOneZ`V-C{@NlX zj}TWzzAK7xHh49DwcIrHB$w=MficD-*?U3?jhP%5q&}KO@nH;;dL=?|$N1gDeB{Hg zX7k?4`@Qe3a)`1r?^k|h!g}N0y|0WtveyQg=D=jePW=ixd{AdvPZ^`Jm&JTrPh$KB zt<)ckd0g`W8}RqI>Yrg9k5yXMIdaAr%y(f5L&J?5WP5$f7>zQqmRMgvAAbyE8S{O$ zQc|93j{iBNjym9eNWWa{K}C)cZ$BV+)&}w=bdada%J&hyu}(?Tda3SQGbYrau?Hv0 ztM$(Pn6Tg&3_dKc%p>FrUHVGdILCOWG552E-5Q>8qhHGPHe)psLD%-Bdy1$N~n&`1`>^I7n?8&ojsBpHI>QM~cX?J|z z_{%>(z5C}se__n}>D{LsoCFW@EbkhMW?_l?X_?6cLbbl;fD6}L;L!swQr}fNXz;)4 z*=lcjHl1@Y)A>bHbu3>&m%r#nWr%pH|LQE_@zM}l9rVX9w)R?dBxXUU4D3I;_tmns zj4B&=&%6%)C_Cj8LrnDIhW6wZ9`0x}zjElMb=)z&{g6h6M z%RyO#;K_D5M(EIuzQL#EClN?lHuBb+G>wVQ^AUSFSLn!uIzfJI04pEcMDoG2ABXat zv3d=MV5l*A7u96AZMoS#k*^p&qxTx2_a=V*DgW|?6S0sN^k`el)aa0P;*aie{`qFj z?S7)}{>;3O_Q`#89*|+(Q2#CGD2uXJuA>`~0K9UMEw8AHa$C0%dA}WQxYwYTDU{jvzJUK;9Glo#&lmKNyJ4PR+L3j@>L2sy! z)-%f!L}@E+JH69R7-k)#d?G>eX5W7T{{tW4`NG%5t+&7a@(XQ(eTTH5PuptypuQ{r z2lf#>P~RUg0{@}`x(81%Pj${}bAw`2EaVy2ue8L| zdk>Nz{~jh`8-ZS=9RsdLedZGuBNOEgKHDHM*{67?-4?HH75brm8c#n4v)p7buJsq4 zPWjqC(EbuFHoK}Y?3D0VS#E?^wK8EYN(X# z;5-b#W-q!cKK^n@D(!;poK5t#hTJP3SU8d%-6j9#Eiy3w-nJ$78!0PvQilA2XzC(# zY&UIpQ0w$@%B}6ZF1*U1H2AYlEAzaYsD?tuAeyQI8H5c zbIl(Du#P63aPat*HUZAswh=FkWp00F`|Lpn))Vpf9Rz+d1!Z#yJqU@LjNMRoAJJpm zcZTaWT9!&=49db0m%`JvhKeirz(L;9N2Fc1osbtqsV-VaxNX~MHiOp!^)F-0)M@kY zSXP}==ExA4wVzV5VM2EO4<9L?36~>q&K#(Fdq@*r2~Q&P17AL@@T{>>9!f=!{a1Y% zs?HaXA%86m$?wpMqtceQb*Z1di)XOKUHk+OaMIn{;GpKmO0e|MHobZX7nJ`s^ho9qXc}OwYmpi3kdDR^M$~0=I?^z3rHxFB zzvXWE10_GSp|T_nv!(p=4oBs({uE8(f=!^vp*mp0tK3q{f~N*LjW@NDnq7=?uQn_lx(hr8~^Bsk-2HvuqLUo(AfXlx@?unYGMojAB+@7{n|2ZCb zSIGYrm*t?IPpvakODBFbe<{+p3{$r5U{>5$owT(92yu!XJ`k_#SlC4aUkx*&-s&g zPoKmHJ&Tci4PC=?UcmnY1Bjp4@b{0OF^KqyMKu>7xO^${z=TU2E{wQfEL~q@=>NjR z%~RktM0X*~#d#-mnb^~rZ*oxZnS_zM#k+2<#OV`8N7<}6@{<8NS*y`S7kgvCsW?pQ zq#_F~%0N5-GYjcL5Tj60C48I^DFxDtLZI-oc$C*hBvODu5r2PHS&3-_+dKgYS$+&X z>MW8O6!?}uA(P%PH32sr*U*xWBYe^Pp5IO0zX$jN^SdPQSIYFS#PufgRrd4fs8jVV zS{`Lp{dYY(>YRFy*R$aInk&+GEscM)#$v&=;OcPgGDS}*$A!$3>T7bNO#Na zm&hNu@AUHq;DJfvEiZ#$D0S5rVopqLQj~=oqI+VoFBipRiYO9D~}88xn?a z8=L-asE4 z@jZTxTasA?@;#0U{rz9-$eW^mnKZ;oc-M!QK(9Ton%v3`z%Xz9pyBcPfcrHJf6K6~ zzm|XVWIo%%?=mHvHXsr(k3Jf?{LPmwuwe#ri)xcqTKpyySdi_SheeaZEN&5SEhk?5 zgFYNg(f}FBz2M6YeJ{is6&di%bF)T%cJ9ohxMh2ejS~cy1r~Ch6Y1nD=;E|Sd^w6BZi+hp4MFJNITqJOjzz<5muIsI9MlRDI9pS!7M}R$Gul9xd zJG#JQRQlsMMlqzDzHjwsJ_EOUzx0LazwH+j+89x_ucv<&kHZ7U8iS5UBaF+KY>jd3 zG{fV>nLr@!;7JFoqLugwhCo{~Edn^e`^+P*iRW#@=H*<`+ra z+|;EQaJajO$$2+<;a$9+*zEquSL7ZzEHz(`!y5hZ#HKmI_yEkiE0$*--OR=y&99m{ z22c)R6W3|-@owHG&jL3;idndE&$63`<~8tJBw@enS_mW+=1gRm0W#|#ZMibR6&=Cv zxJEf!=0_uRMuxzwm+h=!WwRU`&{$St(;l0ZvtiE-er`VIphGuPE;_rbj+yDYYEB*( z2U($E6Nnr9Pssd{4b-21VGRs^*jM`Q!Hs<+a7^aAf;m{WV^hnJF=W=ig0R_LxFZVu z7PI5+<`0Svwna$32d2tgStJU9D3ffsLI&!oWb);6H>$bGj*V(D#OI)bJBnQ2{&)%>qaV%+(0CJ z`b3arX^aH*&)N-a#6NC8%(T$?HPaig4u=+D2vxtdJam)C1yFJXS)b1{Ybiv)`X zj{4)3d@g?~R)Z5W@n$2rQ2^_5U99q%kpBjE@~69Gb(XE~g%9AQ`!l%2@sify8~oCn zFx-HrG>4Jbv#`gWr#}mS>!Gx*E1|r|*D-MCP3^0WEf=7^GoV$=rq<4}(|@GCxK^%# z7Mo1zE8G+)UX6t2OJo`$^O$*X+a2FQ&R^l{r~#s=Xd|Yu;&Y3i_=)ljoY{I z{Q+fkqp{)E~xzAT?e%d%&ErUxdtHdLLBQJE0HX?&vrMbbmh zae{qp-l>6WD~Hr6HHq6IP`=z=!tkSy;6yeiX@mjx0AiY6#KEKa$i4bo$D+}}#0fLc zlTKHJM1Z(_3^zLrvAH__-NVE0#4R_huKetf7vDt!7YUq6pw)W?S^wG&*xsf-CPpA{ zYoPrgu5EPUXs{A)+}uV81^};lC6fv zW0+L~lf`^U+3wIj(&l7;5%1&e-!+Wv=4RSwp8Ybm24EUP_seQ!wfQ^XgGJlg7}|o% zx#u1Gr%ty2X-ApA(pJfb#E74Y6y4?z+1f z(U5bs_oSVx>jL&k@J_=YW9Q6ynV*qw4SVE1Ul_(f_x^4fkwPbihzw`kK%cdK=F2gh z4s8v@Jk-rEve5^c5Bh{Bzd)`5xL@$2Pq7>vdtC9p;45LzC-zccta;*A=I+#g;(n$- z%OPBDE>D@;_~YlcACP|LOZJzPLHLkI;_Op2_Pf=1Jcq1U2HuO0Y)8 zBo<{3pMi!48{|EWy2uzJ2k;?K4ZdQbg)B6Xvpk|t;8pI|@T?J!27+$#7pZ!t0hnKx z%=pQ7S3kLbkS`6d1C(7q$RT#1luy49o?$fkh(RX%1@l$iWrXg2KI@@|h3$xDRQVS7?}D-#yNk>|rozZPUJDkoxRboRPI(LjIL8>c22T z|JT1h<%{SZj$z$2Z&z3DSz_Jfio=pP9KyP44w46ODkrqcz~npioVt~C2E z@Hez6>eij>Qy|Yx*~`17V3~=rc%`w@DmU}SEeFjJko|{_>4kwiWF#Im*IE(Ya&SjV znLdd+Vx73d_*{K|{D3Zgu^M6KOZ((rEcvT(v4-faham!Ca!5H*A6Or=%xt?1a}Cq6 zI|RZ5szIC?LwERdES_~Scxpai8phj}v~6}S5gR4*#X1|UZrVE)e7+*@mvYAd{rTCt z#!b8I*VkRMixDJD=RgbynJ@j8jqRTO8Rd?4M(11)yIJ+sw&Pjd ziR&Thw5#wQ!&ll>>#`+hpG0#}BlNG!2;I64jAdduSN^T@@?udba}NaA4h3K>^Zf@6 z(J^-Z=;7`fp?|CaI&-ib6kuCtV)2>}yy`(*{e=0mxkO|RfnM5onST;D?b`!w~j&PH0UF1EUtd4UvLp-NjExf{}lst^nJ;@{UxBmt4!s?a;6;vuXU~Ce#@Ucd$&=P zC5q9^lP+?SE@gwx*cUp6?*WpCiTcyA2WvH?vwh9j9Q?N7>1TKkl5w43wsYh?eLS)y z`MYcSJ2OV|)UR2`Lo-^oG6HPgrNBpcQ5TSh{Fv6_vVU~U?V%!$nKC|%0#O#knW6Mg z@@RdZ8@+S8DAP*L%?Zb96F7d#RltaX0{^+Oes}&wNhHuMS{ZovYE24K& z3{rjSLjK?ZzqF*84=P&DD09n1`N1hSoDiPK0EZ-?VwIDWgITiC<+e|@epMG8UsRC7 zk&~4Yhijh7zfTQZzT~ZerMLTwbDjggC7RIXN;iV-voha7mrrzh&(IQZ48w2QWY~Oz z)975LQ-}GpEx&SLO~~)2Um2;rzx1 ziRa|gz5$5LVXV6ui!oO5fHcNmX-|NV@43cD34)JWNM4%niUaJSt&UOkBU^d#fEQ_* z7Y|V>gJSbu*UDZ>VCB^yRWUBG0TE$5O(zEV(M_@yEuQ0D>ZWPv?vlX7hbw#%)-{lc z5U8+`Z!^}cqzb^4?TTfZhTq2D_td(rGp*k;uN?RdU7rW{s!QQrD6XY1sY41Q*SPEP z2oF+os7~0X7xxYqk_$d@8xM=#4!Y7pD}p`J9DlJ*lARXxU(hZj`QGxdr}~fcnVA;e ze;n&?F|5x2sVntT94~cZuo}nsYaTD|MFJNI{9Xx6#llTRJEpV3O{J^b#@{qv$M}Zq zzmv|8fybLh`n9-$F>d^IkH2E`vfn6{!rM*nSWPic0&?pRv<+x@h#$BNWQfmThJH4a zFD5E^mR4YlFU)j)PMoFVf)*n#{C##Z*To4oR55|eOL!MlkA5aIzN|6MQze}=dV8Jd z06g_9eIxiYac4m@r}E}2=qwbtNWcq;2Nz78JjsHKiHwk%PdPK+;^}REiW4HrV?CVp zo65)>M^?>N2KM}PF+ltaZ`ob_m6OB{+Oi-GmJl&Rxkw|?b!BiDe{qG?Oi~JC>R!9g z=lM#ytZy;nvE$tQ0vAJn=V9E};tV5yD+*~Ho{sPr|3w0SPy(v=C>Y+1^;XzcH~%2& zA4)cSq#_*{3KApd!awy2*u)q_v=ecgg0?d}PXJ#vu zHhqfAsi8EmcdTG&jQ+%8-V;z+e09-` z7kc-;)WaCvwp%gCm-t+viM+(J@CTv(3B$E*ju`W+7;JY5$t^Pdb@UkLa$n%%-}i;AAkiq3FIL-#4Rsp zS0s`UVHSt+{nk(63sp|PO>qXZkv`{Mm>sS_K5yU)?G9r=V_TvxAyBn7dhHccFmvOJvqe=5J?yYbM{8hMK;_-Rt>Z^a+$Shi| zf%XnHeCxK&-&#D|!fjwaaPmLbV0k0X=tSw68{d+%bC_M^XjPtzvn}$_5lO=*#K+0s z9yKAQ)wz)=c-~Mnu6W2P;kN&sca7TxX~R|q!t?Omw5IF5t3~r8-6xGtBn1rJa9`$S zp5-PF=UTq7*7c=Y=U(%2O}F!;b3W3KLsYzT&wNVW`W1A?6l_%EWwjeaJS@zE!W?HP z2j@k3u?C)uDKa0k#G9G(JeeQwzSMA=f#s6LeO}C*xLv=^i$6wU;`PdSMf`_5f-n+& zQY>>RVFekw;bCHa)IDOYA5MQ1$p!u*fr|t#61YgtmFMD76WtTa#xzWuTKD&~VvEJf?AVV8vn)(x) z2eFB>c;#vP3!^@T1p&jm_>JlYBVHZ*bq&^=W03ARuttgHQ{;q49In0Hu%>q-eK)PRp@C#sV_WY~o~2_S^P+a`Z<1S)z$! zyC3;C&~`3;L9i0GVBO57zjdWDI3v8U2o!vK($vlL!M4qK+Afk`p3_g;);60YUj&u8 zyhtQ|G3%Mj7@m(orQ9HK3K~6Xnmi-F7@^x{G&nT6M&ZW&w&5NwqbxM;uw1htdV9y+ zwU=zNs?pY6#HH)LhO7Lc1q#A^{^0PnpeYrRN{TkMorn=nxjhsoDyxYg;LcZx^ zbQ8CJCqWnx7kI*-JeJS8;s+@3@CKTWV<`_QrsccHNUn+WoL!6LSy;eVi2jhv z^FU9?5%LVh<>4ZMiv*OwRGCPF`Z85#YXer>kO(6Oz_!LXp0hz(1lBkE2M#*LF7YE&lkG6=9h( zg-a$J>-<;t0TaFnlij9XH6rIEwD zm``b-7Xy9zQTg-vk#LPOPx7=30#rZ6M7BCNgHF}~7Cj3j#`B@29uThs!!bao96Vs4 z#+0rnROc*D$3wNGBmwIaN;-cU>cPFT${ZYPl)V4|KmbWZK~x97{jK{ou++^Li_I^g zfxlTF4X{sFXVXt2FUwm!RA1+SgcG`|tgp#X^b39*hx8o~WrY!G#vcf{#`EP_)m0t+ zh)Z<=Br2o->IL%gP=pL+BtV?-j+Z>J&aa@y5S@bpKX8!Gd%iL}2Dl1%4!*IJ)UoIn zyyxJ#=r?7At{Vq<^5_^_+9?C`1VQV#yS^38$+vK;dUJ6S7C*Lru4<5tkG*km$HstL)Y+U^kiggJD4#KF20rn%GYrvJcB!T4#;zyiTp4Wjgc>7 zMJ*pa+`@KGJg)P6d7usCi^HdSI7HXEO@*cS_-AXudDckS{Zt#VQg)eW)n!<2Tn7nJe_yk?$ zChp87)O`Tp0?x9o8D_ldfzLP`p(!5h|sfUkG^e4Dr4_*1a zLvHGu`7({|hI*WL!{ExnkZ7)lMVyeYyxHeXn_`=4`IuO(%zPF37^YsDG;NXc0Y?lv z={xNg)3?!{OV@g$tZFdT^0Q95zxRszs&E0bc=7WCt{90TmuC-1SZV#}Rlc(9nyWRB z{88l5k!SQajr*gLL+Y1F7LeIAqR~? zyYJR(gq=AaO0PVnt0c@s+BrQuy7rhpRkZdAi67llE(8!)dE)2lIE3~A8G7K2;|vdx zzq|JU6YwB=>8ZPxhlhbER}ZYHF|BR3Wr?hmCHYb9rKN64&no+bJTyLcUHkD1`i4IT zK8T+`^)809oGAMKp0?PV_V_)#+}|>Op^ecfUR|zHn`4U``g6WC{t21>qMnf-Zi(Le zb#mGc_46Tnz3E5X=Y4i-46km{4lYAZItx|xp>f#IFSxaQ(nLY7P-5w8yJGDr`8?i)V{%b)IQ2~*L4OD+;Lo_ zZdp2QhvWx8?SZa;60wZq4`gXZXoTwmDkQhK_P+^j+QqC;;-oCnj(~$6KtzDsR#Z={ z_jer9aL?`GzW3{vcXgAT(WaHQhr!q$ex+X9F4zx&CT(G8@OVQxWWOnWhHXjepl&{# zWc{-*HsA779F}7a0VcgV4&vl)4A3iII64AfygqYAO#h`dMS44t4G}Y*e-4FHxp|f(Fw-{v}svSGrbIP zMb5ybgjd)et`7=fi63Qe5LbO9*wRc)<6ryTKwpbIhKUMb=|&B7ji0cMC;1R&_&QQh zPU=Q~GM3o5FWx|JKgzQC%3SscE2h8!z!m*pVeud0$}=@Z#=V@&Q7D_-O$Q<2a`RA~ zHV@@sCuX_87fDjmB-r(zkf{f_>fm#p4X2%jv>bH!Ee@Z&`IWYw{D_%w{Kh6|!3YxK z_2f7F#Md8r^DTE8pk3Go%hc8ZYHUj{3bAiNl$i5mk8__kl!-N^4?AyV{kJA&*s1s;&ZUs z3Dyi)9i(;==K={6LkH=kPd-+sn?*MV+Dyp&Ld;0jiT#)3A^<1i`C>ea0(l|fNp%|W zOOE`3G!t*J=x>a_>!IEdnfL}Ze(1tSM=BE;xA=p!s0rN%1%t1Hkqw|ZTI`5_GC8Qj z%sk+N2O+);Jpg>;4PW1c?g203w?Xp`a`F8SNMO_4ddTq#QP=UNK4oye{K^DfRDV*h zGs+I&@0ONnH~kAaene(?YC{4Wws9nS(^(6MpIMe^%1G*6+ANb3o)^p}BT4idxVDEy z?exQ!0>Zjz#X3w4&~<4W#~A$y{}YQ(&zyF7R!>Oh3dhwA3#+^gy5UXg6<^O4bM>r!kox)*7mzEOb{RGimC8!V|e+&eQQnJiU&%2{|S|COGwlOxIx8e+!`PbrK3lrb>!e7JnJsS3CFNAk^ETz4L zlX|ZM(984zFrGXA1H0NXH=R(Yom=ifq>Px7cWbVdv`cF41JJ&MddA}!J`QUfy zf1l~^45N?p#f2|6oricT{4FnF`-0As@;w3FK2Q2`mGBO8Azn{owb$~xj}|B>-kHtTh~ z5;42@D~rITAD8doTyAn{2KWakq!K1sp8N+s{w0q@hpxWmIVKE%_&1=%XChXt^z`m9 zLnGsa9rWV6NZ=xYiv%tbxJcj!C1BrTCxg4XnT`eaKIi)gSC5nqNx!&Wc60~a>5N06 zXb+%LoH0`SZO0?MGk&q}Wjv8}2^UlBb8))ikc~a8cWfK=j03l~NN2`h(XF(VhU z%^!Sqv#jF<=lzap8Xlg^0xxqt<4osz2piro64!X#O>9?gw8l`% zO&J~#2$PI8%A8}FZtQcs)%!8MWkk4$W@+%%sY|^^ne#*;iH)-^edStZZ zYv^8rOAw3qg!@B2l15+!MOuFG^S>uHB)Af{`~h6Fg6cpm@XCrC5%wa#mAqJ_s|>(P z21eF#v)0n}mdM{S5Q8!wSe}cjOI7Fy2-u{i-mrAX+8OI;j&nsVU)CEpwyBTO z23~!0fEi;J-+#p5!VT5P-;I&HPpR<*_}mPKp~fS(3^>+j@sQ20g!@N1xh~0nHitzu zk~8(%9GWjP>66d#p1GB=L1tXRC^P*7+jN02zW>4T+7HpMUJXCQF7F`(fhp^<>H@hImNpSZanm_e9Nzw zGRb<1Wy)=SO7ABqUOXn_&b{;)xUp&;EiuNw<2nkdhF<+;Jck{gb;n;o({T^r z#QJ;=Sn$j5MFJNI>=KwhKOuYVN*iI-vJz20Tfw$U#v=_GY1^W_tbVQA*6+6AwgK&P zbthcx_OZ5AJ$#)uAA>X7GRNk2ul7?l8r8rC1I&E+S7Y?|F%U~1;5^p`fWE{ryKOHg zLt#j$F*663(H@!wjZJ*k07`=>`wFiZ2Li)%GVQCmvCXnyNIKyBdY%t4vNMo>rQy6m zBV)ReG((;5YSc-aPrLu?uYC2BLrA zDee%2aAny(Dh3zGRQSB(KwcJU428*~d?-VCKQT|V-}?2lJbili=`)9;APe)UECh4D zcVdr(2lM!@;pPVUQ5I%dxEM<_mVzW;hG?7> z;~Vf=<{fV^o_OQ{;$`sX*wruFYM3Ff8jm8Oz-PQ;LV`1Pf_G&+b63mKcUtO<_0B_- zb7-9hJZSv6jKl2LGiH)ckU8JZm)MoPw+4f`9ZPqNZN15Oj5NwjI;ro<5ZzLbrI~kT z8UV)60TUQle*D;v;G~XFcA0;pTh?o|JG_WPLp6lo+f0~O9j~|$Pq0kjH9EI-Rod>U zL0U1c!>|s6*qg{4eFk2`Vvs|h_k7Ih9UD6)GhZZ+KVvlHW48DOFWE&Om2u@u6v(Wr zHHxCV9OtP@t|w3)CqCLrGlknY4=M66geSfsStV7AOxXGll)K!0=K=D}+gdKl1M67bP*^LgV}j>%WUR(r|#)vrwd^wS6ApphWY7@(i1 zyD`WGcgCgQQZ{1E90R;Dt|f1e6ygK-iG5@0xjf%y-igeRvH6v*`lbx;@9ej_J|~al zChJ4?1-6H_of@S-@g+=3CH(4I&#pOGC4Ck66sH`uOq*Zl!J%`H0EoQ>m-SEO&-yH~ z^NXT8%|LAIZ(uA z)IKPOjlyb;PpNy%dBl7F5yOAJn0V}y=;KCqT~&*1>K)G6znk}^K^ z2hbdlkab|#K|j()+rB`Lf3i$I6%)LeCdvY>tnH;eA&l_qLxq{PiLz^Z$YC7N|M2lW z{WY@ZPac&y;z?T|SC)gyXrJJ`NTXNl$^T>TOcdgY#m6sch zaB27V^5LGZzkhlB^!5HbM#kTe9puga897ZWp?`UK1A1H^&c;g1YWrd#|7!K|PZ+hkzHfijLpYIE zahQa5uMD~0Q3G|y4>49p=5z^yXY$ZBe&_N7Q@4G0X}m_*vYvx5xMy-Z7{~I{9;;_9 zt8G3KEwR00)N9-1wyLcbZreona=+ z{QN0npv_g+^y^m8xy-j0I1>iFhAZDmt*Zp%f^VBQ8>fK6r@RXRQRd;)@$FZP(Esu2 z)BP{#FAlYvgPN|dI79+GwgKDetR?7%e)TFmP_JgJi~L1bA-~mC9>BoC2k^wh81DEY zz4ER8kZSdI#=Fsj^i%YGHe-$6%Px5iPKsmQc}TIw{TiV6a0&F4eQ?$hkO9XVQ*-3zvgkMiSqp8HPhshPq{40DL3l*LBH)ocY1Q2%FvL{)HW^HOX{u$1+CVkO9l)t?b%gsVBuC{Zf#zwPUmBQS=zHl5sfM z+OA(6PgrKaU*VDXZJX(b9B-g+GRAkzp&UX?b%XOgZpT-Nqb?Lu@K|^0S8W#r<`9eM z5#z0MGdF+rvmSFDKH#PftcS{!Wi2mB-=wqdE|1v*ea zbB}4y;Ti`Gp#vTZz|RqV=p#gaS?*)xDz+r&8ra~QvROjQnd3Kd5BRyY|Q45hjG!=3;n~Q zy>S5$WV^Qgi-y794NiV@l~3{ZM}Xi=fXQV4;dSEcn*&Y3iyJHLqO2I zm!Ao|j(Id>$NgU1!2GN3d3oN$7vdrDkHEf{{t{-3xLg+wTsZJk4xnIE;8EP0I(;XF zDqUr?-#(9;teRSJ3s(m#3l+-0Fg}wi50kp8c*jwgJw0&!U&zKerYE)eCaB>ljiZuW z6Skg#&5P9xN*tKbAre+99c7j8XyYKjglI>2xOx3FZ8 zQJy*A@Jbxsx*}TPXLXXyV{}dlC7maZzMz9W3AaY*P8=E10TaBSHs9zi*L*nD`!lB(h9(!BOh*Q{ zD8udG5Tb4K>ST3p7o4g;zx&eHg;v9vxVj*R_LjOuCsWqY#R4^%-k`Uyxjk)HtmdVm zw=ej7angmE(&_@0UqgT8C8w}1;y9@dc5qsec>#s;?-x<&e_YsNRWA!aPUa|+aPr>E zNt}dbaff`f=%&&SQ(f5O;1c?wYbJ=_J^hr$wJh?uKtuX2kWRlRMsc^_GtcUG=}JvE zUNG?k8!Rb+G$bN;C9i$S0yU~7-JF9bu|`|8VkU@(@ozqPl){$*E&xrRwjqfp6vWq( z7Q>!r(}(0HJoz2kPXZS;$21Y;q6+G!D}{U7NwEL}G>Hl&9;bwG<8~F2Z!IHsSx>D`~{5}FS@N)fkaGbhIgGA+TSpr+&Qg>&$KOdi+9tSH2ErBg zSg-nr0@3rdJ?i&?+l2Vln1wHpFEcy~)_yt{Fs5+;s3c_ziQ+;ET@ok9ldpiz4jK z!RKZV4bc1MOney=@PhS${x8q;<-Tl@;QSHpoJ~57x6kd|&G}n5uQ?}s`0j=yU)HV{ zv*2-D&`r|qhe-}D#}Bx4vA$gV!OIJ2ED!U$)kEsT@`b0;VIS?1t(Wx3cb?>-A&wWM zS~#JkTTrf*10F;AR9L}wSaAj-Al9#`jpu(N4KYowaSa>z6@{Z*1>qia{&7&4Nv6u(|&zAsP>la&-~p|h{osOZs^{x6~3YE zt2;mMIK-FVFKlKo%xQI2Px1KD)zTZFIaVP61&hC zKjTgL?zl5+LGpgqgn$)yH;3{r!SStM%HjU<diPhI1PG`Ch_tDC<&;yFm+osUPdkU1i|Uz41c` z$Eh8&W>Z|`aFuI;r;MQwTnZKG5rOc-4WWLCmf;s0nE=97x?$XlWY}hgHND&G|flh#%^(o z)Za@phQF(Ts~px>%X}WLd^QgYQNs2-3|K@O9YAmmK$*CO@AMvV6~|VNWI=tsDSOi# z^DyOh8T~<;H%!_R)pL$VG>aoeYD3|YN7EKkH1nLBHWvM)zTocrQ|IB%?bSK$7nD=u zlP-FUc8lVQe#O=A=I!WM6o~q*o2YF|(6AV_(VQ^pBVGDiHxL@jz3Rg!Ax_(fAzrEZ z1KcHDu)bY{iI*zvp;ygb>q5y#}T)vEP;IF2F`UCzX}^8LB1yEdvxVOIO|fG z^0L46+0?!p(ZI$p%ehJM$9Ij;d0+C4Fu&fKP5ruVNap6fdJT!h18sZ(&<*-AKxdp3 zn#>Q}=9WI$ymK$s$TnbP2cEP;;Z3m_pf5x8Z2qQ0$|1)zSn&v3_;8hSn6Kp_M_him zm)zm^FJVb2Esn>!xw{5jv^zEl#|WKqAFPH~=d^YVvF~9JKVjTc=E;W4LZ9#5w((af zFe+g2te=)>JxJ>DI)6UKUHlggTsQy+Y$Vg}Wyospx@q&(F?qj(zA@0?C}LpL9MOge zBt9@`jkN8yap!>PSJ!+p2AOWg&-_80>Wse6*5DFtf*wKVyYn(*^)U{jufkaL4x=r< z44ki&`gKP8EBkrCGiGO8{zIcEHjO_z|HuuMWM!WA^ZoKKM(VzxNAC&aI==G+$Mzi~ zpyM^1LXSUUh^=A6^lRclfu+2Th16B*H+d0*E7tAZPo?4cufKl#^v{2O`}FHS`AX@p z8lq$LjZvEhHGa9!NrTkKkQij*$XrUjpRvq(e@_{mUwJt;kcP~~Gbdvnl5`sQ6y8q4 zy$KH(nyY6u+SZWJe(f7Z=-t>D#h@pvL&}&OKzht?Ez? z&1Uah%YF_1(m&^L61#Ru^P-&gQSPJoLOuTQa9#~|*>e#dFpn}%@oEV7j1|v!jOpf! zq%rK2Ld&g@&vy;$$RAm;&+>MSqVs3UKv0a+C{GOGq#c|&7=|+B>+1B$`64Zo$n3KV z9^UQP9m;P}Qzsc`L#A^)*CRBP%-1_H@=Cv({*yF{h53CkPj1R%9mFVx{JW>aeh<7} z7G0h(KKPETP(0{Ep6yf3EBRR~mZxheG5FJv#(jZi2#%S*cKoZH>|>trlRSE`n&YI> zxQ(eOr*-1Hu^3}Be>Xki2Y%9#hh@sUDc@@`-tl8Lc{c^!*0*!x$c@ziLHPrL#ftS?UM$FZ1Sy*E!kq?0y@tYrPddZtdio$E_y^V6mM7*3gZ zIK6!|IJY!(&hNXzYYnVvH?EfmaHY{V`8_hfzsBG)Z2(>5*v~bB=qq^Sp%quiptAA< z-K89RXYP>p0IyST$TsU$)*WAS2v0K?fxjsu25YX9z4%4#)U5{1DQ?9Md}=|=Nh0g;7!?;S?iv13=P(QjE$8c+Ew#|uktqX$$W<7!p9Kn+ONaww>{}P zzGHsCVu?C;))TE)$G;vdsZrGDC*&7nI~zgQnXGgDeO8986KRY;bp#IN`1%>%vqsov zB!6hf)N|%{z&XBDKJQuQdd*=JtQV#(ta-BZPgylT3S+*;T+s)__>y|;VFKOGFP!6H zQ^rRR#`voK5f4->`M18rrS8ii7vUwcs!ri>h!~-RBky39C3LMkg^Hyw zT#v=Dn%gg1dr0JpjUI#q^aS`ZAXnbgo~UwZ9bUUPO?>`Jn`KbGEVK1&oJQhQQw*8I z6KJa3%3n%}GdCzwCf8)a&^Cn#yM}wm!5?3qFyI7tFjJn6^?A~u&T^p_t~3l)m&6z&~Ssii^%pUj7HN} zuDL0j(B_=XawT)U=u8dvARui)eP=(@12}XO6&?Uc-B)(ZTr`n2X#GOoUok*`_=UVE zqwo0DbtxwWrC78DctBgrS|V*kgIDX;^;mU;e6bzDh1Am{cy>MLz5w`v(YSr8*e;9F$PC(Z*3o*n(wS{rax9NoSVaw7@^|NvsGbT&|hBB4xVVYZ4WKqjPvACl7z0x zXWI|$k@1IV^3`;82lWT1kf|s5c>C$`=IPVj;|)ByWenv3ILH=%meI7gw82}-eP#QS zn&e^mTbDj(oMx>313b=G+ugIl?Li@VM?qTKMfO$Zg92Rxxx)bcp8NTpGQbzx)U)X# zucU#W8E=v{`zL@9#%tCp;nv}5`xU`4+%owWp|?RMR##keQv>QX!P2j(pWUO=?rB3FMrS)z=CfWtvWjcDBfIF;+go)Q;|bd{88{Z!2)+Ae z)cd|$K(=|mFtRP}eK%p>=0Qe$sEGb+Uyo18LU-08P`x!k$7r7Zh za6Je{Lv$FNGOGh)T&#}CVF}0py8OvQ1&|r_OWFW(P3!se^tyC0;|N*%!h0t7?@(3E zhr<{=yjVkiFKcf7=h;N%cV#GTRWY(}wQ4Xzoy+I0&Dh3~VFRcr+kj)fCk|%$&Y^^K zr(}Vw(*ABZIKqbE0Vn-pyYgjSGG<4fH9+^U6b}lCUPI4HpLb+MzgIJd0O?XN#ll|{ zJ7LNTG9q^MvC1w%ro|I^(*NcHw*QH#ToaJSOxcm);1nnriy&dj=?c}SKVUl0n=z^~ zM>!*R%eCwRhbG*-gK=!8A$s8Dg-B?ROUDW8JVw5oRyj$ZZ2wHSP zAKiVPAg+$-q@z4k=Vc57BW?F-`|@`2QeMO0-mZxO?-+3#CsN;Ap9>UH!A+j%VM?*P z>ZiyS9HcJQE8cc#Yz${8XICWXk5;L3#DcI6dXv!wfGS$Y^9mx}l!> z@a|>$>X~^iPkb`Gq5U<@Nx(7k#J}S|22)P*EzY5%@!=_`m8It4!{lX|CY}DKiM#x! z--?vzNF}gvU%5TNSuTqKra}_gc%K(vkeR4_ka~c?i{lR}<^}!pLceZ55Smvz%P<@y zZ8L+$Ow48d;h%S~8Mm#zs(n^zk)Wv`YHg)Q29y&cxr9_4bHz=_M*505IK!K9iAuU1 z$=-K0pXiVP0h3}BRz9U}&GPIDgaD;5yq~g1DXq9|Z8xCex16>kZrTo-l04kEfIsUG zy4ACjFRpg(QFcye2pa(dTRT{ubAlA};#oMLCmu7Y8!&%ld{3I+ojP83gJfzX?jgiOMBY0y?#f zD`U8t&iDe&q;4xO!5MgofWby%av)KPu8`CPR$DCRy!on&IC5jBR_IBrf53>T^_JQ-ju6^^6+Rtjq{q$3tIK@r1JS zg+*V6RJ$f$=_I*v(Pz929fR~P8Mt)J%ONI;w=A~C2pxE7O+GGcxZtsLZ@z}kspAi< z!acGG^2ovn?=e2{eknBhWpvgRz{g^oH$+|ocjJOCi!JIsC#YkH?n12#Duxl+Nf>Y% zu3K&wsf_?ceWf5mBH0obrhG+6@A*5=pu!J{Gx}nJlF^F@!SaZXo$DD+! z7G^DjG8hk+oZm@9ZaEKSq1VY@{zDg>mEzKBzGl9D<2~`OgpF9glcMfP)I79EE7KLY zI&o9&Gz_3HdtR08vp67*Ia`L`yOgE(FDaO72a;b2PdhOO;`ZoC0ACvz2oem)j8g<$ zDE2hVC+wbg;fx9nB8KL_gzJ!p-pS`9ToXR@5Bx%NasCHz;Lp^Fuzv>L;{93pzs{0l zP9t&eQ7~~wc;WtP_}`I-IJ}@qvLHojqFxQ&Xftrpc}~7y3F8Nlg=VGQpAr)|6P#Yr zDJv-XpGLoZS_HmD*M6c}&>psbr3z?Yrfh{X^~4KHoX>~&RpNvf2b$6O6EDzx0qa*& z++giX;l4oYNzTrFQ>4Pt*4gra)tR@UKZ2m zLTd60JBAUmFuC9c_u?Ty`3vyy(Eb=dW$`5zTx;4ONDmVn;>k;)#2Ps{n7&+JStf1` zE9zgch9zqmeoSTAi5rYNp7(r*+$KTN&I4&gf4MFkxNzXYfeQyN9QZppaOhKxTu){8%zpAY?a=IM-uy2;8hk$}$WaoBtpBVgPZm+{58 zFMhcq8{!;;xQ0-eS!>Q%gfR1R^5{4#e_0FY+Qw|^m@gV-e(C9saN}HB=!OHoUPQsjqnm*yKV5kj zz{DpY&xUg?+O~AkD<5+Uu;A*I{9Yz5>BVDs5awqAEhlLV=ROgR6&5i4gS_bsDNdu{ zTLKK?PrA7sdpah}%{tKjEH8jf)svQSTQ?4Cfa!)!z8u8nFtRnqhe5KBg(rLqZa$`k^w;kbBRb4pnl1mCbJ_u5UVZcc@s&yL$IFpE^# zbVHhKfRi@n1^Jp5a{~_mR_<=@31dUtH5-pDw;N#PX*QC>v)TCCFXzEKH^Sw%%x(;F zoKIeUO`Wn^RQ6DC!#5YoxYJgZyNI%UwenV3c*vPKRmaNyuJ)!+bsoH_nK4>3?kF1(W__S-H0^ocdpPkFc!oBWqst#XRpdr`Y)P| zeO&r4bq4QMyP{v4RJeXpAmT)xYx3WWk3w{U=W=WY8NCm~QrKzY@QFnKbEceK7ro z5)e-C$#r}-nQ1Q9g##B3{8bK2iyC+E^f`LihL)D7 z4p{#54J*y&&|cAFY3sK4tfTimc8qb+lgdM<1D90~!qn#gD}YAyHT82Vw(2Fm$SqI6eZqGRkoEo*qx0;u!T|lZU-_!{Z|o09{~&(T z(uawiLZ^#n9Iw%ubC(z+IL5&kBXbk_GTjhEK5#iY)G>^FR-YP|d6RI^rV;%k<-!BKF%+sORI4v44Yg_;Lmc(M-AHKne|{@xhEk;==8PIKMEfn;~Hk)t6}Oj zb>TQeBbXRcSf6YzXV`%un6zskWIrf($@jAc?=^5IApCyftCrvSlB5R;2UqF^dBVuZ z`I}*`E3AqL-4ZO#*)KwrG({ev-9G*ioR;6Y>{E>YJQUCJh=)`m2f1(#=YwTHE;5~C ze(2XlonPn6&{!Dx)mq0P@w=VSBE+k2ssz_ z2;>ER4Jbcb2GaZ9DaPUJV>RC+KkyHJ)(BDB%m@~OtK*Q$lEL9W{GCJDfQn2xHmebu zUmbR=L>)19(je~9!yocN91SK(>usCV5DA>NaJdp({7?)fu(t(o0InsHF!`|V@dwD3 z=S>}IoEG{mgE`oyjoU|M)hCn<+U1!Vgu+lr%c~p|pYqW4fmaMbF|fo)?~%LXYIy>W ziy9uwKlP3EYn$|d0ThHXDSAkGqVAND?~F6|gMZpCf-`F!mca%FUDh|nQJ07#dXKT_ zHqQOVdJcyr$!GV5I?%J7yMAPPRfq|*_M$-|>q)mB`hlETZ(u}@S~nUYe){|kUOaHq zww1T=x}48Crb*;2KYFv!;U_xYFoT)3r!9o1+Sozif{`G#zqsDr`i_)w45eL8Q zTcQie->)a%ur{M%pC9kIfhfyEisY&6z5cl4$-@Tt^7JjT1>7@-NK9X1o%mkY`Gs?e z&*%*0I5v>r)8D;X%A%QC9()?CL17KksyFOsqR({!ii}W}ln+{-8BaemAIm&6{ez}7 zv{U_32`#j`q!Y$=+qP}F<+=Hp2YsZ670B<%nBmGWZ5f*F-#kPs{Z{xTw|R#D>KqTF z`0{vPy+!#<*Sb(X>QC7)2Gz*NrjqeIxO1=wyp+$DPh;^r8tm!2>(bM?gkJ(z2CYA_ zBH!p$*R3OK8skChGx5TG#$eultzsBqu6B{Pa zJiHwTzEN_O6s0u1y3@Yp3tyCe{D;P*$P>ksr!f?VUl5S=^1`{Ybvg#Sv)8~j5+ihY zqPD$v$|(n2L(=8`Ce8+FEZqT5$dMur{_<%^fekSi^n$T0VTKa%Jm87pUO_^ zkDrX^f_KTa`K5ofPSF+Az4d>C0s1YnbBla9pH)7srye4}dI%NTu_m&|0RTBD2&O4B zl@VA-J2H*!DKfs~eBYmE+1u76&^{@I>gKA}g2M2md}>%Kh!jW)C~UY~;UzA?2B}*j z=W9HeuZn-s0G&fLez{^TN819{a?sVvkGQN;>p4cf@U4Bl<)E#(4n(={&~55Z%TyzD zd0{*3x{zx`Uob*f58Lk2CXuxl`nE^f5mG%&!*Lp?&T~W86DVuf&wgq*svYJC_Zjcc=L&u4-(=oaHNWY<6(h%%Z>_g=n zLFhU4fo+g@+K8~jOR0;FT)dUPxqZ%U9SJ3F>Xfit;27DFhq+C_)%k_8>_`9LHETj9 z^l*n;`lIyCIO?;=ROHDzQI8<2=(TI~g6$#uh8Ddc1K8Q4>~nH73}3@Tbm`n^tq6(6 zG{K|Wf~}ARyrr>p;!i30wL|mX0Fq7_7Gv7^!nEbFcw`ymkNsWS89IqEgff@)H2Xoz zU^AIKdA8r|zGdYUIDTn1Z&bs(;NyDoucd^7wUxSBwcUEyU4h*fM8nuumCsv zvcyQfAWeP>kL8o5{Mer(OSv=7>)tO)Mmld~m$KAv|08`SHo1S~%e)loLPN$l8pxts zvImPYqE_-AOjkkF2t7BrWY8r6d!4p{ z6$r#he##C+DnrVhbLwZFLkWO>=*}MY3jb$dEY)77MQ?EGmk-FY^rXM)94ya-@AQa6 zDs`o|cz6nt7W=>^z=An(r$0L_;@fc+D)=TJaWACFg-jAJDE>t86V10}S9*#qkplBy zeurxP{y}4I<7oxz$#_?rN_$DWq8+hLaQ)f$*SNN+api?%CMo#f_m8bjH&5HQh+k-v zpaWjp&ol0VKgD8vaSgWS*ziR*j;Z>)$4~CK>97NmpT;aI zqU`DR7%w`A^yF4g_KiV=8=n|>-^DoMn#F|MPrv-*OD122XHuAfx)a;d&{LmWAZHnB z7P2$2w&N9syvWmmwiEX(crr$Jp~MqpGwEYeV+SmZgK`&m^P-&zzF$Pw*x^@RjC{{x zuZtI5>;aTP!s3@%d0Bj%EX)tX60v@a4DEf6m$0q=5-$v%?e_V2J8WpV8X+zQK+@n~ zV#`+?hsVuppin!}Gx$sdKSNiZ9cUf!?1R8bFH|z$v)*twmk#iSYu@?}znDcq3EG|*Goz^7+?Xn=Cqc0x2(^ zeZl!1ef&Va-=Xi3+kAcPa^+&H3ui2>U1OluuZ}Ru0)G}AfD_gUnmXIb=`65j^5H38Zt<@RN>vC62%N-?k@-ViS1vhvbL1Ey>fGVWCQJTDA9?zl|b z^vg%0oERC`l#^=DW$V|>!05CmVdol}Chbn;js;jBb`ZRMI0OyC1$dw_utJ)utw)4O zy9!=>%`xp{vYUKyQv}?m+q0Q)LK=rV$qe%vIukyTKf!g(LzW>9Mfwr0L%5j-K4opf z$se$PqwC+3Y8h-5x7P7R#thjjvGD==$xq>ruz!dn0ooyt>3@>hX%vYnLsOPgFV@PU zSbaKTKl+bxABCIc#2w*};rfr)z#H~anm-yYhUyZ&MGZNj1=;*(QEm;Ra^H3~G263GoL+%2uX21bdGjX{>#S;B}j@X~fvU0uCLs zn|Eq}&MEGkWbO+&PrC0HB60Ei^20vR%dt^T$aRjDyDu-Y*i64}|K4!>NBmi2UU@@k z`e4fCX0=buq3A|4hq`49;EQQrly=iY%ao#$&X+QIfgT#@@4@Q}H|KF~ba=@8j4!7! zN6QOiUtBX#agOImqMKbTf&6k|))zSwE=`W9mYPp@)El8Aokqp-Qf@N|I!_7FZV~DGpaQ#Y4HcqkONJFN4 z)y}aRUp~3Z2C5e}0s2)LH;%bxVqsZla6N(z02+w7kuIB)T(4&AHJeZzpOIVm=!Ss2 z=yzkCw7Subu&k|w?xDZ+APd3=9*M!<_&m)-yKG;-rrYnP?Q_x(Yc-O2K8=S)`eJ%$tG6^z1eWdSkp}~+}TLY3Q@l5moEU7KfV`uU~Jhh z;B?JOet2c1H}4-@Gdjow8D)&zubr@Q`5Son;_Krw_;6#5n<+`g*t+9M_gV$(8kerp zhFiu&XkdVwejKys-tu?D61U;tQ%1WHK`LcY`I9v__M+WD$Bi4lBCmX@z%s$u%m>)? z=2utlF*d(v49eSAHbL=KMQ^``o=spF7-THXxHy}*T`B~3H^I3PlC?X= z`R=-Xp$;^D|4d=sxSK^e+!zjMkOvu&24G&SXy`5jEQK++5gN6GVZEmvZR=6Q^okZp z5@E=J;HE4dp^{_5S7yO#Xgr&CwW+=a-n2yzbAQb%xm;X+?L``lw;xh2#*wH z-gv;hynl>dfW^I$t>Qs#kZutt;ow|^B(QdeRjpWWRU9o}L&7taFb?B76eUyB#kM$3C;h+A?e!d%HzW65q z06+jqL_t*S?5n1~>PEX5PbG|S61rBR6^>s-jWIf9GOt{?=3t@4-K(y;Nw|mxym4J5 zh5|&}C&Wkx*>IfWU41GBFoG+2oBu+q8(GL$!tn}vo-u~~t!pYh)Wv>b$z0Ztpqqq# z*;3j0@kqbJJ9mxHA6dJJv4We7U9*XJTM+BJeONa>_lcOSyQD6qDK}*Vf3@@?)N-%3 zlDNdnBKhIg%Ec4$8HVc>KGvHj`^L7pZi==1&9o_mPaQWR<#xSFgxO3?pX^};**LBt zAiQ?3JFl4dp|HH;>cNJhg$Jw7Gp2!8X>+)a*%}8=*oZ6r$wV^3E9e;Eo5BQU@niY3 zCbr^_F=4<-KoVSjE*!XU;J|@hKa9?>(b)EutDfi2OFdIhS}M=Fjioh5Z=?@M-|GB@ zd0KAfZ0OX?-JHLnW7Um-k!I;c6uMtQ)WD3F?cjCnuc3klK^kM>pLs5ZEMrikURGau zIFv{kZ7;o|ejD0{ZgsHA!kc{5Gw68xItTE5M{Igc|8VI~9x!}!?O(&#?vHZr_v+W) z(A4+%)v?jDj&atQD0M{n`i=r>DSPy3%Ir9b-hy`;?qhh4L0aEMq~8M8_Zkq~xmH*r zzn%M$hdi~+4}9U5a?3C4N}X+ab4ZJuy`k6nPTq0AVr9VffzR}PSA5yna1UIGQ8ays zbE_O4!`C5WXycp}zSlUj<2U(dIqaj9nFl_wd`-tAM_fg7 z)id-)IC0aBt$xV)bd2-hhh;D?%NL_1@R&^g`q#c^mrduQ8lG$1)h`;4Av%Zlta)zt zqmbHhSmx;aT;6>0rEiVjxg$5!LBT<^d-U6=MFz4I5(Ij-a zW`ZG?ZADq^eh>M8tXVfy7iYb18!X_kpN9{QxxHD_N%fMS^6h?=j4y!A+6L`PoVGXf$YBBD8FB~B z8tFeWZhSKVS+`P_pZOx|(-U=r%-wSEVvMgeq;njE-ije8>{II9 znUWI+S$#oORh7L|W^C|`L; z`Hc?{Jz|LN!Ccbs0SoR?B^l51ap?r=sE1)dyYDrjNorsC^6U#=e0}}l7uY(zkYthffu*jj{Cjjz5KO3-_dTbFnHET)q_o*@5viO_h)p@3+qbCq3tw>BETmP zUr;|NM^7}StMz5_6b^oYm|ME+OS%?U<23yf1|uXzdH^?ty57Fc^-yKg_=HnNjV?8Y z&HOdxqy3Tgnf3`M11EfV0B<6P=X<+H57{OMIgFwjJi;E)P2moUMW`(You>@F9TV6N9M8)u`=Rv9$b{ka3e+p~yXNfY%{F4)n)h4= zC11+Zz5R}*xIL7KFQMOKgnsvxb#C9Ga63-Pm(XdD)tkOgF^z44wvsYaPR0z6cO3_* zW8|wkvWDpNSsJ1PlRY%ZV2#kL106#?qr*K6_K~&rYxL9=dh-g|dIFbqlk%Zg=r;16 zDs{n{`PAB<)Qk@L{iM#F4nd1(x=Zu|z5dSoM!dR+VH7z(a8(1@O~2 zWlz4)tUq0SBN_TzaeSp2YJ_foU_DQrNQ7gc8}{|~pbYei@4dtaQI<*F-b?wg-Sk$? z%|U%znDGFI?-0~>YnGJ9@UW)%0wc7KiNu4ew}mkcG}xc{1$hgyu05?`B3QTeu>5Hs zl%cx3(1Sb7Yg+Sc z(r|x!YK<(I0|7yG61>upU*^^D`33w>+~U2a0XsHw&di%}hEDV)j@eI2wGqGNZ;15g zRzBk&*))8oJUmMMPYsp8l3h9)LZab8ybA&+T!!Y6$I)A+o3JJp*mMgoe4RgGr7OJ7 zgFnO?)~xXGu*y}pZ7uCDx>J5q&TvoJtUgVDN2_YvvK@KT52RgrATDbw^as%)@_O;# zJYs*L5jx5>^JvG%uPAf!Xgjq8UUgeKo>G`ZgzV+Ru|Qt9Dcn5w(I;_C@~~@5V>+*a zT8t@BqgGHK2Dk3{U5NROh!6M#9&t?@7{WGu3phki!oSO1G!Gr;^i4J=d4B{n!2g@w zcgh~#Ek1r&bjI}`#$6&V9Qcptz)=IyvE-2-+EuzT2ar*S9Yj@oPoooP{AFZxC}G7q zxK;YQKaeQ)({6>(4dyg;@;969>cgh5SNOn{UTJGY@`)dx9g4}NyJ#G$GKe&sn*l># zBrxG%!k0~UEVSs}tr!3K5;_YU|G)^{m;X+NvjK`pk{c|XKiNS$!0{z_7K>R7a`CW> zVhny<%w?eM0&gbpeyPOCI};u|>n!$#_cIVEd?s~F_H)9h#ttk{WTBUZj7KJp%AX zTrI|j50mUC?lu_z3Ctzxe?JE*No_02Rpf8ehbw#&{>6_aet$-WMlQ$wJI@v*v;l;%Op}Uc%J3nZW6WfMBrHAajc2L~tfMcmXo!>JW7%&*bMKuGz3e zRO53Zo>>@pLhrhv^*akm-_g5wEXduuV27@9dL#C(Dv67Hyw%m}XBJFd^!-lyZ{+hG znlwarA|{p6B2T)w57j#FaCndMMh5= zbYY8yo-A;=&}u(Jexx(sWRXP~@TVNUY;T$7g@P|CK4XB+fB59W2ibE&32E|acU*O- z3p{g`9;uhU-o~XY%Qe>LUIOuNKWeyq;%@Bv4T$lFE00Td4#9dyuCwTrfc86sbq6p0 zdG6*5@)!4t+H=*n=eKHglEzJ$NOuZZkR4?)7L!jblzTyQH_Cs7j9;lhm1XMB-rDy3!p$JRx#KoO(AGU<`@hHB64X}u~ z1f7e@QIAcVj4qtG1sHH5s>63!u~!GKunjMs@BzIwl~0fu0+NnZ-_UJ_TP#W4h_TZg znv$?dd_gxa@@HeUFSvSt`SkS*hko&L&iR)8pI2T?u!z|WL2Nvx-;R+L{dqRfxzUAj zf^)HMN};cZ2FVE3xP6&KI>sIvkYDpMIsI$;*rnlxqo0)i>HqDo+sR0W?-Ce)I5*Q! zD8@q0`JC5DpJNOOw$FFGpx+n!^u;hDn?=BfVtb>LvxIENvdGt+RYE*EKTg#-U?98kN@y>}+a++;F5Zc@6j+2XQ-`lQ?Pj zC%ni*;xl#vpI^)2V`m!;+a^RdIJtrP-i=6KSZ{F?6&q;p?l3NPGgQ_r#K$^~n|l}& zJ@_>sBvcw48vT^4SB+KC%Jz2Yc zWjQ-8c7u=`xBR-Co6Z=sg4g=-iyQLjfsI_Dk2ytVZb2!bVz$wbGZds+)26lh5I`G!f-xi1IkHm`!n!&8}b7 z;G~=8lv!c@i+qxAHV12f&bx~n8@8j!$b3u4mV}`)fG3qefh;rZ#NvwfZ-*# zn-Sf-cf%Lh*oPcha}#zp4>QKz>ViCcUvmA8wJ-O`-y!Re!vKk#N8&PDGk-^VVH?5fIpf z?D5m2OfL_-2B-dGxX)gbM!-q)QTRu=3^%(pfdKt$xTe7B{eAAx0(EWA%>GIq3kMmL zUgc4r`QWUK-^=4?WnamH>qHJDGkD9C<|X0g^*h&=;i4~z#I7Fc-MokwR>unF!FjFk zHd)qgyY_u5o3m##<7RL{qR#7)MRZf=-BC#4yV=+lvu(E6dFD0sdiM&vbj1@ZaX4&b>Ex)o6nL%D0w|M}PWwNi!n=}#F z?~#9G1{oMR*v2)=M&zUQab+9Ec_$$kvvoMSHVhO?b6H+Ef}7aK!1QS+4f#gAk-yZM}1q&RZO(`%YZr-x{I& zuAKHC!#Z@8IuhLzBQX^TcXW-Lka=g_FQU@V)A7g9%rSO!s5;fO>VNeJ{R&@+jbV)a zjRtgXP|nxq)R&+pK{hn!U3fP*yAEGNHFX^20MCo>yw!oI$9#d2Ij3`1=S@%9lf~Cq zGj4DmLf?ZPls@~T2Mo7=`^_((uNy=3Z@*zUn0|r&ka@5Mqx1sM<6f;ALNU7GS;IEw z8hlcct~xk12I_%3b^C6?8h}=5lwNNTcdr;mY?j}57KHzRX7zC3tug=AvFLRB9}WKG zf%^&MyZa05i|o&zD2qu^)#L-8xwmnXeGB|@Trp#C^Lc^aZWzeB2>X-9%@s`sjB8e|SD41VIvzIU zy1{40Ge3|)Wx#xuAItrOvFMYo;wqjYbiYflW*IUh7^{|^0>D_ zyykmHnI5s$Q>R^D!F+WSdp9kChuK{{TPEp{2iBeK3q7#v8wQ7$ISda&^t4ysV);&tfw`~HlQ{Bj_ zdIlZy%)s~Q`5W#7JaSA40D!`}9-`54WD!!*#wgQg*RM1pL}oyoG}e{IS@J8oMOpF? z63P+-W!JT+j~pZpjY&ot;X9sGew*HUyyeU0t|O|qB*t>YV3~H2{am^pXu?5-?$Le4 zc>m>|x}GngQ)f@y^6C2>hX0K5)syO+M+kYOKChU+f* znB4se`V(#7GjwC!9)tAHCiG5fuu|iPUY_=Wwc!O|Je+{T`*HgX^bUNH<98aN`{ilw`>*Ub;NaI^a9w+Jt^As=H*19cOu2sJ3+7kcDiq~a zee{$zOTWOr0q=VZ(Qh!eg+-yuw&<8e{r-=Cd<~CW-*w&B_UfS^uJ2lC*HGu9W|qx7 zUQ}R|Lt4D7&nFL_fY&ub7f?pX4`9MqWQukPXGv?A(bfyJ>lap;vkl7QxTY_TxF!=v zZt+Y9EGuu*yZmN2ie^G*8Wa@MxRmFSL#-9NzQRhqK?n{KJ^kK z^yp3uw72jz1&`E86v6~do&ff=~+R5$HeDBBIZjqzzy<<~r^Zg}7=z0gP-_t1n}^wr&0zJwkl zbPdMOjL<2ob$!Fw@&-KF2TJ<%z3A6R^lJ}Spnpc^WZx*^!B3p}H)9{h7y3bgxXBY` z9DFp#YsP6;7*G3gBG<`3ztESdJ1m=P6{!p8hUXpzpl9_xTZk{L)!*|a^n2R$ zZQ8JMtpR!ty`fDwRA4N7-{RD@+ zMZWB3X^*| zQ2_Yo6*Mc_kl^OIZCsj#Bok94i9{F%Ggr8A$=!Ygnr-{(4^zJNeo~o5hGyNi<32M! zWG>#ICnI>N5qb_xnzEa}@g*Vr?U>1QwpsN+{lbLK(2AccEj2nz+E>04E`VhMK5~TH zwyDmdPB017NcM)gi-%aGpQlc1G`n2jt z%yGZ;C)D~q@NI|C*A(r;@dMkdZn&Kvo~_Rfeu2fAG!r(@y7FVfahJb+&w5OrN@D*G z&28xj<({Ua7JUGoXS^2Zz2q@^d z0}NRyY$@H#&2w-R*P$Sr*V35^soi~+IpsccgV=rto>3=AkQuL(b+@9VV1%Xaq?3S< zx5{HY^4_%|$GGT2{yeP6cN{tB&vj>DY??V_7rr^xXxpLD~w{D%JEHf_vy zWoFW;o^@>M9G)>b?a1*Ct${f5wd5%S;7MUQ7OA>Zpp-`lE&&XWn~?ta%rzns9cO@P z4P5bgHhesxH37g35V7%2#z$BGJg;;M`Nr-9pTUUFpyaV|Om%rIr#a}F^kI1jo!UpR2#zzMWMY^>yJRrvVUf!PiYsph zsU6h1SmdH63kyzc7z}0c&4~)6b52j_)xT{f^aTZQc>(NVjuU;w`P4G{KX20nmZy z;LOa4HIBJ@LIX#B$=)9nos<7;)&;^I$_4sobh@X+J}}wzw9FWxvlw)X{v9K9bIMn? z)c@jH3j%qOmluj`_7w+2e}^88(DM>R*r8#SCyTP+u6`#_z34M;7P-{>-cgjv8A=P+ z#R?XAc(!jSyo*^bin)2p7ZENf&Y~i5EC%@;*zi6Ie@z&95$B>Iw0EO6i`w)@^f;7- zeuCS@k88@q!NIs$A4&c%ES6uZ3u+@`U7#`_bO zD6hcd`lw&*UO~P5BhN=ysYreqgP6PNnl=ayKZcsMS0O^p3G`c-!tcM6vRL|b9&6~t zP?8onVJ+9h@6>bxH{sM<3O?~W{_zcVpU1yJZ=QjE#}#HJoLmRW=W`Tq!j1q3T!TY5 zX^3;&^uOb>ME}m$)Pt?&k6(WVR;;J+BRT&X@7|_9LNQdx^lDDZpEd2?t^U{d> z9M@UcG5QUzk8*9I{HpxCK$}_;y>|kNFzTeO%LpzZiA!4CD$Rr^I&lWK-&A2POsj1s zgFRVdg|M{1##=%vIfY+@6{^uI$Oq~0U%ij|7fJs`i(eRf`}7#31M|8IM?CP8hkcqG zaAv;s#&yFrb1~;z_P3_X<^^5^`RLaWp24AAiSOvIT>Kwby13mtHZRGu@dJ9Y3Cuo| z@c@qp9!XosPMq_u<4f)I`6-CBIF#_k75yx?V~K98bKYhi-WJxef%Lb}2p1vDdYc9D zj1|D!KG1r!Mue>2_G}{<~-H?5bTiR@AO*Ub6)(N_%FCF9Jp}c!hs71{);*APDe8RjD6trI(AI@ z1y8rO$UOE5?K7MDSc+sZ!MNk46W<=%pEk$LlNqF}y(n~{Ys|!kgi|B*j&Ev+&RpLO zO4)Gd%Uj1ZYXcJN0&c>~i*)g(gElYznCIuEuA7S-|L_LvGX?{0gl2D1!rYMCaS)}V zKc68%M;HWIKyKZxLo@bqoy0&c$9vaoBEI2%a-%4lf%^qg)?L^f_sGUkH;?A)cW%0N zqY;ZjZg_R`>;s!AH6Zo; z)|=-pHm|}MzFjLUP_zqkvZ`Fn|fkrTgZyb~@!9`y7|9{vpSg)a_2gG)jTJI5v7E$M=JFOQ_(0MBr( z@Zz#;^32jLQm4`G+>&Nt3Q^jGvE%hqM z&zu00fP5k+$xr{9Kd5PgBgfg?rI+?id&~P(+Mfq|-|$|w?@(RWW+PSfRrRRv z5hTy`dMVDoj&vB|?KLi5^%B4NOWR(4(=R&3vkjAw+0-ZAJ67S`+ z{Rm&3^6{^k$L;#ReXvF`(`UGT;d%;m(`Q-su4T|~kv^Mdm%dJe3~wy; z@MrQ*Nb+pCOk0`JAf*2F4noXf-|$d3)GoS&FYShI{Gpu6k46&R?7lY5!i!zLT4|9n zHj&2Qk^b=cJL9467jEJa!kK~?EY{QD}9B=Fuse)d!FcRE0TZU zrwKS-(jOkDK$Zi1)~9a#(?)z&HoQe5zLuE>F4Jlq`#>@ zVkgT!T3jI$m9= zVY_oP6`fxwRfkK2`D^^5`|bM!UqAmXM(Dp{fd1P*IWP@_QRfUAP^xd;uwKIx`vCmR zIePe)@7(PlG)Upr7>aV=jL`=CaE_F@kkvuvp$o*8{f2q*gO+;Qw^A4OPu}g1tCQ{H z@&!VTD8MbBV|+?@_n6U(kT&lkC~x+LS&z;=tz)RK@fGE;{$(uv1L~Z-)aUAXccL-o z!r+44;V{Te`lO_1G2>mfAcqEd>RqGSYreRij|uSA^YRf{iHrm5K9K z@(`|Ek?po?)200=6T~||wEgjrviL>x8{mx#A8G46eY%0RR}ByCmsqdSV9+nr=g^He(s)vcRzauVmcORMtYOj>v{chJB zdXP3M4?*S^J`5-?g8w!GyB}`Q&#st8+6(Cnz;t;%@A75mpW9}-En}xJ%>t4pLMal z4`&CPfTi8CpfjoO$fWcxF_Qm?Th_~8>92nLKnTDkf2=3__ud}fL7h|YRzSiWqj;F= z`Xaiz*fwihv~AkoJfKJJ__Fp5hPsg%;*{6uByg#Z9xy`pwy*iY!IoFCjO|Y7js#pcpoP&HjJtm;GZ-~--!;? zXjt5$tn8;OjuBF(mMiJOAtq^0ne=d*pRGaHyFbRE4~Vypnn+#*fKbYo+OS5N7Sd84 z%Va+pJ>&S$zTuXmE0C)ksKN8BpZDN9*JH>Feu%x7J_}xcTJ9I*TRzI)85d^kNgwI@ zN5|p1_63&FdZB#OTj-5}EqGAy&+2?~Beq2|E9egGs3+;Xy=P56hr%M)e30)evVF&g zBChW^1qA&me&-7@Kqvo>wbf02?BS8}lfP^0gBKa`0Er*$+kH`H$Xl9iH?DQ%o!Dv{ zNvrOM^*7}6A`dZ|ztwP_1$+BSFY8*}qioHUx?VD(zLp<3P#KxDUK~eyneG`q5oRgU zyqkD|U$+1Bt&ICADAC9^{PzRC86!AO$Uz$-`ZFw+0+qMEW7*HHBq5=vQ=Q{|t~ z1#cQ}FR7a6h#61*3G8#?SGlKteUy`M2VR)IZ7V756V&;**0Z&pK7cT3%1t8cZM)@> zbB^fJuCM9uUSTPa9I)HxyPgE!7;Pd`_>r^Jg>BrMf@Zu2N%b!`B>5w0b@lf$4@wld z$H!yzPzpRSxPdP{HMZ5SMA^@_kBwpM)g5(JBiZeX&52j~D_@Rf)k#+xpL;+w?q}wt zj$g&C9aF!EE@g6ZZsNm7!qHQA;aQAv<()j^u6}8`#gO`r zr$4E3*|d1HvFZSG?k#7|Lw!D`9)!zPR)iq2yy-<98gP#_xWz^`%&Q;EFGtzw@W$RwFJ~T-*s?ehEQJY14$#_bY`>XBz2T8HGUoE8Mv7 zZFt3_vWE=P6M&PkpK^%LKO%$M=VBC(?Siz<8FT#;xyd+`wcs6R3gIJ^H7CL&5T9V8TNl zb>r71DL?A#PMFCbfBLDo?dURCCa}5qOTyq_@PSfxpzeCl4HG0!oOf~9%}yGh_iN}H zpEoR%7bY=TTx1}gi6sck(}62f5jIrOQG0?N9d9Ojg#;xB+*_B|U)>Fg$?IoU~+1&}8=^HZPx)Ya-E>MaH+3p{=BC#$RtI7SnQ^z#HhmK6mtHAqp`RPH1krZ3#1@SG5qnO-h-q5#3j^cFLf(W zCR_PrGZFdO&xM=PjyKaoX|hQCNux&Sp7MCdo0%`Xz>$0M;)VN$iMU^ge`0~qKE^a8 z4L{6K?4fT36@SY<<#z!hX~-v?XJ{&l#2E=e!3;=XMLhTyJR!MK4=(zUS<>K-&oV-h zFPw{Qs9On_(@-B)JkFvJw}0iRBuotc5Y}W{$rm!=cf7@194ilKGc-6-9{tH!LVEEW z=!6$O<(p{SV;(DbbB4SvV0qpPFZOfD^8P%JIs)=IuyGSNA#d~j_XwE+ zS`PjHZsi&JC4VGq%JO&Cz#*Rv?O(|UJRc<^d~2D>jsRibP4FH)?}eG(@WJKtTq`_} z@1;Epla>hzU4uvWh-<=a`%N_MX@fGDJ80rxb(gSX#EvVl8v!eP(X+G+v0Fd@l7E7T z!L7Ic1wdFaFKT#tC#OY{>8I~I_Dh{#tvFzcBRxm^SoOF}SPHl5-+Wx8ch0pNn)_mU zUdD9;2mLqwRr^{}m@Ihoc&)O5G%ug%<1|F~g>PN@cl57uc@@CRmCV!VWAXx(u|i&! z(D`&TOlg(gybz#|wBPf}3-Y`)AW!h{OB~|_HZwS{>pTrW!nQsV82f8^bnPOs?KJ32 zEzgL9wmunPBryE-wB!H>#<9doB_OvKA$Y~nYutq;$tO?YnkK(H=t_GIw}KDOVueKv zuGH%mUs}Te!nQc`A!<3MT>du%Y?v;d3kNP7xNzXYfeQ!zI0xuYrVE;`CH>CfVMFHY zR4(G%+v?Xh*WMQzn*Me5(_0@ZTJAi@^=I{N_VYaCYv{b#4GfDQna5}SxQ1n2Pws~O ze4#QMs-=w=v!KtWP5XV9s&j+W&0UNqW)q0eZt~0`3*!mbpv`^8IVq&bSS-nx4jhjF zXM)aYQ}p!L*$~5K5ywPs!qc6xkb|A`O(f3QSwdk9#5xRy)X!|B&!$n{z||;%wz@pq0>MZ>m#=C zjE&>@YMGn)*x*h1uMLB)Y_g@yu3c%Ae&uF1-`ntfO&$GUgW9e21kKqHWzl7j1<18z zAnQ983jTQ$FR#Kg@-$yJ^LI^b!O1sZy=;BtPAYFx8{Rk_Ya)6FoQ*S8A@zB6vz4&D z#$Au0$M&1>^`kTOJ7e^~91>5RC*P1{zIz@+Pbr2@B1>7J%CD`cNgLROWU*ii*JFI5 z@#y;Ob;PxjC+-+7+?EEBGWn<%xNWi)LDb%6rnCJ9 zj=XfuQy`F4#-UimxmKzmX-MT?0N_@59P`+2>F^mBn&nGfaWqcfVzb^{oyWJmH|LFz z#D|vs_VF_994rUA0ZtyFdeRDK-s@ietz1!v>P7oOH`e=}yM5+vbZ6emlKhZZ zdZX2sMITF>;nI$L$(tB$WDSWgaAUWzUA^T#Pk{9iCay@%wu|3JfhdFQyJWs88^C42AWOjI=fZ&t z2j1ntp{=AH@#8=3Dea1eMGHH$Kbu;0PtmQ0XBrcrn`0!9wobceyc~n(WyG4jO6W-S zf`^(%&48#F`Bk@}FQbz+U|7bG>dL66r6jVJu6`lEr=^egFH1ktA283s2>q4K;q(xh zQ~bbK$FY+Jd#_F!_|M@{>3p3OqnqiEua?oA+FM50-Ke`dhG)Kk5_ewkj1k>4b4=ka zv%2luZ;u$DKYaQp2I#;3-)|V9`}J;(zSMb^(tbswHfAgE%elDIjbtvT<)b_Vru=VA z*7pgk@w)tx@4)hRjd4Q^3Sp4X(ph8hHFhCPNH2M4U#Ic%tp+;Id+6h0+(o21TfzCOe-Z^5%2UMQMmFAoJ8P`5Napkx1j)RmT zjgZm@Ms^)5Xov_;IPusL*oG1h51)q2lN`1nO-MGhXReN;N{~EA1Y(%ss}4 z_S-2pZSI+a{T?w|j*+4=$9j_T5*k=@kvAG?Yj`f)Q*OsN8dTYqf#r6Lk~&pMSbFMF z{$Eq(%7$}b!vM31M;;i{=PRk!5eFu?w*_9rfKGV@P8^l%t|!&-5LwaK&9rHkUE8n> zlA$4vX(67>$S|2szSR%%v+ve841)C!zpDP7F$|fxl!c7ZhGHO(jFLV^=)isZ=GTce z4&|o3&lj7+BLw^vgIQRp)LFx_BzNkYdK=ydVcjVsl>>|hX#@8dNP3{7V{@9|r&|vR z&?r3&CKbhTonee$!yEJ;WTy?-X2W0T_n-{dB~&J;Lk^@+t^r&!rJi(*93ylMrvdWL zc+omEAKMjt^|p=0xDLWP_Fb|`+wp(`WyUu9%%LC8=m-r4HOzGk2p24J<^(FZl%2wK z9zl7ns!(P<>GN59tuNbW;z_7JQWrVSe*}LH20?fC01FcuXZ>0x+g1&y<<$!YiKzq1 za(B1J%*WE0s2DUCKNn>;qFv58|pH16l9q$%B}Lqp1e>tEUqa}kQue8@}Lh#{x4x7Gnt2KA}sqLMXO%^HGi3GbB&*oT4a5BdRq ztg1L>m~{xVNc%$u+JvqWSXmEA+ri`%?O-n;XU!-#7pQeP;W!tw^Uj z-!i4XY^Q|l*I?W6mqzsR(}NZqx433XD$gA@OjGCbgmny>pTQen+Ofj z#|Ygoq00x`MGhi{SmjV%WB+9v^1vYF!Zs^y+FZP{rmy^K#B4dFJ!2!rx%mKv>;JA@ zq&yrzW4VP-soF-=BZj$V^UR?)jvG3jwp@*?5wmMU3!h_YXq0bxpXSR5F7-8EZ-Qku zcI29VHTA1p$Y<{8Z+K%Fe3#=_&@*xxGziYk{QI08E+~Bk(CR8tVtY`2d%CwQq0$l+^l7X#L{SZrit22Us7K zL7%B7%QtuU02(go7I(h*zU0R~-M-cXkd+B_n0>*F4P=A2<(1<6;Ey;O5PulJ12D)< z$|V4KHxUnkB@Fm5QM_Av=%qgiFPIl-9P5{wJ&>w?51z5Vb}VndBflsM0%d>tnLfub zMI!8y0<$60eCgitVt7e}ak*`kg|jVZ?b`^_=v;x*u~O#L@aI)ybY1w9wK)8x!AyHq zj;x~~N?B9JVBFyb{*Akmy$LT9liz8aIM4C=EPsru{0%)r)>|BgNcY6FZTOV5MEK_g z#$;((<1t2=Tvws^&HqXR?9edj11^2m3o_9Is2uC9ytnkqkTo^3)VwCg=CJ4tE%gVF z&uQn=&sd747|_?t5Qt&&_yp-WiX828M}v(SO8u9)3i4fHPQme-pjJ22J2hcMf<+JFo}zC*Jda!ix@tUW)RsV+$@eF#Q2Z7Y^X8?Bw;ksvcC-8P`+zyOa=d7CrqYR|UkL;x& zbn9PtClW3wdP@KPRlc0W#pEYpE>sZbLd<#ka08m02sz+!&8l}Nq*R(9+FbK`y9V9uSJd^a&?(dvV=)k0t-xjJb6b8=n32TX06)rac>nHWk zpd)3FABM;Er@U;aF&%(XRI4NPF<}`jf@jN<`1%(@9Md!d!%EZml5ZbMi?Hur%3mW| ze@)q8|7n6jvEg34C=H940cx%b!NQri%1t9S;M0uWc^bYOHSOgcuuIT?I0uxIk>{=5 zo$=%-OGb@6orh1dkHU{>2gjt*EiG?leDv)GD~L_Rc@NhFo;)ck#j(1?^v47!ic9OQ0c~5cT3`XzwbBtDeyz}?6vcpj5PlN>4*Zt< zow)Nn2G>X7M_il#=pm2&8aW*P>@k10Z|FYB{%^@-$2_gz@6Y3JA@l z2k$_Pd!BN_H+K`-g=Dqw6Z{+SJ06p*Otu!3wtNa(>;j?)PKX-fai_f{>4fk2i}4U{ zKyaOf9oudb?Y}4RXX!Rx?BMa8q&dVbc(FvjPReB<0r_8U;(?4So+Xvrvtef!il*WCbo|lWxGspC$ zhPccledKz%5+$E{C$Ehs@`l_6hmg`2lwt*s4B_^vZ)NbwpH%>!wtO=NXf(fErmvev z({at%pl(9=q1;5v*G&$L%2m2-f8~`(2e$wdkDEODEFSY%=5`Def89JdexbiMfBW1T>AK0L8*Q>i;F!lX12*kix#KH&=B6Sy33fuuE@-ZNvD`g7Y|3>LkQ;N{fXwKK zu~f%V#p$@q@fI%SbkjvP#(+QcfjM7KaAPU?WWB|49k<3089$_Q^vm%4hZK23Xntji zC2Q>wv|UEXG0W0GH{qDdYX#M3ofF~O4nKtPAOMR}8R9`lHq$|uV^ZtQFCOKK=y{%T zDK2HnW?Sl2G}rLP4V!M{bR)YP>HWHv^^^AiuKn|Vz;^}x(vbB~d=3#83!1;<ZhipI90ytaL`J=9pW{%q=|o#bmwUwK#ai|=OG zIEoRvMh0&1lorMHh7F5IRu!}@Icw1`+?*V4PMi%w&HyVN`b-b?ax^YUZ>-HL1#mLz>vsnF64r!&Y>z8%fr?!n~ zs3J|sqiDs)qhadcA6Y0jz&J1aO5cIvGnk)l*6Q55Ee9~k(h+Zc`P~sI|F+8oD1Q6U zg;Vt}Zu`^C)o=KUi(Uz_CL(>(>iD2*9_V2e#iBy|1=jTvMjGbEn|!;$*suE4h?w`# z=)tSeXItIaKUhxb)GfZjDQZIcH~s@m;!RKAalB)kXdjn8!8mbK@FI*O^)0Hj6X#7-}osEUUa34#Df0whIB9(?4Zbr~-;neToKAJE)M=@6Z^T z5hg0+r(2k)UZm}SuxoHjE+Z7VQ8@M?n+ExVG(lU>`im@0;}Tq#D;dPXUu9!RCQM1+ z8Jp@@Gi|7w(|yFM|MeK*5r z{^1;ixgc{formYN%h8Wyj7`(uizCcU{tx9)YR8}GCpvmI_@jf>`8g!Y0hNB~*XPpA zpE&$MsVMkgDJrJZS=_5T@(&> z*3R14L(;Xc?vs1vGsHb8hzy(uOOtH~7u%?Ux4lI+t)D(GYFwUgxK<(m@HF<)di6qr zxVE{E&Y6s7{A0jzkY74<4(Q?YTfYRI!zS7}9P1qASALWiE!VLOb?P772cHL#Jh))X zBD!>Eti!#1$hMWW6Y+Bpn=fD9P-icck-2%+I;?ALxf!ivN?@$!<@%Ad^bnN$2|Z+` z;~=^pz2VEvzHH5LvGZWb$XJOrg6n)WTP)&dEdcx;Y^4D1?VnvI$iZvc@f^I)fknU| zi8gCHR=%`h7y2)eqMUuyMoG*oQ zVE5I7&aaQq#+Sb7NUm7rnV4-h_?2r*BO+JzgYB21*BZ#n6EBar=>c@Vrc2U?Tll|$ zE)Uw-W~?7&@<`mn!y^aL9}b7ndr;ID`E6exR*%v6;RqS92ZuiD*wlpq+gElCMMky> z;DAXRquxD)Zh5X#+}^QYhz&z8e4Sd_v*reWS*vhekGc$xwh1U#zQWkYXAGG-W-Z7r z;<#8C$FqH|=sHoy$kws)&DdN@EywX3Yi`QcL;neyk(M*?!qO z>A=_z{I_jv<9q8&nsJ%qlywV1F7_VM)3}tK`u5mP+V0i8>V#Jm;I*UZ8ODb*|%!B*kYP zYEwPn=z*QQD4l)B>Xui(=u1JW@MlE_-SpV_tpnKQy}Je*9R-f`Ys#lMXtJ&eN6$l-e~Girs_z{eAQv{sc_8}1m!aKpz?6BE{p%V& z>A4(4Z@bI*f%dD-kFK?yL6h}#3!ZR@KFR@p^ymxy%a_B*Mb@ItmwB@X_vrf^V9J-1 zJrqv=(NcyaRX~TNXn#KO(oA1~634wSl6m03`y+a)FBjEjQcpC} zv~@BX9{w#Swv@7bsYv&XgPgWci->-zF0n5{Uz{GqSgHJLdZpUBocf$_d{V_$7<}5U zw(i6w&NS&1DRerHiakcI@Q^iA>d!<$yp#h8&}N@a35laRXbbXLUik%1UCg0#WX2n2 zsAG!CKo{n*LA_EH^fJ2{t z#0N-aB_G9ED1Q(4ptV3fuYARPZZ^5Y2jyq4iL#?^`Nwyhq(g?m03Pz{+#4Htr*OG$ zO{MmwCvF4!t$UY`PC`*xdq$x{pwcBJ$1%AzKDSgzqaYT-~sv31k&#^?)&72 z`FMd2FVVTlm(V>Rj{ZYOs0*b){%qqn8P`A~bsLo&Jm^tg`oOVv4ti4*dAp{gO`|_D zzIBXbfAQrPc}WO;OrFGFUATCKIrWovBX$zo(fvFMK{hI%!idX$SY3aEz7v=8jaSB5 z+6wiy^?+rfSLiZQz@{J!LA6ul^O=Em_e#A**{y@tLlC8S>$3GpZGsgS9T&l-lC+1? zDR1pK;6UC+=8t|sDeF#MCKxn8jD0Af7&^|C_g3V4y zl9CG1Acn`(i3P2~@Pr6i$RlD%U9j{N zCs&owl$|;X$A=&5XS-cAZufg&aF5JPDIb$3j4jSc2@OIOJH-%3S*x((GaZPP);sTo zt22M;uEw(oTJGsJFpU)-G5I^fS*Ef6=Mooj!3S+8XoWEj;ObQx{C0(i4ZO4h8Jf`o z_?fp_uh?0~Z>A+KFh_a~m)`u!n#FpxmNE1Y!VoG8zV9EdY<7;Eoc zsKD<)qGdLYAv$SUOw8sc7NVH&-e)nwNf5@AiN)rap-T1i$;Pu6($hy)`P+PS|`nu#Y>O?a6pg0 z90x)h*i!V1ofMU{v!7w5ZS5>^LuMe5DQmk6Q5`NF2N#7DYUo~h7Z>6>Nbcusl4vj2 zMFRh%1m4qu=W)lra7_DA{1Ha7j&Ktm-}G~M&S74Qh=gwDkRb+wBFn@k`+6DrHYHeB-%ArHr} zPcN*hxgpB8nC4&|u=1*#OPQNL4=t5Jv^+434TPR9Xm$}; z`lMGaC;xXYu;YK@se)e^^TAdYI3~ZuK$B%gvc&fxz4oUz=$VH^hMO1KgatS@C%DT| z>&M#B@wW+D-qSK7fN5(jPkfX~9{Eh>$f)JyUc>@(>6t(Nic5k_5Fer@J+(Y2Z7ZcC z3>QP&!J;k27TMwV{a67OM+^2GZrz{IctA_=M@fHHyrunFxK8nS*O+szE%VR5{BxBA)-YkPm|(KGCnp8n^fQXhR@^swpr7a1y~_Xa>$B3_vK)2@~Ca^hAHeQKss&ylqC(ZVIavy zBp&c&g9CCJx>84xoAV~-XXMGl2lF_tS>+4YGJ=)p)Sfp>a>{-BfU;O}2S;cK+{n0e znYUNznY+O_zWICY`H!&Db$SiFvFB-x5u({l-(d!qX(`vFDSv@|R~#UJ$0dD3@841W zrOb;2E)uv%;39#4MFL~1#@8C{w)GVAZl}g%WrVT}QtUGcxn1b65HVjd<=Qdhx3QJu z3*4+TYwiu}ueI82n#-7}n`K;A&Otoq=x(HTp@N6nneVgqo6Q`|iLY@9Q@&rrajiIG zF2`MqwycRWuyMU%+qiIGIJ8M0u7}>)e8B@^MizmR&p2`5`t>E~Vzc5io4#IO+)&xE zQ5i=X(Bb$hUqNBG;=Uy}J$Dml*T}npa34pxxoKQCM}m`aqH=P{z%{tqLDhsr>pvPzJ zvt~u%sq@qYvT-9|HZ&?LWNkfKZxZZ7emB6-3Mi-e{0$rN;l;HW^5&2RxZ0Ki#ZP77 zhBVjTl&yHSb}wDFdH11Gmd{4|+(o>$0cmk93E%NLWhukpFdw+we4kCBt8e7Ms{I2u zY4)3Zjm57QxlxpMe0}B17J2LoZJ?LZZ1(jz%*`#EW9?t`!)`v6PTV|0;Y*9zRHlr~ z2_eH%sy(s+-ipciUjFzuobf*%c@bCT=H^1?7JjMCgFA?m_Rw_aAvruW zoyLaZ(m{fIJCEsG_DkSzkbpIDtc|@6Pl!o-HtiI;ca#6<4_;djEr<52pRjF4oe^5pdrd*@gPBw8Fh(FY+8!LqAjD(6&PD!(>fXfAK@PkwMp6w{xZ%X-n!8yydnz^54rIz8{DA5cn^B{wOF6wo5#y9ut-b}p|h^! zmqMTXS}2Fm1xZ!dp1A=Mn*#A|1hiYr=s}aLbrGLU_3D4}M!zE$a;&iBz`=TUK~1Ml z(yl}+L0oEchzecuYy8yuBt?*vGk@lqzhW9m=|jIfv=h2dI<~ESPP^F+*nZKB8bP+^ zffvSj?lcTF&}>_nx`;oUx>p;>xB$5DXPxsmlqbZGetAnE@};=OH7dWuuYU6!5--7v z1TGSIM*`DRrq|FHRk(@X)7mf9W~Ldx!U5!d>95!T`@LgUTn?H!cIB{{Up>{S^3`=$ zbYjQ;!Q@Xmu()+zOOFt*o`}xHXherEz2dyaL(`9s9>n6XYF~Mb$4~zHq3mhtNHs<1+~7dr!Q`1p+@jdZ5vPxYp9c{NFg3 zZXLj@G=1c-g<}=JHY)EkX3tylZ=6 z;n*khDRDDCvJ7zhmo^Xkwq4f~tj8Qk)3%G2oPiWx8Ku<1yWWr&Rj}Fty3}>^pQ`>$up%)?U8ApkG@j3%{)x6G?>UIRUL@g{irc3+PP+%HSZ`V$+5da>=?L<5Byp zI>`20-RIg*8XR%phVG_QF7d7p(V9|T#;A0l9zZ`HLU(*>->ts8y8|9xlwbNaHOOSs0idgK| zIoO2^xP#vIJ4^RPhpru2zs@b>dFnZ`0X8~lk(qWu4*4CI>b88jQ5GmMyRwezsVp=I z;oZ^-_ky5w(rcTu?LTsWT3RAI5V!8^d)kW5Q?!MVA%r=9v-~^f7rsP79n3*W=O2-y z{Tdq7587k(p3j$WX@jmoRpo#Wy49E0>VChPPX4aB@?vxj=lJ|r81=LTM!wLK@wU`S zKk?3qKcXYV-9Bl2SXU6>usxwSpd<4Fb%<-axEvaWPY>sLc;5D4GtbwGxyQ07dGO&7 zzJ1&SrnWcNwcOu4>uRqrY+p{jF*CyHBsYc%psr75xS`X^Ywh57<6DcmUmoN56w^*Vi*gw0?-Mj_1p} z9>~JkZXQ2Bl8=Mvd=1_4kK>Jelnh;$^;hakf9BtQNx07Xqn_l!w&Nd-Dvt8gUx-)4 z(W~+(e_r8Jc*Emw2?GFFELDAV1Ndtn467?>pZ-b%SH_IC*9g*jRUui=l$}3(!X+ey zbEC!q3Ly1)^i%L^|IO%Jm6tJm@+EQFG4R$cl(>E;@{d@EAXQ)YhPj zZtVk}D{wr?<@&jN^+gV={%K#;o5;iU1@Nh7G9C^7*1cusB|D4(9f!r%Qn>Kxhph2D z^Kuy5-jgqZvi)V827cO|Fa2|H=$c#^w8mFEfYw>V^QDNk=WE6;AJrq`SC^2``w4CcX_AL;6cyaAeygnyT=dOIdbSY~| z^cU;j!@bJF`WySGj`v`@nG;@DrBtZx}3m}jrr(9B1*S%z`a*fIdMY~?HM_OKUPsU1Ufb zja+F!kr(~nKJund-`957mfH`Rc%}^H*)fX+S(ZdDDR~q83Hyy2%bWoB$adeqDF|nWsuS-SNz^V?O-@y zwytJi_2rt57I=szU4VldV;O8UXvsBr^p6Nqo)MbEao!iT#)~mO1IZrF?t~d*?c&KS zERf#81Ov-8iON?tJaCS?XA!+$KJSCtE=IwT#vJE((h_qRk4c&f(~%!>$}E#Wo(eHKIW@Y{zwOfs`T!-Uew-!u2m_}Rb&tKGCDrwl(JB8C}R`pno` zkVoSx4StoiH_EU-2XEj^^9shOySdWA6J(X)W_d!k|` zl4$+~mv}R*O9Wia&aZUw=hV*Mt^WZdsQpuNP?so2^ezhm&X=6x=zMRtZ`%&DCA##yN8M^r3g1yc)J0G;T zY`~Q6!!7XFeo0^x{aoVWQ?&nVzvvH!R@zMfGdmuxxy67I5O|CC$Z*&WDSwjWaYFwlLWQ?@{j~Lwjg9N(nItjGG3dJ&50|6xH|a`YGcD^{Ea#C z92B|K6}rTP4s&^%6eYFr8M)yxpEss@)t|dE)|8VhMiEi0M|46_JKN;9aDx9S*COn+ z+(p@uz@34Ovnk)Ulj{g?gmTGctC6>N;09zte&>CM^E-&YIAREvFvI5r{>9|{i^WAZ z9&v=7@^sGGa9+NTnU3K?9e*pMJ@;?sA7v1fw3raF@)}NX$F+yduyib;6WZtI2>f20 zDE`Pbc~ieDD<{-P2bx27WqoOIOS8S zy(=Gv%4q%Lm%qa^`R9uKb0dYsn}r7~1+fl!Hm5=viT~T ze;IfAFgIf@$6JoSe7Ng|pA_mCWQ~Cw2k96?Wo^0BY2X}FfQS73(nP<8?wF>`K?8jE z@UrXIY-nb~b2de=xyQBd8A}PDKj@d>SJK_^^L*tdJ+BO;K*&l6ID^;m?cz~?oN{$j zl9zj*`c-wtZ}2W&zgp@hWMx=>5m4#{`I>2w-?6VqC=Z<8r+q12P(_q)sCy=jryki)DH>S9_gktX>yHbIprC)@3)1i2~54Gq1=C)~H~+wME3aU+|3$u+WWB65=?eaATIaL%JMebJM- zjAMB>Kk%g3xsVOU&1g5-qy+xeFSogj{DZgaN`|>?WH{;1^B?=H^0OcK{3p+MyoY=0 zp!12slPPpkB6Q1t@vRVODR4Cb%WP22LWR)8@n=G4>x?;VH>pH(L)8>OIBBr)*>2T& z>5sM};_`1j8SI}qd!?79{&Ts-{JpJ{O=V%`Oe|~UG!Vd zkIFo!;a|JoYqBMJe_rymAJS+@;dz0iO(Z6?A+ddMSG6uInGQi^K^N zFzFF|MI_b;VwRiY_3iuB1L~UE&DvkW%NCD*$>E&nmk<)0R8A;A<-i|0&_fLEsYaIG zB&R?^$}d;_-WnFKo;p}v2^J1T_#F9}FP?_);4^beG_;{hY~vT^qz&KtT}#16Wh3c~ z9~?tScWq5Jg66OexUxYRfAmu;VWTm0YB?#;I@6xynF=0e*{42_hl zOdSgfZ)vjQvl-)KW5%u-7i`cu!yJ6FB{u6w==^MqN9SgI5WuZ~Lu)fwapzeRLO^}CrN*|#w%6R)*iseTd<{~Wldiq|gQwA!$HTv>Ik5{G6X4i{gj>J9s2bqqebOQ#O&L1e~r9stuZvG65X>S%Son6!sJ zcsJa&ZSY1Hbu6szU?xJj=u;4Tz%DPJVjkptMg5;c=o~D~p%DtnfpO=&(X9BcP5$8hfa2ko7& zoV@8+%5>}@9qK7{Jb&6p4_K(@v_sbjEAtBAeDjuvwk^j!TOJ^C50`tnZZT?(cbt#p z3yztOpgW`M!3mzst-+c3IR_&2EmNDI?eU<`mtVf*5c+)%q4V`g=EK?#fHKsJ`Y{mf9do9^sHS-zD~Z} zI|l{8G0z3gxkc6>H0oT%scp3c?#XBqGJj=AoXrwGoUh8avae5ub#E2*01EjCZ->ul zEVkc_Nir@0&i3QD0r|h!Hc8K#3Wa7K7i7YS-ul3%@t4BPJNehR#rp4Md&7?Mf&`d` z4(=HbDu-v+OPt^0{Mu#Ke;mIq`tcSo@K1ReS2~tV+qL~_o1K4J|AJB`_4a0e;K14q zpGbBLn(=4y(N;%D%EmwI3tD?92rNT(0^HlCB}6$iAXUjXeppqf0ID329yGZYW&dm+ zuGoB| zXipqkuq~?P+$VXB{m0IsMBnhduZI&E^Qhz0 zW#Fd4mN)vq4RU_IV;_ny1mv)-hhwkkEXpYBXl}Yx`#}EnZ3M^xRdu9?@8E;JpLIF= zdHOqjMPROsK`B_cy0A)$w4086%N}KD8_HsZIzB4ut z9_j)4INo#41$N5pxYjv?B>Ccq>yLLFesUcL+0bWoP?2iLx(a#QKJn2(9tyTS_wXNK z^%ySt&{E)y;!~GEfve+cNQ1ZXm@*cxTl-Wdz}k$RM|nWZ^{^ZagFb19{eoxP%PvFo z4T!~U8L#$n+YvI=WnUKb-traRx3^nh?!%utIv<&rG7Jpe7df01^0&C5x6DI;V1yY09lea+kzc!}?Z)vy+bQxwS9>`81zVSc;_Rdw`{EV}nQ)kE``ewBeyTAHd`9E{e_zJmL4YvEXGxpDM!2h0gMPGFB#sPHOan}UN zE6tQIebUO;XOLO+bs%=N>@kWfatF;)wtB znM3I0(|LgX5j~}@^2HgCU%7w4e!!=+XPzA0MZKNODdo^QPf^0Ww{@6d|J(p0% z8xuW1ntx)K3V!UvZ|WY$p5|6h(0-~bm!6Cb zs6JEYWK5oUJvI{EmT|c8AeQIz-g+G|&k z1nKrIZ4CX?{_9I$DC354eb(MR6pVaayP0u(U*6!DUYpQE=+@~Sed&c4L_B@s#WTo9 zU7mjB!Frz=#qPHaNR>Jw&%VI#c=et#q&xP7bah#u;b0T_Q@2>BY0Ilw*vkFOnmuy2 zsz8^qxZ|_DNa$Hxg9P&uC^)u4lqiVW|EQ11A{E%$AHms{(!Z2b?L+dCBeVcbF0rM3 zNQZV)*kmQxF!9clpfU6-bLzhb(dEE)Dq)sG;W~J+4ppU@FHt|UzS;Q*{S9Wm(XU=z z`=MN&JY}5iID$6k^CMpdr0&W(8*!Zr(DrDz!I5IYAx-mVU&G0ddL#H{N1l`&Z6;%! z<%j0TwRI#|ujFVxfBlb>X5?Z!$T$NVYkSsqbFaj_q#^s`tbYXJxK~+S$$RPz>7`u| zYoB7KWPgJ(=B<%vW~(3p*RrXWOJnh6FV0od7QmNrA37@XiN5cCH|g{e&au4n6=mYR zmonon{3PP=BTVuOyYL*6S5W-ot3T;euJV<>c=NV@ALu#+%~?6G9!WilUUM*GQf}>CetI8yk0DAl_gJ#_foHcwQ310MdPC}jhxKQ(nMJN{pK7GFD zEwi6;KO2{XmEA7pGKhA-=pZ`-$2E8)oDE#tAd(@fgY32Nz18#6|8v! z+5T!t;;GFbZnB$oi?7a9L2DJ%X3Xn@+OWK3k;r(`cN{G*BrKi`=N?}Qcs%gIcZ&Pa zGP)oAs*3gblZ$-9%)%64$r~Aw;v#{I{62R@^~XJ<+^<s^`oVCqG> z2`evQ&F_5{h4Iaii+af-kpPMEhjjAvngsBKh$Qnv`l12u4JNz!9P5~SP^2AGW?~0N z?p9oiS`-luD1`+yKfy*0uK?qlPGb5+fRef;_wHZ*m8#<*_vvG&FJ0uaX=2?U;gPFM3b-+0aQDe+I&zCE(A2pAG9- zFA^oJq~76g+GgVX$+g-p5>8Kx9CkSeYJmnK9b#pjT1jJQo;@cpD_^~)v<-sE$c3wY zj5uZ;NQ2_b`nC@%D=-_u+=+o3`Uyq0~uJ{+i#d{rok>T}rt~;39#G1pY@zpf=`| z)IU>S9&sJZn)vaL6vS?g@mL81xB1$dWrkvXu#Yh}{KOLRlOaIh79ineQt>+vbbjy4 zc7zc3Y^neqSM6KJRD_+s;@p%un=l>oY~vnGz6A(xoL{xlIaa!7z1c74cyF38GGKY# z2#Qr_Y~tqsZeHx>A!(B4Y@T5J=H@=XK;gJ+D>t-~*EMPuAoRO=(s7Vu1<8_T%4Xw* z2iX0ZPseO-!jPUIfgSZtPksz4UBHWVw`RL%rl8T&yZgTlc&{g+*JC+rnYOo$mK!w zC-A#bVW*Wy8*?Uzyee@`6qQHD*o>9)l_=`G8z1YpO@d24E4qwrT|0Vq!>wO1a?J0h zYui=)=?m7#2-)_xJKI-WHUTae$Iaw$+?*A$jGrBk!V77;VB)9<~kLx1xzOTa=MqlAFj##p(oX8SACS70y zkA1)6I&e=p_|@ptCoq_#MJLZ0pn1j<9x(EGi5sq?i^765BE;}RMs;QMC9+H1lWtY9 zZetmFS;^W=s_u4N6rZqmi50By9r_nf{aTYc*#jRuPlupR3EHw%HfJ znJ0$=u{#p0Y&_`Wm|Wd@bM4DZ`1&bd79q2E1hNA^1E!r7849ncRqchGdtKDiu1uU*gvXfI;(v{TU5mkMCB zwTF2QgT3Yy*fR&9UAwSs?T9>SGvw*c`5K3)g|>-2b$r^kWP8fIRa-9JILpX?>dN{e zI?nR9F;Re^O{y~PSouPqZGk;8v}sxz0DU&+`jEUiMt5wRmrcmKUs!$S)#8Ghg9c<4kqiEBjbHoao-njAdnx{&U6H+%=8c8^$}n#OTf!J-Ei! zH3zx$a1ttpFTdjCJ?*~rFnwct(PR!csUbm6N6T6fvTgfQrjFseCsbYd0IkAgJ%=@f z96nb^$_u`0GxI#u3bd~CGVQzK$le^_9yy5Y0d!w95glWnL9~Fcu^{6>6hY{q?b}ye z|M6g=&KJ~q5Z8H^hkJH>qpZX+xA@6z7clNr{p`IQ0yA}dJ z>3=FF>NNcdSy_f3gX#g^`^Z0QF-wl}VB5?AztGA~BygtBqXXZl*XR}-Ie6s{FZK0- zL)srXt*uP%IavJVmrs$m_Q$rOe$ZYh<2u92)pa~&ukE5OqX(6v>$tug^Yx3evj1UQ z_K$x0QK&RA=1vtlb66?w|Y2G-ArGil2PLyIQt6h z-kz0{`vyL`&JFFZmA$Mz0I`?o95<|2W9q=K-}aJ2{?z5$C)ytQ)kRcM<{Q~N z%srUX=c&r9hpJaxE7RuX@GAZ6_MSaw9zut}Z{K_&gfEM*PHAt*3aW!UFGUJf9XMc$OV>p zAvl~1+4gdXoOa*=@?XCK%K`K+zkL4iU;pnH=;8qVJ@U~$pi8_yeOCVW>C?77^L_ot z!-s$T_ty{C|82qY3yq}NkGiHvTckhPPy2#4>k3dC16S;#?ZdEbP42bBX(s_lnlb@P z{tPF(`I1*zieotvk-)k)ZFkn31kgIxpEXIH@h4eS%to8B)#u_OU;3s%#)pZNBfTjt z?ZR;-;{oUQjQ`kpsd~K<}PP>El zJNlsWZ$;(-bWW(Z-&sqvQTfZ^YHWiC(Q~+(K9cbbQs{aBnFdiwRow%>O(vv_1R)4@P zjCaCWX8rUK61vZfcBW#L2HQ07_UF({oHEM%#1zX`Zj?8NzU@D$XA`5KoqLfFaDB%% zj_W(_3{kYvjuViSrPWDmc*RFwxn3y$yB)QyYHKKu{N|#-{5C`xhkN5{hYWbPE@_-{ zBEyl9(WU{n;Kd%@c7|6>kx%MSeSm&J@#~ic`>wbaeaaJA+6$C@eA0ASIlRaYKWlkBna&Os@sZEM-ijo&>sLxFu7dc-P&TGo>g=UtuUGgbyfh+8m zwgTogeSD*v?Fesp#U7(YPW<+(eAhLhMdAsq1EW7Y6z+(;;h{@E%xsxgsZ#*)g^?m? zihdm`nrUziik1yDup631LSYwVaFLL-1u*guWW@)E;Zu;%BfhM+9{h#<)Ekn=KTZAq z4md&W`B(H?+n=q|R$xe)M#juA12@`4JEL97I4fc6()QOpVS^k0TFPoC+%4>lj(iA8 z$hhPwT>43oCEtqGJ6tU%Wi3I@JXZP>1$64zGD4N{5)MZjodP8}euDXt0I_>>03cs* zj$}+)=~L!#U$~%IcqVL~UWkJ1;bP$xTbMV2xbiY}QkhwMr5Z_QdH?`G07*naR7v%f zi!8SHYTAkja{qARZWfQllG7OXYA(KMkMb8d(GiJcH#(j<(BdlM`Y2Jl+T3 z2R|c|A0+>U(I?!?#46kPaYN2A&A7>X?hjmHTHnQ$X~dxWcl!T10{(Y$dBOd6OWbJRXW1Cz71kgq4~>}%aejHjH#^1=?t;GqOaVc!a>Z9!8|MPcr%zWb=(8C0 z<^IDjEE>3Q@R`HtSy(4i9>Ql4fQ1oc;#=tQK$ZzjHx)U#>wr7=F0MKGKzu%^k)?+5 zxtYTe*#zVuoyn7nIgHN(ucD_oU^~554$>~&;q`EDytSl=!j@qI<~eOA3t;6f^mZfk z{2Q4|oZEE_i+vLDtryEay$%^1bSLo~N*L?f_yc@k&+gB&2Ijq==KCYyvXIH!pjkyW zej;~PjX9%g2fBnV68IM-FkR|9G(Tu`=k4^ne19O~R7ZTDAw6^JOzo-PL!HG4kDI== zlaKaZOzv^O<)->RJ&e35QoX(b8iStUK4}(7dh!zIQnT^Zh1v(x{oiSho@!yIy3ulk z=h94=%VP5eQgP$Vrad&?2VHRh_kp{yQYT5^0!J*B6Grto_u7nW?mdLw#Yf=;9KJ(t z=uSFmc{q(t^M0J<=`2A%HX0nFa-qpQF0jqRk8Ch!afS7JmI|?VEGA`>oC|uau)Dyg zA9#^)X`_70Zoh!eLpai(e5jH|8mF+7h2tgBsSlQ0kO$%Qu%wg5%1C?yMM9!)5lc=> z1}PW2NV&dMk!Qs$*0DIr+itrVO#Y}-@g%>66;W@!Pf3YyZ4wdS)QVzGok)n1Yrdip zRa`57P=heQ$(yjbM3)q4Z+fB^Jqa6afd#Bjf>IbTNvl+Z(}ki1`zLv4C|503S2);`cN^+J8hGjL(+E`w$x@y`muV_mfC@#155*_PsIys{Z}uFEjOzi z5(B>|I3nICISYs^d5-Zb|ACWTCepBHSX!czIpQ)r*ACv8m4DF07e>K~iL8@5{<>#~ z9GOJ^!D+7=NKrR@EP4;Civ*G4QtgP_xGbEb*L;BBqPP0r2R?b|!y>m2%VRHCeD6b@ z^k&LVZ80Ay*;Y>eumL`N^}$3pvU;eB2i-h~cf&xxI_04hHf%WmvJC7|?PBX#IVk6G za;Kbh&WU>1iMNWgpznh=AJQ9V*v$aWpOlAoCX1N*wfJkU#-k(bhj4jVyH?I34}NTW z+8Lb>&$9`F!ZSxp`}9FXFQplKK|XEp9NL-GSVHqGywy{sts8{oedrth7xF1X!hU#( z%fwR{>%#y1q-`ma<0ZTR&G(x{6w&(Kah&E!VTZ&97xq0qp()#&q5llza(j`$MFJNI zTqN)xBZ1nkzmJ^b*z2dMSmtR=qPEXffY_&H$guF(Zz1Z8_BLY^3*^4=f-E>A7@O&3 zER``6^XxT->KKQ6#yXu>Gv;x=PI@zu-LY3TytpBd4KI1&>3Y#A_m0bibuRA)Aci_F zKJYIO&WqVt!&t_R3675z(V6QzcfY%1v#ejn+QwTVbM3(mMXZcC7GiKCetC90Vwu-j zmvz0MxO+4XVnYVddh-N+b@%*0}X# z8DPL`J^Gt5_^Q{`ebP(-a%I`aZ^j0Rfm|7ni)fz2GUReIjf?f!T%$bP)Wt@xeEmEd zSq*pdm%g&>CRhACC$Mh7!&vv@he!Bvyexknp5UdpeCh7<=g;`mmxs=%<6F{{LB{RK zKsMTT*wpCSPnku>m zvr&(*&sJzx%03%-l|6G4CN`rnJxZe8q@) zP3JIRK&fT9@)}XZx!v}uDE$>**##nlE zUouLU0ec94(V4b^T@nSeF9}9G0XIj9*NygWRu|r865X!O);_tJRL7cuFHgBy&OVa% zs*13Gr)DSsDJaNBj|VSW8WyNjxMaaPXRc%~bd!Dazoi~g<=E&B~&xkoo=4OP2A8BmM9r0qFoTI*i! zGl*SKp7M$lwd+k8uP|ou-h(#T7|*yU<0Wj>$kPqP$=U1)OPcHjr}5C~WnHHJ@ga*F zAhZ>X3DR%FkaljtI@XQM;Elf|aG&QWbSK-Whc`S#WINTi+s|(3dl_3{6YP6rwD@E% zZyoF908z#@tOau5Cw-hej(erSW}Rwk?)(le8I?7!)M<-oM1Cjr5OoP&Byf?yI})&; zdezrX%@x%);Hy)0std-0`S`{79zxHH$34iwe3U~z!un$KZ+t0O zT}Jmucf{n0N(}_&>Ll%*^N762{E-78e*IH>g`dMTIcVwJh{91G;>qDR57bb`jbBOB zCgi{i^HUC^_rO5)zIbpC-y*F(V3$mIEPZFz44YY!pIQ?wNjoTb}dm->)}IBW3Hd zmOxb<#}v`K#F^h}oN+{7G$${NHQ>b$rF{9qybx!cFYz&tWjXhG;)I`e>V~%Z&Of6@+n|ii zZA&sLb)w82Q&4fz`pkOA)gx)v3F`qKF4Mhm_})YK`mcs@u1Ptr2(GO8eEV?A7{>J* zX-fUmO|LRmv;$d=<#ADG(GLcp!2@(3IasPd?e`)TI?k-XSikn!tYtVZCLCE?uPFmr z6VCHM({#w(16q&p;@4_#Jc!QWZpZl^M8C?50vRjOw=C0scE<*r9yoX0$%~INFX&5< z<{4<#Wn?5mRo(=l(>itE=p%a3Lvz-z2kG?RTvLMX9Ddd(zGE-sY@1D=0*8lb;WcX^ z=D~U3?)DKnGang!Qk|2-cO1aJK7uRiUXyVI}YbeE`_cs4vwGY$9S@E3Vuh!NlxaJ59nVIm&T`#O+Ivuwu`&869~EJ~ z=$EwBv5~R;nmjrD20f2gKHs(s@+mLHblzwkxP~WQU)&;CYNO>ue&DA*b+vEf zgLBn|FMPQ2MKp1?HSpmwUt*5o3k9eqkdp;G+Q6 ze&eCpI}WItheC76|IWSQ>T->ahReD9uixmm+FjZS0`@TZ&BFujRH!dJGNy`I_hzo`xX&>RvRCYH>JR7CyeLGyA^v>*+ym&m(Dm_wHqK$~ z#~&R+N5&b8k=OjntF*21;eXl`$dm5eIkG`G8_h~AZGn8?RL97*0PQeKupyHIC2j-vCzL;245it>G~mBa16godzoDlcLIE^<&O(=NrI7e}d| z(W7|*FMT3oU~Gf7C3{?vQ|yKHhobptr-cp6uPycEF z=eZZeq_qP2O8>lu7wbIoL8@EZ22Q%855b=?J*nT6IaQ!edVQgPqZiXwz!94zGUcjM z8LMk)EB3V%;;)D;5SJjK5ao}2$f$YNy*;Xj$lKS{A3i(LR(O@pw)14+C(jpvv6Sp( zZYaef(&)hLnH)SBHr{e9R~c^47g0v-4>F;g7ta}wXy+ZPkx$!3IP#P({~#{(^=|!A z5QW8d6Jvh}T;Szr@{z{BrG^g?%QyE%BrZWihbsWeF@eD4M?$!QMj69n)`Alu>?wS} zNt1WSUKuxS2hq_>*bsUL|zk2J#uiZ7@OC~I&zUUVA6J0`vLT%WQZv_^_9(LHv(%s5E7#YUjMvC z?zu6Jasn8?vBT@ip7ew_@TP3*3{w`6rAs{0PFq1Jv|h^9mKY%K#xpSV;VK9GjW6J= z-_X2hmxsie)bQquSMa$)#N0F?Sj^|&_`M6wff9cMD`mkq5`>j2HJz~c+qLAE8cQ^K zeNO~bDTq%#4GScM;ij!xb9T;QOh1pCw#@M9Jo{g9f2w%?iV`nnTqN*w5;##N_Mc4=sc1$~A91QQ{f{bi zXoxLqxUq&+y!tw?PlEc(QviHnEQ<^Y;sCKce|-btN{RKI?8 zp}|dqT^M|J;g5v_7YSVucaG78&gQdF^Y_8NgH~bI;F5u-1N$-Z1Tm9Kj%%QxPD#v) zu6P05>Mb$-5oRvJ8gV7iYZ8zCgoOZhK$GEka~j@pp2oMF^(*QmPum#SDRb^ma=mji z@b4u3MwGnFc<+?2J=1yoG0zS~NK*efGEUH3ZY~n|Unv3lMs!>{)UF+t0tm~~7mvOE z7`A(-FQS-UpBu*c&u_n=hyXiXq%%XEqB2K%u!rHIUNBUkM zg=#2PUKiH1S$XK?qMi?PSaE;#jl7J4U3AN$BBf*zjx-Mo=0P^F*|-ZfW#{5sHWyi_ zm$oUkiicmmU6#DsG2=510IY}5T{X4Nl#dxr&<7S@YJ0!E*7vwQRIv?Xz}zK8ZK_fOpp-d!j^ zmHoGX_wh<=Q_@$<+QbxgrVSEXa@zi<_V?J<@#@52EA&X~gcc$j-?EPdrc8lXp!IHP zD^n1OyBGpWT|*`@!r)-eemmD5+~X$g=qGE*K1QE}i>r8!VS$eMzY7a+#0`ptCHefx zUm{iZO12QukL1mG9!9f(d5w)sCpweUo=L=<+xZ z-R(o)EP!KIv;|#IRg)K={DuM%r6E}+0awzQW4S@hhufRuL31~K)t>`Ru|?v?g=q1K zm`gho8`9h8_ijo+H&T=jXT5imnh$pK@SpjX^DpTO&GN=%(1-a1h<6_428}M*&z#D# zD+^)VAmPRnXrLT%NmTRpzHt&Oe$)N2t^m$|B-MCjyrm1HD_!wp{ewhAXV;J71)H=G zc$Uvw<8@ZhdobpIFP`Y%y{7En1-t-UByf?yMFJNITqN+j63{kkXEWwGgPt^_CtZKS zNAQ6?`c2nv;X3(j_?VG!MpMu*W6Id>lw;bmrH3q%Y`f{~7M$R9V{kV9D64SCT0OPJDh6~{o&@(gAe>DG)B}ZtWo4St zQE

Ww2F1Q5OfW2{3L<)8aPk_-;ONbB$~Mj8$Ku&6fq#$$P^|2W~bwZ-?tW(7}ef z+3eTxCL5#}qu;Ud*^ONufIvFz19PJp1-rqMKgaWtffAqsgyORc(8K0w(q0g*aN%3J zsrI!G%VnEfNlP1wSopELvaCF&-6|6gD*IyG={E|ZebaVH8>5chAo|SVh5SXX#T7|Q z00lUPc4H~cC>z}JjK+;lwgKwy4c~{hX?ws$Ha;K0xrr&8nkcKAGQorFulVx2{VzE= zumY)stsBSrZh~`8fJ>jS{X>Jaq#WpgKc8j!A};%NHe0X$IR_Z0FDSU?lgsT6oZvuq z9DhOnwUj9AiWaU?iSA^0T(S51XGB7 zq8qe(S^Gdgxaog$eG#9w+;xu}wpSM|`x!a;Gw7mVHS{Pz)>K-Qv>fR1&NT$|g7K{b zb)Yuam2!1T4XV1u1L+>@bDhM4eZC}p);_vU4JPs7#&26kauTp;tB#@^`m!RcGu#`! ztv3TqF1U-&|({$uqnpD&mB^)kl@mI2-+U+M5#6cM6a2Cg zB!BD0`i<vM4f0>?pb#gKov*OYL(PPom+U})a>C>8-jJq9kGaq~5?c>kBz(^g$vtaXhX!9y_ zLVRpm>34&X}X&RkL3q>Xy;3xyu?(_Z;{1GAPTz7IhX@@xJ6#xoELg{Se-OZ zIz5o&SV5lUOulfAbJbxUK!4+_)Y>Sb8T0yeS#^*4udtG)?oxr~dHz!U+6Q>S-&nEec5a@nQfE6wg|S2kN|( z_4v|zeT^;TaPPIUq)qvid1VEc(CG{wc-x=;a*6X+@!6&nY{4pH?zKO$k;^V+Ud~*Y zFULTWHl=xjoBGN;lDT6JLuSo_*6JMH{KCo>{_Ov7++-c(kQBVxmP#LucfGwiCiR6LYQTQZl#gWGs$bk3f`#jY1Twcf?v`WMuGq;oF2))dU=*gzt=GDgkZ z)wKiXyS7ye0S|w!Ie5UuKI3@Fm-Wa~l!qwNn88P|haIe!)y7i*WrT);x)vp$40TA5 zPk6~3fJ0{MFpdXm^L0q>GY+xh$m8K$JZP|OJGSb)J7YIwV-HH%=#;K^k&p5WyPlM; zBm$iC_VnF$s)oT~`w3fS-RjClWJV%M@7RsdY8(}TV;Sm$vYvC`mGvgqv#6U}UpVC# z(m5dKOC4<69%Q@c>)IZ)y}k7yc3;ZS7cWb!5#g(C!+p87F<)}vflBaybAH4j zK;5N?s=fwf?wq)kMUDqq6_v!IA_UZc9)>GO7xXBLL(5-Bt-M(d9*P(15 z;^0{|IIgcBDHA!cZs%d?RF&nc*E#U`3AxoKfHTGL^AV*=XHpNyqx*@Kp_x;zRNG&` z5&7_C=vxnjB5Ug;`51jP#tf9Mp zZf^MV1rpDGA^hmuzP-0kMK>w}+Y0j7eDzN$mc}^gTF#^-6JbBLEL{M?J7EB#R~DX( zpM?m#+}V~XCw+u=;dn$TSQW~X_U#(>^&@;RK6?Df3uzgzUAqRsIFP0W54;#PL@q_(6xMF8qv!kr# zti#kV)K~v87Ly;Ywp(k>zm}7>M4h*9DI*e)4Ps9@>akq54~VuuKR;8Rx`R25_O&)P zBc8(gJ7WY(5=9(8;LCbY?oDg`Bazfg>WQ%RWF=arm_;M4I%{T=eTp(pjusqsIL|N@ zYx+jWP(R=tgHrj{qdGJDt)QcQ0$)WHIv0F&JmKY6B+|0l7c{Cj;-EHimUi9xONb1| zXOUUQl)&&S{pF(^a+^y*dFhw$@V~=^x>6eDr(=}D815Kcc-v9iQhf0s z6T`~p8|#cjK^inuMNa>$?-!iY0YppzT2QD?dzKGnK(zhOdfLk0%K|8Aj*&`Fav5oo zEN++iTPKa0upkS1gPlOp98(Q9hiT#{ZRR)>HVEGFk9ms!eF{K*xZZ~mK;4?M@qYw0 zWe&o$;Q-sBxbjneyiNN@{}i_|`*oB_@Q|eM-?2R!mo_=b7mAU4sa~`vw=GdptZM`Q zoL)jqv@~vW2f!viIg+(4UvmK=0@Z)7TdT=YTvOZ&&P;XRLX$vE2brauwaKBnK&f*#&Co!AOVbnAg2 z$wsU1k{sbIS~4${_PWrfJ;rUYOPCaCZG=5@%Zo{yI<_GB^Vjy*;O1|Hh-ETuZ~`0O zOqMiynu)rocf#6B@jbdEWuNJ0@q+BQe)xd&4R#T5so{%u;%1} zMFl4GS=?bF;RE73257oYXrOBuCh=>Cf(I7F{C+w8R$NYebQ);1aAYXm7-t9I z8ci1pzw+?pzyJGd-dvf36HIVjjGD=M7gx+L*@;f)|6`g%Bg4aly!} z2mA6xkmhc+RQzJ8frt-wT#>FD?{NiQ=>jG5 znV-x8bCC%eHn2wWJNdSV9|SD)dsvlxuJ4mh@L2SF*N@_beK&0t_a3fG+(iQa*Cn8q zu}fChoOin3Z?S}3THT++m! zlh$iiPqF|k3>gg9uCmdY4FS^bTmEvzX2O|>Wgz_T!H`R={`?9S9Ep)8?KLzqYWMJY z4$Wo-9n|8?8 zpuh4;J63MC>&ZY;7D}inYU^M_2Vh}ew}+AfYu}puk~ZZexTDDJ593T&#`~13J= zMTmInm9RwQdauBh^v`(UE#^NN@6fW)TJ0HI65pu#U3|qG9_8Gt{7-ChL=F>ET-6Q9 zkUR}p)6^uHns22a_dNc*9WF!pa7SqKL8>FtU=^T4s1!CAt7 zo8od?`u7w=>tGu_B$r1P4FWbLd`lW;Oue=RwPQ*dko!Tag3I zs*{w{POu`Y)Iwv(Eg^|-+P2s@I)3wgc!)L1+^Dk8{6VBeoRDFkKSdVM2$6Xsc)qlO zZJD`O=1h4AG`6Jkr`WF81$k~ID6V|wRM&WYymdEZFyTlAkY*YY5`X^KIr zi_owQ~0$NrPhy{2T%!A+QR4Hs55U9C+Cv@1sll+--{dL*u)-QB_)47gzknd5A|nL5TpiX@f;?=or`j| zwm?MN*~recASR*h#XQpGxG;1wfXs$Kc$LTSnGJhx>SS}68{hQZz=xCed}&QNBPus% zy~2~S$woiYeP$qj>&4CbFC2jIpapk0H=KI-O~(cl!Zw*~dXUsqu5Ez!i8Ggf;?h}X zsjsvh*_yH_V=iF)6X%U>&Nfe0KOmTmXG+X`BL@PNhq!lnWE~{~Y|{(0az&obGsLTN zQtYjTpXbp2%tkrsdu$@h3#ssLaBe8Pvi|)@5c2!LS8v)i$iXHbK9+V{Ft$CN z8|iS5U>RD7#0tpUrx_wrY~^PAwiKXS3I1eADl>LGDhrc4eEUC~dk zX;&UNyj?mX^<)m~Yh9^p>wCG7HS}?F`)Ke+WLPX7E;>CHMXYv_8X%i>>Tt73`CGpF zI~RV?c;C3KPLaiw5GmLOj7cOvfGftVQ4pDp`Poe5<|XY8{gQaPQPwrwB!AC=^Uq&6 ztndqtuXuja5$8bzMeOEh^=-azXn*A2;<8@5%9lQIs?S2J_XQ0+DL=RtvNEt>CZo5l zd&@Iqe_70?yx2e#Zfw3fE8*SGSV5bnJ%jf8u6KYBRO0l{|KQEKmf=L;;am6yQ$1|C z1EY==#>3g-Qf67Zj!t9){T%S)*|c9l%-4tP-{$cT{S})|IC2E{(6y)tEa0VufO-9C zTz*SW+7R*9H9Nv-0a9Lkq1l_ov$4^6(yVMtd%{$C>{7}hOYqA3D)S&7iSmoC9wdJS zpW_s9*ak;OkN&=i&Zd5VX&SszFO`9LppR>@7yA`Dka2V}I899bfx}JX?m5V2Wo&Q{ zqB{ok(B0acP2LQS5w(v4`OqzGJtNsI?Ix3CjxJZWTb01=5*lTC4_m|&`1TGTTBv1{a zK5a))uQ#kd@giQGnq+G9YoPtrqypGF_-ahmo$X-B`;;A4Help0V5*xyw4Mu zmwvq07CG;~We$Np#n#55do$;OuRNPZ_vM*#>{s<=>3elNRXB6ZTrN@Kji3vHV;YIUYv_~ zFwQbEo{9bPkUsX5Ly+2t;Nh^$2fn17vq#RGpxv)nroOEk4toYC;|Q+enUwBXfQpVm8!jZd(RsP&pL3njEYBR^>9b3)r;oWs;s~lVZZeSW)tBYX zzOP*SvYysGy_~Y!h76}It?{9F?VmoY^MI}|KRiJ=$<-+*Xwvz@?d&0OZ03F9SWgnrwq;uhZs^aLkT}XETgsHqe1O9KLfITY z=4>#BL^+6jiw?NEeP!N4`*%|fYYZOVzV{fSLat!8Nc^NyhNI#WH>j3^{@2-9D@j)GIJEce}$#&;h+Jl={ zyqH71=eS=Mf`Iio$JQ_K>(|VER;|r&&LhmrEwoUl*0=ELdv&BQ2eEvcCB#xuI6lVh zdT-ZE)d{u@+dP2CO}W}{)IE|<&4G`7Qq-LWNx~L#y?`m zZTV@7*p}#B%MUHKVatauZKbxxig9tC@@#tw=!LJ6tE<)BwuQ=6Jw%Ymo>1?k(cPxDb-yvWM69=gZ6xFU0d+{;r>_j zp?wj3X!})16_EB%V^J*gG7%_I=>3=YD8ojDnPgShfJ@~eIQ~cb7G+9b^bXM-qj_N1 z7pRdDSi_Hbi5h(Qq9EjDo6fwO`k~w zFWb3s5K|h*m)^Nd4iD6;p)|Y&Aj4jRgZz*azJ$&kS_#<~e z`jvy|Upav8`cPkrAiL4Iv`2MQ)~@jF^LG9mR7CbP!q5_ZNx8Guq0D`$lPG`oi>M@#h07ZS#eyEXbWsB zQk;Zh^WY)+U>|GPCvE4-eC&+APWrXYtuw;jTTXN$bw&yKQ!a+^4bB*%^a~M17(4)m zme?U^QSax)#OgQX|KbX*hVM?Z??r0!z|>y)qU z3B?AjyV5Cj35UAUT9qLq6hgfEHhhVanAjTRp0A)g_F_z=a}2EhlXhilJ0IFZaOhUv z;E4lAim+V%Rv~@Ka%qVS)g#)^=#zs~C6RhnH&ynPGqDt)Oj?dzpp@%#jXdEm{P3H# zG{=zgBM<2AtMnf;EL`NvGt>p=*!vY-Q~s5M`R$+OU)aciwm5RIuH;|$%DCZ`{-iDQ z<-eZmp@mr>*YKnql#_a*?b0z&=9my~do;fHg=<;mxH4$G^=84Q%Xj3U&y_q4wjKy8 zQ?s;MBE*EHP3+bA39X#J?H$YbF(kNCRLeWUy_aA5a@lCtbq*)}A~im3ECnq28b;(w zIZz8Wk@T79q@WkNx))3D3UB8LrgZFk{`l>9^c0;(rYllWb{ zdw)RWU2d_`3(lPIq%p!*M{0w#*qMWIlVz&Wwy@EEq+I!}3>D%faan}d2MRJiB zB#8%q@(WMf>sqvZ49D7nWnHOH@rCR7NnL`oFUE~7v7G%cnL@EeR>qsWM=$8(LQ-fm zKX{}|AD+gS{=DpUe0X;}6B+YsTeJSAS}La?C7v>XF;VFbZLJ^plYls3{I8!IQ^G%i z?GlTTTf`wi{4|C`>}k;Q_RI7xMM<@t0RzuKMyVDN%-zLuEtE z4yU2bKwP6%J!fvEB>mQlSmKtET2&zU*)`=KqGLd7%!^QzL3{ursL)QbOQR8`b?js5 zGOn&1He>6VaI!US@{{3ru4H-#hWJ+KJ874wiv%tb_`4+F#^gVIz1zJHk>8<0v#2xE z_faW=E(NuNt^xBRB3d#uRV!_@nSuQOpS^cclN-m;ZOeJLtLE(Yf6!?=uRd%2KOre1 z(=JzcSMS;1gfbPRNDu%)5WI+_f+C6MVIZKB$o)CK5*<@V`UMmR8YgK413COURVEvO zPmlv6Vrf5mB{JBAspSVAeC51&LfHIA2EYN(Ni!zaX#=ych<5Fn1>B$RcnLr|a>ry; zHtQMhDVwx$Hx3Sk$-odEK)*qpSK^l*{I+c$cwKP~yd^u=_*UEk^C>a=&nBY`-2sqBM6%KZbkH8$EgaA0H~pfa zyYt2qdD!3*_w%1t^@DD_122V>;Ek8RLg@sh`E+u5(ZgD6SUm-xC*;tloy_ou%R2Snb67@U}*jY7etGVpC&5L zA44_{4u{2Hl275wZ;>zkB*C;x!59SERV_dEizM?>QCpb}3cj3R>~fdW-+4Ki7lYb! zfyJvWa_l1RwiEhi0Ztylj{L#_#D?E*5N}10{_v=D_aLe)xhb zFG$5JKK_FR9@P4UTYSPle!K=aayaaI=t^u?7gxq9;=jh9(2CUbnH zoi}f7i~16YmMOec=ap9|EmD=!928gcWtu^V7x1=+5BLQU8WcwsD# z4fsQk$V_*<4g|K~2^-f4{oKjZF@aGcP z+sjkf^YG(y+vUO^{R7ixw{2~jaGI{R`$J>eLfcBhi1aW06#7-U=zGvzNqe8UVEQ-v z0v5i=lSNzfdsqLlI7*BWXTU_?dy0{_OH##mhke_^EOqeWM=nGdjgZ7Y7`z8!| zJ1LzpxL-@SVd27!3rb+lwInXq?gBjegZ6>I4Mr9!nzfY|Gh~+S;6PZsS-e4i(lNVZ zHM9MRpBokn`GpN#`%c}6MKb$hELICYLDe)__3K=LxZ{G&1z{v7Tg-oRv)I`MuyOfn zUN&j75UC4@=mX1#i<_Kxw;u@}^Ae(R0mE|ng6pP+SNg-<F~JQ*gVO%CEk-5zTRFUc4&|FS4MBEGb>JCxQIhcQmZAI(^brF4-(@Ue`jb zEMRw07`I{CPKk43vekvHa#v7#pcwq-C0C-W!NtZf@AHY?=D{@+Mz9LI;@- z$yd3Z$6=99dh~9TATRRjyoECMwmihuo4jT5wX_N2HiOPXnA7No+2>YI#O$&qw3{qj z5FC#H6|<uZP{a1?*Ya_SYw#WAHF&_xJc{rg`wk`I0k_nb<_oy)g?*u|yiHeKsXhuS z&W5|Nx(mmCBGj<>}i$7UK1oB4Q}j^ZKw88nnS-b zX&tfcw@vM47wIS;V4>SIAWFrz{&{P)h%99C*Zjx@e8f(YW?h|=Cnd77?Xe%At@R6y z`HG+Krdf#n%|*&A_%$x`Bq*6JYUW973&Vd}5B$1jv%iuOTAmFJdu1^(2}`)}a!+I8 zr+vszE^!;rwc`|@VOr73H+byV7{2Z;9XL`3L1ViOG-1+H#-bOWi%qjY7X7xL()S;_ zZR@uU=;YC`DT9$Q2)E067!^~^%t~nL+6PgGvD>_p@C$p{fS#|>_DhZF2a-VBZ$IUc zO)(yxV|hKY$@1%eyAj>FEZQI1u`GrsF6Bv{%vS^+!aOBN8Q7j^dzCTI`qw>uNEfKP zMj?5!zL3~7T^!s6vo0c3dfB)SuJo7MVurS|L9zL_?PVU4h0$-{*+>Lg7X`{2(a6vq zf%;;94c;JZ8$TOD(zjUovP{(>i&om^WxuNR{w6#>LUAql_)ilh=<;)sz(oRQ5{SXr z>salmb&JYm9g1-?jW&?SRn=BV!&kj-+S+N;nsnM5*EMyME>_=7H9wYr(lFZ4R6k4?lrY8M?7eR2bfeRkVf(rK1F0PBr$!FB`p z%0``zHNorn!tqWvSS>q62ipTRVoUJnz+BdgJ+Yzs8#EblVY3`R%e(xMugFG;k(1bb zc*&+5`z~xerY-FTbQVkJFss@VcR+4Z;R~2&XMzXH=rYfS2jOIZ|vKc7i6dKUMQgS^EC99vkDWx zn^kQa$rEMS_Hw=7V)N?F?YE9)Dfbu0#n7@%jNdV%m{K;$20!X;%f*n;wA}I_&xYa7 z7#G>87nYeDYAjCxy7_K-)!l471x8uAF-X@9Qdz%e+opb#EPWK{+s?mG7HRJiXT0rm z-`;zW9R&O$nO{VRe6(+I` z59R&6VTCtDxz7X%|T3Cgr=k2i9*speJnDc5aOhmUq@~ z+Lr@^eVu%5nEIbXvha0N$fF8TS_zIoBR`=LoDStT#0lJtLWP_~3H~7>k zc$CsLW#R_NS8%*S`$c(q&=Go`x^D4OuX=!m_1!rv*3_H9=K*rl9bjNaT zMCWj^d&)GM&?$4v>V~iSM%SrN>Z?3dCJME8WFnt1>YX_uZ3lNZ;wndNP4gMO(gynt zl8_++q5#3GhcWg|*Wdk-<0CEs48l@8Y zu>pjQ-U&b$BKH6QKmbWZK~(U`!%ShwaZhvyP+eLn3_G}BedZG6J8|$~ShN~fJfTN? z)(YH#kFm;)ddTM5XATN*Q|*)UhSlXc>>L#`WtxP^wc*D@9tcGWfr+%!wSeflO^tcxN8 z|2@3>1$On#s**y-0_RhY(C|v zhS&i&$jiNXbS!sAo9l*o!=wAq>pBYrsdV8dWrqFEp(Z>le}Bwx>o%Ts$q&g?dtB&v zQ+`(*aH?Ktd$liK%E7k8xdREQ=jxvPXCu084KhHZN(H=*+-bZwcH-a4yYvc1IZ95r^)Kk)YOiAp{joZ-=A((d~X=A3SxQsK;30gj-AVaS9+?9Hk9AjD3FvZ{+~8kZpl;>87V+lEdLMg>{4$^AJd36j zlIl-wu62oU%bLIG2W2y__9b{%e#x`xQV_s~jr_xMrd&L&@IL!XbjmZb1D8eH zGPPPFL+rD(?>Lwyb`ATMO_#cF%+t9Moql!t=8wDeVelkYzJu_WanJ2>^@w(gh@ zD~C#kAKI@l*ZKaIM|d^0JeprzB+5fDCfqRN6Sio)Q4Y3A-6U$?NZxfP%zhCtVai0L zFY?wGgYqm@i6ejh@@p9^e)H_RE$-wgF{B`g^0lbqE*ww^Uh8D^Lyx>0oK z$P$H7=9a_tCu*DY?1&fR=0Y&LrXgPt1p(V^!D4nW}i_iY*5B$>R}Wp44>fLA4s=9ocf43k3HsN#Ao^(91ep! zCGC}x+3)YEH-9AS1=1TOZ+zf;#(+1)Uk?n zS>4tyRkzV5{?IMQcGe>sVQE__L7*(PKSE4>NgZ=uhXWpL^USTZWnMa|L-dCn%Vl0f zS35(T{eb=@tE8DLoP)3Yw>_ZlI99R^n{pK#IrLB7+n>yQIyB@zVdgJ+F+b!Xb+)`l zu4YR-gzFlAa^+e0(VgW#fjdpp^4d#2biv^N`4yd6xFX`Mu--wQ2Ym4S0Rf>5mc}?MHFeiDIFPa=9)NxJck*2^=kt=6ZFaR-H07 zH?_&eJh#SwGxWszZ=;qPhXE#SI)v#A*&zb2?O+;}a&c^!$M71~PM?h)jYMt|(%@;{ z?SMHc?a48r)LIg{27V6QT{ zpInp1#Sxt(_63Fm@~{8sW{8~bl}!=ichJPm;4Bjc(fUa%mEicr*(WZGgi&zgZuREu5<~1R8;WhcvHP0~EOcV}WPI~4PYx@@tDD79^&tx93z7ES* z+|9Om(c$uO`V@)sIYhyOYZy&*^Jbe)UOWxdm%rxCcTP`0G1n_(Xh|!1IVxJnB`_ zC72wOhm`yR+o6HuLD_6+-V+Q}5iaf7xM;*Ms5}0~>~#A4r@u*lisM((q#XYg@Mi#W zdQNw^KM(&Ip1&R^mG&v2!$bXx_ONi^?L%%hxS?OD4`C%TS9GlpQxA{P1Ezb#b&CBM zQ~Qwd2#BO~0(%5KBLB#LU^9Z*I5=qG3CtN1jIr6<>5tXf;gVdNM$j?;35lJ{8<&d2 z2~X-tH=w(4!NaupN*If@U6g(2i$Pv!`VvW#t)1M*L-L}1f*%;##t6zt`bHeC=rcj% zY6A<0Yy04Bn4FnjH#xPf&}XhCl*j&HSSM}AJ&ezI@x-)z=(ym6=7Y9~@JtHUj?1v) z#9rW#l=q_HSk28M-EgjL?jjpsc5QdYVvG-y5tA0TZC@72QdY`j_-=lrVR-KnE&YkF z9K(#jT+OgD?zNtMh<9Mk(*jv(CO^&F#Oazw;7M!PJdbPGG2Uk{!zO&(W4K}9IHECn zOOuDQp8?iAzlO#~ap$n-&&RamKZm(IUnFpmz(oQV30x#_DuK4er(x5t8TT}{t^LeN zw#Bh!Z~Hc_TptOypF;;`UX%tWK*D7qq>CV2IKslU%q!C`n=gmC*sqI1yGX+J)vPmrUl{TWlb(M6!h#|fA*IhOL~;RTvzatFJIG_%z{SpBBGQZa`i>_8w&#a(ws$z9AObf!BH2vt+^K$@Ta>@oNnc~SCax)8HpJ*oVZW{M*Cn8$&R3oTz+pzy{5>*MBg0Y6#x>X(}^ z(0AX%Tp1Yt=bng3}R$#*wWcXyr(%lr2fnU_!f#f3x_(DFWX?-EX=6`DGy~0+|(2O zLsQ(ve@Z{;Lt%azZ_b(zT@i72A`ZVWM?5S3SHm}aqfdiNJYKp#qG9Zc+wlAdKFHS3 z(D3mu@_Pbr`h}|k=24ES11azgURbZm%g}A;X}Dp=&-Fo?Gy>XJ;sUzhw{qe6;18Du z4N6?~v6r4cbUf`7xzP4-yC^RkWtn%j@p1fbTjs`v%yE2X13DYgAK19yh6UTz*gNY8 z@P28=MS4zND;qE-Ox#`9N1L?dWVU2`<))tPVX-^Tg3V*upO^-L%%n!0M*Bp}st2}# z&PUr8Ut!}c$`&WwVgRmLP@1p7lJCx23m}Yj438JIXc2qI=!o99#upAd?A(UNU0dgc z?Xb;vfwgla-~94dzW&8RV4mw|-h{AhPJ%z%`PdR<8GNbxtDTi6!u|EH8Cg8|96=c^<)ODZDPa!S9F8q#n%R6KBvyPBGE4X_|0qKcs!OY(Qsj&iT4*vdcz0zb4Cy zQa2@_^0qbhqii?q7uw!g_dozG2b%n|J!a)wuwIr^I1ECf3z=AAJB~T~p!(75tE7fG+Mu0v8F4 z1oq04x-xYpHHo-GbgE;_u%X)BRDSB%^0(coWef-yZJx50i+b%EsjP1r`$d>0m=O#v zwt~-v#vYM4eFN+fn#;4uM#)T}%r=6lL%b~K20X&S&4WSxdhbZC(jGOjWTa?YH zehJl^aLlf@_LctrjqRLtNRx34-Q0}-XdhnObm(R-@M?3-hkaW66!nvk#O6HjE8H+h znvAtTDBf&{#n!q(E*m|ud;7+8{4gpmH4Ohp@ox(wPxia)f0>t8#-xs=UXfeY6fhQ)*N(XzZ__3qTidK@SH;!aaNA6I_Qubg zLOvAZ8a@2kh)mn~#F%#unDgtqDyjM=tw@1q(dNOt^Bf{7vaps9@Y8NP{z}{A<}@xh z2}i%hW01&5+j4-my380;y`BSAl!fKs_*QbcN%(5{$(v2Ifnlny<>CT(gSw%RjV+m% z01wQCzmiTf?LY^#(%-e(? z=0E70WaB&=VWsEhuhbdy%x(TZF@F*LMmFhJASdS@QuiY(vz1aPBLJH~WWRXZ=h7vP zykwJ7GB2b6OJAu=nX_TetDCW$S7Q@8{r@}X)!2kCzu9Q$oEJLd!39V%Jfyz4{v-j8 zr_~SgPMBY&_QL@bu{#y1v*;N4M*@^Z>%4t6=S^TJ<@tt<%AtYu<6g=48=EmRrxUrX zIM%$lwzIZMUAJB#H(c$9W$ot87ba=mn18ZdQV${*NJR~Vx4hIL{MsD+rgdJ+dZ7xb za~^l@Yz-J%~J^2d3Hz{0aI#GB3xS>1A22KjalRJpp|Ha8oc{TS5T z*@T|{usZ4fedopeN<2D^9$bOfaYrjs`HMagW}MYXxZIYfEPyCuOiaWl^2E#&wrVz^ zGnQIwh3yOH#DV0{T&`Or+=!04#*J-rL$oy14a>p}<2TM7xpqo>hi*BKC0%9gEeQB5k_aKKC zHY7g-FMk$eHvX6g9x~20cSLudny;X<`TPrq0sQ_4?(eK8;7jOt57Z}YxN~{hZf%+y zg5;z2nP+6KL>)^xY$wN=|Eky}jE#sfkT2F>BV*e~%Y*>9lf#IHAHodxvYa8U-dyn& za%J~wJLd*;4nuh2K+$XFO5OCXytGBiGY5jWSsT5H9-2pFe2r{gNtgLL>y@elWw6I~ zrmczH>D;^}UtMyZl#iCFvur-Ur|hgFJ@mxv0Xdrrt*68(k!*%C`!O)9+^|o!xs3WK z0}qS=7i-8Y-_Fgd1Mo@xqbrd5?B?^YZ2bL3<$Po_@bioFmCRSeYrZ;-T)dQ%UqpA4 zyZmMjTYX6#0H)%uE+}GnGo{{$y=dT<5d2Ce|KQK~mm=_4-M{9oC%eD0}su0@g%mAHZCi`0{8_JaQtJy(UikqW%mp;u1w3 zr+pb4kba8^flqsnZS>F(OX@oe*q7@Dba|xiX#dck9voyn_O=d#U%f;o&biojS^nmu zEJCE2*JMZDP))o6RFrkhs32phS<9A%`tD9UMzh{l3>l|16qMYpGk%Q<~)?7mK zik-2}W#rHxZ8drrokTBlXa|Nf2a=%Jc3thasUO$Ozgq{byB;uc#XFDLgzf=R*0ZN? zeBIqm=x(4#hf?>>-?JfY1&ne!Ww6N1uo^PUO>BF`CdAs-jKv}_S>c{NIeUxWoN9X z+G}pxk@RUS-{%|bjeAJQyLyXW+9p~CFAQQ{umye#hyVwp);K5IMBm*vm8-`Ep%(Rf{9`=_3_?yBF5XGw9VpH*nEoEy`I+fPR(w259h1Q>pk4RKH&&A)9F`86GNDjV==vrM1Ov*uqJ zD=YpgKhr`HG>(5?&?AP*=m;)#;uH16KM(tUqt0gRx$;Xwsl!ioTz-_7@B5}t8Qheu z^)5F$tRDA{aR3dHvOGy2eHHJzli?zcS3pJhE_&z4Cz4{sB1-sCku zbpeXJ1yC-0=i-#Su$dNs>}96H@zmVnynq-)*gjFIKY(;+dk*>9e3OI_;EYyR>`2JoEW0) zg-%_e-n`^+r?yF^sU1Ym7qt_qEPm}+nvdMhF<^7;e|2q!;mw=&qJNb~@*?l#EA^TB zYyE_j|G6qpe*O2sTb9GNKVINpZX>WC5r%ez82s5MFp%Lp3EuyReI}qx?hHk7LYiaDkc$`z0r+)O6hXvg4V=vQMt6ELHnCfydyVc|2O zONA7_O=Rip?8eYrVXeREP}$MrG%qKX(ln%L!w}Q4XK;ScgvReovi#S7efjMV?k`_h z)WE5KPJ(&*xdWd!2cb-=bt4@EQ#^Nos zkr)4LK4(JVADkxUM)Z8WWKHB|13LIKnN6A*)0BVZ$6p%Fkd`R78D8Far08>g+ol;V zuhR+{AEwER;riE=Ht_J=VCnE5Fg)Xy5+J435;orFY)pD*_>``!OmjqgAwO>x|#0_qvR(xz&0rR`k4Be8f%?v#Vgj$IY ziOn6GXxq3z!n3rSP)GmViy?stmmOL#-y<*tHFvng8SZJb-+069v+eQ|ZQv~vTu)4b zJu(?~$ICniZ?@;=P#zozW}=LEUx;M`I%y2cB-BiN$geE%=YN03F3W{iA_9~AC5`Z= zDz6SI=}|C{WTM5>YXvkJTDrw9-VBh3jiD+F+``)G`|{oi zMki*!dZ^4bCxr6CLz!I>_lbNv!RHGI%RZc^tVzrzx8azu4l+w%%6;Vq$dp`YC!tIT z%8xPGNS`*cu^UV%hUy{0gogbnT+n0Of;ZFN>k9cYvEYpx+yixhPl9OtLXnJ&jM_k;yF4oRwC&oICQ5*;P zg3y=9nHz#%8!DCmJ1|W-sMH|v5OglVfz@Aba725s(n%8RA0U%B9HQo-lPs{ zEy0^VY;!L06YN43=FB^m;w2LEhAdX%y%XVXUh;(fEw;ZgaJezZyC-m)hYS(P2lPoA9f>WGA^4J2X)K&Jin6kU#0acG%hd~30x#_k-$X)=MtE{;(7Sr`Fx~#oZ}pt#vgCP zeSQxsoc)Q__Xz_0(~#4S9sjzhqJ1j;B&0>zbT=eK1wTtJn$lm^;TrTo)Z>0sPEa2OT`5FG;_V`SdK*VNPHc*nDGw(IcDCy`S8W?t)l1VIv+F z(b&(-l|@JRS%7JXP1wD}w z?+4t7e#0U=uWY`vUub7pJoXJ2zCsW;&ji?GiLT(c#PXzG`Cfs=`L8SnGfz*vCz!*L zS_Ul>%f+u4CGUJu#}Asg(fpMKTK%FQi_BPv`4+z6UHC^fK7*5N!3w!%;g@_ovLN-E zLjtatzq$JKNSDtAV9NG23xHi<3i#recOEGZUg*2viFX!Ru;%7=((vA$JRn{3K{?uS zk1Q8VSc@!CTjG=x$(>Lv+VR=G| z>SAO}V#IGzyn>f~YthxzT@eTaFD}H*Vr^Y?(}iZ&%-MurWT1Y#(1B&3tmok?a=>_? z!gCgX`E@V8G{hn%7r043iz!`%$3nPAHU#EN!Lu11S%pW&6fRP85#K8tL*BTbVep34 zeAdnA(Ts&6h5aX04Vh+vq`JufqU|JreOT4KqYLMa;=9f9NWkN zO=QyOhPYuo2#@U5iM*HdF>-g2M$6D~XE(MqOn+{5fPzWd`~>+%C*+48AC^Di^Fvq{ z$W6RJ1`6O$JU(!(xL*yICv4T=smzXe^dD&q{$X{|4vL98!v74{G44lceiVMjm-$#S zp_bq$UCMBH3WE(#ets4%ZsX?~9wrUp87HdT+E$-eTx7Y@ZT_*zb%R@!;bXy$j6pMt ze0-;6bf6Mc#Pa0=! zQ~D10Yu{Y3t!-+bVVn1mr&X6EAg`sRTs88@DXojLA_f`gY*41h7KqPK|E%9Gq;@`D z`<2D*ITYPZ&Te8c-2On@p)6KRN-1kwq!DHekSTxS-Dx*G&r2svQ})O8O+&e=PGQgu zIOIToB$~`EI>9G>W6hCC@{8XFn$Qw5X3;E%4K|gxak$`)OISCy_-q`wmHpg46D|dB zQF!b&^O=DAr=pFV;c zTv&K$9dT1>FEUesB4#tTf_0YP;A8;Tvg(8G5!mO;b&(pcCu^KG7h8f2v(5H$gNW^6>;#oL?Y^6;Kp4`% zcnF(yI~ES6rq5v8-ggk%QQ}FUoA(NC(564&c#FLp*kQ&DarL`S%bPwryxhpBZ6@Z z{>M%-&Uo|mu(x~i!zM+>T!#BjL%S)hC-?(RL*o{WV^n>57P zZrE44{@Q$+SJ=%ho(eBwNM3H!$LdBzH$G0^fBO09!^jtWO7ewuch7u@-SMAtSKhjA z%5-ye$}a5)vX)ObRdB$)1!j7+p&Ee<*%)N{D>rR2#xTKa-fj88n7iK|@9o3%K%Gb0 z5gNpnIdT*>oBzmo{-C7{vYBQ1c>=EC89$QDHdwh+0FjkCcSXO^`xCqXA`LetV-%X=(JEnyn zM!?*JS;%!?qG z_DdWqQxEJba)SU_EgH7#$X~c*%X|f^@m{#_WB)0(!XL;XK4fby;2QzBdC)#nzCcXh z|JE;r>(XD!CT9Ck+^+NR>*w9%MqP#Xo8Uq2%#FRGm(TRAzoL8JUp+hy7L+Oe=jU(e zD>88W%1l5vbh18#c?u73d;q`iQr#47!Kb{fL*~yu7;^@hlOpfkoNwN#3vAN6z57ZU z;Ic^pSmtOP-&kbV)bSVVw_idg-gziDq`QGaTSh?Yvhu6lw6A1d)S1?c`HHsj+7CCO z`<2@N{_lRNwqJ}DH~1*~TkKcI1E#0niH`2J!7%~pu?aV@{p~CHqGHO5_9b(6)=$eA zAy|?WmnHSv4RUc4qBivE_K|Y*i_U;8y;on;7qpDvA>%mv!1RAD$J~~oWt|(Glt(q1 zxfIGGWlLB#lu|chOOS2n^_YWF@x8L~3_hUQn;_{&_m!)9YTm39h9E^HW{~f~+fZV+-M{XZdZrTWL+ic`|g)V&e&`{{O z3H+A%IuDEFDSGMw3X$7tGhMr_9o3#%rp_V1vFSTB;j?7aG;psnzoy*cSKs+dKZJsf zonh0vW$=ugsOIE_{CkK5!i;SAar3A4FZ~4k@|wBxW!JK4*g8X=?wGTGy~keIrctKb zhg>M29AE-I=PbX`{(ocR`?trh%$IUMeWr~iEp(&#+9vFqiz?K`%+0w0m^znw9>s1g zi46lZ8+R)Kel1k_B*(cCj!Lr9#V+G-9YD{R7r%L+EO*gAz1RQ$l4&$w|?uMFY|J!z!;2~yay z4zKoaxD!Wu>z90Lrww#Y$fi`={Oo-0>X+pmiFTel{Zwp6#@h=&jG_SW4UW!>$eWc) z9_C)Wsmt=_%_WNMGK}Ywac#=LdQ?M@cef9;mmCa}{u%sW(|ZVl`pBI+EH!NfeO0Qh2Ms?mM`sa+U+2N@;E8uX-Gsiv z*L#x3uO9B=W_b895B5h=4}r67f+wxJhfG|7_c?tr*Aqb(y|T#K@2Ea(?Go+#E%#OC z{HaSQqivphJ|^F3^O2`ANgc8PDJQI24&1Q5yrhs}1E8!Cr7lEXCi0R2F6HMfT<2cY zon>pZb4*~`AHAcVy<#tg4KL`ddh7T>J7>H9Y@g=s5qfRwgPZWgVLP(+R#*Fgpw&I~ z!oEr7*_W=VGs@2Ot)JEg^v-3$D>gKTdzuYwO7v09S@KlvlQZi=ayY-zBcJxSbWyBq zj>jDtL=MUWSwtSlT^po4{p(fV_KVKN7HPAOH;U#QlscJt)CE_Y0*}^1a+yO*L=2e} zP3RqXCZg{ka0Lq+@xhyU0Sh|v5;>vI>K<3-tPn;@v*6K@wwEJ^%F_KwIk1Z_*=Hkq z@K#QXYUF@8K#Y$`+k?n9xuUFy zuRT;B?O#^EqB|gv@2ku+TfWVZTqK<`lLohHpRzG*)qzABU$A);+R)`1EWWyp6UD$7 z-fQrj!kQM!g3ooJH?W~5(8R@Ei2`Kq2+Y5cg$-VRxJQn82WGpPTUo1vxbzpV*dUFR zn?C%65O{-~kh(1cgXMp2^#kehoOPHoV+zB;9YJ)c~|0}b`Vz?zu7L^Ug+j;ToIK(e@iQTFZ-sB`CQ#Wo%6?dHa19j+fDO? z4TKllW3&8f#na|!uT0In_;u=BDFr9Ea{vc6PaV#D1dU4$dx&*LZYPY%t^wZUA-2Nt zkadW1rC+`En=o+Ank69D&iVAt`V8nO6RHj2#W-#<-~+dR{^trW(zO(ak8za)cNiut zE(xi>VpsMevOM-o1$*N8={2-?T16A`EjMWyUbheV7zfBo~m21b!-kqfLpvrdQWaw{hCKm{vTQpkdO($tDf`bePgq9R)fJcXY-cINk5d z@Atp`*Z=qVxBv5h{_D3t{^&+@Cm5{ooyndznWTd^ zCm&o)cH^Qi8Z{dWCpleY=V0KGL9Sow%b=XWR8I8D#4cQter`LS-p$p@TLi6N4&PsC zQKjb-@kuUJavuXAlxL@VbldxX_x%H2f*Z zq>=BWB0g;{p2kAI6z9OEmT_>|Wf-j9*I3*E7uVtog=1(e<{ zn=Rtvxk%vOAc2qccy#XHfc|em@xJb8<1=6(%KEL1IOc3}WZZ=Bb@3x67Y%@i+N3M= zJQ=Gs7`Nel)^FUvnzEK7Atae}8nQw%h)+&*h2C+S+Kdf9;~Efri_Flot<#@2&!qO{ z#NmX4c(~hN6wt)IoiJG#amsI_%3+KyAmPpUb!Pz(eow=EA`jn~YI8E=nStJoHxvA~ zywSU3g4~m-ZO>CsV3N;iC-d?$ZKiD}13B8mEOfU$Uzw3pdG^o$tP$x_Q(k$U%Fq2Cj#{>I{-YI)V&Mj6dL4&LK0j7EDtSXt@NGU-Rc*+@=rQg4rRR z7Bt>GPnyzdz75+i@h9Vvkq?6jAAr&`b@^yYA(Af84fA)xPAL#^xCZ7Bg!g@l!hH;V zn)Y9R+Nm@zzm9Q--*K4w5FWYujBesT#6)0Ayu0ue`XaFF^%>`zMTXF&P7hzpS(bRxclu3o17fS{PDYA3VX=cD{gpMR6B9>RiUGgm- z!htLKAswcYeDJ~qBX-54dQU3$1%rHXJN^rfKJStSx55c6{a)tNmu|eYedN?|H;eG% zt*0j2_Q)SeoZ-xPWf(QRy3P$v+rLissX4|8$K8M2MK8|s17X^L;!GJCVDMb=9-b2} zdgN2N`S63wbU%sSBEi*5cloz4g5n=!12gVQ`m4^M+2nMdaM3>x{-tM;d;jVst&O2y zO>}{~NZ=xYiv%tb_3g}^$isntr(gD&jp*Jk(s6UPRD9|0*T`IS(-ZXxW!)zd z!qN8O_yxv13C|DV^uhR(tBljkwp{U(MOlvRStR5aDqPUmm-KaAq~v(~*a=D+5Y6IR z=LB3>la1)~*IcC5FLuzEgDt;WmksDHcC_y%-N>TjMtf;;DQ}j-EA*69Z^svOG)a>i zK=WZv^0J!6lQ(SSeqqyjzF6e^1os^$BE7M&OF4MCpstGz@<@8n(Kj_;{slRK^_NBk z6oO&U#mf02pPS2hr{GuL&1)Csu|Nzx=pwH!2FU_aH%=%6?%CAr#`8B9Kf4K?h0EW$ zb;CdTu@BzG#V!J5^91xB-Gt99jwR_k6c^IgcYZ@TS(e#Ypa|$cTbH`1m2nA+S8tK43+5b$#C2gjI^iWg`EXI$?e({$6?YcG zsGn@OpSr0o8t0X;6o5a#Ckzz_t*>P`!z#F4oBGNmkK>l>rC%NltZdrfClB!aLS4FM zA<(r8c3HfeP3TN|S>9Pt0-4kq-s@z8Ci6bN!+CNeIt#yF{3=Wq=OGVh=ZgVwbP)^?LmOhfwswnOJFZR1ZMI;kd6YAGmpA=hM}I^5I{>` zUSVTiuyNLx)GzWFH@wvLr79;~lLt?JprP!@Bfi>39;GC>zk8m*HRT1sY+A|Jt*~8g zMArsC`h}@kAdW56##_#&)~0|lc;dz`5U0^G-eV-9oGWmkaSNZWh@+v2Y?XUFjY-qL&Xf);n1V%PBUW=N84} zSs8F!+|l>tmyBS_(1mU;a#LU3e4dRY^bOtEaOG`((1U_(6Is}1yXY6?yge+y4Ml#X z()K5P1p8dJ6JSpkqci0pBojj{k)Ld%k#F*#t8I^cH}$$##?-gg2N9dTun7-Lacyyd zX&N_>@0;ET1Ad-gvIz!$7aiqfUR@L6I~y1NS--E@z>JEpB|j@_V5c9WJH?X)YRGCT|Ju@^@hWgB37kbcLaDUWuA+3V8HF1)&S z1Nt@ND8Evho4$TFpYvto?F;PS(N@x>07g2}bxcJ&%{bJ)_YGf{HBO;PcQ!Pcui75Q zjxI#(Q|&2jS)lW)tFikZ;Fcegr_Ee8glXI59lkAh zH%@;dZ@v>pHNvlL<_H{!58i1hG+OwEfM_I#iJ?d}N}Hs~{cR2Y6CQdX{zTibGV zN;$3m1W&o9A)04h!lbR79Pimrviu!KUQzzJSvzPSEpknorgO2r(*bnlY{t2yOMjF6 zIKN?zGM{4JqBE3Da-s~0n>u0n+;LDGG@{qRBk%2hCr@scjT}uYKKskMP=t5CMxBEX znD?>$ow`DFWZg|bt%Hk&@TrP*oJTqyEPH+_CvG>PlMOdo=RF`ao*37DQ{m;G{>Teu zBM$T4xfSX(GL&uuuKhQuQ?(-?1a#zOn(Gjp*78--D`G zWITCNp54UX4T5uEr8=Sxnuh8ItaZmSbRJ3>Y5$yWAe-tKl68*F4R_DnPs$tj%DEN4 zvPs$->WSr9J%V4kMme-Q*=UXp$>9RlX>B|_1QzjDOCY3yfOqSOdT+lq?~(OGBVkC< za*m%cH%uAl;e+ZxbUFx;yS!U&mb>y_o4_m&X=(E<XWl3c!r~1& z+AE5qbxdx-!>l1ZNt6gSC4Kh<2a>ox%Krb|-9zX67;C+-8Q1l~9{S~YOxtWZxT(gn z|HQ$OnHOa|@Zg+}xRp0Dv)`;8G-GZ)hgDptVsk$^38tO#@Ji-)J+KmFYz&7j9wfmG$>i~g7;wfJX7c3#){ zM#t#t(p}u&_!J0MJbI~Y)GfP$_naESS*bU&yP(84H z_23r27VhD$+S_Z=y!y3RH;h{+AO7IB$&N$TY*EIB+Xt`>&Kv}5$?hMJ zN9IDsCk=Rjm8|oo&NyfKM8K7PS zN5|fl8TKK!?L_5~K7>lze{`q3B%JcJ`hRGQ^%fozVU3m)(O1Xl*nZ5(&%0U5Dh zcK^V8CHj>Qw2cqxo1r^w-oB>pdzglHKkIbNt93#BS?^YCPu#rirfnot)lg{aJv^uF zQ6`EPCZWw{!*(@?NL&Aqg|;mDN1v6aa#JqeQ3dS{GPZA#{vw!D4_AI{&%FZ! z(QNdG_7%EygN=!v1(~+qI!m_gC*yJ5lblLN5c|mCMVIuBh1B63WaHP+(`Hn6)#2ES zN6Lu9iBksTy?syU+Z-U@AdVrFxk#G`XqvBe6Tu%kWgY=hw!YJgU4X}tV}uIFVzH(w z2kQnzLbo4pgm3DZb|P(Y+6rX~jo1VHrYVv*mZEZyP}WsZik2i&>WwY!MpA8RHXf&BKV%_k)WMfsXd`J^8bOU*$Z)~J00N>Z?e^`$emO(|F zvOo6w+7F?;n4k9WAzCf`(DW~KYwyG>pVIK1i0g}USU9vigye>IeBFeO9j0Kjfp+y{ zETg$=zsceuETRT^bP1h#$-X5IYLco4a)+^PQBveXR7^)fIKhKN1vHeJDop7gI4Oc+*|N z1T^8wTH@D_!PWC)Xzfhl&84=M`QOgdAw;4ITB_yzroFSn|Yg-p@=Q znrCw~C}c6^M>}L*OoMcg5Aw!tCS!|8WM}Z&IY%OUZcmQXGYfCs7SM#^X;CC81CzJ0ifY>l>)e*n({urJI z{R1#|xvs5`F&m%DT4}$Vw!cD|V#h*sF5!BG+yHEW(FQiG|F(6mh1=lbWx+X`FAE7|-G%Us&2mni27Vu!BPe zp0t0NWRhpX+isVlzq0H#eT>S>_NKTJAFna|fzMlH{`sE)8aOg>$FqG7Cu8oISiHZ# zXJV9vi?r>Z;qeP_52R%tE|={tTxOtL|M1v&(4~wM2W$?Np)L2t8;rn)dF<`rH!ts) zDDo>^=FXPauT!ZfPJxZS7^b}PA}cO%C27|1o8Gki7@sG9=ugQ%5xiINeM7Nb&uhOG)?$2Z>t}a82FfS2`>29_zc?O3MazaKU(=5+9_vXTYAc;^gP*b z$G!5hGPE2sv0Vu}#Ov)#rBa$)nP6a*>s;{yl>SB_OmTiqg3~y;n|vQr40H(|WAG1( z4QRzrxR1xUAB88`M}WsLfqsPGN8v%7d{zH;cAA1;Jw^_SMKVr=n;Ka#IUr(eO_Gk( zfNi({MMCk5TvhLdJEnJnR!khjHrRv|&k;t{nhF`-OKit>K5=nOd+H~3D&lGAzw2p< z0CYQ@3DYfY>yt0ksQ0`$b}Tce%6l61XI}oe`P{E=dfzd|=|Up%PCc$|(mpwX=0sZM z7265k*k!`T)n;qM^D>ziFuusjh9dcK9AtYCsjhL6czLPN^dN-U;^svyljEtIP721_ zf`r0#Y-iizqL5FFoiYwgp~eyvhe8(DazS9gy2oEv+TGa2!jmk-Sc`VC*LgV(%UDm) zG4_P6bvT%?QKjvKq=UV=g|F^N;r&Hy7;B9FX^RZm^{cWDv2!G*U(tJ%0mq0TGT}nL zOSF+isS!FiFuMFJNITqN*+Qv$Z} zwyV=m`7>UG`?E`s%q;^LXrE1FzeCSz+S1k&U>H4yj9uC%vX1~PeKQxaUOC5Y|ICHk zE)?R+DQ-sp!W{D*{WRyiU8G^3imnL#EH)XjQ1FTc1zGgypc|T7i;71!OyhvuJlTiq zf)4s}^v4JcP!>dk-;L9L(dn84SFac-nm+T?Eaq`h;PaY$H^K!WE;4eVQ}RgQjI-sz zr9zRnV{h{x_ULN??D7fISSDHY1&+-52UjAL3@N!&boK9PZUg|(2Y1HJaV!D7m4y(4e~I*X^C_|nvqU-fWtUN)IM@@{}d zle}y209+R|yWrc6;Q1;N>D=f)3$FUr4>XXM@}GDa&g{avl}xv61*p#7K+i9Z z{Pq}mt9SkK6#79qsnA`_PX8Q!Q}*`d5nb~p`E~a#B^1!>6 z`+QA_XTR367N#kvELen(Yj%dHy5e(@SH~MIWxhWE06+jqL_t*QgL$SbaP!ii_dyCe zx+Kfe&8^Pj$)Y6O(F<@6|Bg#s80(nj)yOrm4>OHI{5V2bX_ft_$(%j(J*_$|Eu5FXIRaj_~U? zGw~Cb1zzasEbK}$We}aOzMH-`&Z@I8aJZxc1?#jmX6ORzW6?b~tZq+y+Z5j&yVy%zb5jyGi(Y5JwluUE zR~}li*ot0LPuyUV#lYqRPwR{EbPhxzU=eWsN!!~|Q78-KhjHxlAalWhm zm8lgk1~18JwaQ1`L?o>Ef;xePo_JFpv=bh94lnZ4I|M?(GC$t(cD%*S74}EpjmHmQ z)f+A;hm!@2Y#wm~$#piM`^81!EtmGg<}3BtfX;dW7ZvA5hqHJ$Vv$+?Nn1HCX&m`8 zuj)4}esT>4{n9LQcA>MkZ5_8872J%H_7vXryJ5)NdB(J@(9&NV+IZlkt)0i6H6F^4 z)*$&;G`UTsp>hvnS{<7c1433kG^TpMKHGxmqL+8}Uhi_&)C+9qj(p&eU8 z|J}p5+?<%3O?t6c+BY_f-g=-H{CdfUc2N5m+@e~1-(ax%Wo~EyV?R7!FUBrw=hC); z!2BvY?WcWYHw@_-pLZwrN3iKnPdBu`Za!s08EtVkq5IizH=)1Whv%%1Qv&o^p81OF z-LqdvXPgM!cQ+g6vJHdaDnZFN-)oK`bRA#u-ukBN*_5e$?Bq2vR~8+kxgk+pE{F3l zF8f`5kM0*+yD<}~q&%)zh^@HoYi08}8+qJp=@;hRB>2j~B9Sruvs(^#b8+~TXU87$ z4Zq0Weq1j2wZG@aOuqswP3Hi*NwhkWuMNvQ*R09AVUw|AkZhQOAN5xqP}9=a0*~B4(wrM4&&XNbGA?5jY>54F zxL}ULy6VPoH?(6XoG0SoJLehfTe<<=P14CFyjZ5$Xb9a$4&c_7_@0*xCa>9)W!|V8 z``5JDY(ZYlmvB$i9qYo(e`SN)+AP|70T^=f&bpwrKG#DZEbl8eRrASabcFhfJo05u z>M#`A_e8$YHOeUcMfjKIM%io{HizFzsTWq3-YeCeBcCuR5im5t|LfoEPhGSKEQj@Ncr-=__nFVx5WV{5aC6gTU% zp`pE0FSL2uz8t7U_;+x;QK!CR;~|(kUq4@V#b=ZM)+) zFXfHt$y|cAF$XMQySfqG&FJv&;R}>k=8r80Z3--e3x0S?*_WTi6C5dpUPrgQH+7L1 zqHL{})|rfNB|${=%lgD^U&EZL2NvbnV9O`%F)*W#5v62-O`64t&L+Q;FhCpGd>!Ie zaO2OyO_lNqm4ySE3PoEP`A7fyPU-io^|5WdOMB=EtZo*zEqLYd6H0=7G5=2+olWVq zP3pKCOV#_x33~@-{1B0lc_dDK(pK1KoLifsOq$MdyK{1^ujF72H=#e=f8k~mI{n8S zu!mYi7vagTHv9hMJ2>Dnc-r@Y7WV0t{^9eJwhZ2~34P^1yih)yy~JkvC3I|O-ii2L zB=3sg7oNknb}n^R9Q~U^11v!(L_hYq2M%E2u%&DIt+tig6)Y}^Yn!HR*A7_kGsiDp zc#}^0^eBmCm-cJvQ2Kq;iO7udd()Qk@5dE48y`JW9@e(nMypfX#+HeAZ=jQRB$i>(O=t$*AjuBu?dD8 zFHpU^gx#;o%V$e?$~rPP?+J&Vsy-9;R zbt3X}3}KtzzH?-rP3Uew?Z}22=Gf(Hs5+ zDr^{ER85xjy8D&jJf8UuT?vh_7sP}Wd)YTkeCUrh zBIS&|Q17iz+CGh2go1sB2m6w?4IWA-udc&z1G^ufs~sWMJZg7JQZAGKBuF5%Y+s}; zlH7fsHn?Fj4#&Y|pC3Gehk4`XQW9Q9u4@aLu%Tmp0}|SC2cEP(hu1A^JR!=%atHTB z$0hDuc{YAbH`mcWVW+M-@~>R~YWiQH{hy&>Imr!u>|bH(-wjhG`PT~L-_6qn-$epP z38a4b6QiiEa@#W}O#RKkiB3;nEI5F7BFuryngDYmBppW^f?~FEW6#q`o=&s~7xn@H z8#}d(v2jgbLwZH$+{s!ygYqLDCvYA(LClFvH=uv<3pFeX{GE;Haa|zDW)9hR^3?&2 zCqX(#kcS$(J1)J0%$$80TQeE13}XqwAdTM{uVwNA zvv@QyL2bA2>(SvhrO?3>Tx+m}{q;Z?H(ozEsH1Vbrp@}qWcN%?*nYPi1j8aJ>U7de zA{0WUL`qF?Xssl`G)+7Agd3kfkOU}5!}mUEB>|u^3r1HxFCYpZ{+K9mvcNB)=Vc}X z%g?0nYv{bU;n#_POiSVF@3UAN>n}I-GhvDxo@u+Uu5ACE2&64%k};FB#AU%1ZYQzr z6PTXTV{mJ`CN3wugWu=F9f}L4Mbq|r^eTJ^#6@?87kx}j;_BZo1$7dzqW(g-pkZA4 zc0$C*4R51o3u(js9g=#IUU1=^+#@-j>>Qj& zhVGC)k1XNy(39uLC~!+!G8I<5j%S!i_hnUVpy8IK zLUDYjy>x=kI&R%}GPPyjBJ;(-65y8S;ivA$za#W1d3A&GUiLRHWO?D{Sj(GWsC2x^ ztBILQRRAm8iER0Hf)L!{zc@>&h2U?V{RxatDYPMQ5Vp0yBID{0KAYk6|Z?FAo<9TqJOj zz(oQV3H-7Irk`R0{l`Ac5y{@3?x9E6@ejVZw5MA$w)!zPp~7}SM*9nNWby^3j30@& z&*OZz^UT}LV%hC$PFY0jS5*8Gx*MPE7uhblK&T7;vM|s^1&kNpfD!lJa)wCyHvon} zdB5#*vABqNXy>T6z8uhwd$U*x%+9;}r4qlA;pHM0zxMp%3szV6!E?TD#hmmriz8oL zOv)JA9Hxf{NqK0W!+x8~Wc&r6;fb=p7a&8^6P7284DmiJ-7N33G#V5hbTVB(5WdYB%3!q$>MMmYJ zH@W0F`BDd5q~%H-=j(Bp@<7hbYjp^i^nsuuS#Ib2+mFVD5BtZn0sZkGd;$C)k9--1 zuh4$ut2TVy#!cvMOmJ~?G63t+a&xnbEM0Kr*F&-?n{auNFV{7(Hjo4N@UZAwn$guP z4gw}+3=TG0n;-S%iT5tsB3+a_e{K#~%!}DF>PBq(%=VX;0PROB)-EDsJb)bD_)%U0 znZ|_~eyPqe$!GNao=q5jy=BHD;&P7UmQCF*8tS5E!^|^zv>cr%nLN6<&SzhWz}vix zm$r8nzLS3!es}R!av)^8zp`d|WGuiUT*|%k5+Z=;nl}>ts~glQfM8hZviOTS;sW!0 zt;&VrZb;X4BRY%4^IpgDNgiM`Iu1fGb<$^-Y2*sy2QE#(e|2v3T;}yJ)mpCgPiS=1 zu@nW8Vq=kC@8oVoVTv zE2ZXQi%qf}a%2=xgQ+8-z0sFZ{~M6s_0!yln%Zw#?#L{2B5jwlX$#p{UT#351A=J9 z=ATAjm&s7DU1_LXHS{EN6Ba1q^S|5*8nk673q3`%F0pBg>Mo}9;7_le_B;c(@R{1Y z7TWY^w~!U{6jJMz>HIpb}?zvlR3XKnviUjao+_kwmQdfoy|>ex}pMh{c& zG6Toh(JXGW|CO7WC}l0h^nF;U&&E49r2K;~T0YW0uw9w2vf1y`-g~7lO1hIj>V&yf zXFI=+YXF(dmlN%euESumnKOqau`a{@Kt@MYH=v+1`# zZ{77a%zLwcqR~=e8}d$&0o1kaOTxR}$~9McSG)R?d7r_U*^O|HpL`F2)#z7-u|w%| zI@ZRf-AW51am~7~>x>y!-x|g>myVrY=pV%rYBd zDiZq?E{1m8XSc&!8A#QQCcbOXHj7tVDZk26+uluU9sgv!KeA%1r``2-)6ff>$}DqgFlAe8X-^m*w-){K&&e!%V7r)RP9!Q)v zieJ-(5BlzTpTv00a`Q{r9pl-TN6zVc?VHd;lMQcPugn|VWFthsCTagn`~S*j+c)MO zUiq5&9baoCB=nr0sII6Z(I&98e`2`TBYC*Jd1Oo>uC5(){b2fmq;FZ;$MZ|VZdeC? z=3dYV-}_kxZdkPMlQyaaNBhJ2z9>D!m4Tqj1UZ^_UHgf)PYU`IIIdG}wo7ipbj;bZ zR-XB?aFLW|@Q_<{qH`ME9F%nd>MFOmm2Wo0kv1Qw`3C-!-&wm#ak?PhV$B!3p`DGS zjFa8u-mg`+4*F%pY(Pi1w+(pk>!n_{Ph4v&<13$mHL7I|+1TrZf*~kN| zT9||Q!2O0CUY#?doNsBj^ZpMUTmOVSsTV0<(5j0OYpJHLWSmH*)CcRnb;Ubhx}Oc` zzR$!C-19|pH=?^i+=Fngm=o~eYVAljppyrfzC})St;hrVq#irZ1!MPbA*5mEqC&{SvSc2~Ih@CD zyn96#t+N8CE0)O(n~j|x5Z67MIMwSMh7BG!u3j;hkPU*))=+Q1v1#+$*C*zQo-$wL zhIHxpy8Ieh=UYy09N*VH5BPQJCvLZwfPtd&*P z?byE+5Aj$OTz=8V=pu5_)|k}w!`8i^5fhm=Jqx3v-{!0oLWVq1F5F(0@pp7tRl8+P z@;$cDjk?`bX?yna+t)h|Ex7p#ZEQbTZ`*0xV!N`+7X5I8VQdJr+js6E4EDj5vtL2x z1wC=taK!ZTJ2sqh`A$Dv2AJAHUugfxOXjGR)0wPVCr4(H8X{``2vth4MG-vtal_~* z%E>RGld0(U3vzXy(sk077keU!QgT7en6cN|LLP2$@6h2} z+9arxKf=iilHu@H>OV5<_(i_OEe>(FWU3pKE9GgKX3QcHbWEMLPFdfa&wJ#MmXs}J z-o;hr$ud{x?Wd^L_+lfkv%bML6B};d2c6KCb7VxyD)WVGK!4&em26V?02$gJMc2dd zk*T^%{nG~J%hIevfA+8$?7Ih6NK<*K3m#PJeDSwOYzSXE{)GKa-6ao>6}2PP8+229 z&)xP6f?N?z1)Q=JZ)CP217${Y+i;*$wgc3qtlx*XyL-yx;SCqr`vq>tfI^}N>Tc~q z4_J7_=JlXE<$_MTc{sKO8lbieOdFQ!Y~^R^S;{AOfDQAq4E+~JA`)bVk}vrp!_kL@ zC+qmoL+iYAnpr1C(!FgQd4USt<}4pb5xvz+esy^kN98AV0roq1WpTxXQKPx!K6w+# zAK=v%KA!i559$5RdLQ2@kq7#=FKj+{{=j)J4}cJddeeDz zAK=tH#MWu!yw~PyiIK_ZI%%{q*4f?Wh_m|WJcfs}JkT!t1@zAkUw-@Y`S-NZ9%5mg zv;J8BHRtJfkg2py*fnj&6%jUL+Qato;ETS6H${^xa|W6g^7F#RYW2qhrTQiGhP~#H z-q9;v`R2yfdz%j^BMax0%mZ@eN1ZnR_BU>C{h$(Z7+F~7EN}}bGQ?4wqS0>T&I2MH zIiug$fY@p5T=VQ42Q;xo+9;7}m)!VU9t&3<<F{c1SjbJRWCrnD(=yrCkw zVk;c&A1Q$@546Q@Vnu5b+D-ldn}G70c5G4yx45Vas5f<}dX;js4O#8m^j~y4ezLz< z`Lw>b4OHs(VPD9b(wVDeMctzAi!)_Jr2Oa~*P;_gWqZJ3-B1Aik2|=BkkCn<@%n%m zYWXc(Q)VQNnk-$3EagLFmbZT^50j+w4tuf~{m{)r2#Zq>fpHrbnFk*+;#99TovIAC zda~r5I%9vpgUYyrQ*@O==nT*4w;8VPQHJV~bzx+fWcd-Mxe!*%AfL&f@jh;>uM8QO zdwzpx|Jm}n;!RI%#A2RLQcJ%9#9hUZ9A+&Q5zb}ZI(a67XW2*@EVq8? zk(oBb(sFxa*?tg(eaC(;v+yPS*W_ z8o~Af6Tq`L`R!w1f`jKRXz?nB;y=2;Ex?p#V3g%aqJ-{0D{f1-rxa%B4u%tbCUC-o zFL|33JB8pG#!iE`r`B2UiQ{lh_#x3?gMK1M!* z{!5wt7}X{8B7uLK1V%5dEU6;=)OJvju#DC>JIym8l) zjfw;MD>~Ti{4yv?r%m<=1GyW_JIS*WCOjyBrGrg+JDE04HfMIoT_Dph1@@~newoI> znrBl8nc}GMf=3G7bL`3tm6jF!DibQi6;|iYZ9ar zMuhX+_~F*}3k*r$uwt*oD|29MJh{`3Ext1eCO6RYEA8@Y%P)JKkb(y%449O0e)E=i zJN226wnTZR=}o(AnAf5Z9EOO~)6SThp^XL?_?-OD%S-wW-GGUVfawb^`W;O6;r$WV8(InIZ^Jf3 ziBI&y;T!kMRkv7Px4yG}$O=}qDAi6TP!Z3AbCVqSl z#UyF^WCl#)woG*Al1*wLY%vzt^q{Z?fU>cGAwo)MXh-`e z-Jz*H*j`RRUKxa5fk;2s5;l&y+)LN;hDPv6$NVfjwj1h`lZG1mq-&a%VH7Z^)HRLu zKk0ZX8h}@dW{im&5g6Ka+r?GBpD;_(yr#@8+wUyO&X?lo+sgat zMH=$u7uMy2HH`3$NY{ADUam4SPM>(wXW#S*-2kur7&mfh9!a4N`Q9QjQ~;(|$C+zc z8yD{VT8az3s2jc$Qbx{^xDZY}PxmZXWFyG)XZR$)S%{6i$sxF(8P~u&?}JvQl!yW# zz2l0g2Y8{IvZ1tif09M}=zbOok|L!O<}GXM5cDl?`1bvfS0&*BA($d4`mOAcyYg!} zz^9we*@W(we#WnxGGfC^@<2LBB!8Bfe#fF6524r8)h?)5x)T=s;Wl3|T=gQ2`AA+2 zD}2-qK0B5&6@C`O`E_?ov4qtHXj?(Jl7=7u5^j3a;qGI8X%BrMBUiPAa%K@-=C&-f zR#0FUZ-JYIP}YqD-{DyXBr827&0orcFfQ?U%j+xoFnzf;nF;5bP@n%@;r~-{{V0Eb z7r!6NR=r&sBw;Sgii#Ik9#=d-fwPTBSl!7*akT8Kf4iRq;9UBR;%i@Bc-sYb*c0b( zu_?B3+7}cB6-O)4#VmZbj;5aKwhqR11KO&Su7-6(EK4?BE(kr^&Q+fzJ~}wO=!Q>q z)ju;3-Ax2|y!h?^7TEAUp6mL%=is$=wO&j;oVp?1{@2Zef|5vhmQEg)>+|8N&=>E7 zi^i~3rqk{j?pNlz2(@s!x^uv_ z@!PKY)m`g}={@A~o96+wmJMFd zY+lY6$ZN=xx3me`RJ%B)`v2KG7xp@C9NSu!WIO5m-T(hXJAUcmyVl-7osuFaPLDG^ zeMcqnkVS$32!i0F$STGdj5gwYb`Y5$3&+iVaVriQ+T+goFm}=x2mJ5B+B}OPPxiM3 zckPt6HFj2+?88f?mPFp<7FZC9U92kVR0!3{MJ(s zqNg7LCjEi+rXA*9I1d16+dW8ZUt>Ra!=X9nGwtUjNr7;AwwQGZ<)MWAg`3Rzc8u0T z=#L&k$9Y(dUL}1?4&ypk!p7&tUC7M?Q<--|TVLIApWPSc#mY|Eco@(6vQGROy=*&&u;n9Pigj*$z5S#85p|gIz_;^}_N&NHX~RQg10T*2jdOp1 z>f!s!GLN&L^q`OH6Y}Idb^3JcK(WX{$EKFGdj{J!IAF~DMp^IJ74&APB<-syWXHYE zr*Iw=jZ;sg$vyL75BBOu|B>S(hmam9Zw{)fU1$JkYJ<1qgnvlr;s#PbE2^h@6MZpcQp=RNp}fbaH^t!)7`UN5>gvT?0IC z(J`N$zK6#9Iaue{*SlZz7Y^3_Li_Mw9qo#8*CtAz!r)n^JxFak`^vZt(6n25b6lm~ z-@a!&hpxZ9>RO10In`}-#kq?ISCyIg{9>?&&p-OI!jJl&Io$3`CzY{$xaMfyiXv+o z%xye0Xsou#cC84qW@Ot=do?7+HwK#by5ntq88lhhAX6$WhqT-$q+M~{ zYwW#iOR@dHr%#c8<|eHH?J3B_Cmq+WaNp>QTsw^3@UoL0By~Kk(=KBbv9o?5J!_0` zqC9+_?)-xlv(Uk8WQv=JJp&OF(_zvHa$GD223|`|0{jlXFse3AJ&8G zh}1#%Lu=#V2|KYbwh+%0BIS{eqBgH>_*i???h#Gavdw0yZ?_(Do<}Sd(F(~k%>AsFz4y9gO4sBw} zLKb}M-NWiudTon!iq20PHeUL9aRfYnrEPseU(}E5yUH{N0YMQPs!m2v)HBMj&dDQu zynV}jiRyAq&~eaLUiPBC(u`6r>(ssDJ0^0^gkMGWkHQDPeRlYCF9&-%)Bz8RTF&%= zq|1WqZ+;E^E90Kej9uRH5&*x7toqqs?;&*OZ*6PV;p(Rfno`u4_S!R9A(om3S8b3V z575S_f65>S<$b=v3p9M0!Y$7kFeRj`>+q)T$^-Jeqwf9k`DYKI<9BUT7-TYYzfR`D zN@!a?b!OZ^(C7XP?O(*UY`ro85{exQ{x5PJ8{IXGiq z`=`UzjV)LS3TEfC+ zOo0t@4DYih`xkkNEMi~LQ$Ox=>MuQz?#D0OXKA0OF5Rn`{43wArSL)|c9SAl`$ZgM zUP!0y+V1jvjrQxhvy##by7q71qfN^TrCig)Ha>B#@j36Xufp$3acl$hiBz1ntQBbe zwN1NrA+NW*c)|}A-LMXE%?njLh^|bOQ)Q)Gv?q!{-}a#%acfxo+*m%Jr_tfj*R>$m?eZd!=%!5C<{1m} z@|Vcm7gKDXh@JGMMT*0=?;(FrO=e6%d3~mo`Gfr-xHHaEzj5lkcEI||n1*uf8WspB z88QL!mH1%8B6vjw@>6*MCh)}(l!NXOONbatsJu>p@T*SiYPZ%0=16-QPWx3x;bqDz z&f%?m$oZKegrglyA;MgLxNo15s%g2XKWZLM+oo*P7Io2;d+tL)oYb|rQS9gqdChNp zGg;~CC`jP3^9+2Jo;V2vEVR81F7(#Fy~;b{41*_}ffq`B*Hu<|7NOq7CRE<5gORE5 z;|$4%oLn4j-ufbq76@I-2R|2<23l|Ui63nt%KD}*MJU#a#hbZ^vn=YyM*V>=%))^c zUV5i}3I;1UmoXCa#xNJWC!nAEdiyJ?rVOqEPoBf8?gdh;z?x6gA(9qIH$nM*9%%bk zW`XaoXl(;=QOGDIk;c)c(yl-)(z(KV7=+&V6>511AHYTi{CedV6oOY))-bggJLI?w zTj|(a0IrM|B}967*#eQgb)J%#p7PY+@oK=6Uu;mMue871I%;_?WeU_XSr*Fk(B1k= z(h(QbW<9QEeTw%;G_d6`2;W0$K)5r)?2&it>LmKW9rpyb4VrKc2k=jRgpVx$1n%!l zewqFX;g2X+#{I1xjz*tEy!B9@VfMfGvao-(lK#CMzf%9-Ab}bb8%Yg9Y$|Oq6V?py zouspXz`)l<9Vbxhj;|&h14KKacFqox9W!;o-;PE7hYtS^esjP)ePoTd?Zd%>{OD{e z4x;4CuFN>;|N0LOp#Sm<2hW)-G1+&JwfUR~xH#nGtBb`xOzGR}{OW~ovCEt6oMhv) z$C;2pqYG+GkRa(IS~f#C$<3e+;&MS6x5=jg&*XtYj)juTweRv8z6q>hO8YmOE9Gl& z!N@P-M#eaAy10xFPU%S5xB=#-#@xuKSKyAQf#3TiQXnT)hJ%m5!QAFv|9SpoBBQ>} zdgMBIB_PgZUzpq!?tj$p!SoMsCG6|m_l zJu*4>g|iEZjnN~#_BB8;WQ99^iSw_#nVAdRmwe0h#t1WTV{4mY+;<9O{GxdVt z0&j7BLp^=D{W+bln*-v2b79n+PF{0hotrI(=!cWS$QrPw`v4#~oa73~m9Y+G@`2`q z8z@LqPU5&`5in4sXR!zP97>OGxc9=SRq-u{_Xm%}lTerjcrKPV>E3|Auvtw1J1^_% zrK^0FFVkD6*6YO?FMolh9`bM`vP7@oo>B;sF;d1%6siwSCXcd+3m8AamK${}S6H~j z8`q5n33+FLYs<0s=RBsJ(-&srdw(nj9QdYH0JgewSui0EWtM+0OPh8|SlC^-*!aZB zt9b$s9QEwqrZq*7#*6pIX-KzaOrB6V@deJXil56eS(nl-NDI9!? z12|QvXh|2L_^7@vbmKF&??UP<^4Xq&E3EKysS7SX;hUypM0bXh9UgJeZYNa+p23(Q zY!E-mfjNG{wD^0y)CN}1k37!;Tkp@JpWHr&Io~IYO>r?xP4_{0-k6ta{ufvTGi zJbb=i199F1=?W7?v7NY)G&ah~4|Hm0tv`8^SNyTfwF{j$C7yI))(ykudYR^~_0mG5 z9*>_XShYjQ*z4l4+@_yhQ}Ro6b59^R3!Zp?;`}jjeS*hjd^1{x4Ijx9poGt(a^E7F zCT_1&xDBz zzo2le)G?o9Hpgqsc{66?&=ebYx{=Zio~%!^GtRGTXRHQX=35zqg%xN*H}~+U&w94_ zGZc`YofiyTHr<0*`i|oSOumdWr<}@i#uARW^FWzR8y3>P{yW~VUOJzXj}CE!*CjRh zMFr-Db0Zx9~^Ro1eguakjo?M3`=JQa+})^a`;u?3mv*3+H0! zAh_Is>n6}|=&u_lGY@kHB2VP=IS1&1;sLnmmAaMZAEe2%B#~iV@8K9a@?)CvZvDEs zBQMx;Gq{HqJampOWW%osjO8y+@`tmGGyicDrJLK_Oa)OFfdWvvX-hQOul)R%1X zVjgFCqT7U4E|yCkm*4n4OT+D2Q{n`gON|Kclnag%rU0NaYzn_Auf?7~@{&ID!sx&4 zgt}EOqW^}>Z12vYz0~1j9;kg$HskU|K2B*^ZPIfin)xX#x8T7uUOMrWul#Xqme@H0 zlIoYx%elEOmwC^Z_vZeq@#1)%$2hO&@qb3+r946OFW~wf?Vm~G$8c4n{FNtv@wc*w zX?g-j4TO6taBI$*G231nUmZQ+1VH`pSuDT#%Yk#(jN@iQC_u3R+7#MT4lDU=mJQ?{ z&~VeUn{Pe9(Jv*TvutebrhjzM&HuE=tQVu}wxb+`Q_pJ{QWljzyod4fnP1DF{}w8F z12gFd4r!mS@GDs<4gV*u0~*?d^v&+iwB>M} za$45QUpLkj=TUd&<(k-p8)eM-GS;~DO)JdGDlw;Q+ODUVC(*;_q{TLCueltT?f65E zO>at)6I0r~FJ2%lchCym9P*=i${lLwy`l^lvFQ`Dw#5csH}T@M-RT3e{-y1s{!&jR zvDoADlYST4{R2kiAp)&K=gN}w@-oMK>5fATeC^Idc#hM&tQTLb;yPdYG{Wr58=ri)zJ|W{!`iDK-UV9Qe;a(QWT z>VqeC^x}6OG569?CeU#1nlMSm;PsN`xKmWhUejjZQ>#|XCZ{;)=J|>IYQNdT=Dsx# z1(|MtaO;6K>qCA+V{sV)AK^_NoeRn%F1%XL#A~}PN)F3#sLU@--u56__Psg2qHb?* zIh(GWqAcJ71?n zW_R$}`UX7pA=#NHidP4JIXE+8U+2UgK+iKi{CtTWxE>_3Y~r9kjKle*Oh4e`fqB=f z@)9xFQ8&VqW$1tW#8Czi;s9c;<1I*iS)qFNh+OkRHy95s?}o{%TrDOVjXu>WGr!W^!*KQ0A>}FKE2HQL_i3vf*?#lQ1Fqn=ZQve} z4<|$8ZjgVzatSf9IEGM`?_8go1Lybe?oy{$9!lo``nz@9zvIAr?m1+q9m*lK)Jex! z%p9Pv3@UdIvpza5^dKEPJ@6u*uXi6PGknSmb+3N9K495>L0`T`%^IO?MpPL$Imbq} zU$CX0KKm7P4xw|P_46lQN<^H8@YFS^KwH^Zv9dwDg0=35a-8Vp3llR=wR$0)wU`NK zeGA<|EBRC2&bN>S?eiULE+1HjaSi4gWxBjjKw{FYzV9Ags8D9c$k?Mh^zVuT?eC)0cck2A_E`lZSin{kk@K znR3bnvU4wO4_^5)0qeKEakd@Mp{n47$G0Br1wLA#%&Z%j)I}fBqg^xd;Ah9_m8o(S zQ|c;>L4j(s)N9vuO(sud>iP~;MplW}K0v!|5;*xHU7dLU-Z{p60sG!hZNn?9D^qD| z`#o4MZ{?}Hhkx+MTGoZ+pEh`d4fiY6?|8vu4r_wvk;Cq9k*kMV`c-TCV*1Y<-quzq zs_>*v+it8IW#nX9sW8YVI6<8PQEtStIM9rT97@3-{d9zwS%VBV>BXj0$#qWl~{ zcdgq4>ArA5y{OFrA@Fh#>4+aj)V=x=7C@wOXjuRGHSq(Z7mT)P?c?Ccm?4PZ!9Sa= zip{_4lwNJa)(bLIR)Hs7p7;rlpd^gnIOE3!%d?;GU;M*ji$PR)P5n<169d)IULK&6 zj50C$=vZgk!+YlOzx>*d>dDg``r;ai<2J`f^1j#&rYww2&?p}`(7yBp9n-cEC$5wi9NHoK(~e7cR$#lJO~f8sO8B-c(L?)1@c06y2M)^9%8()U z%>(Gbq$;o-S>xB{QjRd2IO>LGq{(+I*MsQX=g>KNhu_2K*emSl9phKqwR}r6hr9F4 z4gR4OIl^9TrTi1EDZ&G0a=C(z!+_(g&hU($y}d+{jD)Q4U#uLO#l-{UmiR5dd880&Q>u z4WFc?9Lh`#rUhg1`pf-W;6gxpL(}|5=5O;n;cIklb?yoNkjfA2M5o2mwl!@`8Crky zxAhs{94Ux};U88MtvYRAmSB>K!+s3Sr0f9WGA2vwug>(vn&KG;PNWyywKHT^ zBnJayNuv0w_${&)kDO&k$`Z zg9!gX8h(!U4XpX&w86Gz{IHz*7(E@_!B?z7&!*Tk1m|y??5Cr6XM=?EQE!NRqcSK1E*aaxU}31NNmwW zxq#zW&wu>~htPRjmu~BcGF(Yc(2#ucr{UYQ2^4&KhRK^G^9~XZq0=27_kzXI=Uo;g zCQ>ly*!QxY;2Iv2GZ!xCy8+>uapzQ|Kj&x2k)u~_dhv!sh-PdX;^jdeeb6I+R)Grz z*E~qL=B>#tvifl88*%AhxfjNFapn;=c#h5dgfjt#G!&03Ve(i`7gT%T(znsNsOn;) z6GV7mEaL+NahS)$qW3P|nut7B@AQq6kGP>!nx?fkIsCz=z@bDYauat=W?f}8NPe~_ z>*2qMxy=<`%W&li&;GZl)wAkA!)A*7qc2SOky#$HaL;h+%@_0H954AG>Uf~1ESShA zo-~SHzz~aA`B9p3rYoZ8h<9ZSwd{E&(c@TVg?pB`o|U8fKHewkJ1#_7lAFDW1#qT*ogd&qLB}-pc8DdL;$)sf!wft(AWAh2e_K}=<9zLwZu4+Pk zn3F?0wto_i#d7O<94I5k=(fTF_a)qrAlv=}o<(hWG2ILUm%5b8y`;0@lOGz~3!neV zV|fDhQl?1>!cE7|fA2$n$OPk{4UrT11{a46jIMu|_U|%J#Nv1W(JutS>}^&TwzlVE60H%goj7@rr))lO+_vL2;3};W#p=@iO!cK5|Kux+pzmt2`(le0DRXWh6fXRu4euIRt*c!r|s8H-hp7 zUFtrYt*C#WJ5T||86L?8Gs=J}$l8GTb#9=}L5<%Sqa*Wv-RUFp_e!lZ05R0U(E0NM0~<-KKAkuv^=?CN&Mt%cWkS1F7ht8|q?$k*QlB)Vsm92NTH6lUp~Z zX0x{&cu&ql3*AVSDvYjFM&L&;a&UY%(X+YjIDAeSY+J`y(A`As=C^^dZRJ26Johji zg@VPn)EykFHq=Qr+l<~Q%3j7()~HrDeYtU1p42;b(=#>4r9mho;<$VkRWkls27DDd z(pFBk0iF69h|13N=!P;bys5SwtJ`XKBL&v2cEhLu4e*!!5d=J;g>x<_(u8kMx zrfnYMy#6d+81pQyh2MDny*BN?N<)1ADlUuGjm<$K*MP9EdC49+wO^aerhd}Y!9&;V!`STU;d9p{d`|lt&ul;blTF~i5&wxc z`57C`0j6(v^o=~T^@SeV_pJHK12n34lmT5etc+S3f?Nl$dpBkARycnA=f{L8cOs1J z)nWn-Ncu$l^fg52_eH4fC$`UY4W?~FUp)D_cn3Y=r%i`_AmdBJT=`kwNF&O$Y_^@Y zy=Rl1Hp)$@)+w;^GYkMt@X1f^694q)EP!prg^h&7d4LeshD4thMr`5k1ZR*q{|nae z!kJ~`4L`fA$D_VNyEL_%nlbe+W38mAC)hm4XTD&Bb*|kU`4#);eKu9o$7=VoK24cc zRMJ|x)jU|Uw#C?8@t`){g!c_<4gO; zZ2m@Pca5|gN0CtUxVROm_V+67%CRMz&i(p12Rw85`~zY3WOxAGzR|pXLCdg*uKLmu z4vxzMY2EzpA@+CKL-FB*Un=EbOP;S#4(xfxImjntOV%P?k956Ine8F;_Rl`|N7J^C zb?h>IriayTr0HQM$5)m^L0P{lcmKV4Qp?q})N)b+{siLn=2Zf(5_l>BT8Xv`TV!k6 zHWXV+O#4JzQm@0tVIQ3v<^VTiU+0i{xmNZ{@qEBG?_mtuwS64sBeu%t6(0J~rrG~$ za~+?5_ywEd3ofuJ?{)0adY_kE(C_;k6(qMFS_C%x$#U=vyXe6)@VQ4<{>!(v$g=RK z?S;?ur%8t<_QVgP+%qoFULA*H;U_jGMQxdjLwl~}2B{8NiK8#R;+F6pW=hu39oomd z-LKnXo96y_u0nreACqUv*gwB!{f)ym`I4-A%{avB;S@#*k)d|q1N)jm!e%5CALaSd~cMf^SA-R0bnf}EW$a%QhXPTnU7}B{1 zPCdyQhoy;fx1LvSDWzpnTsq(ZjLX4&XlE|uLoP@t{?2(+3Z;zn99xj4-|nl(O3u0| zzUZj!$~@4_fj#`JNrYG9z&TEScD*6xQb$QMuU|?3hKLM14(PamU>G2dywMu_RqD-g z3JRh?ajY%fd1&4?i)=YC7CSI>bFhp4S)CA3=6vXoUkeX!>RZq_FZNItb?$gwXCE&e znZoV*)gFFQf|eyZNM7+jaPsWyjR%n(|DaQF{>TCJ9zthbM%s*R-eViyv-b8u8-ec8 zJSeXPbqwWvpSHt6^A8*>|H?~rtnYh?`8@TOm+IWGFBHA?%hY)Z7UQiPv~x{Q2f{q3 zR-QC^59()J=bB3t1Uk_2}RjG|*LZJ6?P z%xJllQ`Z-;3&_BOovxQ;?FJZSoC?&Q7(^o0r#FOf#w$nLGS!M0rR&;E>e33=7Py9p zt&tDguP>~$t!M|7!#loqY}mu5Sr3G+{J~1-+ODJf>OH#V;c7DEa;?d>dF2KV+K4L% zX++2_?St}W{UgS+T#MI3=$0kx3CaPR;{kjR_Ik+IwW53a71Je_wKy7=dy4HZl<|8V zFAmW81$tH9dXiMvh^TjDKk86kYyf|e?a0GE#D31P zy7se&(9s2O+wV~m#3d8?v_UxFY(C0KoTwMNsgC$1;}5FjoA>ku+E$>VdwnsBFF1uy zbYAOXxxBO;ok!*tqVNH6ZH_Noxxub7QfD0Pnw)(uZ3cU}hq(7;sbB<`<&n4Y9y(M` z>?Coqqm~ZV@`5+?#x*Zr#E|iT{g~@vI@SbJ@8ov|um1Uh1J?+vgK2&eLN3b4I)!)I znj%wI-oxiR=PI^8DpC2lso;(m9b9u@e4S;nOz-Xyp5=%xQ*Lbo;|b?Yd3lMlw>>as zxw`sHJ!qSJiQVTnw;w8#d`;c;e&$Qc-98u7@#YHKqRw5p=1wEOrQ(ogY}^<16Mc=Y zetr*q>rT4^pVozH9z8&!_=?g?{j@&!rC;~xt@ZFgpXFirTgsJ}?@$hLwZBmpk%O6o zx41+fI}+U#N9~4j_N$>wIQlqQ&ECrpF4Z8svHk#0baaXKbUin z7mpy@^zj_<@5_m_HKvPO`{EyE<2K>=(20Mc8M@LZKG*tls}~+)d$~!v{EEnY>o!e* zL4pnl83f8|Yg5&Ga*!tqY^UHx>QBQO~+-cv8?i26a6(4YECocc>Yn!e40=-%6|gtyL> zh4$6?I?pBW(@!W5((F%T3&l0+m$pWF#({JqSJ)5Br5*XoBAhSOb4}yn5l6p`^`#8h zJVF#jVUS1qIIM(XfKvJy$H6^ML9{l>gXrs1-s8*Y*dUfmVsCONoqJ8ELyfQw=7^sk zW#e-({~ir%j~#as&cZYIc`=Fon}_x7t8rn}dPELTmPztbZiu*ck#UaC2I#YG`>9*& z3p%bdWlU#YTryM8)~`6acJj>^>#)}P_KE_S&u4Bz!*9%^MEwVcI;-B;ha{{|p);o$ z9^}sq+I;&tZH#S?CY<(Y*s`tbVM=I1FPE%xsjtebeI+_Gdeawj6gJ#M9=&hb%BN#k zZMQF&O+70Q^{vQ5L%SB67Kwoa7->c~4b43@m~iGS#>JTqn#fnVDiiN<>Qh*vPLNN= zulmSO`E1)K1bE#O+aY(sU35_>AY&ucBayEY@Cci2`4UH&+4|36bnze))6!O*N7%0L zMH9N_+q@}|cyRt36o-6M;L5zosdL)4?OS($%{nGR62^LrzzEy_>6S0$nRUj@v9vd| z)aa4)prkzUpM;@pUz2BE$X0#;h^UHJKDn%X2X(p!JS zqA*?9k#p`$pM2&qE#AhFKKN?yNepiMK-Lp=bZ_|54!|j(+6*#KZ)Qt%6DQ5QsLY6i zl#~?3(yU+IL+;3`4x4e(&AhB08*}tW)^7w*MvE09+-heGz*T64w(-R7Rd(QU_%gn^ z7NWj*#gj&pC^Ixu@QHU2SE_qI9GSj3lGVFrNL8J2Qtv5~V}b=6zu1s<7&d(w$1l2I z>J1UwhNLZsbweAn9hH5;#6uCk&N6DpV_TgMXlhkk$1248atzmFTi4-QCOIQjhP{>`V497Jbh!$1G|kqr*M ziP8fe4)EbCiyL$VbUbzl@|p*zPI{Om`2|A#cA$XF<+vqgoZ{G3d8h1Z+Wqxje(}x2 zPdgn0`5#Q?;mVKk=DFM-Nl7hk?B3QZNCzs_+^#0V(pn)_C zcuL95GEV+d4r3%KPs6y4GK2cjp46MV9h3}jxjpifGqb*Q1?(u(bBF`UC1Gh@VvbM; zfW3Vo=K=Q`c$L7vUjolH^t+AVC$hRU*8daKVNt6ISzUEo|5_ChcSlZiQo$!WCu(%Illb* z$Mu9x<2!FGH_|q=3ns81{mTS-8T=dkj-M#ciNBD6et5UsT#;QuE-C@<|AYY*7QcBm zu#q8f0@%U|r#oL8hcDtFEPR`6q%s+ZpLXvU!9Opg@+4jwo1$*CJOEnOq*P}T9|XjP zcCm#yWpBuLhR5GF_)e(u{K#03dO{l z{v+2}cQ^c!Z(QQ_2l1tTzE9~#V2<3#*gElY&eTQXE)-`mnUcD}^D7%Z^Wf(z52^P- z6!2Rgp$V@14Za}FkFh|KZrk*sNFR*23D-CLvJphbd?^PV+f#oNRR9|vuL;}GYTHJ~&b-YvrGu-=R$j{I};`jan(Gd9-HxXxAhTDFh<~`UR zI?pjay%+g&-h_A_w@i#5n>FEh!W+OP>!gY5FJI4Ti{N|sUV{D01Ya|}O5jxjuM&8b zz)zGw`qvY_z2EWCDe0#Lm&S91JN|l!_U75Ng+1d~n2l?%QxvLw3 z+05ye8$Poc&o9E<=Zhtd<#4Qn=YW;-YF!XG9zh=4HG@Ae@R7ML_mQXZz~IGY+^~5l zsq+yZbf;e6-*I|2cPj63mfg#-P#zk`w-DwRiK)=@S3)UYvzsCs_X&9!c9U@HU*A{} z$*-RCOUIvt8Hjd+DIBsf$&F8b%<7&m3f*w9IvcKi;CwjwlS|9cahv>PL#Xpz>rIDD z@7sR>b0~^KO zqTg@btnPt=;6Qfp@3Rp%K;M4mISrd()p0Puug_AdhsW1L)LSm+eQ(%!nXjO;5gO1i zpp;v6VvvtuB1E0dAz9no9z++7?bc0s9-zx%bT;3{cM}!y&N~&Qc)PKrI3p|PCg^~h z-hA%k3y5>jo;KAN61#a7xt|?r&V`KAcSD00rBS5tTxXrR3Cj{6&v*m`M%vxj#QoG! z))?VcoybcSwM!zLD~GoM&PJ*wqI?WP$3GcRFRHI(G+r400$bk1O3@W1vNTO1lnEJs zwSMyzy6Q&{%V0nFs$4heLnFLNuJxTYA$bkEOG}&4O*UfpO8WusQT@T!N=&AtR~QCm z=%uuOC0=|#3s(v__{1B*jsG)fEY6>SYh@+Q<@-DQMrWoG3jRc1%S-F4brzt3!>a;~ zG@@-rM(2c7HM&8vYd;=-c)(V?!v=h#jXtvB*)LaVYh07angjh-zJAWe?@zqc!)LX> zef)&aL39tHe*|t0p+C~+eq+OW))mod+gx~paQYUDkcOCqC#6#&%drKxNR1Hr%-WyB zdm3pv0GIY<7;+fU^fTOOOSASt`8Hmdx=;WI5vMC@+%J9GSZ?%}A>jv)$joy=BRme< z7kJw&^t16#TV+0z%ol-qvsY5MS%2ff^dzb`{>qiV$}-neFKLAmf<)O%uI5hu{G8GT zM-iI1t}**-xlOSSDjx3q&kX|b-}3AD6F&W^Yy24guRKp!+&)KmauD5hGK_ic=zyh+ z+Txa_o7J?;vCh-2lNMnK*Y zQ+mdpl)!%PaQ1Py?8WGQj2kwsw=V=I{h{Mc$E`!?&11gA2Vc?AmTkwTImFJ`*8?5- zvY^D^Q5kq($`@0*r{arW@ANUtr zXhX}KM!V;@m+|i9%a?J}DDSI}h2*{+_+~@O3$az6S^` z*TqqK`US1{`H+H!$Zo^59Lj>fJcE&@b}D@eL1;TqxBu|V@Q(GJXXNGE#%U`#Q0dD$ z>{ITskMhw&=p0!0(2nyCev;-5+&J%Z|D1Apc<{jDsqYCy`2e(Kw@kpK&eF%&&s;im z<)7!(_7ONrllhhhYOKqvtBbArwl<^xl-ZqSUn+5d5O}3=}CGWc;(MlC>^UPwIpnS@Ko$L5X5D2Cml6a<2;m+{*^B!%y}vXrSPi)d8G-57{|?`Sl+hgoX$8^$Rx6^@6Mm zj3`?s*X`6v^pP^(qo3VRs+^(`k!$S?_t1}yfWtAnvJM`?Wec7a6udjHaBSrP^taS2 zZ{&gw?F{kVGSB+T7qi8yeQ;jo0X`4pdH4ET|jXuKhEyQK1?ZA9E^TT|~L-CWQ4rINI%6IH|IfTwwlC`tG+{8My*tB0w&%1LblUt%HX@|} z+6Rh;eZe7HzrHI^)|qXzYjxy zSMND+orCD4#hydQvMCqWx-bab<0x!i5I^?9O3Yy!`&!tuYw~N?sTXv}ae;Q#wIQ8< z+M0Xt-(e%adH7Es=YDOJBlXck=v`B{Jvwg&^Ktmx_nll*o}2gd zjT}~gcyu2)=x6QCuRUkJf{wUfT%PUU(EjMB^Se)^YckmA?SjR0Ne);g`m(Rbv`SsUNtnYrHY|x;59-jB57&%-|v1~h;TiH(1 zkSG;(;@Yx&zW+cbXlVaDbnFW)-k}%12sbv9~J-Hgj|rwR`x6=m!Rf%a*oV;F*o z6DW0?*2fQ$SN0^El#%5Ghz zZHZeV;(6|x5P)zv_bH07xt9a+al?PHJjVrUVdI3G8|45`=~HNH?fWT62#9~3SqY1F z&@^O(DdHrUd~#wwBaFi(*vu1)2~elXdlV&hLV3!MHq(6GTc7B?YuwyB7J8e*=+ptv zn0&#B_MXuo^J&Ls>V@RRudeEBOGq63zw!BjXC`plZ%ag-%sPYz(CHu?Yxx2g`=nIv z;IrQO4{qblBitQtt;yJuG=1~sWqMyf(~q+^$-Za4g09A591$C@;KlEC)Lo)8XfU`i}L4!6xehK=Id-cP=`Z8pFWZvPN0^_`O;uzc)k=#&u$|Yq_Ssgb!2CGwA zWC8{!aeN4jE|Cj7dFH4;HYgkcXaCc&2N~o`eqjF5v9UOP8I+==eBx9&W0$N8oOM!N z@RvX8%Xt5aS1FlNSOx2y|6WjNd1ZOZVEL1m@#43vscDxTYgv!$+V;5 zy?jc`u@@kTmuBXdEiQLL4sW>|#_-g^T(l!{mxghUAJeW0+ZeQs%1AgIze4A4LnMTf z_~b88`0_7BwdsNRm1Ft5WCA(cUjRtmIrk#0aUk>7k@8Z<1j-s69kU=e)LH6CoeaEX zCZF(e)%UmP{}uA3%kT4}FsED%%w^a%C}Q-m_1nHAbHwo3zAf{m2%>qnjN!q8h|1E6 zFTVGQOgZN+afvr=coA32q`%R@QUuyM;fL4gDenv6;q5B|uOWV_?nOTVW zuiV6Etg?%JR|jpM)x|xK?zlmn-M%Qk6wD{WIDfZP#>oM``r@r=r({_&LY4B$eZs(# zR?Rkh={j2MwR%#jrC$Wn5$&K%sCdznB_!qv2OfDY-%2h#@q4+pl}Je*WKMjzAiqF1 zBoktki|VcMp-(@VH0gszUc1jAfkc>#Dg=xlY=Jagudu((_-g7unazglk8*DPhb?r& zKCzcraj6KyFJINRkuM;VE7_t$$tq8zDJta^Sw6LS0T2ejz!HeAP%!0^mwFRtoyxPw zs$0#|{N%K(lHsbTPxp;(+J+^@b=C>TIppmkIa}N{r#6p^G8DdyE*uG)Bn56_lp1LNZ@;Vk_jqS z*on?96C)P@eM48?;Kt&xUqR2LK5@Ru8>8(cBprlC(}@TDfMamlWe4WivJ93S)Hx~9 zc+xUsk&Sck+=p<$v5rpraT8Rd)c6!oOJ_IJHLFjg2BHNPzURM;LICeKf3_` zkj)LB*x2Bk?OaIBgqjJ5{Ar%sDax0F?=JSaAY=GE;gEL?wfXqxYF_ViS)>W-g=>D~ z`LGFzEZhW>r(u4}RI$XUHD&Qe%mT3(Wy9^xyV-u<^H1ct!jwfxlA%X;gnFfx*4)^hZcJ zSf1-y13ThL)}mg9$eUsPXBiVsTA$q>jq{4H1}eWsDv2fDl16zJiHa2Ob>gi~*V7{>E*;{hXs{;>erj?<+9 z7BQKK-LWv{mwUg^|MRTj%{waQIE3yZ)X-Rb;tUSG-1H|4F97K`TtBIlH(wlaVFfzZ zPH4ru7wIXve!`^ne@55c3 zyu@EB?l37?@30X~haSSvoc2-xzXuMFJOlm{7n`7833r1PKrS2oJc|Cg?$ zkr2Cv&Fn*M+M@?WaM>v3#wj*b2pluCT$lTi?jJJG8%+>zcp2G5DCh7Jl^MgEw(+n2s{~#p@G5~<3A{?+ z?~}mxq3cVj;~)FUx-<1N+&SK`X**_m3TMwNE`a>W_|!2fLBBfT#&_4SS-*bImr-^@ zm}|{_$m(1LN7l<)IB5)~-T06%K=Hu&&iSGE;~e|AcH9jR!e09@b71ztw|w;wx??QY zs_UFbIlSnLzq+RFILwW|j@hm_1n|v`r>>g%4 z9&dKX(Bq`Z9dVS+F<%C4-W!?!{^Z_##wQ0sOT%Tf@wtvb?vfr^S|%@I8aad;`{WlC z2NnbJEDfAxP~1I;Zdibh%{umU4k{e;?)c5)My7me!>_8b$ubAft?z7<=r~)%)j{W| zGRZiWg2f@1%ylz9W}M8J*)N{o%^@wnyyJ_b@+EZBIE>KEdSU?|%&+ezA*M^0`OrN3 zp!QQY(s^KG?cF|8VZ&>LKzd*hh-I|sM?H<4S~v3U1-#E!*jPzHsar?zQ)gy`r+5Z| zc8o92J{!4m6P<$QaubERtsc6O*!HtuTQcXa0eBc-=ML6|_|_8r*>p)Id67`@1| z9(LIrz8h`T8}&^W`BV>rCbENnb%t_THl^kz`c{bdDVP!CiqEE1Xrh7HHs=>yYC=fm z-`v@3Aot3}jFC_ADk(FltH*)W*~Vbk#~ zFFnhbQIP4*J;R%<9k$F;lox&NS?W!paSt_s4WV<$$nOXw!pP&gf< z&eX+b9SuucYn1(6)_?Mx_%=588!z+pt9+04y>3#cBi5eSU-`n69!}tZ!be{a&cSp1 zPddUm2*-<6?l^ei^Vn}}%ym(*ZK8dqTv(#jQ&gOr@~AF0SYC}Ct_8yROKgMn5*BhI zP0w|?R|IungSh3fnZ|M7H2E}4zLnWvIy_NNnnBxY)+5tqg<6{8@SaO}2_?*r1#h9f z;a|JIaRM0z{>X3K=_2{Zu0`-YXc4V@33dn_`Vvb~WF{S1j=~#F{*v0&O;?>H-=e5#pTdC zW7ixaQ}SvcjLVGg$ZO(Z1vbZ#?_)fP-TLZL;HZ=h|z7@`ci`9yaFC8J&E-Y8#uS zJq0&zpA~Anwcm2#v__ll99p}Tm$5OX$UJ!b8xFd7pt$`5unhilkOQRl8`>o2CcYrq zaZBfHBw(MiUrjpLI`Mk|{R{TlJ!Y098_o0V1R3oibY9${eXJb?mhd+Zn7fb6xxVx> zURDmut%pF;r&%^);w-aaSkHLgH0-l^ABb!@oFfe&u$ISuseO}mWPYBxVv9W>@1?B1 zFvh>-OVq6&pV!&v$cKGX$Nl1UOe1|TroPkH(HHGnM*EkUt9d9yy$TbRo&B8kDK4Y+ zdzi`kP!@dAkMvu--GD(+x+WqIQX2kM z7Q)icrMPCS)E62!%XE%uza-!ACEv*823Z%@y@`{qEPxXpv@#rDxH#pNITcJIypjjj zxB*Y33FB;sktM%g%W8anmLadk8P;=>rFfeR+@* zXV*O5UvcaUCOusG!NbawCw9kn?b-n}9;|2ZON;2ptp}v4lhSj{wL&V)3>+x$bfdlc+xr(!P-T6Qnz|Qv2Dzl?%}ZRkbi`O z+@j-@gSPkxu18+f=mvtw2o`LcDWuUJh+=K>t1okmE?76!6=~jMbIrG7N^R$U3ElDJ z=e)qt=g;Vb@{&s`SKH!CFFb5@qk|Xw4xM53)pZ4ZdCORuwK6{K)w)d7y2($RRin1Tqgm-+dW^c0^UMZnTvive&*l=XsaI^4u#M{_&`U<=W;B+LI;=}WT#J`gXr(EAL4p=e4qLcFWN2u zT~pNVc&QrlYr1<^d^X^LbYH5I1CPi~`i^m=w2{4z5Fm!B`hc%rs1PLH zh7BAzLk*hYKePwodV?o`8=k!C<8xlk_Jq^tAl`FbWIkczE#o?Qik(1D>chb!yZ}J^*0$i( zABLd5=qfK1BK*J$cJ0G~laE(1v_g$Nsf;NPq21=)l18uw`9eHkFz1d2tioo+RZxehSfUTyi?% zTLy`=90f){j;*E>#3i0Msmgbd}{Y@Ov@=b9fTXnJe=(s}t zuk@Tx+U{Ei#UiHQ5F>DVWnRbms!bbaTIR^E@~e)buZ+`$*|Dj-+g{vj?Vp!il#|wH z#%{I`?FzaiZ}J;wofH4$nsOdIiEVgpnHKtz&yXTLuE8No;VhLGr~{-ZH&fHX_vBHw zF^kZ*RBzFny4AG_OH5m!4uNKTV^C6Bs^JQ z>mqCr6GbwOB$88P(*{l?NrR^Sc>t2xaO*UR0od-77LQKlRmn)3u*X6Q$BG{BrEwh672u`7Ti5#U%4j|7DJdY2Xfi277?5 zBjNaoz93u6m;ORIM?R$$n<&EMxcrujWv6#X+zC1|l~@whEpdeUa704z;~OL#P@xQ` zKAHn8|G`&Fbt!=N%HgkF-^)>1feQepY(MY17|%b)`Bz~72;0xX^G7sZ)BgXHfX42R zbSM3Z#is2Ey9<*pYWU{1I36l^06lIt@Oe1h#U92RW034@>H4shc_X|N5RkhN?!t`5 z-3?AI9`sA-4x$}cIbi8T;ns&3`!-7!{CvyT4!&v_+SIk!H+{JPv=gN-*%0Q! zgkMP4v0%mn)UQsiTnvSkbcR`kfi3RE<|HIpRrmkOQTv>8y@=M!|>8ve{hj54!ejmdQDzZ%=ID2S)?vM21 zbr$4T!R68}dEGsI{8#$;&pga>asL(~9zbVVg)G3*N7;8v>(B+M!OQl$soO&!Q_KPc z=2$S9MY%4XFeaHi^-gjlqP?)(KELF{^zw7~iIm@#U(bH8+Hzv<1&q%r{Mr5T6x^U* zKlvt6zVaVNP8bJX*+qVHzw*t(OXRKG`(W$JhcPVZyv6lFmy66iw4$Ew(1&Xml^OfF z>DoLci3BaSbo_JSGM5Z8c0Bl-XguMQ2l(G#mr_4XYkYo>v4wY`5E&?;3rf;NpA66G zB$C7z;$O`39bC_{cX0`l$m@J5mx$q6yfE2R9z0L;$eU??QNs2V+-G{HaNCP54Ovtn zhy3Df9krgoJbZ}4@vR#)ebDGb;ywWGTb|v>&=*%ZM{sQKf_E2$T7lx1rn+#UZMgL- zt=b)R!3Q&z>-$&v&F``fcyr7z_{3eEChTA0#W+0z(>)%>n4+q59%Z=ox3qvJ&T>wjsWWCc>F~J>B&J@(+hUn&oV1Nwe-`1r zpOTwrFJVJQew|?fSRRXUPc$L8arxMuy@4u^fXL|PM-s^j{f8YM7 z9GSkZG5y`V=eULIyn_A=RKY!CQxDC$DKL&T?5=AwKK<~H&1SAQGiGHS+v$g6os0!C zRSX4 znE}eim8?}eK4i1S17D2t!FKv^`g3VzyyckD_3CVP=~{a>y}0qkO*FG{%*`m~VRFt7 z1(p4BnsCY^|Hg}-w9Fdn~t9ZEKu-dCNE&=9yr1f5QESBk^SV} zD(Dw;1Y_=~%=u$4P_ERyW94kVCf!Ylgxy2CU+{Tw^Alg~(S6M(LN`UM16%4zAEbj{ zp_gl7in(-Hst&#_PnFB4s3$wR`4IdFEjr%Vpuj0?`Z? z!J`hD*o_tHCL7Wyr-vABKQX^!1LbaVb#rB417BQi$577m95**vHvByI*=If0UI_bJ z`jIg*%f^jX1l39IcH`60($yGi}QN;X6xzkCIY`po9I_{z`? z4IqPylz^XdFE{eWUpCPgQhu&wAv>RO853P0UR~in7XqA6ZhNq^q*@k`@jV{o45(m;k6=>$*P=Yr_!9qr=F?d1#T z*|emHxFV&YXCX_^cro)A`-HBmywWn07re3RLyXSYBZcsVw@Z96nziW*|3~qKU7nKl zMLMPoK1f$K@^GdhOv~_c{HCAt{MB+Sj?MQJ*Iz1!s17;ihM&cEN((9CO^WUt5k)L~ zZ2jiD_*;`gM_$?q!c?@s!ABKGALLtobc3v$(A~g&Prv%`9=%}0>EnBB+6@O%^F#dK5R6(>5KODTFeyEeVHD)HvX5sN4@LGSKT3 zA-GpK%Z>O;Bl*i*f%rYL%CGR7j=wpRkLoqK{Re(;F8PD;K#;r3&(!=u3^+l#B7z3z zcM^trH)?K1b&Pa))rMwpwTQ+~?nPnThBr9(saM0wTNiw1l+YMiNKH`t0LNDLr5>`p z;_wbRvNlbC@s$Vi*b~4rg}(Sv{Yp(_m_nC@tyik_FKo8%;Q{rVvBhyXoUw+7L)=+J zIi;I>#vsBZ5Bnz`zj+XmG&X{|89Im09dqi~ww;Vq#qW!f^c}1ArAh9!$brp0 zfbJo3;9P%&$@B|ri!FOL;CW;Pn(2RK5jbi23`N|wSH%W+X}1}F>O<2#BCh@0SG$=zO-|xm_2#Bg z&}cn|zXN6xU%gieyh`Ai1Zt}^qOn9a>b-14vBS10;z`TPgRqVF9GuR}Xc^;w=FoZi z-W)=w59Z!49)IPqpnY%;X=_Jl&y10s!-%f;Jd@4-uzuazzCMSnW{{9g#oANazc#=z zy!Oxe_Xoa+efPlEVzJkdVQh+xz-don2e1Whf{!h*T;U(jF+uE(heACp-!GE;vW(j= z(7CbS!A{8|tWpAOaqXA#fQ{J0*ja3%sMA+5&TtdGclIH^sO0l!fGOA<<&U`Ji?grFy1S@rscDk$|7ZjUgG56uzttf zrpr&VE2F7vpn|}DP8c`a-@&70jdLBuXX)cNo;ssW#nET#&^V84}}} zJ5A3x5Inw2Z8zh~qkQy>%E*p+$Q_5Hd`XGV2G4WF@GyrxJ*1Q`XVRzoWz0Ok@<6&9 z)9Git)DfRgwGTea$9`%=V?ZJ0IU@2N`Pwgw%i+O2(K*;k;GVuXb0u}$oYa#jsIADL z9j-X-L+eaqhatFGA8;<)oJkkns#2T@aFds_6CYmmf$X1$Zoups0Ohyy^uVZpt|`@7 z56MbekQTq~A&0k+Lk^$m1LxSL#ATpmpC7plAB@oQKoXk6VP))%b%hKb5y%zetD9@STF$s9U#zAQ zJkaLB;wyNz{an))TsLsdNR+OriiaMV%PBCr$6i9_g)-QID~R(zH_HYfHI z8;MTd@%3>~%VK1f^*QCPypdxL8TwV$sf*Y)m1K0o_HN#^dtt<*TnWn`r0}83I@6bH z+cM>+uJVOw#)GlB$^bM$qz+M^wYk+N>({ij%X}G=Ho~8F$OQtwx_L+aKCmA*^F^Rn z_k(rHmi0ztdY1#e9*|P+ZL8WRkiF#EY2|0eGPPAEhmW^zJM;TM0Ib2Eo=0#49&xO~~epbF)H@@7$KF*h~e4^iz zM_>NgzOH#3KVRodq>iP<(e^G+l_xR{ui8s0F*<`d?c-egQnY}C2gT2oBJpeavTj4Z z95}R3#^vxCl{ri;T;TbtN$-%E zPx-l*YO!R8ThI1># zq#@hrg}39ZcPp!ZX7_LW@J6^1#KVUf)mv>pebPBp2WF1Lp92n{WB&kLCo>w zy3h`yL4(&g11C4ZGfnYDEQG6!ssI2$07*naRLajXdeeWvk~+Tt5_8}ciT~pF8rgc_ zEcJ7yn=rTnqd)oLx4o)!aUgt1nD(u1;Bx2~ebl}`*uH`BkaZ5BW2b$FurC2|+!1Lb zfBTF&>VbQCRnNqmHFWZ&55cCa&G02kIm8Z=KI^g%)ZOvS>+a!8`f=O7wuf$)%e<~# z(8gW2z=_`%Q{)94z`zMV{5medp4d-FCJ|mZBn769KFvzDd}S?&o($<@ah_d zHooI-d8?Mlq+@3D@S8Gp?UFf^{h!Z<1TBO1;_zk z%Z%~N%QWWf%ng3ar~JuB`7Dnw7EGNIHa;Y&vmD+}-6LD>)mzJze#*XB+JgJTO?^VK zMcO^|fEUhKo{TRA7&{^J{^ftd$(LB-O%Pn4E4cY7J=(GANzSb=2m@iRjyR*)oI#IShYflUt$2ermAoD?1792Z8 z@6iv(2kGH~Q4WWLNB4EFJX^QgqS}tk^=*sV7M%8|eHxIXTcuypAs=KEGM7AAmbpv0 z3^)D=Ed!Hnb5oN1Pwsm2OKl`{WF=62X?dF7{F!GtzKgSli65Sd&b)?9@lPD++xnxA z)k$>|h}BQ&&R?13F1Sm(bPR_;AeT~Umuh4$2ZBxh%^{pomJcfsf4ZhYD+hZ`!IpSqwv1KBCh2ZC4XlG{>)UNZ(7Is%eSU_KC~3H83HV zo63(o)I#daQaeYY7bT1ORJ*pm1gm2s#gZACrWNn%D&`(s;?aLWHYvg40*FhfzOaEj zuuelDlH7Wxr=1H4Z+iY3P)6Pg>mN5+rxOWf;tr>OcESxT$>v;i!3p$mM<_S-OJTxz zOWXVsKJW~0fh4?e==1^C?16axvvPbM|GoR@%l8t0dVGZbgIJXbS4Gh9li$|Clc>2# zZBBNEP5<@lM}_np&iiwPJWu~o&cyL&dH*b3T4!EhlV)=VJG0M>M}UF$zXgB zp*wkZ^6guku9+yjW5SRx)c8eo-g4Q8v^L!uj2zCuAZNjb2~*zO=L6F(pLkH<*Uo?A zfhk`+&x3(C4E}lWoWZ{nGWgSe$M{Rvu$XJewevps(1-1$I3Q1`y-;kkNcwe8LF}OH z=!7DZa=Mnn`=sE>UL-oBGGi~Y_bSMA~^JVQ_{VeI1}U5^!b)|j@%ZzJoyKu5|UJFQ$XoskTzjnMxz#>smQ z2g^BMDQFfil(f@xcLH6@C5lE1vv%l`+w?!b1LDB_(qOgJ zMjq)K07;ynkqH4{Yx4-0Z|ON6kYxIci(ioZ!cKkr#e~uw7%PFs=|{EzxM?6sM5O1M ze8PB@C1Fne8n>CWlm0-wZcpv zwDfb8{6yTMay*s;=q|+k0q)kn@*X#~VrxJ@`IWnAxfyFfop$ljIY1u-Y7y#PPBTM@2s(oP=w)vVrOfH|{ zvAhgYQi8l-2g}-l%_*(R&4xpt=)FInPw0#jF8uC|aVOcdZ0AfZv*2FbHE{U7E!|1e zK!nBK|Bk0Ko<%pZ7;HmBPecdMDSm^0l(sPAAN@#}+sOKdQ9GxZwD*63&R~A^UM27< zfmaE1j82%Tph+cb@2j_c+Is zagKRiYj)h{CIk9gyIKY!9X~nt+V$*?1>H25jj(Qt)$s)i$AkUS&W!ZHVSd-DQ-1t} z8OLRO$8;=Xy1CErF?372y*|H`)o{j>`7Mh+gg=Hk0hSm7 zbmzOGQwI4RfR5BUR5363^o+pIVRSbF$!ACzS)FnJhZ{%Zcy6J8oy|;sm58x5n^JH5 zaua?w+%R`(-A&1Z5TyFS4QkQjvL>M%^K8KN1~<7dHcve{Ue{S?Zm?p_A_uU50p9%9 zyBo*ZCj~-QF7mvjYY*8pg>D|3$jI}SP4Ln6Y__2yc>o`_hP^DFD0^P==H`dY4ET}l zlqvFY6G)$}RPR>zlox={WN$sVnKb2P&Zw^4Gjad=l$8GAd}hS_ zk8-%-E1RNyc0~FWi#2&US-#W(KjAXeWdeGm11P9pAbWH(r|m8qjHc|cZhOuF0qc== zn$0e>ufEu<^76Tk=zF-^`t62NpJ%xikuM*)NtN}AZ0-Y3aJmuBwhuqbk6;d1*k){- z`vQk-&a@q?bMmfEB*2gVIC1oM^lER59eB#iW*;+1D?Vunrjki_vriDJYn6{^B$bP7 znX9VwT*YEYSn|k7Fo>m01+{W?9mdT@=;}3Jvf8hiSw~sxa!wE2L_|+cC ze8!r3} zdMoPs6BvLC%w#Y*xb$(}Zx(`Hwfp!fJY}%23L*f9kKAtD6HbbwvRMMgk$1D7;8(t- zHH<*RF-|)OZggnJ&2m-73iy&Mpy zzwm*CeI5CHdHp*Nv2g&;Lm3a>`UOq<*0&r=$U!FdHaHHw(Xlq#c7)vp&s+MgCtgaP0UNlTXHz~h;PrGw0P`hs5nZt+d z+i-7#Wp*6%=Blz#=iPMfp-T5r{Px?&;}E)WzF=1I+yB1fjX&<$@ZgDKog6~<(2M&m zbdHJgwM=N(f5WZ^7u_6q97I>%TX9DXPJNRmf6)QqaIe>^1YRZZk_5Ct#@h#J$m-5* zrv0~VN_&CLbByo&5^RxL zwT_#N^RWl#4f4{|7W?9IpXth#hVrd;gAb0p=aAPOhf)1vqus@|b`1OZz?eMKA8fV7 z@rBCicO)zdobk?adoa0&JuJgJUYMoLX7T|&<{{+OwrZm@{)Uif9PjS)s&^cs1B$X} z%f#>8sbggik;u6Hga;ZWtn73-gzg7Uu#E|Cf1-sM<|@vUJQU;F1O_pO zftXlj{#VqBp>qqz{+e6mrt=TKWtg}EB+c^qhXga1%E1%ta1Il>SI%)-=5mx9`@b*a zb3flLw)~d9>V`hbeyloTe+!?;&%uP@J|{9=`J!u-!Gm@bi%wD-?>Nxsyw~vH*X`*8 zGFR@Qk&LO}$>&b@)cZ9Lh?T)R`$Qdi?I)Bo(#gEcKI?({dtmP7yu}aML=7x2N-^CX z`qA&c}R+K+%vC!t7Fdi)@Rxo^LC zH6=Oh0m9C;6XsB+YbSTiU;RRRj7eua+_Wx(B*| zF)m{W>za!8g+A)5Uqg4C9vaazXiHzcrEHclcEz)2%u{ydLCZ)zS#6Gk(%!$mee)%Ual0NA^@6|Dvx*5{ z9+~nW80Wcl*qJ);6^()K~Vj zAP3869p40&I)x8)x;!dJ$L6$M>RTQ65Y@eFCX5A<1nk+?LJbDJWSR?jb)q~X2Y7r- zGMI?Z(5xC`i^*p>3VH z#>$ai+K_9rt`{L^Z3gA&8XtJsbF6LZbA8GuU&>eJ{D-`}e?>PweY|_~kr$zT=7meX zbkVX|KWUHHXL)da%lkTY!L}j{f4z<0@%+?-^=J`1Lw|1oD=-Q!R^>_?7Hw)Ky(It_Fuk0%9j`HI^|J5ZDN=j z*+9|*n65wP;5zbAPQLWP_7j_F`?j)OqeE}2oA6KEBmD?f18>!L`x##}V_$Q_A^Nwx zu*wO%<!`|KG6QHBX!vqh-%MrAXSpqpK}d#<4!&iUS0@_;^b?V zex2RJ-ptQFZ~*<=E++7xYP<}Tu4<%&4>z5ntHFSMXt zxMpEpr*0J)%-cSr^VDfDASdythnbscuZ>g2;JBjy%CiV{LOW%B9S|A#^_wKH^ZPi%;!PiApK3_H z;WIeJP5j_?Z@hon7LfZra9~rKKJq0Ej?$zajlU@K0Fzpxcodb(GRNb}~-nC%3qdC;|!V zQ>NMwZQ0qzBUQ#DzKHjFUW(#*io6gCdbSO(*pjqG^^-i<5$i_0Svz;^O?z;iUi^8f zj10zR(4HK-pzrE;`b7F^Ul?@Hi;(RTP)#gQ#|RlKU>Eaj4V>?MQ7yL4y5!Y=8e8}! zq!F?0+Lvk9^PB{d_Is2m$z%8X@?iO<-r>o*?D|F9Rmy-}IbKBT^Svok)|;K4`x(IxyL4}w};%OUqN_NZi?#n$KD|0>u-?+q+Zp(zV z$WC5k%WWpir()-*1JlpXDD(VMeO32p^Oi%QC?`x-1MvweTx%M-ez|$ZF9^RPsBVfV&Bu(i^&CdW5ADzu){Mdv#`zasxKpNG-=-|Io5)}A zfwsT9rwtizTlS@B_5-m$VgYyhh3JbqbLlg7AG2-JI?oy{dCHJ<;f9~KcgJoHM&x05 znnIXQnDUmwfCGH+p{-I+>%1?W=oQRGXT~2vSUBQW7P+$|6K|sb&1(UZR%QvybYUaK zWhc3dt3jkJ38hWIu=CGk=f<&C+jGmMzU|@j_(dW(H0L^awg#X6NkH9Poo)D}pP&dI zqFOvM73BC5%GE%Mc#b}$E^;F;s^pKT#FyATy)a5+i8( zaeD+z5I+&|4cGNBI=A+N#D!ncntr~-we2Tie*7mc^ZE>K$QE7Lj7?KNly%1{lsJ5h z&ZzVL0aHDaj{p1^6d%s^VY^Y!E0e|>E{0@p8ItiS|G@`JizPRgF-(B{A(pYbd{`H` z440316CKf!5?!JESJ-iJYLDV0i$3Fau4X+eS2KuCxZxH53I5y-eLQjn;`kAde`zC| zRzJT~7_zY9<^|u@{qf^(Z+`puPyCPl`Z;f-y`Kjf=B-)ipowu4 zWgSUjbWL>-k_ROe#nPlhG8cf6mBH}9J$xH(d=UlT-~=M+<~Yka6w}kkD&;m={2@NE zgqvR`1Thw;mwV%Soo~(a^RIApLi?NvX%aVH0lg_N%C(`1v+Rf9PiP0~9P>O}T+jJ= z4)tUAe}v_a(tZs4E7Yq5UM1i_^`&MVd+=f-`+bJmj>__Bmc4uK|FPVkQz-ty;SKg_ ztnjd|)!FN8%bpkQ|FQQb3U=ci`zqNi-=ZUq?_NGZ_1A-W(Ouo zI%(-MlCU}=S-dpK1dkhd@@FG9N;?^mlBhnzI&VWALvO%{>-Oo;wP+ro?SR`QP0I#Uf$i%_g|AYzb^H{ zB8rSBEihSBk-a0E!>+;dzYD1`@$FAFK$zJO99 zQVY?@`bb}Ly?{H&{cV_m{jnR02VCESRwVz-)f@*mG$!wVFRjTSbscXOW+vzjLM;UV zrc$I-cm#G!Hu4D1A|XaUg)xhtd1>bOJq!PBSH48dX|!oWs~wRhWhlXB2hsn5D^-Ca0s+-b_@^E}Mw-$U=1F8V9li#)kP$U?fWILxyz zCVc7J&0rj&^^KR|EH=BTH1jFpxyt*v!*69&TDzP~A?}c}%=}+6Hk|*&AH&|lA;#nt zk6lg|sT$S=&6XV@P0sJg+W@$1!o=<{c?>`}$>XGZ?Hc_?+N3)DnR?UET^@vyoZ4!%%pqE6>ieJ75^UgE`fIm zyi4F+0`C&|>k>Hj@A~I?Fn3JS_1*~^cjA0L_T}Q(j8ZisVq^K`T{nKYj_g-i+~_=p zJlAey;qKQ-@Ml8~ykv3MF-?z(y^eM+ugkBCPLS}9n6Viy6PFn z8RXiQ09bIiQ2kXZ`0_!;;mkQ@kDjI<0hN za^*!H;V0uvc=KA(3yDND--K`+$G)J7(w`~hD<$o?|Nl>c04CK;m9ssk( z7pU+FaN&cfaM_Fjy`&5FGu)=3to+MmGGwGo-MC;`KDn8J&9N75+_g=dvy66Q~ctf#l)2s1cf*7kKNzg)*O6SksE zxRMui!JdN+_=$T2`W}sAxQQpt55kRt_km1h-RK_E4a70*_s{?)G+H)Cc?VN|+Ez*y zI*U}>W$S=ztW-u&frt1Sow0F;4U+X!)~u!4KdBZjDSwT4H6Hsw+j?XZzx7L;8Wwz; zFQETA?{3+s$=B%oLV9$l?QylmFobTXPAhX!L*ZuSPv&LOpGNXvzRaL$bdAF~xfTSO z3gn7U-f8H`H}DBgIh`R`XgBoG?4{n)Z`tBbaGwIc^JxkZ_1b+aVBzs8X#4?aIRR>T zeiy3dYtjrajXb6th@j!;jQSbqF;;&mF1phpB#$aX!{>&d$VL|`{>&Axfxr?T&R6sa zm>^q#4#wb;r@qDep!!tNgJ>%UBi2E{Tp+omwe(+0}_qZ8PhRzb5YzcbYAnN&VG^4 z&7$^C(6Z5Z%Of}#u0?WC1Ybn=cCUqfp6MJb>s~JdDZlU)V^E%L-^%ui z{TG+sj1RVPmwQFXGu=+}DbaZA8n}vK<82EF3pb^iavWb9VG%{YZ^_89jl7oWvm1G{^TatM1sCXq9d zzROfk_Dk~4(|K0|<2lrXJUbu6z81ZYzN1g70f}o2*YIo>TrXH=t?1?})(y;;WS#%` zr^GViG6C1<>ckQh7k%tpv?-ved_eN-r@`$D#buv|+uDd^9P?|QxJ|Fvq^+^(Hb1p`<}z*md0L*LZ!~`MgGeYR z@(T_RO}`+oHF~8^fZX++Faxp7ldM1Hm%ePh_y&}%7nRB=&6}r|@ z>jTu-lcF50<9v;qy5so9D{XPgmmsT-ds>qgzSRTG1Nle-^Mc2+OPeBJ89zCO;t)W2 zvn{D>;Kwmm+97H3KADPma=<2KIA2D0UyB#bCo+>4X-#?Q`p|v1jOPH*xY5s3M-#a| zV4LQiHYYx15Z#qz|4G@H(k*lQ+9?&bp8T|)7!el?HHT-D^&G-=Mm|DND)IQ z3cT%z0B}UUaoaw{EH0N%)G_*>S-VOa=fdPqL+>7nh2b`Pu|2TrDn_8DZXeurtNI~$M))Y^?q~?JXp5mtp(7H z*V>!!eu13%iq%maZ@IW`2QsqKgDN#b*Pxz5F+AV_j`F4Fj6LMnJ>wd$+OI;?^3kZX z`N6=RLt0&zr(q(0%=GVQ|I`cD{N1PSfmm%j&67s<(ukg;-sR8~^5R%X9bkR5UEFa< zt+G~+5y<~4D|k2GuDzNS*VLl_ajc&o(U!-lz8>9g5dwF1uE5|6sqg=~x8( zNZNeZk2c^TD~@Snh|ai8<8-Z(`?Yh%v9@3Se7EMf)-?^+L+n@87iQ;$Hu9)0=1=(j zU=qT4S5mzKynQuwIfqTy{OlWmPeCga@+sc*+2q0Y(D!%fTGumdZ>(v=B`)vefH%+H zBTxNa%0nBJ#`z2_h{0iG*nZr;40+Q=6|uvcXZ2U)95l&SXXY=S4N%&*#?EguR$~inE6Y7EESgIBY08Szt z=!|@Hk5?a+k!eiU^tSfwBL!L0%C|aszbCvnAsW~aH+AzSz7pBO5;069gVw~eOZh}N zb%L%a3Dsj7Q_W47;a=36w7O^8pXUl9mCd|Tw7j<&WXyw;BK;`$V_l8%* zjT49XYqMoN8Qw}`Wzx(fUD-~?6Xs8!7Oe?A$OnukPytCIV^-=Sp47`VPKM2A>q;KU zz|^hiNEDCH${}T8d2pXxJc^~4r71?91J=?_nS*=#i(e@IM14p-c#fefdUphvr{@^y1z>{FP$Dm0Zm_=#8jXU}WR{0D1>K@_~=kp;f=|O4xv7Z1UqRkd~sRjiCYp&Lp%9-}1R3F3!=zk$Hpu4SzR@{eIpQ!3d00lh0QO zM7^$vsS;zp`WLV75Lp0&mrc z;qtAr{W1Jr*Tj*98x&lO#+lH2YO7yGzu|;#7d`HH0ei>Ed!Ia+J6}V0AVNAP#VD|& zu~8-+jJj{WIiN0{yxhn}EeGSY=rmYWf_>7d^2Z`)2+2I$czAyyC(M8zG=^11er! zaJylGMZ9&pf!Squ-}_2AWnJb)zpll{8{r*uDaZYmzl7_8!C{2-{?RMw7mY>tD5s&F zIz6y52kcz&t>@;;n1vI^7cRGiTqEaf0CyvTn;Lu>?1u6zPOeF_cy`nWG$2|ihwk6* z9_hY;Jd2@(?``o7puZ_7wfQ{1=lo6l_rm36;(*ngBzR8T1J48Apm`eiEVK$@Pc2eG zgB-^N5@53EAJ?|UqGetHFn+hy0q@JcybLknO1rsY2S$z1x^)H+g>cHBNxX#IW2BMb zH-D-R;8i})ZuM1O`mXRD>+rJJUIceCr!+~<)im`FE98w}Uz+KWo-Zo88J3r+yzKGI zOkXiP>K93z!|-yIjioM>j((GG@n>^{i^}{}lj@eVVRIQ4xh6o`6SLsw@sOrLr6aB4 z$4A;GZ&^-03*VTsr+>HrPyY#P_+Ca$lk_LTBCX`b5zhfGz+l>t?651UY;6q~L?nmf zL?3mp0aFjQv*Fyx4@VdxlV5@C z<-GJCVG{U*pFaZo12peZ?-F>Iz`F$ACGc-2fxXZCzr)&}p=X-B5XPPK;||wHm)H0D&R;IG zS%pUiKpItEFt5qi!RRvOr7!DNZ)-P7U(7y}1du%I;+a*h_3Vt@99P&!GrsZNKg-(N zCY*jLVmA!<%9rU}Gj{`CHk8Xs&>#D3$ApGNxv{8!HjWDUn%h$w3U@WGiP>Q>0I98bB@s) zX|ox^&4ccrw=BK0{+~?}_~nhC#o{(TlT3$>&{|CCrktVC@`hg$IwsF%1~zo^$|CpU z9DKXNEL(Ho{MfaOOERK?#S`y89(lKN36I`xJkQz$c&#&TTy_&la!S}fPL-zbHRe5w z49l0UB^uy_5wgkE`9{8MwdL4Y@sbeXC0~{UIK(BotrE*q;DjYUs}|5Y$n@F`vbGD_ z$49?>LS65sZ4VIaSCDNJGoBCrMaMeb_eN{e5km6-!tz9#$PP&;f6JM?nE#YJo1r2b zE3Y@#Rz~JoLWE~y@LKoECNDPdsuynVmF0EnjoAu(#?TO@kn;>4>y;aKbJzgyv$9b< zF8D3`#cu_toL1e!wXVq7Cfg<8$T5Nd)(V+_1LjVm#Z!ZozMz(e6#7m`>|W-#cl-&H z{z~JHer2&A_4wVhE^g4kV~k)@e10( z{YUZ2WBLBAlz*1OZ{aHNiLwFp30?4j|8eEdwG20U_bcdb(DZBQ8lbyLQv-B2 zn$yNr7O6L`FS!Yh`W8J|ZY!a3+J7H){wN|*Lz^p0ohf8q9o z)nTWoY!6KuU+@-g+DY*$25I=z5TC@&ukFnXM_=-ifCdg`VYlkCc{adTo}Es81F)#g zV~NUD!1mbPSI}W|CD_v+eMuxDb62|N&$P{F{U!;%A!3;?xFfpw4cB8`(4}7WQXV4C zKR-hk@9Hn2za2F&-jkk&4(&tjbFAlLa?C(Y)^?Ho4sPsp!?j-+j1l_J7@&K9)*$>g zu3z}ms7cB(=DVR^;=W?9gkLPb{2mpZTAxV<)jo2$qKCZdk1`x;nX z1FPQQH4fR?ZXLY zETk+>_G>5$Ze^n(`Fusu4b=KO-m-5rJ^6BzdB!@zGdjkID2DEQAv7NpfN$xnYztnY( z3Y{C=U_fEZZZ{A2=*26HRPQl>xo4d45&dNfG!%LYpZY(F0Nue@p#8jh*Y_wNcwd69 z;lQ>SyD_G%BIiA%Uqtut4|wWf$%qEM?wF;TrDKds1~2!d%NPWmrXghv?C@v)zMyf+ z3lowayy3jy{4Sms!ev^cv1fFC#x;&-$iIf18s1)UD-(z$x8^f_lJnV2dJI<%d%RCw zcThH_RyN$rA8=&_?HV6|9@Vcd)_5_8n=o#RK@zUxIE@9=o+K*^ofGY(|0@9v!1~RXu?Z>l-|0^aAYL`%S8GgHKru(qla=~ z0Ib|hTUoaRG{#A|6*1#d%aJ)e{9L+MlKY7}<*Wgdf8w`%{9ClM@6iHZxA}4pf?{aR zgb(m1zoZMKjM${XVL986df6@=Thx$?a)MsQ1Jpg+uX`oC=aT&mv@7{gM%G;kW^Igh zv8$_l?r%J6&?IeeLc(#7{k5>povlCQmS@A{K|E$s%;NWbC1q#%dAJSF>;NF1f9P7? z8mU@7cyd^}o9q2z_m%PDAza;buMY5l1nS-u24FQzuH57)<2dD^&=|LAP=!QN*FA{h zqibgHK~rL!gpLBg^+(sbZo9;*5Mc%0xvjIaf5q~4?lEg?K<3IAOS-1xwmv^0^wev| zVdO#jSI7}@MwS^*y8ftqLl>sYuH-2jb%^@d11LQNh;EH7fpzlTbyG7P{R=wl z#jLlg#}vM6BTx>_jJ44lH8>Ju*D2^3#ppmr$o$DbGFD|0J+>>3x5Md0c zN#lF*PY<^r?ufsA#`umepgVTHT5C*x$=$kD<8yhlqM1LCaJ9BjXVrzu8KEn0>%VQo z!_VA9{LBBacX%10+lFd@&N$ribDYmcA&VpAkU`3hpXg}mLCf*>h2wS!2!IQMWDHuohg|%cIN|uO1e0L7Byz14;-!)dH`Hfw$~h7_u#rWa|(IxVK5&4 zN*HY=WkOJ!3_9O=z<}01((*Bn>4((c+%X8Wkwuo^XU^r}8;|HRD`5D?dr$tI zYi9lk_!a%6x>#A8HpaaintOfoK>1SdJzye-OmM&#GEtV%15`SV=3wK8mr_Z=;?7@% zLRu4BFksmX(N}(`8ToSzG(^-Nx#AKoZX1^{2)eLP2}x^gFyl(Y{FU2a0m#4fG4Txi6ZU4r~Lb~9hL z;T$I6dboXu^%>r+E9OVtpF@C-g%y0%19)rQ17^YJkQ4N;W3Sk%c*v&tMol?(PT|X_ z|FC4grT)wq3s+t4{#3ZCjM1O!>ByS0yHPj92z`y$KoosIim{{du%9_pC;H8CV#=+! zvaSjb+de)dv741I(jxmiNNdB|M2Ja?9L0Dc|Z#5P4_3wBia=9L`x3E)d*H z*72)1sV|hDZg2`0yb?Fva)*_zkr5Dv7E}7oBWcoBOt#{uji!VI7mde zuGUTQ22RI%0}shl(hA!ATbJ+^RyWCf?zQ_WOP_-pSNY{o9MV{ZUK1BT<>Db?S9A#0 z&lkp+kqzl2lkwV0Fl{4kVAEgFO#N%yq1sinq!#Xyd_8G^9@&F1%!=7-(n8C2WYZ$d zI+F`n`}DkLDmRHwEV3Fb#hIvh)4#y8^&;(RA@g~IO;|XH%!`ZPK}!C^s`W;H>Lqn) zjZK{k^=_YRy%cVeFZ8>!P>VHq$DMkYgpHBO{Dp@JBlHCMgdg1#0K36OYiC2>DHH*N z;smTUH$lNes=(nQTmLpp3t{T-Bv96y-!zNJvGN6>iJRY01Wq|3aBeC@lKET|i4(6d zUSY#X{25?3iDc52qK6l(;1Qz0#>c6D9smt=egxq5f2B)5P@=?H@!g+popsz!Ka@I9 zdQ=G$X8aK%#f+B-`49cVDuJD_VGZBmN+XRe$db5d=XWJZ*~E_zFvXbuC;pMI?t!E@ zh!58METrOD&%rB9o{7+($Z(%F()sVSiE(Lpwjb(*f2N9Tl1Gt+*$`B||9RJM@jOBt z*DN3WTmHclQ50W51{4>~{O7K%(fonrlV;qvk{;v6e=NWW`>nKZ#T$0S^%l&J@c*9l zZ{d6o`{yNaEVuDn*7~Q+C;TmL25w-s31xt7K`0qF5=B2d@nE5xO-8<0b%EiQ7ona8 z{*xzj&)3i$Ag8}kX)$PFM&Y2viMprGef!2jnWx>c*!}g|ue-3Q-~xjKa#sjR==>t=q?r(0(FS$H3cUO)^he0$ct29auUym>PPX;85YuquzWT_;Cksb>Evwz8ARK$}xJdmgf~SBsRcT0jvF; z>IR{LZ;<*N{}ywqEe=Ev-aQS;nqEfKG({X0#!uBFa)=Vp6;2+?Zy#4+BlvAxfBV>l zmmMx;?ITPbq321$TYBJzFG+;OEixhf3BL6_@i6Q%xqmWW{?{|0HIS4Rf6|m6({92t zT}g_0&;NP|pQNFW&Vq6h3s(ur60xGlHEit|Lo6_nn~wqFM_ln6w)@*}8?+*ElRqiQ z>vlKqE;42;;KB{VoJWN3*Uz6B?`S;jP=t33yfkNA!n3XmpYp62hIi3>g|3tB#a!e0 z4L>B{-=FeXH*v%p<-W9}9$^apH(y7d!-tf^*CGT!YQY%~Bh$^@!Q<jUw3|^UzyU0H!>#hlZ^CqK8d-2>f!V8Zcvf9YTU}@aABfSlLlHMRXgvr1jF;7( zN@RG*)ApktI=PKGgk___dfpP9d8E4K!MGz`9?M#Jt6v~57yUKRKn^Z+G2iipk+;U? zzVPYB1Lrl}fZ&&hJTcx4JkEQ9#ra5hR3=K#xr{t59ShN2rsbnjGw;E$n5=)o#x-=m zOi&!U51xMxE}q*5jW8MIH#L*8>8a%+5+M~@-lfWWNB^SD|y*%Ii2DP^5PF{=@h4N%4m34 zuxF1uJ|j%;wfc} zmC2u_chWx}`syRM!Z>!y3$b>v%;!2La?{ogn``dwZa>_7#{V*P!{UF&w^Wj1R%_S5i(+pp0*d|^(?W^iAUrcd8S2pxMl zHtEJ;FanY>)O4duPL-ql;*(Fu3gGLw!?6=`NHoowLdOY*pR|KQ7H%ET`Sfou48Ti!kLF z{N+Ikxt1sR3D`K!;!OFLZpzwt5C&HIu6OvZ0b{VI+-%DAIh!4X;(|Dmj!}HRQpVzg zMh_QQjTx?idJHx61MY?&Ce-lDSE=xC<4q=1Zsh9RVYrtu62YsEq4K{d=gHT{10A_U zwm@Y3OeTEMe~_=!jj7j4{q#>F-~(YdP9a>|5OqCYR5f4vV~n2M1$Wj_$cu8!wb3Bj zHcj4Q5T~1a@dq9AZy72JX;vnN8wNHuZN=5UGD=9weA@FzU@kSl;2`B2zkv9FK?L)! zwA+v}A^d^}$Rbjv)F(GtXMVW)PXZ^Evv-!=xKiHS}Uv^$j;~Y_rGwMO{VXke2U+)m!%-v=`&^ zuOGho-j*-VWn(-B0dDYCkLC+W(-!SFQSKM^r&L~B)6~V3`D88mDc=(`@-s_`#GI?I4@H-F>(b=@>NObp;lz961Wo5IWV zpj+wV_(MeNB>=)CgNaz)m9RkqEZ?|C*cBE$hVN;|SBP-9qgohs8q+)b4uHyA_4X#K0k6K+Cg zF9Jpj_WOkKcB6Sh82KkR_iL;F2MUn7RR`*)M4`z$n&ryt7~*zI>k;^tzK zc^HacGxH!L+ulcvAC!ZApc~rrRYE=-;Ku{9X`8+-ZIjK)HNrwB;H)7LW6>CzFc#&I zOmBy{;&D9f_{MRocMQOIFl*QErMinm{NW&hmOKwo39>Aw=B?svfGv8KKqay-996E_%1w4OXEJ2(v zcC&xYxs!)=z+;R$FxG&Vj3p42hiJs$Hiu+MUxOLm$M~*_b%6);XZ0KV(xRxwD zzCxiQTIj0_(a}CAYj2o}ERcgq8M)K|PZ?597mSrY^37)r7X33Rj3u9ej}A6(?W2e- zJ%}n6<kk^Qm(!I!e)*A%uv@Z zplxBg4=_p1L+==2W}~>GZ`t=hX3)@4CkP2?Svm9I}@~}1cB4+LrG@UETi^dvLr}7nZaF!1FA*~OVNz1K$o4K=p zmRGWz{Q%(;KSX-F<^zL%9rT&LUmmaUyDzhIFw#>8W5a9JRfOJ*3AD@9fICLQjIC$A ziY6N%=j&guI3(`MFHhf-HyRL}G{4Un{<8PeHUoUx0Q{#dgG$~0e-r!1Ab_^o#u&-N>_gew%wKXSHAXqOl%K~fb% z*>LM}Cx5VT1%3}JQcr(GFTbd_<(Wf0)sfcDNKT^tuPY5`xzAUFo*r)E_)`NP$A(LX z@*KlM^GKQY;36|%IZ)2%XvD{@fv-k;7}YbrzGa?x%h>y;`@0XHA3uHg{B%p!?mpaI zVkmE!lZlKkDHqpwvd&As+BYa4FYDT>t2Qpm)AhQn!?6chdFMk`?j^<${hnvDdC5H1 zuX;OHr|Uwd)CF}L;aQ&puST9ZfRMVOjted7poV|mml$YYJzkOL9vWqPO|DZmrdL-R zVtIhyI?3Pbt1zvX>LBW-Ygyiunft2!IYs4jIr0{T|))jsDqibr`jz`O27uxfOG)Z_cw=FSlK&6H|7y zo#K-u7}qWKjDzfZY*)yMCd|Ebqw)Yj`k2**O)q8j7TJp5agqEeV@ouB5Wiv_3!T4>b&lsdXF<(qyi;S#8H@Do5i7aHL;5rI(rJI}E7!7MUe0z(| zxk2}E*o0@4#`xdL(&_;(@}FYGL$G=a6X(u(U(Sinq`Kjkd#DgXA%KCgIjNC2>~ zX$A5jxb=GvExfsmmzUz+>QvGR%^yj0`@7>YNhPe5DO2vF+Z6C7Gqmx(W050cdX-Q4 zEw9osH|C@L=NTZ6a!RDc>xvP7MW#mp(;L6vQN0Up`o@K=`EQ2eZ=RJ{>z4Iv1UEyH{x3~S0w)#T4wqeIN z#lzUwzRi6}_ZXq4-hzw2>fGx4^fL%xJKXt7qv(zS)ApdvGW(`0J0`Y2pzWnUFdvkJ z2ji)q8Q(Dws+`D=a#ghII(5ALQE&WNUobx$_JNcKtu2T3s1sit53o{c znc4T*585Z@_CR26^@nXaHXrz-Oi3K9KI(5JfKKb3btw8p{T;u2lOM_&^&UOp!B*S6 zN?oI@WycJfCj8(gpRi6|O$#H0Io^}D^c+hlV-Fm;i5r~_Xx=v=c*oTqB48OluJMWE zc7lOfat1!{MJ5l6_sJ8{#^g_(kUjyJsBl6}+kRsAnTZ1gnnR2*d4x z=pMR-^m_wunxqSC$sxYfrHnJEN6uL+!^8q7zKn0x6KN;*LFfk$)e35W%xrEsm~RVeu@wl?9v} z>f>g7PTib3YTXL#Dx<}5N%?yqjdT;PC|XB$FuC$7jBpFOnJ#Y&BF^?RDH|Xgrp|kY z1eK=VGswig^`qqGia7F&%+$B~7f*p&pa$74dzF97z$@S;Yh->CgwK|$7Fs-IXITJPo$R#&gN|Lw~^UA;@oxc7W{AG zsmvD)d2@axx3PMpLzk#Gl3`LW{%o&i<|(VpDJA|4C$mlxbuEH*J2uK|Z0MJG%-p<5&L zY-Z5aaKpt)2jN{T@N^TV6wvYo!6<$^UkA%oCi3gEzwwRk(B;lzgnp8%xD!T#4qkDa zm*6RKkZQ* zjT^{QyphX_I@P}fE(VUesX+_-k--G*6M$-F00_?nYLM-09DE{DSC`Z3c*2hOS8Nh3 zhSC=$V0_CB*~Mex`8mT?Jcq#LZ4b!Aia-1Q1W%{t(?TmH|LB>wzn#>M*FIyr;~@DQd2k8A?*gYV#AYgA`)M3I_(|5cqrgt*?)q3<7=@hI=wES%|=EyGx}=H z&0SiWds?<*0)`GeGREL$eIgqu^Fp2U;$0}qu^E*EVB`8ca>YNeQ}^Nu!<{^liZxi* zU2}_c(&+yuu1%H)`}0zN$yZ*YgOU&B!k=|Dp)GMCI%hfXzPx6q zdD2tAvfjcZaB{7L0&kao6P4;o@Ol`rCC|sC5t7N9ejYa*c*@H$#IPng4fUx)pFF%y zTjIu_-@z5czy!=6@p&~}{r#POKJrdwE$tv4z)8-t^!97;jXN>(tULaR8+Xx}$5-y8 z8yxfe3h)O3ra58w2e9u^?-F>Iz`F$ACGejhfmh>{e>0|!{OLcxhu!Jh#~nDIy`5XQ zx%2Msj>Azuf4IX~=Sm-4=xO!qu1=@?!XG@N%r+ zc<_Nu`>t!dL2z6R1D@D``^=ZQo;5;;M&_*HzzoR-FHz+MQiuV5l?UOH-mbI$J$aCS z0OZwi_y>6vI*oUU-?@l+&mMAV$(QRd;SWbtK^JVBfA~sSgpUnyh%Gso6EGq#D21JriS(&L zPkyxvw>NMuB8UOzo*?c<@dxUeUkAVV=&oWe%YuzqZV-!sCmE1W-;)f#*7t1i0r3Ny zJp08D>ku_pppk1bmkfe&%J{;Pu3?ks%5o@0P2Dz7M@7ryl!qF3HFx@G~#eg z#dkxzwaI&;2frdm-dgX$B``c|q|y9HzgObK2aNBEvT2Ej6MBGF2G&vh)@eKmD<}1{ zQHDL#*@!HRbx~QmDOAI|d;w9pvZ?0UbkYf56c+xpM>5rRs@$EYQNFY#Mdt?Uel6=O zxVXQ_3`v3V-IZU_%d^=#@F~M?w9N$_s3uW#5l>5+h}~MX5b6^#SHkKI-tlhkg4>=q z9Do~FL6y+~B7BnRj=USKECvv`1*i+>xs1-ee$wtIjdP=w$eOfj(@ObII+LS6M#K92 z+m|w#4E{FsUxPU3=luDvVV_Ere1m$gfqR{(j~HZAony&&{U&P*X&Url_A}_UD;nMn z#tOG=ioD~#@ry{V`606$2!dkF#>m;&8Y6TJ(Ao6=O#^iLIN#f5lezoNT@VGY5?Ai| zVjsNtmBW1La5auf#^1fpRX@F!S)fFSr+y!LO&xfhO8S*ppcmcNg%u&bc=@RZq=7!E zGkg6pZpFiiGIBHE;v~FnPvlpWax5Qn)sI|O*tiEVgu;%;l=C!LmJ5xOD~}-Fcsflf z_?}{zYDj+N8H9)A8+=F+S(miq2VPCl%!>1HR{&+vjIOAx$ApN~<`zu3RDajC!&q0C zuSSZO;P8@Y({WR`=`NN5`sIhu9Jc@Y=T9F#vDe}A&wTydgZD9txx+C1)-RH4q{RM+ zF?M=|7dIknOyofe8i={Rr~Y-nMD|=TK*(lO`YDKOV8Lwt>}x>=m} zDvV#V_d}z4@Iq#p+LSl3T8jJnni;cJSm4IIU+)rlm%yn6_IA;BVoS~d8L%07*>B@# zz?pW(JhcW1&LcDk)TM35uWstC(P!du4bwQ#0iYE6=Q|mwF}B^*GX6`yUdsIB8y_0k zjL%W1`GRK*pV2$I7@b|lxCSFfjpw*y+{c%)FTQ>G@Dcsd`84AK#sb|-re5kjhl{EA zy5FqEWFOJF`TBC^^1PEohvwra>`_w=F$jep`v!H{lV4Z%YmM;joJ@u37kM=pdr&W9 zx#9REU+R@OR}D@a-zaPD1|yg1uSy<18232NuTF(ihf%`XKdJrH%iOwtX-=Lft?<>d zYMIUWgp|fMKCXR-YZ)R=;iUhH4qbg?4&HJO6ytMT)$aNV?Y2@_4 zSWrV%`#`cp-k{CA%)YF3nYk%*R_CIk%@yU1dFJB_NRoP^O)H zhJ%zp;j2IVxYh~+82+SZJ8&I=(%^P)qaOsa&(+|)?GJ{;?K&1PowM5Jn^so?+ANUB z3!Se+Y}X>j5cl#D15PUd5|SUs1HPZs0BtMVv~}}anLF<%tJx>vSm1&@Uy$YkCcpqH zN`ZXW(A)Z@0a)c_K~O?S(8DIYDJOX+58fu{Q}ESUjy=-$1SpTXEkT)~c2EZ|Y3r9{ z42rY^WW%l8i<^9l79ueOMFu>UAAF#Q-|@KHQ}dPd7_wS!j#uE7{jJtHo{&%L@jXU* zzPr`<@1A$LkB;AUVYXzYw!ja_`v-{ z`=sb-N92`qds3NDulz`veZTd%eIDf>J+7{9YXuFWHA4uMG!USjkO$J*rmee<1Fq?l zujr32KXS4R_#J<^t|%7kje5|zlzsCz=2gzEY7gyao*ECNK5`8k;mvb z%HQV*ifx>#0 zqa(IZ<0t&dW0J_La3N460R&E1uiVIZJTYQU0YbXUmEHYImF5M8BcU5by8f>pO8pxj(lmi_9f}3kvIO{>KfCUZUiju zluoL~=o#~gm&gz;fhpEI6-2(~ii`6^!2bP_DB^%*apS+qU+fd#5(gq%CX)V z4jg~RBcH_QPnsAZG-jz9hLmJrV%$JLnpk^_m-uhTB~ z{@wZz$}M}!QzkRFb4a0##Dm-U>cUHyc}lycoX{98C;KFHO5;p#ISWU+XtOjk$1dr0`R?(j9w$|?ZCt6yFwj2Rc`l)wn2umqf4*5$$!eDt!q z8UB`z9$dOR{A=9h0je9$wiVD2R?@nu|Eqndv+Xx>V4(D96aJY!<)*Dfuhfh!N5_l| zJ;RRvBh0yfc>N=3&T;(yXESX+tPj6&MNlm-f0KKY^DzDm!wN`T{K7R=XW1L%@AQ=J z?*!h#R74@WnAl^MFHBjWy*9AT^D4zz04XQ2F(>H`4@XIyVfd}b@c&yt@A3biNMLWQ z$5N`_xHkG@1v&c9Sw7SkDpmg+^%Cx2g@G{R%`SSdB6Gtcm5bM(F+l(6r%$|)Wl}>} z7P3i_gJ;^o;A}Vm0 zFfD0Z=*mf5UpYCK)AT)EKN}guMOp{{!ZU3l8>B;#DuEcEoWx4j326&>rRup0Mv>`9U~n z&QovCM-o4vat8JTV((G!68PUKfm409%OK%ArVZKf{$yi%mGp>kh#RJ^i@Wo%`3~XP z2pUBYL?+~}&Ohq@VYpBOyJ1}E=J}M%keei-6%wGN-lg_lVQekE@WN1KujKTVkff7iyH#E$)p<+@=|6NO_RP{9eE00 z65&$X<ME*S9w^Jr}oBZE~`D>x(#C&A@I`Ug6 z_gpDs5nk1cYHJ~v~OY{!!!>j9cSVAPYE4V`{V=c2uH~tDrsRbfD@sr>s z|HOo4aO~-KNeZOHmu=Hfez*wYKXKM7g{ zTlVv8*dQL)gyTQPi{t!~!oW`4k?N#Bx`+6;k`I~_?uW)9?FRSmd6&Su1l}d^E`k3j z38>Qlv^xLe{lT$s{jr>esiARzHi~=emjTJRXMy*s%c`21fO4E;Bo-T%ehgikQZS2j*E9`&n5_ina#Jer-ju5q(I>^PI+8v$H@=GLEKhIpJkf;byU;8lM6BC+!*;WSou zEch5hbT^Us#juVAyJ^IYY08BkU+T+4UJ$!+$eTZDMJ7JMrgsuWoF?Y>(jA!k6THQn z@)+LO)8D~G=%TU5Pn@GY{1pVz`gf=!jXhY}b8We3zz$S2J_8a5H(#*f!3_o0k;E_u zQa%+7G7Ea#U<7783%gQO#6A#N_{wTM0H;B@Gpi|MKrL_S^4i1=N0wRZK-11fa<@_X zLieM_4uNOm1{*`XZ`{1UHmv473B0*EiJHR(5>l~==+Sw#_uBZ=O+s)Ftdy*4**I#Q z%S}Y`f@6J2hWaj~8>)TJv*wA-aj>lH7ct?vBtlsx&L40+GvC)3)s3ib(7d1yyKmG> zdi`n_?bEN72n&kDt)En9aVkID$c#AOA(ic9jC6Iux`xe#m!FiauX&asy0TM7^VJu& z&E*x4o5b=ZblS8Vcf@6#aSl*M_?7h!a1#Y=R3v;*NsI`iSrdHF;Fl>I)&Pmf>AL0V%&!)y(b^DBL!eTEdH5q_eKKd=n?m zAU8no4y+_2^DI8Wlh49i%z=$Fb1o8^7P>(pFX|iI7t3`;CmxuhAP|rKKYWEqNhbY| zkc9S10HK2N`$n4c{weuA$nu=eP4+==eh&~k$;#vq^_KUT^ccUR*?q_Ozt3-ODKCF5 z$K<k{d?Fp%GKQ zDC*`|4h{dtr%H2kc(`9dkFhL$@e8`cJuYrWRdY#4455dQ7SvnTh>&Yv^X5T^D?FC0 z+$Vh^0=J$I1-L*{rV&MeN$LMeQ$&53Ai=h@Wd<-QdJXKU+vp>PLWdZU)y1Sen-0XRX5sb!8ORvX(?(sT?ACDMhKYn9# z_cw-#e68>6uN;*BMS}_UFKD1{f6UsSeOtKKOn*tft};$` zBQ!E|)1So*9*e@#SYCX+e15;)CGakR-;%(t%hE7yN$65FqnB;kIi~Mm)HOB2^4(1R zn}HxOj^pQG3F9?JuMxV~{A*inA3d!WdU#`$k@tc0*^I$67RSI*L)N>`j1z44AMY5O zySK}E0b>PcIE=~F-5O?nviT8jv{K<3+88VCMmELhnlRM+iXnRXE}oz61DoBH~Mq#t^rX$==-GSIG{$gK!E>Q>u`dB zfb))3m3if^;9Gl*=ZU*6-lVf|?3YLqBT@simofdH^)`?xzm~nVkU;UyxP!WPWw~>| zPzM*GxR#yIU<#sgGdL}4qbW;pTDH^Yx^8tzJG|oE>ZJ$AP&U`sgfTua-!Yi-Xf@N1_3B!dhQuC>P5FGa96hkW_xP@d(O#WDTfyXX z${e(ahE-quFCv?CQ`$28nHKIZ` z$Vi^^j#K^ScvM`<1v#Y#Li0lXfQ4Vb%a@>%l8`CKiaMs8}fvVT$l6xwuWBb7)>+Q(vVf%3DUGd<#dD0(#~iP8lZpTzQeC9 zm4!MKgwaDDbmzEDgWy!PsgIO>jJH{XP!B2tYq;$)>cNUB*@dU;oYsHqlKgu(5XF_r zT=(> zhaFg7e`Q|wh4yPbRgSL1dgpK)bd?`iv+cz2mwFzZm#Wk@VOA{ftjWWpefc#s9b>qs z+=EFUp1x5FZ3pi4_zEt{1;uppc*7_IeL{Ve_sIU@CI@$PzUG&n*@wnp=H{BZmz%c7 zm(Us3&?Io7?VqxdhJWfjrGZf5>0S~;EHm$r6C$mneNh9qD1iJEQBvx1mj-gsm$1b0 zL>|;J>k`ixZ-t6;R{N#&-=$)_slA4G%bABJ88|7p^qIq<;VNKpZ#{wLizlPiX5d6gtv1JZ_#FZx}__G{CfdySAWC zXrJ5tbL#HOxVok~HR%ap9+$&(urwi{mwQm`39nb5myGxT2rwcoty! zBd|K(vB|36;gcV(=px%JZN~mgeAZ8OiMrmylcj6Dkl*M8c=DZ_x4PHOkMbu}s6sP+ zVxB7t+d&MTp<|z#J}TpHb)V_xx%fqULh z*wk%2-s0phrDR6Da68rmhj`YVwyJJdzd%-5=TI#T)46Mlg0d5%eDSZ$#pi8ROkKv0 znoTo8z=OC4)2b6(3R?!mJwx7M|{s>)7Nib8IOF)I*?;+4gMXMco6W*vxjln zPe4QR$=~Ejy(wPgf}1|dcP5NwUF$VYdX>LAb&UtjD|t67zQ4(sd-nY~PUzqR`sh&i z@ky1;XKYO-)1M%d%wtmLMmVdbP!^U6q- zz>kFx1QE}8`w&dRh=?EHf-XNkPg4EXn*GnhPFqTyGEc*cy!+&B#oHo+qr}9+wf*SH zgQ4Y-bY&aZ$WcVBC5|lRg>c7#S2=LaI_Wsv_MluHZ)U_UM#3m(5L#QqS2ald^Nu-n z7dBdk^g)cz9;vuAduW-i9^0gKBK_mWdSz!lSEKjhVX`Ir+;J@Xfil3n2pHYnl!dH6 zAQeW^Z+yU1#$?vF@T6)PoH! z{y{BBFivFMP3NxhL(14R;v2aIH)t1*L0K0#Q4MS!in|m^l!OPq76Mv8+K2TabEbvg zTH5i+YU{vT?&VF~+@YtW^{becKe+oGeV<$#C?CrWjOqFuhX>`WJZyvNTjwI}r|s{& ztNZKXUwS!Qnl^YB85j<^{05@-F+Ti!!|1=4-kX065 zU|oRWEc8N6Vg`k%Ai~QYQ&+cqn05mkM6cYT zHXYnffAPK1c$M^r0V$?4h|mJ<(46S==Ly#}?zeMsO!4l2m%#r%32Z}=v#g(l|EQsn zF6|8c(UxD}X@9%B8gPJD=pvFfpCEs@mh;MQ6zNy*nf@R!gs5ZHz{Z9|ej5As?~#aU z2J1m@9B>r6{ge1gV*rSa{%n7tN)5J&F7yGh2nORqW+Eovbk6HGuOKKGj^R=^dU^1# z+%DXGYOE>vOEO$=pQLjVYI@5pw@(1iBvT|NPQF&0&IW7d1X_I_;gP@jieLQn>q##S zy!j&@yiKhiu>R;t`DS7XfA)`Je+1ssrsrkZBQHQa72gdEzLa!9PJGQ#u!xD{cA^Ua+lV)DbsQ`Uw`;0IMO{QW=Vv^?O3Hv|{i>B_MW#95ZPU9fQ+ zB+pz~q;%oc$uQ$F7ayOqSWfPo*dnWI28(^E<%>dIWZt?N!56Q-nCXJ0aFQ!KJFW0X zuu=JyrSZ#`etBzbV!r>gCH_9h0gpJvyheTTo3Qlt{k3c7xiX*pB{P2w^&z#~k_#%5 zMm27N+1k*`Nn|)uO*yQxCv1-sPurYfyXt5-L3xSb*Kcwl6pPJ{#XqvCz`Ekg5Xbb{ zaI#+ZxuGcwf26G}TwHO^pbK99XI|V*22!D(X5m^RbQg&T65gow+uFb*NMHGzp@b)w zNw55y=b?MVCt70{`cM-0`SX!hf!6OK{f0Lleq6wDY>td(F|=PCK;LL|6d9S3wk2O= zm~YmK%QNx{BNasaHci>nT>=Vh;ruNZd%D6Mc{AzT*Q7nh7P>L``yYU?1wHfk+Hbn1 zjiQs`0&U2BOquK<%@@ySpn)i?<>}+%H68wvhxCXvW?++WL6;kU>r?KzqKNe`h!x$) z!|%d<2*JB>ln2FY ziBfVO4zG|&#thE2UdBi_Y|^^B{rusl&;R=HUqAo%hoAn7b?N`ZI`zMpv#`I?O=oVJ z)IjG0U-WV#^|PBMdC}&E9XI$ry|7-)i+QFrF+$>(IZiiUxc=SSxexrfzU;h>xsjVZ zTqKUc9UB<^!i<|>T#t5M?99=1>}-sK-@Np9+?yGtvLz?1iFbo$$AE4q@kJoPNq@mQ zHbfboC_4jLpLaa$9Mtiln+bejDW9O!?Tbw5_iiag@FLwVSYMZv^C25MY_mR zp>!;sIzz_%b1varUD@=V0=bD0ZDZVE@ddZ|Ot08tv})eut)z&vt_jZttn(S?cs#US zu+j1Xqmp|!d%Si@P2y!RBSZxah{_Y^X;D{kn?lJl*h@ja{p4W<$SUeWI-Va+DiO!%LW< z9*}4E`Puv*pzk}WmbbdW`r?Lx{k>N<#FIcH8s+@VZ9YVYmkaF4FDxVI2H`ex0VTBQ zkVNEXQvGrHDad*{PY+c8lXmon`5Io59-5Mswlvay@eDmlgss5kiDZ_$rPkZLOn!#7 zjmHUii=)5n<3dbwcYZyngfSM-)pxrrM@^a))XEUg(6W~1d=r_RPo>Q?lw zJkbf7g;u%ZvCFehWyqKD-z~=G8tnaq5&9USfBIQNKEEXE#%uJuyNYNhuC?07W=)SV zh(;ezEbKpNK+cf!>#vXWF&tu|(UoI2D!m8vYgl9V-Te$2q!W+91vvsYCPH_tr+>o` z{Tp8wzNg;;-z7%fAGz)8-N@~_;k^b?jAg$<>XtwG=& z>+zpyuNp<&-Q8t<-c8;y5`vkOJ=yZIM0&NrTD*p~UvNFw^~+0m07Z4d;G|%dvPjLW9i;%)e!^;kHcs0G;LE8Cm?iJMR+s-zb5tVXVyiyu#La z9c@Olq7|c$5)(hq>KxjheR01|=Vnvy&Q&$GbZ=D-me63pJM$FcabI{&GX!5XKv|Gh z-x01+JCW6`=qYrrU#z`B*ZlPKQ+m6P%wK#bs4n(>Ac_o~SACx^pnpP-V({!ddJgA} zu?O&uEf-a?-MnY|;}2tXzn=UN7^9CHU$(P-rrvZ+}GLFqnb{!*h-d&-4 zpPa8?{OXs{)p4e+ai#j&{9zP^;flIco$324=MXWDMBXvn!{{Q0((v&DZs#a@uVeoJ zzRdF}2aT>DulwFi<1^=&UMWB4HI_%RSdHAA>*r6IQ=WcF)h`j-p=wYw#wLhjf5>E}=MYmD7ui0(UC3xIsaKn^i-K-vZJbB#eGcfW@D4Wr;M^kH8) zL`Wk#4H_(CjV3iV*MEy#UtaD?k9q8;&(g7_;m%CgLt(8}R^z6W#t#B$5{k9;U==TqiujT@{(_U$=T0GXr~5nlu6_EFszm@E=+ zoa;!AX1yZd@TQj{TIFTip(Ol58siZS{Upghm%r3q0*b58StFoM#As(TK!^X3C@)Dp zb8R2I(%)Iu&|78RgWWI+OW(?&GxYuWnsN-IspdBrT-(3K7^`bP9)9rbiT2V{5}A9l z@fkx^`&;)>J~39wJ6JOcUE7m&(*2Wu4Nlo>Sp9{sasT=&hY4WpI|moI-_yede5dbW zdCH!e4J~f-V7|oZT9mRv&h8JmrmflDZ}TNz_W|5SCtL-Nw(|T99#|{jYt7EH?=|S= zM4Nkzy}$XT;LH(dD;S#QOQISzTTaN|9H=`ksbAMLW~TpS-DhNHdv&o;4&m#OJb!bH zi>(Poss=1)i?WkGa29Wq83uJ3qM(4?c>EUU1vb zLO)d!9+mB-#>wu3SzorcU34wE?YqXIyK!IFbZuL?DFS<<LmI=9S=ruIVMq0%{@8+ za2WC9A~|dlIjeVkFQC5J_8!^bfJ}W=7qAZT>o4B4XX=1@?fKdM44p+j-FL2h&13X4 z?WODL#!>H5&#bq-Y`f}d>3K_z=8OI&Q)xfEgZTP|IocOeqOa~bu*`Nz`8u9R+r#KQ zqnr5rPBkSI69`(1AnA{iB6H0NGLBjNJR@Wf4uTQxN^4)afR0}@uU^b%Mu((?))Vx z^@i9mOuB_OsSJx}IkK2P+{w3L+;WMNpXdbWC$&EtFFXhoyh}QZMxYnD0tra@C&?B; zn3vDyXwOx`!ax8*CcK8vhPn3d8bl6H7=1x`Uow`Au7DToV%|5YU#Yj|Gi}2IR?s`P zJIDK3-=%+?F;d%vV?K1JeRwrpy2;|VPx26=+M$)U4kMjRUZ_XCNxY3Z8FH&FS$u^zaL8A5 z0+r!`zS()j6$>FbSR#hnlpi9!$QZ17PJIBkb#dO`g}fZoW_q^{39C%PN7}>4mbnfJ z_}el-|MKM*#&5r7jIB|6$2s7`0R02wbLRwBSo>SEnc@s?_%Kf?SLIE)dH@E0>Z8ky zA>q@qh>oMYJ*0*)(;O^hUp_{RkJQEc3r{4b7833=IWT8DTV~c>%glL(ddu5{`3|u{bi!eU~$*d_+%eZ4oqkL_6m9{Mf4*9#x;gG%V+Vj@@&*f8kw}8SoPLQl*G!k z^waL_cQTKlU05eIM5p|&D3i>mG)AW{Q_`tlxS89+m;ImmsC`{|QJiKiH*|VX9Oc7& z#KUAk3m$V@7D(?rVXfR z{H+hqM!0^%&5~_opaa4~vznX+Lv(C&&hxsKJX$Wc)s0dSSopcbQ{oc>K>icG@GRg1 z!;^Z8IRvzviUi8?92(#qHkNDnNf7kXmV!-=R{EVeaKJNg*3Al|aG~ND3LPXN>56;g zw=Wvv>h0{cT+1O-Z|tERa^ng4w9;;vr=v(Uwj7J=&x?jZ0n{xBA2M zCA44q!tB9Me?5Js(3P5W%5Mp#yh6sdXFsC8U->26RQ98etsj_wM@J0~vf?pIlj{3>_e8bn! zyP)l&ok~p{4mxKr`t6&8)HZYnot{AV<>o5`S(K^+C-4jJ;?_3|%70~H;GR<@^F@&t z2h2{2z`?D8w%%va2wYu^aN^~F)_e_47YKxBQGu}JIr%Z4M;TNJ+sG@6rQxP8q6oop6mw{1kLijA|`s4M{>p*kYGdn(YO))iX6TR_E zlA*ZAZpinb@A#S};D^swNsS}&q{5x#wlWc zE7b1+?-F>Iz+aSrTBB`r_ldIFB%`ZP*xPY6M*`1c_OQ2oNg2dP9QG3ip3;9C!-VeK z7*f5g=QR9H=+FYxah^2OH?(h!N)0w-5Vhvo{5YWD&MP9u9!l9UhzoE`&v02t1g~yh`h;e=nlVxG1dTC5pMHuYAtQ8o z)m?d!xBNp74|^tZleJvS>BB$pJMtLRBX8;I=Lh~Ow=C4)S1yw-ijCkWDw@bFA!!9r5)gi7%|(z`~{zZvDO}QNDxQ#my{$ z($+#>^x(?eHjIebO*R^#7x(Zj79S^pXAeKBhm*dp z2}Q^akLE4B;#W?YD=~SA;ZoX~pyV=(zi6W^W0sX02dp#YS=ZiSj?fEvyE8 zr=K3WZ@U$Y^VFk|epFZ9#$)I?U*bN7{lP*wA#{#wBEJ_t@$c@t1l}d^E`fIm{Qp}5 z$A0E_hZZ*?_@V43dDsl;bxhY;H=wVxtWdvQ+MP*e?L(W-a@{&7&DKGv<(A z)}s3|U%%r6ZZn?oAcPqEWv+#raWbxJ-{K!*){enl=r+Bs8^XFNOq9Oxa?L=0*YuT` zcV@cr@vvV;jIXwJ_aq`e3x5!cQ&7X! zWq@MAoqCaniN{TvgG=_3CNx9W0C5Xx18hjRCH~%0> z;3`?+p$Tbk)7IF6SQ5lQn`zr4?@Ic$g@Fwr6NH*S@;GwL+$eF)6RE&YesEiw&L3%4 z(y)EPQ1atubK3a@VvNy?#t|C2fLk4L!=}3@4x@mZyhqZY+^>{nzeB&;MAfBddUTWwM=yZo}@FGKj zSP>*>6 zNz?B+Mq{TCs;`2`b^>mAWzj-h4pvUJAD%JLWgA| zxBUozg=$oP52+`O$r8B{+Y*SOSPVMGTizSkPX7$*gaJq=SEfrpQC@Z|%SXI$T`M*p z!dWJU@}zFVt&TGdr5Sy0(Bw~7EmS?9^3GsOJm}%T2_v4$DzM5={C1S~Gaex1mjTi1 zG0vi%`ZdLFG6vU1S4{LD(ZTe|=;%vyyZscZn*O7Gh=wMPEn}>WL5@aPpD=#W2;Dsp zR~~BOm=L9%^;Y?@F3@P~m!CXfMq@x=c$*MooEj5pm`ySaSjv*}>qcC^Je%E|}d|r2=_}P@b zejm{TLq`P9@nO{}b zuvO;r9t+uJFB|ymk1m}IExvsZ>W5jnkF9X_VkG$Rgid|bAl-Lx_BYC3g2a8K49t^< zIA2o^m&`9BhgFsqp?QfbJ}^i%zXYaC+4ZmSiH7JVbM4|889uXT4v9skFXYAkRzsK` z)W=xujsGm`}o|OM=!{0yz2k<&mA+2QHg6AeoW?@{R>~n?!kTRuzRA+pzV0D zo46@Y@Le+wyraMTNu&3lK4&iGxKNmUt??^{$n>F+8|9~=jJVCAFdD4bH`(`hOz2t* zw303M^=Y(`IzS5WYLG&&n6T74D;Dp{F4M2_V4W4-64bbdKgXSxOD^k>V20>b_6~ma zdhtoZXAyRt#RNF+M)(j7=oRz6K3M@kzd=D^}`S%dHhH{_Djp+ zv+iqj?BN1_0oQRe%?Ll`4Xw;EK`!rOoRe~=e!7LyoIf#Gi5^fFq;4T!C!5NMI+?oe zdYI!5Xngs~XM8YJzWeIf;}bBS1D7_Q_G6!JyLa59u#{zQ^Et*@&I$4b@H-DA_=NE= zhY~Q~MlJA#P1?r`^@;L!UhoAY%dZ$NUA#~iSeNr_s*jHOK&x)D9y$k)QL}P{P>j%_ z{DK@lGS0XJuIp#kC1q*px%NU@*5_OY^kYXJg5{jVg9|lE(=gn<3J>H>`I0qUji=OG z;O9N`GkiTh`z2$I&^7RCJ$0<}(MQ82ZMqpYw(GPhaoSH+Yc1CE*?% z>Ok6d<5#ska3Jr@sgvg2y6>TjaiN*{m->KYUeu!P-ku4}z8}(Ak8@LD?T^%L))jTr zBl^!ZBQc2Ad{mdQ-ppYRT@$k1i-Wr1I+F(Il(+eB8wD3_SzT!Vbo^k0^}%_GR-)-9 zt?zgU2A{WUF64|b_dC0$sIljRs?t8waA@03U@vVv<(M`?-H{I>Y$gI!Hs(fY`DfW9 zNAQ^6=#S{BrYk(cBK<-U9Ki|Flqn6#_0Ef|b1Ez3;ossLZ|g1jr+R0NEOQ&G+BJu? zIDRnCUh1BXQ>wGnKOK7@%!SuHs}D7loI6I6YwrX|q94+>FhozEM&Dzbf3aPnN3NOM zJ-a4rU4k#=$G?KHszt8Sku6(SXahHU0g{agBa69Y^AuxjnB{G8FC|jTYaASgb+A!4YbS*n1V?|s0bhGSH}5k zbW#&u`E^1jSaqdSIsE!=24R9L^p-}lEd0_6+$zf)w#QL~k-5}8j(=W-vG*JhP|u({ zAQQFpo&K8L{;Xgr!^(bS7{9u}zMNd@8TxPyjMbfvkMsp(Ee5?ta7nX$$GkB--TDN$ z(OX>;QOBuY)GNxgJYKwppy0qQy}`P4j`KHVcl9%MqvJX>Tj2}GaXU*Z-!@SvK&Q95 zQhkktZ)~-%S$^J=kym;;XQvG2vi2)=@T{r39^|}5Jre_T=fm_V4{?5^3@`bSI=~BY zUD1cs=N4L4Q-A9&zlm?Zme;L2Nq{zf+Yd@w;a2?_^RKu)C` zksr>NY=E?f2oemUv=24S-^hS-Z6C*=2wH4s$}&uQQFFqrcV)^pcN}z#px&(a(1aO- zZ5P*nB2(d`Qsf`)=AUzG$7#oyXi{i0uXjqGx;$W6dIYNe-OU$_=5l71itt+) zV@2Fc8tA>DrEpD)94wg7vGV9ok?Ti|m53o0R}3v{;UXqJ@|Z2{B<;$oSHTwIMkN|t zDdrHh`C9K4d=F@Y{@O$7Tja5?q2PzEgbn~3jfbepjp2Gw9huKFi=3GU?=fYncCys`gK4)SMi z39#RV7sT7w(>EO>o#UdXog>VcWtO=r=Y~q>#CLq3@k?13r*!#zXXjV=)e(%*jHj0# zAj|yVobk(pzI<=v7~t(?sC{)@>r{W+qP!3AH-4nI@CTsZlK+tFTfT600Z7->k&#DHRM}QpWI`-C71NNN@l({c(UA#gqAva2y&p>@J@*&z7ACOqgWOdVp zzFFMLq!`#N_VH3yc^;#WvIBVe4ORn&drKLCuZNK_Qt}Y6~DxOpPtv5m=j#T%x5^;rkheqfMpRO*_aCEVSICF=g99fVcu!7Gz_wsi z#}@iCJnu>VZ^12&L$_+E`J)X+B{b^Wzh8B>UBu3Ngk<&6uWGX5g?%sU;3>`inu$9R z`Dxq$euYVxxi_GA9CHT4KIJP~1TqrWp<^4oR#_`)BkKr;z(%fUQeel3mQxz)2Oty; zMl%@qy=!XW*v_@fBP~bwQF=9c=$#@4mHC3GzX!x?1oy#9e&o*?Nek)&&yYZ>w1^Wo zqF;5BVQmjRoeQEqmnD?2vA6f8z zi{gODx(D%)g_}iU=IZHh>Y=2`4_eB$<3G9JQE;cT9&m<53&ytH2v55<@8MTKVZ`0o zi8;W0+n492fV8GI)k3w)c!K<>kd<#if1P)S25)(G9QSo-G2kd+7neRDq3d2efcwM& z_iiCagdbu5IxscSowE0Sa}2&oA1o6;Fq1y+DW1uA%JQBlP_i zd>^nM9QfeC2M7LBIB;00Ivx!>BDWTMy?k1)Z>9ZK-MrvI`7Nxc6by~Nj56oG)c5fK zwM#tAeGa+zbI5@5;}fXyx*dB>=`%wRlHE;+?kmowl(@h+ErPrYHd{HCyD{aCFXR05 z^X1tm49;)=AD%`2&+FOkf1jOw`h~QgJ;mR-sB6+}fObO!cr*85%+H*Q%{Y~$6B+`p%*SZv)SduKceM^=P(!53Nu`hoL30~)+ zddw649nnon(u*r=0Kw3bH=eA#lhizf;Au9-kv(6q7GM<}S3h@2D9!rk4ov{mrTv`b z<0u2Zbk2qn=J#$4Q#R}y`Kr&_Eah8iV(1GVyxy0RWMLbr~F`Qup>i->GieuX#ZY$Ejw@lVj` z*W#b)^KRCE;f4Jxix%g6$@v0{^&E)Dmp<6m26x$L!>_A7)lDVGFSfnpg};Un57}^) z&1~T6W>bwW*yK-T)2*pT+LU~kZ20L0zt%5JLLKefvManuHQREsR0D`P*l@8xK%33r zx(464K}MR5C9II*Y*@awUnn=#OFXn!Hr$|Qn?T%P5&k7I*Y&VzBe8BU`jd5}j#R>i zC@t+i&>@G|?_b}CDJ`WUC4WE5R;+m|i(3?)BJj6Z`#TUD-=`dsH;5HCNt5T)-QZ6W zCSQ9-5ud^i%t_WC3l6d%DJG-Bx1=oq1TTCEh}0kpDiqdd{L+>+mJc=fF$SCi|;SrN)$C+sZIT;O1@p5_J@Bp!8QB+Q!M_&mi9-0{#Gl zKzzT|k2H-=HZfV(kE4k(>O~0p7uz_Q7!E0+g`XNfwLTaI&?8W+};S=KyM8X<5sHfCV^S)40Rq0pE$NjwNHHOytk|&0y51%mJ^l%Ie zLSoS1g9W ze{=IJ<*l){>jvTs?da@tzxe9*D%2thvHRfM3&*VipnA!86<7~Dv0ms?j0gQ%EWuAvA< zZ4-dcFfUymQ-KVr$>|M}LXEcu+)rIM;A&PaE=KL-m zbWY_VIPU%QP=)n>@Lc= za~qA&J&>UNn08ZUGHI!#c$fea9C!$g8lYpa<(Jp3uR7w{H5KI6K68%6m2}B#=Q8r2 z$xaS@@Svcl7r%b(SJADnaw;*julhxuaK|2545n{?>V3!hr2ac^jj7Rl>Yy5KQ6F@% zb?^(aSF9&`sGVO=zPJacb2I0$^v!DoRfAxZ81qNkOXDA9%7%m|Z6Di6Kk?MrTRNYm z88^*>K4vP)at((^DB#e2E>38QPP=)fo%{lGl0XGt&?|6?b|M3E2Rg(+nYqqpXY$=Nkzex&RI?YkCb2_=q;` z2^?*D>AI3*0CJ@=k>~zt9F~4X`@C>_*ivrV5pheiUcU4iGC@#UE> z9ZQ>y0f*@qJWp%b=-9{e0YPMox+v@FK4mTPJ-kFNtS&rzQD?~4b*Jv+&GUyizy)e( zhL-TIdM$>KbSVF%QMtBn)9WJt(%wF9zgH)CFb4HdM&OaI&o_>f(m;*US0BLRde#;5 zgL4PG6zlpXs8b$wQU35}U5qx7&rjSQ@_Gfq8pAoB_flTycdrlJ_mJu#vK(e1KNx`O zirRkA+d}Og+1oCE=2p4n}yHH{j2jh+uS}!y`S7%K_LMaS zrg1Xfwf^vd656i<1*BJH1niWd#Ml#zU2UYJtdkyCel00*(AYS_wt3Qw(%0Kqa~sur z)nUa|6InX^alVthByt7c;5r~6#d7yoODWBzJAM%mOiYj&)-!=U4*k;VpHz{{ddriz zCL7irs)N~#TqZ!5JmJoK!DRSiU%x_roV&)zj6P3vE~g3YpN`qIIejc)+=#UL0pRF1 zY&AUiB8I~DBj?Twr^wO*W?J{2WvRAolRV0{PU6jcKpep*eG9K+i1TXR&s|+Q&%}r= z2hDs2hx2VTAL8VgYaev}kUph;w*S)iU4zM79h`HBgyUrm(2b)k`iyN7wx;fkL(Ewm z51qq!xRY~Zo5OZ_D~*5-h0u{hB%DfLJsWoIs7trP|8G|A*r|Mko2$XjTHw)72U z#x(C@#95d{aJa)S!$Z)JLH>Q5`MrX(zTK}_ydbiSYWc*pB-0&594qismc{iJGjt*^ zdGBR@%jQa6Vabzc;KY)adg-DM0#5?<&sa~idvimFvgCw8o^UH`nb*+|l)cDl$^s78 zICayMOKrF)9~8=W-c{Ns=+_-b(?N((r?qdVyyR+LwP7x4rbD&Olp$h=c6ni`7X032 zRm8Q>1#E*a&CObvJy`NB*!o+~V;m<1i@*lTwa3=D{7&+MGB}W_&x&uw4k?KSST*r( zok`n@-|)QQ5l=Z&RHrm0d|HrUc?LIZZ-VVtQf-EXWU_6YZjf&Y1)bnl`#0!Jlz`q| z;My3l5Nvn~F~3`MvPwfd@CQHMq=#9C#XC85O+`-Qb0($ip&!-qQG`?H#fJbYB`sqK zYRNS&=yeCLZtv{>E>qaLYTcS3s z_z7Pz)TPY#iQ2LkWYLvLM+B?U2dL!}Zi{q8Ty-N(q06ZwrVl)3uBb)&ckzyF=e^A%i?`3G*g5Q@_D&8|t6<cTWKVh)hFAXxbuIb5@JvEQIwsIHI-%sHWBun=|i~6et2G`2nO;9Ym#}TY_7(OhW}n6Jf#3? zf;e#&KuQ)y-iT}QyLboDhHbDegvOKa5PRaYpotnN`>T=gX>txR8}RgMz$X93hlv=x zdw0rMaT{+Ye)8hVyVHy4EsF&QWb*8RO)!47#Xjkq{?jr~!{5yOW?~<>j+4^NMxCIa znw&(J#ixVcuP+I5W*kMtY9noUkkYXHMUQD#~*68 z@=s~o5;lDlS^?Xo9AQ4_t)u8IkfGub!skNl0o#`Jre{x|{(ZhRA3MnxsL2*^0uzpc z3dEs)gLP03>{gZuL=MfS2gW!H-$oPs18e*n*Oam0UUh&;qL;k1Y3LLi5&Z|5;JNg@E-0abb*XEjRGQ7-|Ojr7ygT+jt@V^y@v;R63m5>bPVA;ev3hRCg9VoS9oMFS<*vHK3GPV;Y49$R0?@dxekThsf?1|C^mh`? zWn9zsC&eG56KJ}Muc<4%@|kyZg8sSQWi?2T({{83^%P#-${%^WK_xSxCJf;eyFt9; zAG(Lay(>+oi(`*x!K9HUee&&S+Vbr4j(^@#j_9Z{rf!M^M8vc z-^=@v{=tC{4t#LnKa&I1n9hYyb)YG8FTK1#j>l@i8ga~kZ5%m7H#@&cTO-WE87Sg6 z4gN;{z%YPsFbC97&vWj&J0n?$0WSRd4c^j_c^o1+T4~+cy7`NYMvojkj|Hye!AexUgu{&{g=k)7>WM(=d;WI zV$;emd~rsD8?x{O0=&2>2R?6`mQpNqvN^^*D^G5IWuw`>n*-e#1C8v3haTG2I`F*X z7mr>rGJkOc|NYI`%NM?!#0HQTHe|jUpES70W-&L!bR)T3=v>$NuA8L0LDpD_z}3Mm zB)^&1t#mhVxPfHxtiT~YcsJP*+gq{y(SVjqL|o1N463kq%Ea&Lb$*vL%{;zv zx0>Nd>*jM{=r=C)ZF+EthhVZ8Jw78;A27>u`Za_?N!QUuc%l{r$z)2bH0-aGnh5d8 zx4zVGzjd=D3lVf)!gP~YU3e$Yd^hyM%PmiMcS9B1IoXKACNqpOp7}Jo?j^<+&ukcm z+3*)$SaGh>Y#EdnBogeTG zzlBYidSsp)DOc*+7RZ~o4JUc4e+e4;kQe=x2OC!&)(xy^S+DU>jBE~f^E%J$ykrB` zO=En20&{Sy%ShA8#_)CSML*a#Q+!i~q z%MAz9rqTe^1{lo!F>V!bC})?xyms}u2F@s1ix{L%#h+MH8mH_fa}g-f+2l)l#$-gn z4WJ%2e#>}!`!jNnd(9Yo$pYX7@yfZEGSq_!(QWjdhumzoMo~Soxs%t-q-!FiDq>Kl zZ@L3h!_@ef+*Qtl64*uW=(2=UTB;DLZ4W%D_f&qEwzgM2G#b@SgHBt%~TO z|6HerMs>P|iWv*!9kQx{$g^wL7|lIVfpd%r91Ap>>?W;n0h-aT?77I-srbsCUyt>R zjrV@l6GOfizl_Tl?=Jkp-wj{F^J{p(Us2f?#tRKVQPb%HHU{2~cX*`3d205sNe{aE znKji%=AMtgvMKo=Y_k44hUnh^gTc=K`+D}}f9QW-+9%!M{7OH)WK;4rc^@h05r)KY zoVkN@QuhMCYJf^T&)tNr@!|^`t%3Dx@zTI{ZFqA7d8FHCZ95x4d9fbiVLX0;m+mEb z(Fj%J9>(NzHiX2eok{&AcfPPBVPpr=3evLiWjsAnG3Ai!@0WvKhan(WgVfqh! z+uoG?RT{+ce44o*x=AAjj1yvjo@ZEk9M7uQbZKu!-v=M#wFgY7A9*Z^tIdG=AqI~Y zYp3PX;KVs)ZuN)VSi&TiuI;>>tQeJ|b1kNGG>VEIm5t=={um@1c4qYz%QU#04|>S=?~_ z#K$BDb@aHBSmT*-9p4#u(f2jbhd1!=8iTxRzHINv*7R$3D>~X6B@LIKcqV+myefJ{#?FdRA#+33iH`}zQCF< zZu-?tzaF%hrc4`t0kJxWijib)C;-Mjzh*h1{hA6qrJvI8nZ%vj2kncL@4*g_)Z+nK zsXABZj~-ZYj=}dihaFscV*q}63lC^_Z;fKytKjo5l8yXQH;rL={(Zra**R4B%QLYL zCfTz9ta|U2{w?p$?V#kfuRd|d=nvjRW}wCCkMqnc_rvl$6l3$BF*w)w{AcD{JSS;D zdhPv*8#s+ronxk5lo#6$Le5z~JcoYoSK!j#%t_CY9Sz~G8SRVr?9Zi6BPg5@AmStVjHZh*EUpe1gZDJpFK6_~&RL*Qe;$R7d!fSYR zN&nD@{T5aCi8gtZX42JN=DDHmZ#De=gxp|Qe8JddM}D@U;I+o`kKl3%#RJu1c+MEr zex^=9PuYo-aq6qQ=9$6%PoKg$dm#OiHNPu_&4W*!D^p$MUc*BUD~L=1105HPGs;WX zWaz~55XbVAfhTBkPX9oAKX5|81MSVIlS3TN9iud;|AZV;PkW&2=jtMLn>h2;Q^!^A zd?gv&9!&Dg!ZxF}V3jTF1n)FB_e=DyecgP5mv^v-TYdLzU!@fLs>aR^A+PkUD|Em8 zFVA9;E_Ic+;|cUY2dzU+=;hWJ-+khtiTc{6QB2Uo(+z%%ThB3)$GXF3w+mhvsP_xw zsL(zd9yv#$ulp<>Ij}@oOCNOZ3X*)4^8|$xi#?3!)xpaw8ZpbeRq5Q)u zs_m*@fWk?)@N+wkyZNVqKL;OxA%Jsm6+B=Bb485g6f7&UoxrM1)dZ0T| zMnbfb+1heO`nU?7cROTPn=ZD$!7zsTqpV8A4BF-4J zJ6EC)I>z}`^}~%0@K7prmVH(hI{)o@H9CW_CN?Clhj2eRCI-Kt=%Vlz9f7WpKi1U) zXuEEKj)EE2%$4sr90#Pqjfb@ow2+IH)L$C4!f<|YPMqWM1@929UO%CyenCgwfrq&i z#^=hL`U$AE5k53&`lxfJbLPU<<(fMAHQVSeXt3R=8n-k#G}DIQyr#~X!_xnsIXuUY zyu45+M`-5&e&EITjV~CVzw%Df_mHm-%=;g1t7mLmY05Z=-+{w6MIQ4$Lsz~~)+>ke zI_AD|Xw@qRRxuI5#eZ=N{X8=;|9)}&3a!+|IR!*WubRi_pr>cX9>$<2#*UX)_`oyK z%f%PU=Q)Cd0CIv2`J6}R86!Mbc8N;+>eJt!w3FWT=j5S>Z7Fw^XG>3&sGD;j+dn!G z*LjicYP@p<>#tK@2l4tbhKg({mT1s2;#nz{WQqOJZA?&aby!)*Lu$%|Hh49*`7B||DBjXkJ#2lr?aPrh+raR>TDSezgG!UXB zG0G-4?P$#{C$@mZDJSWt;tD^ISH?d_?Cb>w#sekJ!FNF&=P)km$${-*_`(X})PXqX zsWcS$k)3HH;!<1b3~V!4*HATaBzp~Kwme}}ehR>E_!QTulpZI1lTV4(tsHJJO*#CQ zloeih4yo_@A_J@3sY8KxIB1((M`fI#Buy#4^ z@~6=JMuv*kY&K_#Sh)i?^iP6Fh%1f-_@)Eg2DEvA#2-5F>b`L`dL^E~h^bNd^1twj z$Hb(ntJM!}Arl-U_LrckZzA`F;5QfmofS^xz=fN@DdW=*na-mEj()YT z=6x8WPWvNsTj=y=1Ol^%{(&iD^`)CUqVJ`-qT3!ocB3afEA9<1$$IkmN+nMWYO|8B98X znuX4=A-zX@!i$;dMuc9PxFjnsi53ue<4UVZZ(V{OU%^7uxcgM&zK&m1?E(J{{MPJi zTE9=&8+Cjam9SrP@U1ddb0ld74P~J5i_Rq<0u9zYcssvPzXF`jfExwn;F6n~ii>>W z&V&H?OiG-X=qJ(cfs``QcCyaxujpFf+b>w87+4gar+amwL*lyEd*t`}^f%G3!~=c$ z4xt~(dkpjayhk;93w$@ei+h8A3wA4fgW6)^`HDH;L~@*W7n*S~rA%O#{ua*;6yfXt zQI4+y;4S`1aeS};Ky`f^?#;9E5bnm>l`r7Uv*Fs1;aQPe)D(T>?oxN&fTaA6+DBOo z>|i(VDZO81`V%9j(2YhsBM+oG`*CjL^TW1SN8HSVc;s-##^W(@z+LeC;k=Rah0Zvj zIxo0<3G~d1BR6%ta_XfgXu4?0l#Y${JX-Tox%0L1W6A_&%C@$~d9Bu8va%TU&*WFA z_>+HHwXKeSCiXNX{BhdO8hNJiC#r)K+gv8@Z>i`^Ig*=9gm<*+tsKz$q-$XFg^eV? zvMBx~_v6_YPAkvNMyGxSofiNs*t=-{h)nf8$F#0wAVaQ%4n51w{92MC2aAZLCGrP; ztlJ;GR=K|$&0G16^Olpi8=3XAilnW&Cid`iM9FEi;kMU%<)+vp91{qx3B2VV0kl^R zjk`*`m9udn=?Dq&r{SUheKc>UOnt{ag*wE?cL*K>hMe~jk3qj5`W~L|rGLMKV+is6 z**K3&_y~P);DZAn9QfeCTO4pS_jY!CxQxk~Wunl}8P`Wok87g-eK;99MD8YVUyqm_ zi}yL>-{pC0oJ~<1vhl2WSzcZCN2y?H?ioc|j!<||)P8#3kD zLd?K;GbPL>4L5hXb}M_#Be+7!TJT(yPs~(KC$8R zCpLTD{>M)mhc_V0mBCP9*QH-0?lvS z{Mh~*rp;5hEr@4;mitzw6Y$AlG^H#QVjk>+s}P4@eQ#gpOfJgTjUT=SBkuI{tvnKJ zG6BkcVM!(IbFz9M0wg|=MVx#l;@QYAc4q^c8~)hvs1f=z4{+HO$)>Ju!to7^o0oWw zrXQ?=^dO(gB^z&iZf~dJ*^e@P`J>zzmrG)ZlX&~Bx0_V@!X_vMvl?ElPwT8a28*=o z^FI;s;wL>3VL)J`6Bk@z4HSE_PC+Tma*f#ECUB8i_~3xn`pZY*qdQ!jaKn-Bt%N}* zX}F+~zk6+6f1peH!E}sjj36KV_=QbxPv{i+YCjhj2vSD+AVI%2^Way$*yQ<)-?b5W z6Qf7pZK0>s{chyAO}zZ67EQNEj#x&cTI z1&}oPLAj}O{o?6{0A;1^D$j(GZa1sDiQ5A+ln*z{Wk;;#k}ho5eZUhQx-kko((Rvg zrhI6V0503y*w&}%4HLC^nPi~|i)Up>^ad+<0*z@|L{2$H5|m4Xl3|Z;aD}!BtbF4f z!vpjdoMnb~d0)Dut@6DH5}|ol{?cxwmytvG4nAonD?gzmpl(80txXyGyv?wRnB1HD zR{SD+$O^0!XdIe4kg08FY*L^>xpQn3rgv#1oj#$lkza=P@QWJ^S#L44xaRPes~Dgo zY3$u(Q7~mVjy|Dx+z9Hr&jaGDA^Lp%{0Ykk^_x8>P zWyPg0tumC;?JMTY6KAtDnk;;lrc30~1NR*V6gS6D_aMm6ZgeJsLfl*Ph_RAiUeqWc zBadUY1_v0#Xwax3{p>$*9;vdgw~NcJAi37@{+dF;8HB8km@v zduWS0(`1}CjH|Rs9_JJH$;o0{o3DSa+xuI&mm_^&$LT257gu_8rn<0OxZDIwr=6>L2H?=(A^yFEvKjWnS9@)ySz4x;Jy<7^3s6 zd%<&vUpPZ?(2vyt%p2vCiW35&i!^>h=cyn1;SkFw6&0bO^$XAQ8ZJKaRoBPO2wfvP zXU%M`WlmvLVpKvM)E}+^XpDD(CCeo`@yartG z3kwN;QE@x)1U(nhc@A=H@hi*u(z0&eU1E5}J4%h0vAe(qmaz(Yth-}~A79b%?v^>f zHYt|+dkgII*riXa13S8Ttjo}ufA5l`Ygu3{E>EIRde8gE?lp?eTPvA8;uzhIXWm~k;Xw+TCK7WbHy{Nmt*S<#;g5GCH%>F=(CjXd2J7kQ)(cn zEMVPE+&#BAy)jC}fY5is&Es&QEK^e&$(vGg_s|o@ix}+?uWXwP6OI6@cKo{3tnV8Ze{KTjT#37 z82JPv@z7));D^uGkeugv#VQ6^JdA1_?z8`PQ7|<^#2F8%Q}=5c!$q6h>>gATI*C)A5ZJn5B3!HL|Gp2_Js9tZstT!%zd7a8;zUKH3~!N zXe51T2v{jUeHH-jmHAb^!ik}_&#`bf^~f5ThH1C-Zw&3Oe?l(#()cw7=~s8?3;L&P zi1H%+gm$}v7aEeeEQomI(bqM)v@hu~>Vq!VV=&uxO3NGx9LSV9Ci)`Wluy!$45-5> z#DlnQC^rV^>#OH}{rnok_-pjV6^72aG1T=S3yqyKZnydu(e8bpd zIiLXx^2*qQoLu>ZZN^T!g!)Bgkg-Knux^adH9)`Te!vLu5nb!J=9kc2JGVdFB8$%m zwCh|Ro|v^GXot(_3FqULt&H!Gt}gIv=+QHjo3$~!CBo6e7t#N$1LUv;+#3#n@Gv^{ zvQL8yDbbhb=sAtjq8FVouswR)H$t;mJXH9dHdc?U|4>Tx44ikr30_(cb!9jvQ!1P_RCtP|GOv%N1J z@04Tpmd}CAVJ;Oq`xj|B(13i`KcQPNM3<(t6}msh>go`U(6Q3Lq&@OgW%8f(9j)&)>jS_aIC!IeTD|;H^`m02L4yjd|_p>ZcKfta}Fm{ zmxX$J8CTkYdZF(fCw!K>aJ*1{tRw((jmGGW+^X-;gt@6`cqB4gr{l7iWs#jaO`yLKwa+pv0L=IUqauEu8Rmf&N14*{qlF_uWQUmH6=pf zD0!=z)`~xr>D-Y8i4DP&@4U#qh^zi$w=hTZD)tlgt~ zdVn@{blhc3VIbkz4LyZ{e8zp4bMDPy7JN2_wt;x+mVWCTI5N8A*vW>4@=V4NQV@ZQ z(YZ$G8lQW3&6E6+NHt94r&;p6=`$1uOg%HFdv*+Z=6T3NOweJkl>dqzbK&fqIX|Nx z{3;F&rllZP8!d8g17>Xx9>gVVYxx&n((%hMuE+*;%Y0OsfF~|=A>||@XV43mT7BTm z7QEp(A?lX#mEYBymMehJ)_WC}^7zs9=Eu7i^a>|^#3cuA?5iT1N(Qmc#elIs_7@L^ zIXv4ERtSKLhTCvg-vwteQ?7m4nlZ*j@TsruZI!}9002M$NklHisqhmi~pXWsKM0Jcj7t^iPy@?UXxh zXemb79uS?r;h_A&?VqlFaQJCC%?nRbsC5!&eik-ymN#Zhk%DjuCBK8i(3W@Lnp2n! zevcrr$3Nleanw;L*{1?YDQ>;TwC}PVKz5ODdxQTJehZUI*-B2XE&gp>iy_C(Q62-k z2+$e|_f`>KW1h0#1^7D4R>rO@p?(XB6RangcGVDR5x1F^Ec~9bc6pA=TljbNO3V(Z zGUsn~n$P9A896=d*8X_TNo49s*K9JdG3a@~*em*&r#1Q)H+|GA_>bg&N%bmo>x0fs z6Xy?gCy)Bc^*x{Eo%j1bgBZ$zZA^2br}S{3j0ei5G>{Z^f2F}|!)@Nio&mQj~dhyhDqZC{7cV03GB+EG<*^{onj9j9u4j|Wr zNQ25uQdHzwNMjL!$zmpPD0b5oM4P$_Zn+GMDM?A-%7Y-2kZh__Fr8GmK!V_i!><5j zrnD*^LPCf5OXq=FF&1xr9WhgwvCCiX3Ya9571)%8zqu#%5Hcp$!Z*Radz@tGjr{uz z|1|$^r3Jc&^t98iW;;~jo3#XX??0_p(+ekJ*?n}KhF7B<-~*si(dO@jaSG$Go4*{lMb(^?etTb8@`TJHZF8&28B$BF-W*{?fbS zTOt0k-zljS{;^^Pmso=D@bkwIzmwZX5FZ1**JI;|JvpRie@m0$ar<=8jO*Eh_AVdn zhqfIrU1|3(eCg-Q0~;Ujy?HTxPni4OF95BLmpNe{S;)&Vzb)fYR zBKntPKV^!H`?x+h@WFu(4t#Lng9E>p1CHn&zh{IoMt`4NCiX}0H`f93G<-;@Z%TLz z?!EEtJ$j6vo5}mC)0F7^!YPO)Zp+*$V&x$+wVsK{WQ@C~(^9e>AkPcsR1jwai$-sk zZbr#QKsIu^p_I*nas4V98|bo8B}VAiXLp}&VvK(G={8?}FkKvZfyRq>HxK)QEzdN} zMd!J$EoPLlY9|7UuBf0;{~7BTP9y63?=Fz`&Mrk>Dqnq`s`?++0JgDd;D>AI=L6^h?L z=bb>%Y#oO37hHhIk>?;}u^YkMyz3@SUViuuAz>-VjU^tWpkZ7${(+zOC~lq{JQm@* zn0%288PBHVyhH{9Sygo02o({M(o{!zDo^?brO)FFDJg}E8_1@#PUIvxd>|U{`q4uU zh)@3##-v?X2?(e%)P+~Ts8$+0nzbiNrt4gcnxq{ z-hw;0L@u}pNHEBZ;u9{kZ($MbFH0Mn5|q<1w9TO}^cOdVy0JAIDE*2ax=Fn9MBSsTx%Ts*5$>!3MgH6z zs5~ljZnTjO2~zjaFL5g&L!`_IS-AKYtvinhPA>DlcfFN1WtrzBJ;5o|!1(nlUAyqg znmU~_Ud#)Z=_5;Kk614>EukDB%XXPOWz}7>y+KI%&<6xw;qoV)hF2N#v0i^C&k>?s zpxy(#c;CC+&f}&>6>yZp0;CLek{gUsk#6!=ZX!R(kYDMFA-cvU=%pA4WDQx}hT#5;AN@$22-S05u5bDk z^UKeCaSsT2=={*HPGwU!^@w33b+Vt>ho0FO=2xH|IZglJ@mJPCe+8beKxO_3gxw4{ zc@blLHjj0)CHr_}6}JYPjte!QWX&Aog-dp4Ymkw(*Yq6$p$B7xN7mgRI05$l3r0b| zvi^!8`j=l>i{;~E8Xd9^xVWXCs=YW*r5-aTIPbV*%yH`Bkfs5B(w&O9X*B|5MsCuL z%iPu|@i^AfW==J12g`V7gLS@!39f!5!+bk@LZ-q1ry!X`~x0$ZdB z`^yAxuj=lRfANpM>HST)`-shNsVRAj`SAST!~y$luW=8pt#4xgSe*yc;1=unPciT1 zv;NW$*(H~My`-JA2_7LLh0M9>)Op58zs48~ec?RN`LFjAVecBKJAMPN8mB{NK0U6w z3!RvF%2HkCipqx;b%l3cIsx0A=iU1b&OBSMvLDb@4_|m^gq?%?7u|I0+?FW=`qNrb zFI59$f;#pJT?N8_ojRu+~OTD*zdR7B#OkjxA#3e~DckohvngIUgD}eA^-Tpuu-+#u4 zEqhMona_$CvT3{v@zU(`&Epg8;d?t}gK5GPhip82Wpgv*q+_DS6XLQR9rJvy&6n$G%NLA}{pzqAu`d|^ zdbkYZVCn%cthWOy{Ba1e%`$s}M!#I^Ww;mcJo~*k-r~w1_seRC=lC>6TpF0S-ZbebmiGs5Hcwb z)-YoBW75!N}qhuIJl8l=Lu%oTHI#LAN8Yuuv!Msmoi{PYXe>0ha5`^^OgRqjL8IKO+WF-!L=@Wk6rl{x0E@Jr*@YmNRW z^G4m^z4}9*lVaQr{X8$JS7HE89{Fn%$1t3B5~m^)29DqOr3Ym``4eP)lxKVHuM@wbPwzyI&&v%mfCuV;V%8~5KopZ)re&pC+R!J#M2>E{7qj}&V6Qz`GSt`_zqt9&TsnwAU#kEJ` z6upNW!aGpFLMlP>IejnBT8ShKullE-TMYbkT+nFK{yu%4eh;rSsCUiOCRfJnht*ls z0o_&^LH{rYN^|hRYu8ql#jBhI0XgJT+oUWHuUXG_;tW1Af}JwxKjgpSpa^Wvkp;?2 ze6nBlKXNFL?-VTahDN@; z8hxXmcGJ)u{5Kr5apTxddtdqdhM}cnee4G*w=)@-s19Xpz`-v;y=jW@nAlt<_ac3} z{=(zTu{yI&PRX zw40g!?YzxC?zrP26Y;A9Gk-weggkYRW5Uz`uSBE|P#5~MaCxSHXW?n)l{`k7+LUwW&>H-E@R#=(p(9Z|*DzO#Av%7)itZr?9E#%efb-UjBN8FwqeI)X z$|&1|LP^Vb4ej~xiPPFS#n(<9T4tA5Nf)w2ltpL5-=ZwqT3B6+DT*;AfW zl!p8^I=}v`w9Q=!Q}_lK7A<%q1>o20xhp@@htfvYhsMqrfh=TQ(Qw+W`ORzg@;^05 zL{Qon9ACo=+sHh~p>l+8>$3?N-+t)$Obd&(xPaG~^#%Jv`*^*x!D?am;G}8F9XT7ZU zP94EnfCabO)XFrb$^BKP4&k;nVd4U9Ui<+u^WZ*Y*?g(P3hhuPX38~w{9DF&PIDZm z{%N6&fT3bv?!--Lh9`a8N&AKyIXlJm4fuab(kcDlqtA3DU=LUSK4wTS_Tw`4aC`sc z*-OIL>Ehycj(~(K-yJJ?o^fDD&!%bY)I0~yRHrG4JF|m?ix6J5Y8!8K71jBo_gt}Lok;M z#hYB1l|YB13Tl9YyI>1LGZScd)i?rusJ*NTu9Ns?e&_eVA^^m0#EP84-pOk83`XAo zc{i!-`GG>9XH^MYL*p^p{or$)LXH%{&&pa^u$I(w}!H#iSaC2QB6GAhUbe5H(qdi;Co{~5LXccs$NE(;3 z<17?*8!$gRv-~3!VFn35xk?AO<2zR`M(A)Op|xopA6grmxh4+ z4SO8TO=6=0Oj6O|tiIh`%G}caVH*9!2aBf21yT5UQL;hMK$71-6ff%Q|D~S%Cyx6` zVw&ck(+&hYTRK3+zmGcuHF@@yF8b{c9I8s_1HaSvbMMgm(%EBLg%xHie@VB-D3onM z1QyZvJ0&V=5n4_0tXRLc5&9Ni=GQLkYY+HwiL4$@8{s zlqk;zP|Yi+CWqhJaNV6d05kn`5gnju~|oU}o6C$1QPD z_L%;`ZrcK|?>-|-WUzi6*&!idPd3|DrqqP`*r@OfYKnE0ih1?_a1N<{W=!>ECuuYB zkBRbH_F4P&63k+79kq|h$yfShC;j_|nm_K2XlL#2c!1eV`7z>RxSjoU zb?9>z$d#L=2%Qeuf#(fH5J?rOua=7hdzy015&X;F&u0ffjHzY9kyh=^@nYrQ7kxDz zoW8lPxl42>(!v<%=cgN*Gm2mA%>P!q(_p%6=VB~*UV7ID47zvONJwItn$$SuZ8qFH zIP#hve!MOt!JEwGUj&_y>R<(p5N!ATUDP^iIbt^R$FRZj&5Nj|cDCam3x)S6RZ>_s zih+K%7caJO4+0ev+UBr_Z+d;Qw^=c}YZ+6f-_FQ*9iYpV#Y}BCfv?8m=Xf~L_5Yxd zBd4R<$6(G(!2fnSBSfVdrA|QBc~81FEGu!F(F*Otti`Yc4x*q0{Hkena@z_KVDbtx zm(64;srnVrUH9xRmYgeX+Ro~LfG?7sED5)xs#z7cZe!_+BSf<2yt#%wxR|`;aG7@C z@jJ&+G!W5_I9-6Z1>c2`M6m?Qro{S+pFY~d`jPKD8Kqj(l)+-X$!CH1CU|FJK)98C zf962)cLu)#>W47!tT}D*+lPyGvsw83V68$A=5mh&6G+yX8uys=MI&oyLf062Z@zZ?U1t9-ERoD zaoJVMM>bRCUY@~~qoYFH9isMA!aQTd@1!O`t-sOE2Djt|!AWR?tIbX`vhM z?Bb%~hB$V*QeL24PPX4RV$GjCj^`8>!oLQrK4>F_zrbh@F!v3UKyz{}#YHq?bW#sb$9LKvhV~0>ooXOhPKkv_GYG-69Mf

*PiYqhX6b?%eFxK4roU6rq-ZM+q$A4WDV`-ZkR5Qk+Gt^yduF9Xr!NGu1n>t_>Vk zlZwEX#scfWRZn_n!(!^KUXRgg>^j3Ml-?2LT=qY0FTouPaYeVuQ|JZxqUm*|?}e2n zi%}XeYT9)Lz1$CA1f>_WuiK~RM`kXM82>mD^8@ILg%UCk&cy=@D|#Z1k`?#EP(BQf z1nLidQzi!H3SU3Doggq`30 zGg2?XfI+T!g+F46r`_H%$E4t%yZ++Q3x1IAsVk0QH}cnH!(2K0?pvJD3Q->jiXTR3 zh0Jrgjq9tkeIC*%{{qm+rg4~bHyjud&oWR5Jdw}~k)<)Bh3T;R^4HREAh?X7%xT8+ zH{wXL%tw_DkdiV8&6zv63)w)@C5q)nLBT*D>)Stj4Cf6SYZKd6t%?Ebh&31ws5U zti0?)LBa6GQwyFSI#n+**`Ufpm0Z65a^nfSbD!fENCgKaPAqV<%B{Gc6Oick7V|O^ zs+d8PBX9Ceyj5CR%;Q})_Qse^(yz1Wk&_ER7wk!=+c(GmM& z>%nvWW(!B5fsNP*VtHC2XAUt_$NCnI-EtbV=6n47ka$ZEVQ{kIDblH0p0s|VV>8pu zw*0y8pV?5Wi1D%k3ApjWZQQl6m4;`tulRcW$&(G$po+?fbXAhWN94Qj(e3m->)U8A zz-u-3rY%_icPGo_x7j82*$2Yq$T0so51ECd5`wz`W=PLCp7UX*@|1r!Z>p|rcPg2DDNpHw z;49rQbX%P6F&8x9yKjB?{eRzNKdT4xq#tXUw5dIR{H0_XY{n2Ja)}v^O zS4QKnv-s6O!gLgc;Ef8K* zSHdC3#q)7|kJL>UW+Z}~s_HogJbya(t#^=26fgqcMg(jxf%r!a`>qe1L;prB(@alf zTsv2HE7yuU_EhgdRmeoaTmQp1nXc0)nFoQvqA0bzk1O0yg~IG(YMYQE@yAhp%1aPG z|5l2?3+uqV@?>M-W{*625)FZf$!eWouJU4jfY%Z-_q37#@Mn$iZbYsZ9s~?v-1WFhv3vWdX9D=kXn?_QHlciP$4_R;c-O44awy)7ZKQ0#2;)DBU zb8pGJ-Kq`aT;}ahV{;vcoC;w9_Z>#)E*p8OOq`UW!zS;oV^qjH!S2Azjdg*rK&3ln zSI)Cvs@ViS!|4`xT0M*PiAda2Nl7%9+W=>J^|9qyGN&&j_aF5k@PbUC;a250-*SG+ z(1`k8W5v3+AQC&PHi{l5s9hT1-)KVYS;0u1w+(Z6@v!Qmd{P}6C*JBEc>iAPM~-Y9 z5v!{fJ7DAmlNN|Z_N-CI{{X3ZM3wYw^ujxb%6;v;JN)2meQ3{D#?tNM7?7@*X2}cV zG6it9iB?POpA5&Y1$L!H$ak{9?Y%e6ehzxaaaFh{?PIl*lyJRgPdNh#UZJHy4kKv2uyE#Y<&6_q0`>d0K{C1<9o=Flo zO3$xLxqD%PLe|p3RFr>)VWUDPun!%wVY)b+!_LdFcUs#pix7hCi2i6nx z`PmUWV9tF-$RvefvU;Pf_TY9&Ha~>V)9~sKAj#~nmwb_jd>znv!m^D=#UfCO^%h~(U$ zl#3?k%cJJM`d_8HDl z*Mi&rq{FfI9@!RV$1G6(w^BaYDs!qLgDHiI?X%5S6{aMXmLEA9|NdiZHmxl&jDi*P z>N||45jJL`7HL1D7+`%=_bVdxQ_&aFJvmJV;o`@U5+zLa*D<$m+)S}U)hRKQYn8DZ(nhD zWPMIYs+kEB1ehA(5=X#l2E?e#PsHp82{z+j{`@M2wnIVc^+v_YCK}D-Nr0R)ZQAb1l-MG5-iFJ1q8w$XP(FXbctlG}37#bT!>~qwapAZNIt* z+t&8E>PFJf3Mcfz#%P-96AIeTWb>>hm%;!PF|iMKhMh2lqI@ zU^d*xerkwnNE%5-*3)OxEdl*ch4li@ncn*W#R^&KHhHlXc~V|kcVo8g5zgN~{CI=e zyVxte8uw;=p40lOWb!`S^X@*kBN}~5ap(Kr>U(DfJlVTBE-(kaU3s;OLYzO$Xk*$l zdP72y{}1yz?yq;NMUZC7E%T3Uo2zkaJQj8HsID;92>b7cIup%Zaf}uz*}cy*kUFe# zmbjKQpW(={a%Owyw1F4Tt^?t`R^&EYx z-rbBO8fZ_2%Iv=Xc=b!_h<*KEuIT?HY(ykM=aDsmegQ*gAqD6{-iu4ZpLkJ|s*9&= zKGFkAlF}UbA=SGNobFhT6Wh`IGJHoC{Y(D77A8+jV`&wmJM^Q!H({k{ZaxV#g(e;F zWHMA*z^1$Yc(B9;^f#$4D1!NHavYrNGUY^Tyf0=a8<($As;kCQW7vW2yCo#5>udY; zf5G$ok^MH@2l8T*c1qW$)HVq&k{Kjd)+`oeg%lb3@5R)iKG@Jcw0pZ>z5=Zli4}8) z2)_V>%P;Y%hju4OS{?QIB%+y(K8S)V{@*V;Jd_2J<}pfd@>#`Tw`s)R7mH{e$CcB3 zWywm%BH+0=2CJO!st=Plha9rr0LX6(wR(pmPkV0}SR{G0WQ8`7!VhP4yN&CQs)Jp@ z2_EQFps);=F4jcb7E5-tMh>g-3<_=Fa$baX@*qj$YJmggN%1bH_Fof#&JHM-_%E%j3>#Pu~20?`y|k=B6_l{S>)7 z?HJkVxJhQ++n@T#zO=x~B@t>yM{;>8hP8A7a(=c39RZxl z9SvNPk0rqC@?TMfVb`+Z_SUxDCZnPOSfBqo6g&xmiMX+&;oVd_p6CkG(e0Qh)0QfW z19X15BDO7OvSBiJRKer#Hfeo(N){WnzY8QM7N=884te@H?^I%AQm8>@)w~98|7}mG zpLkOtrN~~tz?mVaMnADs^m$11y2Vf@xLhkL$oWKHK?ppqz7}TFFV8Qf>%RAkmeIY6hNj)CtB2CY8{?{uw6di`?C84o`dNG-y=qC!e4NS<6Tgra)cqZTOD z-8aZjeCq$3LdYQ5@|?_2L*EI>SoQJ0xz4EZ9)P2&Of+ej zR3rZLBixsHk{jqhF@a$1Uj^d$^;hdfsEXQ|uvWI@7IBbgbqL^T&3NHF*p1xoZEkQl zOki8Y8I&pgaY!bo9UPP2QeU%c+R-GNo-InHmy|ahJ-IvyL=%3#*Z%N%QZ~kwqc0Ah z=<@wW8_vTVln+{b;BEVx*ndNS;<&+rK_An$yn(nIt`E!i&G0csVJV`b7uPnIcP!aU zrFhajrr`MUipR-Fj}`uo05iw)zG+T> zi5%_@-vXY~{;iQmYKAs5$z_4v*!K--eTuDmRQU7>ImbB%-Ue`}rZrI#hn;bAhFHLe zF>OLEmnnJ}*#UOd+x_}w(5Wp++*~Oy2wlS53FqpCz27V{QgGE31QQF4!&XS83 zS)0;}R~3cV&vcWrAyx-!du) zK-467B2T4sq@vT%$P&;A&V5h!+^_EZ4xS*GdVdfl{Pm6n5?@5l2IRd%9sw305$~lm z#Hb-|U`Rpy`_Qt$zJ$y^IiS8}!q{(I964ARxT9h0Up+x#i{;u3vh6%zKH4B2^ltih zC(;uR<5a4YDE%YU=Ki!q_{p$iI=nvF1n$1ya*bmjzjs}4WozX+l=oWxaLG%p!AXoF z?`^N(Q?{$_POFH1(2v2viXuxeIsDy%5QwEPrOraP!2Z6-$xuWVQ+j~3!RbB3b)OVM z{Wj5VC3JG#6%hXz`wyyQFl+I{^gOkp@~6*gk$CjDI?3u91rbru6T`}I5Q%=OW+*qy zZL_rGSF_LwX*J4saL7xtS5~h(R%h*az10|3>@EDlh%Mm7e-hzvgsM$FwiF4tCSKrc zCj0z+hgN%}H6=j%kkR2jY>eFy>r_V8Tn`MfTpg}S6%2R`dC-^yadj|Ps3bb&FqK;$ zsV{M;zXbuCCaUvZc6Z5KthP!68YVS_VZ;rjS%%_;+4k2^hjY?EiC`mk!s?dr*H^!DFA6vAd?4_Q!t+2jx>zpXG9*$Qn5D zj*eucrKE#L-Y)E7lI(~c?t2wCixlS{ABL0Uh)y*2{78R)$EkB1tB*70Fo8talV1I@t zm%J$&-cbkW;hO4tx+^te!I)6C+HVzql9jOnu=i6i1Of|xSHbnI*g1u;Zv~~PrvyLi zkB2V#?#I`k;sQRiHQ%E~8HBA_Pkt+y98c|?uhn`Jol9n8l?@8mgx#fruUExU)lXF3r-Yl20}aVd*@Nl>^RPNWky-z&KzM8ChxXH- zeg>dLzfzNqIg4YGpW40ZTQM9ZO{O<4n@>^r<ZjfU~M`=nkKoi zdHQn8?vog(xt~c{c~4#lUK<3jzDtIHN%}j18qI$?PoN8scf=jz?JEfBC&D#d+U|I5Wp^c`ll0HtT zUeMjg+?BG@WJMt~iDCHBTjoS^dcvI)r*}@pd~iRXq)4Guw_ezut5E}WnL-u|xs(Hm zduep~0$t9v+QiFZ;|%>G>}(%+G8t>N>q>Yl@s5Ik)9Rhq_Hy^fs^oqTyNAmu;0;6B zV5KZWq-oHWr_jo7wc}5}=awr09Nxo*S3-;aZuXOlQ#0;%7o3&g&cK&6DdI(DkEU~T zGW@AH<=fwd*_Zs+*K_R~Y;a}ho_`)cN-}6qX$!bwPxo^W=pc6BFkEZnIVLc>(`tJ) zdKZO%BYqx@OiNZOo~k5~oo0Dh!lYRI-$&j5*3_4ry95TtiXNZ==}nFd&}s+P-N}vz zSDZ&HqQnlsBkln=IC$_l`Iht0YivKq!E-#TzXJ(GT%&S4nkcVqd%JgK%W-b4+xLPm zJdkTBit3(mxygopnKcR3NL8j1d2e=T%lJA_y(1{J0SLpj%r!iLgNIw6C`BqR-q+)_l#_#B>pt8v(IUFXjnUrvj!4rz)YHMzC3 zyCBf=g@jbwKF{MtPc^SA5cfJSQ~;VxrmU#8CQ?T(m(L#4F?fmh{*>S6ME~*4n+Gg4 ziAT48p8DK28Mp=5iYc$C^kwI@jzU%`-A7$>;uuF!(swpr44;^aF3vLn9s#lG`5W~34Ex)jyz^e+#% ztd3hZ>n)7Ru&n9dK0)%C`T-p*KD3_Sh=`vQg;RWoUyMG@D=#1QhK5);%9Au3^3PF* z{<8*o`&4$Ldq@6?bh>=Lr>$nnejN4r2}Um@8&hntN-phpADnzqx2l1hs&-OwQF67$1!ve^ zIO%`1`%3g5f2;&!vNZ=TJiySWYsQ#T`AFG&;(yMLeoSnddFW1v)BPx%PBhIz@L^$1 zdo+DDybrd3u!Q`T5nh--pe7~y%x-0|Qe2CV&CSNNfR=MtYa4jAj|MXB7-a9hZU9NT z&z76e>>t|At66$Xc|22PZ!20R{O-j=!3?Sx{3?VfP~UCUX8COa7DN~)i{qARwkO!M zYn_uK>gf8M?3T{Za>qwjX6SgI?6gYVmhHS_$?7MyjO`+L$t$o3>4R#s+$=g!M}k}8pS_OyjzB}ut`kz8h|i6tWnljO+b%waR!NI6}+&ayV#e8CgK7+ZFUGR}3eUNRZHD-R{kec}0v0e$^cIp8-_ z1@)VLf`<>}@+hQAB&*cmR3LT)RAjFnT$F@w3rTx&m>Tz5JRsA>^8ZfE>Xksf`{B?H z)`Cw3@Nn~NC?n}f*Jpd8D7?JCpJhh|uJK};tzV>`kDa~0A1a;sdQUc|s+5ha$J}Pn z))m<$Gm892=|?Tw#>H2D;S~^{{M0nWZ_RVWe6Q@C98>0ZtEkwwdB?_|U>6`y6^ z?YmL&ymS!6-*_<&%3na5;5w1r@H1LN7Ss4 zwzvwM-azUi!l0y)sVW?8QmCbwf)P&!)b*B3Lur$)2MbR>> z1$fYswvt(j!}#P&=go}a{JgJAeJA#TX+w67!fgwn%R(DroXUZdZrAZyh#T;DDA>ri zcML{+wH30!^I!JlnQGYbM(^^Bk00B;$SFDZX4Oi20$L>G`u8|gnx{qZE}45>z+suAti=Bar8WHm__j1mft5e*6y((TCHU7M@U zdY@kFlNezPgI%X6Ajj3*R9#6|&%bFsr=`KIZNL4<#Z)V!G^jN7BPz%vGuM%GqL-$` zvzSiYrDtDt_P2%MNa~4seJo*CE5~9BfW0fle$!J=0;7X5){}DMD~-flM9xx@Wb@g% z_rTYw6J}01UGs?~?wfe&gOOsJ8M#*H3vqBRw=U`>KYp(zc`xOnk-ntNz2#}E^wUK7 zcd;?KqnuFaY^P}Z3$En%e+HK~Dy#}M*F%O2@2lsY*W*c0N81ZlNyYo2g}svaA#{iT zGBT4#oZ(J5{oz}SEbWoDL6%n$dH&{ze7(>*ZZCen8vV|?fWGNDp7fX&;c^Ttgy-Jt zT*$rWjG3BX4qzqqkvnvI=sM>hazioKBB(HB3t`6*(!{w>q-#3MfbPLl+8~mZI(>va zBRv3I@47YgctU!PXf#Cxvw)Te9)iU@eu~Z(3#&5n9sIH5tO}5!(Tey*>N`0=qSCcu z)FyUH&hNzVHjQQtF<7K%EcP4f&$2_?NTKtT;VW6IDhm}gcP4tdny?Lu2h)I|q~83O zrlQj0cR<1QtAfj8tt3|QXmP`eB66>Td!MBRmG)giOVPh%zz9YB&xY&Tem+&XovAxB%=!Weq zj3tn=>r^qRuWSw9a4DU8CTl|pYQ=-Ibvvf5j$!G0O-`2S1rHFnUm&x(nD!dcIdj0h}(=AWTh?UgX(0xz& zA&YKl+VD|tp_Iu((`@|R4Fi&0yTDSMg+_VIMoQdPgE@pV3^ zSwjsxjcy%#F^N5niz-wMYpEuF--@eu=BU z)RCAr-@x6)Veu4eDmSNdswv&0a1DF(z0TpB`&|jyh!{QJ)qQecjHhnwTeP0WPcM+U z2kwPJ!a{b5+Oeq_X?(CMy0{{WD$r}tcl10ZHpM_H?YhgOwnVqMSXWXx)5agob4$+( z8gck))Ad$x7*noiKdx6KmWvwBMY|db7_{?1M9BhP;}i{ltqv>}Hng{enLlH~hf9)t zYj6{h??9%Ai}m?)+5bf(3s_XrVaxSBr8chks0fBNC}g|AV_OcDI+;1>h18dz`U{`T zY+UYx?+H@-c5IQ~YD{nDRmn9i?o`)eTl257CViEms7R@TY|QKae&86W(KcNOC9#RE z_calUc(85(zGb$SXBOO0azOmKB7N#MS!=i)t7}&h`^h1J4>uUpmTs89rGG5?hb!C%VTQKm| zM2|yh_q=C!t#>sn`zcbs{UM$=EvT1jYUl+{0g6Pqx#gTRIPwm|{#>$t>JC#Ed(FHi z^dyL+5%;-0Df|dlVhl-wY_s{_O*gV^xhb9sLej`OmGJZP*tqgPL6hQTv{;HG>9BzZ zjZOAdvk@m%=&{oGQhrDPpHOL@2hkvT$X$SZ*l~erJD0SaT*>Er>xWnR;sfGIKOEk8cuVAYb`=XM!n~ zq&gq?0ao-VCqGMiGm$d-0{L+<-U|oI|F6xh!o0YJ^L>f?ho6p%*;Dj$I_M1*dGv7f z(DILFKYTJ6H~Jor*D-TlXr2G)?;E#|Ds4=O90%9jd`oRIpP9-2l&(AxV|#IsO17(# zp|R+6VocARW$Jb)h15C6WahXif_KfN^#&xGHBeq0i~)zc7!}=!hhnEuPdVvojxvmG z$&n_PD@^F__0e>^Gx;CiBuov&vGo8!7N}WF4#i#RaR0mDK$s($Q*HY#Pa5{lw*jpH zKRPI{s~ybVD7Ja#g!=s`{`vn{0GFEuUaW6wpMC^i;QVDvjJe2p-N2dvFF7tYoVZG; z0Q4Y^a@B{|MS_jDa`mhi;*A?p&+8N4U14qy_niFSX}CrDpsZ}j2Qn>6f7xJ=dVY^q zpU~JS+pYvBw@&LZ_y({~BG?tl^J8b-O^y@tP^c`_y%y)#3HmxbE$+1*nzr0bhN_uo zxyZSBV9$x3{GhU-De8rN76!EKi}Z=KPT=yZZ7BqBA^c}?DHUL;A!T)k|MG$%XMk97 zy2m3fwBL1nJrL8tUB>Hw^2zx;YuD8SHT?7==v=eYQ%MBvod=0zlLrJqOy8tfw@v<0 z_9Gw}__|H}CSdl+z~_0R9_g|jkP1<9>vNCCk1TJIok`TxW*k2L>to3Pz=qsra{y#^<^n946GUV_~m)A~V5MivJfoPdn4gGmA(;^B&dsvI9l z@Srne%=P8BckTmD-tE`iF_!~e6Mdr5xAMozyYCB+!xmR*j{$tpTVDu}wHNw^yI{4_ zxDm!{ACe1E7D{-;O0NHI>*}iOIRokli`_zfrbctC(Em;BhWg%Ow94VEl(pqOLKkXX zm-b`iaCY?)s3UvaC>4*5J2YIbWryDOclV|^a#BCa%!8oVKXDVZ_nt(%hLUxU@3Dh2 zSu!>ITWaCMm!Y7NcgSB;lo9vLe)phOjy46RdI@cE#J!nCP6+?i*$Afo&N_oCi^Pf+ ze!N6#;Q>V~k`f|#GWZ!9Y*A_x9W~%os4N|cR zL#~}>kmh99=>qB-L0}amT^Kh)C3fhd!7^QXqwH4ep z`q(f+e!n~9L|*RhT|<^ z=n|%h-=uFcZDK1J(#x+p&#?9Ih%|i?f5$~gy|@aoyEWKPFgV2`g_!E$WMNg;iUi=i zRk8oMa&t7YlmL|)N4({uN$~K$-|S;bF6G2%^`b&dI$&ElqR`fR^->~%{dz5-nA9Y| zYh@T{u=g`@dOE@N<799s9X6)P`E11;9Fp5c!$VqW*wC=?^M1_^-@FJ6UmRQ%Kl3ST z;MGeme>}(c@b{+1CKyL5Z+W+?O6%>8wtjQ7X8o|MpeUVxMl1YJ!>Oc0gC4g)r$m`i zadLk}L)R?oqQU1et3;80 z62sz1+UOgVAf{+!c4P<4xVcq8zDdtniC*+?<84 z40WvPNcS-BEqr6;1ABU=~lbz|0#O8Ni$BD_#1h~p!IzFjB5ytl$@;uEo zcb)J3a=Lz+aP5w$5-^EDdE4h7qB`S0O%I)NnyWpeFYY9jVpIb<>t*%}l~;>772r3C zb7%Qzu($`jjP%zsd7BqZpL1}^BGld{?|2X+qg>kj(d#>-Ib@t9w#o{sm_s|8#`Ris z9_-NP-3yjCf)BUpPT(Ki@%!BGN9SdZRhFp#&M|%aoX^DkF(XMOz1wyn@`J>%Am0oV z^ZYOm`hN6@y3!|6r}Z0E9NGtE$#}v%F21X%D6as^nE0{Tvnq}cFdv^8V*`rwTvP+a zrh1+Ol7?$S1{S*N0X1BB1T#)59F{A5HBC9bJ!%^~7w3p{GinkTD!awOTSjGV936gdUabq=~`iC{t`7Riy| z>B%1ohXb6R2}*+d1~l4Rqnq(BQ$arei1-8EF?6&nsk=2YPvgGKDG_sWQN?ruEDj-5 zxx4$s221WJtKgrV^;4FsYD}Q3&O-PV51fp-E2+>NAx_j+!NMOG>idMVDr-+xO8 zdJ}b4y`E%d=IV$N9Wj6VK3ca$hRmG1*B|S5W0lSr=N~T81K1mC0-log2W>s!Xl8i~ zVr!eKwmWuH-k>$M1X*!c#gC<^xiwSk}}AKL-da0>;X4!DyeY8rhn zc6A0T%V|RAKl-fv@z7%ssax)z($NenjYX;U#f4Q8sOMIXzyvsR~9U@Ib;u0hwx1@ZFT2I zeBPsWH2B~cOQCak_Uh+oH&!*rJFo-{(-4@+q{BrYh!NO%ao>`bIC@T^&0Y!Qkb5 zBy_(!kTC>vPCaC8c~NXQLnVDg9`yiAbH{Mh&3~J|duk=cB+n~dXk4-}JbB`Hwl_cz z7~z1Vj|yFkRWgzBsGmLcdWDHWcQ*wUg?gDTA*+5nDiH(lDi^+uD$wPJwffNOJn-)# zl#oB5nC{{?>?6}41jw3=dFZvrQvg4}hWum3uA6fbgTsQ6x={S%ShJ}!{#AXf(mSj& z*sKxYT2I2e+}_a_jI9o@Xr<-L%)U#0LAzaY`>7sh z#c{<0y`Q7LVZ5LqCcr8BU+<&5eAs)0)HH|W<$2G5Y(kL0_prNF&Y&ZRmBwXXI%>-5 zrNm`jMUn`&O%tEpSavC#M=#*Hk6Y8b}sJy2h#50AEPT$ zX2^oCTyFy*&az1WH@zXO(-xOr#Dndev$(_h+d%>d;N;C*Q2*UJO*g{IDqk_IyZGPBzE#j?y3grjezdka6i_(9pBLJz4E?gZ{Q%G zTCa7bp8L`LzG#Zsi^%77NAlESWRq7ZqYn>%Kfz6ZL4F`DczJ<&_Gg|W_>oxKzWH%m z?vF2hZu{or+5o|V$a<4QR9EEp2Mli9%N_pGERhN#6m)7a(G@(laa;o-51?dhP){vBfsPvPk} z@qsK&te_Gt*SzEA*!!mEJ#G=MUN~lT;=uN&X)XBSLTjYcUd_HoR!m-#LG01t@gg~_ zMzS1_=TheSq4|yYkvvvXP~2O_{;({9Z(cjkE!^#dnEQnk^1;zig0%Y+X?JG=$ts%) zEU%wwHr#5Sc4qpO>c@gJ{k^J}cA>3b9#;OoKD!fBjp$9;U)V_z5>{|J;L+TDyp7$S z(}%~v9-rKY zoz*kc9f^01UwsN|`qqB+Vq;OYD)=}v4sZ`f2<%1S#Uo-U&N3Pf&iV>}p)^KhCwQ9K z);2CQMOwVQqVyC!O*LUl9;uJb$QayXI+>9K=^6Zz#Guv+)18f4fD2c*9{@@x z^)$LUKy&2LcIOFJ>Q3t87uolyFU*yfGU4LpeT-fR3Qo z`9BMfNVtN|c0AvcckA8U;zxt^q0?A@Jh9HJTbk#y-LrLgFNd7R^JW*Bj_fbF-apx7 zN?S;FqmI(1mkA@&j=oG96sm*>S=yes>mkzW9J?9%WpH_xb@DEXvgR6Cy(a+)uk%!I zyQ8r@TfEQ91eZ?+6}yFEth1)N86k($9@qA`lK%!gV}-V|S)dj~n$GT|0n&QWCBOAB ziyk7sH4sV9Ga~wwKu)DKVl*5pU!Oc5C)wxc5HVSr`~MUFRi|RW!(PGoQ>ak&=v!nz zsHF?8WdPBFuwT!@NS&<;>Rs$k%t$EFJk<)y@gdJT_a@~L&aeor~tVCWd-E}+8v4;9mbZ6iF#tOCHFM5Bx;;x>p zFS)GlpT1;msxIP#LRU%@v;Vq1sCKwsc7znMMT_tB6Gc!osk?g&dKF*n5Bfej)J*Gk z{S9L4@J0KoyT%rn6=I%mMjTBB!RQb6wGIedZzImC-TFZ9mM15I&@=iBsm0I0l~Hqy z@F2uvyF0e_eZg5#-2T=;JM_rN`NJ#USKcJAA~?}ENT_YoUry*bQBgHe(;yh35DdaA zz6$C440D|dz-jHdg_Crp@m&9TQpygI5?4Ql4 z?WnkM6?V|L$EUI-v~TOJYvEhAmc9r|bFhtkk8g+sFYd4$Z>(N=`6};ioGig1%mP#8 zl`k~d2s-Odmh2}%Z+7RE;AGML)2jvz-ZBDK+BB7`m{AI5pY6I1T>2O~F2kz6%gD}| zS?1yA`*ao3=)W_eN<;2I0~WHDpNWmG^Z`6h99+AQ)ROL2FLuE7nt|s!r{42O!CIv~ zTtxjmofU&uR|WWlZEKe_!{~J{e^>j~P=7mc&-!A#OgWM`wTWm)d1TeTjsvczR6^2) z0PZtKU-MgjN6^JYE1tOhyzu`yydg=AM2*hJ*lyj zSq;)}BFQ))N!{X*RtJO~=a9X>FvM5cpyO$4QpKj~$-S^gd=t>)Q>H|l{#7Y((wSlq zz0h6k|B!XoUs1;GzLxG1r9(nOI)@m#r9`B=LArbBZYhy&kq+tZ?vU>88DfB8*qn9F zS?k?<{{hdh-{=0^ab0+K6Mw%1wQu2x##zKqxP$N3+YwnvPY?Kw9G@a>`mRr#9(CNz zMf=7M2;ezNrJ?-zc#9sHi(whh7|KHj#B3hk`-~Vi`$W-e`A%pSn2l z(Gm-9e|a#;A|rPSTsb~eEH%_mFLX`~EQ*lwV38SIW0s7PHBw5VuJaI{=#nhaeenH< zzBw?)^+k?)-1MOM-lm8I$qk#BYRiDwQp|Nr3UKN~({M_uEl@TYjol#*TEURF%1bUC zybxXLFsDd$WXk+&`le^c{Ls=sDIex3RkVfdaDK@QxQbHA_*loMC!$U)*$Iz1AOm!M z=&R74HL(x*~J(fF=zLnG+18y#?lX;2Zp%4XKs(oyvU}G zPHg4r^mKD}JM9jdOu$Rex+>ik^b5=%eectGQ_r+`_fg)uoi>iufoS0unghJ0et&^PPrV58XzuC(WH4Hr$)VEPvj|Hw$1iS|5QC{ zs*1^NSZl{$KWw~thC7hv*@jjjwA#OGxgU=cFo$67XEmLZWfUoCetDz6oQ@5VZk)E8 zFw#%l5sE6;BKV7g=4rM?{Sb(|MOLE-zMK6y%4gFNq+nF+_colHA|$g7LS;5L{0=NfC!wq(F z``YCo7a*(CFS&*6ah;@E-7}>WlNc!1l2dcnPysHMI3Zm?`QB`d(QCw2^(~k6f{&~O zxh4Yy=4F<`g1q^=_SnnW4M^L!?@~e`Sds?YjDhk{u9&)Rt_Itg8vf*esHN$;3O&!A z>Q!xkZp|>_r=c14y9 zmPrMcKShQF9CTZF9f|wRhCO3MQv_1CabbT{@g)-Z8eBpaX2bUe7keB1hu0sA6aE!K z1(w9*wFa8S6o*XW3h%ZB4;dNV(zrtT27~Fz>B@N&Q(%)*jSm&nn<0ca)~I@T)mi5B z?YnL@Go{?w5_hSV9)x+<2UZ+~k0$M3-|d7=4d^t3>#@2Q-Y-=;QxUCzAKp(bXVHaw z#p`sdw~IWeuMDG+B1x4W&GFLi2LVL!H7GShYRP77<*N}g(0B%*6keA}GMCKt?Ugfn zOp3+LTnFpx>unrjn;mUW<8d^PNV3OV(sF>CZ{Gp!oVg@SX8IQjhZ_Vg;o6=lx4#}+ z2AUs9=`>W$t&SbAGwdOa?uW_?Jz{=tDhtXYVy53m%WcZMNs4-V$^7A|zba$wJT zuDA{n(&rxUZ>xfF+BtC>pOboXuf}+8MwzoY)uE&$qq}yG(!g6obW{!rLO3^`lCbmo zHQV4SHK$wWGjdgy-#Ny8P9XcsvgW=VPKpE)-r97Jw}Jck8rfC-!vK~KBmej?k5^=k z6~B#7a}!6+iwOkjAcIdjAxQY41T=TgDKYyUZQ!Jqn4;(d-YDT{?|9gi8w6{2G_q=8 z=}0|;X;QFLZ}fc7E*<=?PtZ&d--+W&mEcK`iH!ZU^PuLtTnSYQ8<0RIP-jxo>@qu6)I?q$7XHB zo?ePn!G`gdE1qBO$!zCv7dYlasD_ugeQXmQ$Qu$;Rd9DxqAcz8xB}#hD22tmRNBzG zVm0yxp6pt`dhhu7K8&~UxuXpQ6fl&TC6Yc!J(-XHTy_5CR*o8I)<6#$wmY^UfH1u z0dCx5$Q5vu6Xch$sSoelaY^DfsVc8C4@K=R8?Rh<*JP+mdC%9q9&WAL{A|uJ77J-<}Z3^TkJ=eOZUY4v z8!x`yp88Cdn=gyHJ;rxmV(ZP6$9tgb&-{ijqMAb_D>@ufzV-Z-bw#c$Z0TG@iu~Ut z;7keZ8B5omU@K3Aq zb2$aa^C3!q&h|?`oXh`+%0mlwa?=ydc{{_qC=6xb1+h$Gjy1iXb0XKGDBz98Lj3C& zMclru9Gh4EYz3x%;5mxy*?yZcuwvdW)Za=Y0d1)rNcV#O13^8j7CI-qUxRywAAo=l z-l|6GGx5Sx#=VV@T2ajuC?BLRZ!l*R34H-OP-rl!%WCUH$){S%QLF8IQZKp zwFxrEhtpiB4ThQoRES?xS>cMem1iz{<3Q37-WBzyo5;Iv2Sy5{DGD6_JQ`Q#J;tW? zd|QhDn3lbnC*O-F78l@URD}bz-4-W;+DdG`c{CHjjoWYPc~09`{CND&N1=zt?eUw;z) zlm*AgBKOC*{fU8=v+SM{)y973+S^3UM2T(T`%$L71gNsYiO^HD zkv^4hDH>oHe#DOGR#VULnN@MD-jQdkBpx$YRzUZxIAu3k%x}@+5#s(Iqu>8C1BQVx z-MWk8Z-!2YksNbEj`skU0sRo(geK#Q-8kQbsSW=>fbz5hGylPN!n((?vnMy&>l!@C zrVpguGWvg8Z;cX=e`mYwGg3v>>j#XR8Kd1=_o?x{kZc9uyc2uF<3E|gLbzQP(sWfb zy*6ts!f+6D5w9C78S0xfpBiLS3g#&2B3`mz22Tz?RZJq z963#Cdigodn&?tAtxI`oVZr(E)aqHi1=;;?*&LqsP8;)^(<%$AV@p&s-Xo5M5dD&S zh3dO3NDV6AfyCUel(B$0JVG6m{%WW=uLvlL0p?C##SZY%pCz9X{C<-~AJr&NVThPe zO1-P`92!SQ-JE4)33^b_WUnSqxgxffT7xV1eubetSmTM3kv%By z%_KVsgn{D;F)p-=JTLVjQ>+(z^lqv$Q?#y~Vde;mM0Hyp+c+S0L+2c!drRQlK)yj9 zDDtqWXqj|#fHbXI*WO*1`J0f=8K@y9ftORdb)cC1heVqGE_2JxPtWW@#iAE6P|?ig zOyA~gZUEj|ul_l6D$jdf)zcE-=87&{GwZfVW9v`A!yEW7bnEM}`<*DkO`_o;DFJIxTl!RcrqOow##jAKAS6hg zZB-v@W*|>pwODm`-u@}4Ir;8Ni4-t<{BNKbSZ(kGL>=kB8EQHmAf z*WM}nJ7)6@=Yam8qt{V%dm@9)3n)0*PGP>4Ye6gW-wZcLH@Wq!B=_MQrBMM=`aLmG zSqJ`sDU2l6k|4cptt}F(IkW{j#1fN`sKpi$6DB)IRxKc3I2{#R|8p26D9L%zbN$DX zqmPkJ=y@=w>SxFeZya(;luiXxuzoL@)WY!DbR_VtQ1RH<;|v}qCvnk?GB#aOnE~+V zSeH-bDzgEVP=^9Fb=E5Kr^R3FKpQHo3XVtLU5Za*=LgN#CM>M(g8!6%73n(>&1?4S zdhM6>kcp&B7U5PZh#SZsCx+&pbCB|;n-WMvxd>+rY`}vFZ^*uBOM?Bk?HX|6ISf&Z zhFlyCqrM~}`Or^e>Mp-OF*mcpe$xgF^9*dkSNhC6_^N`DakDW4n~qtmx8N^^RfvR5 zUbfwIWu=u8w8j|lAwq;5A9cNKPZX{i>0WS&y-<@()jjLR@b-|WOf&iOGUd~}w|FgR zeirlekf3+}wb z9W9zs^D#0Tk9%QFA$H>&`;j{iqL87=A$YIsP92O=j+uysua<|>}(ug?!uBjY1d{Rr-2`B}e<((L8#sq?Jmr?m zrhY;_i$&e;e(*p6Xb@~*H=OL%xmv1VJ4#@H^s6lrj@kDi?ykDC`JfETp_oDhcTnI) z>gYx16>BOa%9$CqvrWozeZ8|pdxL_;(YecIv5G>LSbokx(Br8{u?C>Z9qxisARj!$ zU`!IcK1p9tCp0#g94VoN`7Cw?R3fV0Tlh$k8-f2b&f$eZf-8t1wu4B8f#_&er zoJPGpMgEn%^?|^T;RiQUT)F;%3&r^$!H^m2myO{jPJFbbo*E*{;`I zjP_<~F5Gp;iIuG<=@DOx#>?lyvU$gAWYTnD-+j`h9Ea8yh5$T!?w#!O0mk<}5Xr(Dzd`8MY4suNjh zw~tgJ2O_e7ci7@3YFbF>m%R?Sv&7Bam<~1qgAx%1oMZ&Iyhewo?Z;EwlsJxU7d%Yi zi(GCYEerPg9MLH@B3pH$+#cQbEquL<>ekZ!;)T;G0)wZ``S$cMxo@@r2*cGd3Tl-g zGwC`w%END;A|kwgF&m|jh&$d8Jm9i-zzA^--|h698WeA_SD*fbTCj(8X=MGIp0pc> zv&i~+Bq!AZx%b{E)oN7T{2$@qU#bSTk@dN@O2AwEh1$+`_5fQYXe2>q_+f6qftz#e zbatDcvA?~ll$L5}dJhd4vm@zU)(2Gze%RMdKj-J>;4xk49G!@q(X$@49Mf}*N^q0= zKI)gr%8e@T1w86(NB0d4!QwZlv1bwv#M2>WGc6I;M25|?@~tirRqsCQ=8;=PkKfH9rdeyUm|36 z7<|0MDFo`ILq~FGCKSnSK|7K37v0s^5HsC_BhYzXR>#uq9aHS2x*)utHvTNQFR=RF zn}J#-v(#@^f=<>V=Wn@MVclFys-lcoc$_)qV+7UwnwGUL$O3mTD8?nR^!0bNzWIf? zktG6;j8peemCQ&T_hB`py_2(3vr*{q9OkwN9qXIs%%llcwD{xqDK{oNAWgDW$0!Z^ z$jh9wU6B!&rHVt$o()G~2VD+`! ztW!q4Lrsyg_YnJN6vKR)#~||A8s;gHy6MMyBPYV(MdIlc<^!8tS<4k$KaEKBrX)hJ z@U1VG>=CG|d+P2mTTs;+e_mRZ)jQqukp9Nb$2ey%3A%_R{l}SWIC(H!zCQfE%=2J| zHh{?rTf;)>)^fu8d!P*4N5X+)Q?rtfJqkQdbT5HTs${1DlZh-+IQYCykosgjNK}bvb znfeA}wMuHy3XZt%y>u=U_B`=+mbG2b^SZv-a zjPuZcDylkvSj$;Qk^!7G@;zZj>_#hajI*|eF`UIoK^^Lm{$~1#K18g)T0{E1>{J*r zq95%{ovlFZq1>s**I1kz6ZnGPUmaD4sX~ki_X_3uVOq+cTtB)8+;WS#-(0PYvZR`V zrUiF20!B@JdsDx~ctHUb_J!OKU6I@ulCSDbqvH>adQ=~k!MZh})@^*G7Bm)+;cDnI z^)<5QN`pf$%-$CWJWnp?&`FOdzgC*c77$O|n^u3;X!~;yMyOFoOG~K}y!?5cfP=~<)iJ{g1i5DolRG5zCR&eRw&m&|EumhhodAY^Lqi^fFs&8od;9+kpN}2_WnGI zlU~|ETOtz!a!GaFq!Y1~jpFeYk5ZR6wZF;E`P|w1>*)0+_r~{LbnVtCc!(eRJ!KgYsAAlN=TWol1wRe~lxje-(TOn&1GaSc`1@om zV_-BpSEG)ExBXraknEyGmS^iV>HoA%7qXLpE$Zdz66EFKMl`Cj9)Fw^riq7LL%A2n zjX!`WOWI>=A)QoT_~E%e+kZOtHjqcgPEhadHc1xim56McN)JaeM+VJjMtglCZu7IqhS0^N%sfAimsHPgnv4&`;VPgVr3OH*(PDq5KQ8 z#uP9_HrW5lQRwf%*y9K;`C-bVxdenZAlkp7d$TcVSLV73|ti) zj;O{|XvKkZB43sR(@fI~mt)q-dvxb(-}2U7xc-=?#Da9L`IkLm!E+xp!2>8!5NApX2*D=a)))@<^_1z?gARkE!`?Jj2WMv$ zqN&Q8A61y#0&eU=7C1pgn?VfD)t(_;r92;%#NS5$;;VddT|fg4?W%f#@Vdlk+KiBy zMN54yx4ceXV!AyD?wrsz*rCc+kOolP&eYlyJya*brqKODDnjcAM~ooMI!Q(l*ml!< z$sV8oJlVI8=l2UGo7I}z6++$azd{9^X>}m#Y8C$)(1~)Z7Ym)%0r4yWD<{o6V9pycg= zvXwH(go78%_U4D^)2Qk8ovd_3)2D1nqN)nl{9V zy@l;Ts-yEsLH(JazyW>YHMdVc#AI=8!ou}(&WX65p-7?LL{D>@y@0#pzn@p@Ca{ta zqwce{{BE^n1aYvfcNu{ghm})amD)cT_9lfl-K4Bc6SaLl@5&S`_H2I?&{4KQyUMmZ zL2Ry)tHHC}B?biaaQ3ba)ZgX1g0m}p@-!f;-Dm+^`xgOgk9$9+N9A)$FwRqx8baO& z$uZ#rpX!4>xOVifDra!u&ht@s(T}p;}lt z@6|@KWzjeMJFlC|ZU@^gE20>=XI48)s%sFkF9rOg_Uhf9z!@}pxZ_}_i#ej}AG=Fz z$8W`i-Z z@tZ(~V?FjbV&3mv7@LD{5Iw%IxBZ@@2&1#tAGbDS-&_1Q6!(@Fw*0z;&pKf*hPC=u zwPW@#|B3&(a7uS?mP`fYo2YHdQru%(%MhQf2UV^^o$*7B{EBHXVH4xOj=UyXKd&F@ zvPZ*GkfliZg|B_snE3D+CnKZ>hoivB>5C6ntw|y8;~8VJkGb-yJ#i$1`IHE9c3aqf zE+mLSYDh;ybjpJQBN1BmHSY+hqyV{tF9$0R7`8n;_D3qWt=2Gj&YG#V6zm<&V;$3D z_}qX)-ogSQIpIH*W|3C{ylSQGjvkQo>FA_wL&6?w&d8>ti;T`9>i$zYm)4GB_hqu< zh1u`S`-7iXX0eT1^}CMp2&d6)cIvRj)4Mw7Ion$ycZai%wz9+Y1AI$i@jMYGJo(vg z$#?jDZ~XTsuB>v}oU%p%GoG-MpU^O=hq?^eJp&x=3sS5$=5dkxK95Il90MS>^lm#z z-s;_gzl#>t8F65y+G7TAKq08@Gn4L^h+%mD#1s_80QB8B8v6^3JOR;%B?QQ&*{4u_ zzSp+ZuRT;MYY!~c6BjoTqv4omOcmq`UxfZl#xUwMq_e8hDP4S$rE^l{n@8*&$cG0d zJkJ+DAff3I!ctO)=_)=Sv0=zr+k8n!oYwmN6zHdtB$sx&2o-vye@?IkI#!Evdgfna z94DR&qEJ#oA5{b+iZmH!bBj5?1FWMS>d)>>-nz^E%Wm`k%}Y1xkr52VbgRnyRSN1* zH{6jov$aSv%55iCHk`(9l0~5ZD<;FhlIb`@r&OBXv%UA(T6Vy%IKig?(d=hP{@&q~ z(QLJjhwU+@V%I93(Wd`Z$`(cVZu3HCH0L(AN( z-ww&nquiDf+-b#qV=jL^jr?6dcqQpYM)8&csh6JLb(HM6M`aP_6C*)pJB+Tsg)!I0 zD3Xk0p74saQOV#No%Wg|oaA5&ZPgq_>-1O(yjk}mD=IM;O`dF+?X647wWu}NvT2PK z+b-{hM7ZH>6HwUGEu!FjG~8de`?W=8dVn~)R8UA1IZ{OViaT~vh;E3&8Y%~~xTmT6 zEqirOptUbBFKBRcpj9n2jW3CIaUVXXGKS%TuA-)sgqh{Akiv_UDZDV6&X#M#TS-t| zN11Gl*SmdpKm$FmKt-IUtbZ^i@+IT6@S!;N6N9kbb-wFzp5Ty&j}a%5PWTqQSzKkX ze*Y-vic@vUDKXzRnuv4io%<_>?0pW^5>O9RqY$hJJzj3D<6YF_hD`k86>%Ta$2w{H z*dJFBtU{I?@Bn_54SMD}w|O&gKOuyexl7sX+stzEe8@}I_*5;CzzDdP5Szzu^l5LO z)J}65?ADUfuWP&j=LpqPa#u$q`0yu9A9<4wNMZ-7Z7JVuB|CfVZLUEq8w3y;wJy__>>FLr1t58k7^>1^vN zP`#19#M=rTcFXQE!@8Ya@e$V1fq6Q;|N6z+5 zn4}~uL{KwvFdmq`^F%Ugo!s=(uRf2u$rZ@h5WKRDy{y;4tgCs@4}6+y%S|d1dx7PS zI}7L1Dpixz`bHY;{o#K9eHgh&t4dpE^wXhf%H>Y6=Mdih!i_+Gt^c6cTSM;zgc8O? zx}xY^)g~_Sm!+47ltV`BX|Wo?is!6G%%ngTu}~9xirF_J_ZX#o!*eWP zlBn%se#oxIn5%_v!vXif<=SH~TF^Xq-S%~R#+u$nb{ zf{)ISAT#AZE+4dPyRY5m$!?>pyH&Ju#0A=g`^C%+3FqJS4?uh^9}ct z+-kt%$W_sG$6lEGZhR19=f17;JICE;^=b)FPMgbgZV9ZM-GioUi#K}-!P2I|`n_X7 z-ZjB>-k%}{18kl?0GO`5AahmBqG(6$>V%Y%r;n#5FjLyb50pOPC)8B3=HgKKSdiI@ zzjW5O%4YJIJ^W!q261bUtLm|=PRqL>ei0`l3GI<3GZm|0!hVgzdW;5mrKnef9W>>; zMHmHKxtKr_fM3T}y{8z+Z>mL7e~@0CxzZPAbbKnEq3f5RD2}^t49Wv%I{J@ZPiCYn zg<V27lOjP{mU}(ogO|<&1d)o&&D!hB)9zEOhqp>K%h8 zEYv2{yndle$PZYiy{}*q?HpMN{OX8AFr<=4x$z}OjBad?t>=*Y84)iXAs4zB;$P-z zPUTNxv)-);$G~Fu<8LQw<`#dLZ`Eju!3~}K$)87Ms<$O*Z*2XE$~cYMxrr4G1SoQv z+Ys-yTIKhX6kYa8hL1Xm@QqgCej4`IL(e)0Rc}t2YeZT{Tty~yU}`4uAPKP?WjV$yY~G&Cy^;^qqo5c%C@?gBtV1D4LHnlqXGY-8U^b6e z;~t!u-f-lfx4Wb4g!f!Q{p4ZOGK(fRb~kEZj>{R{)_nKN?}qx`=3hH?PKl#G6>P&J zRWuxs&0c;j4)BNPEHNv?bF|@(CaDY{U3zUaT%64;)Dcaa!I&9b7(dW58f>WBku*}o zwy@b64^2uh^qj@J7ka#)Am%)h3a&n1N513Zb$engk`O2MsU;fJM+?x0)Fp5dB;*k0 z+x)UihcmHr5cbf23 zR~TZ-HkQz`A{yy+8J2AP&q{HV{snf+G>@@68b~+fu~U5FUC%ei>u9J2t<`FVZ;AIM z8RAUC#RH7<->ZLMVrP;fbs84fVcA5|h)iZyH&pllUI_N0&h-WWft5;JF z^QiS9$A!3gLfS0A9~ehCXKtcK-ZKJPYpSsxg>0;Sc-}BN*d)bMeS~I2ad@KV_OOzn zJ>>TnpaVIX;E%=o&a@j6Ny+FUCLNq1_n(lu)nsA#QP-_)P-oi5_4JGQWr>8d+IL!n zUm2p3M?{egr}g%U)9$Zaa-nv9a)L$MKE!{3R)79SxP4iD_k{yWS0#oy$*~RI9F+r( zD?T4p0*3-}LY|5;O5qFjf3W^o9gkYmKAM}d`j0m=42#7NW;dO<4JNSIJX&JA&w1N= z@wS%j82qDIe61ya7E2JJrK(ojb5~ctgc@<-owN46+kM~9V^v$8P@*T~x}xTO(M|FT zk76Uv@hl{#W9O8-qeImYrfL^J{8?zTSrcY?MTfBw2r=6zg~83rKnAQ2&AgoJU$#CH=)JA zv)gS;3FH?vhi}o3`Qf)h7^`Jr!W5C_{Op)c1_)Y2KQb0qIjJW_%BLO-f;{~fj)>a98~yS;3Oxd!bg3c6$&OIPiTIaX?!;YVDe_w%fs`BxqESh!i$2QCuP zt_$q{TufAmoLVD!?yU~eyK1&_P_jJxO|JEbm4eql4c)yX0bQdy9yT?F39@IoUYBAq z)kOw_$&%_?^VniawT{E&tVh3lFLX7EiKtWsV!Uk&EaK`<@jgS0fNk~)Mc;&Qyu5q9 zVgnAn_zGR!%X1xf5g+na*yxIx{G9)B%jNz<_v5hVn)hEBuLnJ?-6?2hb2-hOc}0}3 z*C+$%K69Xi`x+G^$Ju0vuXJ7k%XwT9kLbAhs^b+0oxm>C1@cT#yZR{G(`X=fl1~j( zMeV+!WNp!-4HGwrV*h-~J*lsbmfU62znd~hC_1I^vgn#Vpq66n7LCsptUh?$=xSQb zTCCAWMA93|W#P29B9v)|4yci#o*I=np~3pDBS6N~?IK~%Un5TRAi`dkd^3@;ho#bm z)+SJf(NR}+?+!Ei{0bH0rz(GJR*j`h5Z98uk|Qd^S67J5p#i7_jP zJrA{HGY3NHwEYDbNhko{%bhppEM87!*;mbapA@QK8&3aPsL`Pw;gKz0ttZNa;IYCM zwI4)Ezm?#b%v82#%Zz;eMv~GZ`vHM>kA=uQ70eJ3e#wnPaZEcn(9*oDSll)0DFf+_ z_)sp$J3*@iq!-@Q%&q@ADH^w>V_+f77jT&>m38{p`*BvW^E1OP*A$LyKD4!XfPiSx z3Zk!bF_+-`qHhzEyrwnm&(WNcjxSN_|5)a}kon+|U#QwC*T;y4lq(sPV%#BaLC`*Y zZ$$jFg&-D7l6uWtyb0@1OT7Slh9r)-H6x)v;u?%D!Z_;E=WPV^ebkdC#-Iti6W%AH z#V7H=w392*Dua}3U2jsbu65zxe!aI8TTG@U2WmiYBW;l5N>_s`m$3c14CJ&5cb9gS zT~;ZI`Lz+)`gpShF11U(mF(ZitPKcf>{Q)7GK#Y87=^I6!mhTZ7T;Ji8E00T z_z}Yv@JH^nkE+lJ4p`lkKq|ZVn zM1vyqDcWFl)(Z`3bG~5It`xA0>;OlZL zaqM)L@o+wHl-6=d(7cD&SwYs#i!4Mo&g3TV_o$IHgSRzjPq6vog}!fcI_lvy67_0~ zK;|U3rE}*m4UYfM0;tEz(&5Rk`ZXai&1`!!va@eD+GFYpu=`*bi^J6Bo-YxOk{saX zS5PD(<+dz0M0v0!2_z_92zIT0@N}Q@b zD*ItAeB|$k1C|$*+=J=}Lr0@HKWoOcCg#Qx$S34EoIUTHgE~3$9i>+Pe>SaztC! z&M#T-@%2%!K7k@Sx&96EfEfww1J-&CJ!9Fw;I`scaGs+f%g;o{u;Rv_q!jga7KXc} z8RYFr!O3DoxI4YKJSrf0wFrZUqGG_E1Me{i8Qqbh3cnk>-<0J-zr$V) zIcEOYUqa{vm-PuZ}^iN+uq$}eTEl!vjJ_~~Jk??JrCqL@> zS0UH7Kg{$@e5CHjQ2E;69;k=b1Y*(9A?+F#Knhmf{bQeM`-^?#rk?8OWga6vr9b4y z-;+;wfJExBlIJQ2-`zrQswL%=wSH!_wcxrNa?TUXH7+bm9bx7sjKi5k8AqH z+ImzXKn9*;D%_6Wnf&rK4J^gK$bUIgnP(N7eg zKGLDD;p8iGXe+mbu$y4`!(`k-Ge_Vnvnn>TOvP!d#D9nrkepeZ&)8;RjEKVnr-^4A z98*mo%kwp(PxDE2q;=9IUM4{g<*n#2jvsjXCBW4f4DKjm%os!4`^b9eqj95f%bH78Iu$ieD%_otSQBwuOGv>}7k=F@!FN0kFMQTR8aY49L;7trCgb*g$J;18>N%NrKp{=@%(lLWJ)o*I{8>N_Lk5I}f}j}xo9U>6XGQSZ2Pr0tvQ zO8R{FEuyPcQ-~Gxf%<{0p|5cEo@`}ZHbJXIt0`S!>w&L2TZ~OTQza4KQ zeKBa4#BuLrsDasLLdd?if_Uk^4WW;CQ9r=aHTdNzm*Wx+2?&_9ECFoNVGbmloPX$kwo1U;OnT53 zlka(7L$`vVFv^YRSdZ5*|MAQTI2=5}Pa*my0O0D5(|j&q9|bIGdR+#Drp;aCiW>K; z2!lMG72$W;H>vT3Y*?H$>ut8!oG*kVG;uT)n#oT*xy{>Aa7H(b;qt5n6J>(939fzqr;SW(4~ zTx(x)jyF`=JGvbb%XV!>=hP3ypqh1WM$q2h-#QFqtHMykHkhopaqFneXgV;onDtv% zK|-`&1+1P@dQew>>(?azd1_1YaF4|){jMvFGlz z_CT8(r_tl5m9X|~ZwY8?W*+p}=XR)QXF!4G^BxlbZX1$&2_Ahb9h$??o(%=a*;xFw7|l;M~PKX$poI^b>$vE zfaX*3d&d%AmO`UTu^Z2K z*DM^hk$Zv-VOjrNRr5SlRrcyVbSPxn3Y4Y_E^x(nhXTI{qK z{D#m0hOQ_iq!wjySWIhq_zZSQm~|Pd|Gb%pUxFxtB_ew?6@$o~M<`}Q0FJ8pY-OW6 zD!KewM->rGh=$wFP^$uC*3}iu*)#41sPzS32*^o9G>CV#MM@Pm%ej;|>Dh2=d;yZho@_@rOD z#aB>3x&Jy;q;3+L>yZkQRdGiPIeAr6qm@#4B(8_!TN3;#zA*b*E{X}a2&LAh=rFoz zyv6RCdqWx5);3TUdG6fAmiHwwESc(p%sb$4?^n#%Z*bvm)NjW%DaOlx@0obRn7wgL zn)B=LXWT!Sw0B{MMZ=zqO6Z`^iIe#Mowd~Tb!(0KMRQbR$cOE=%&;|JbLdj#0N3!T zRJZzW>_$8HspsC1YjuqhDq9vq#rW#?9XA2K@9tP^Dqc4O3$N|%Z_eDx9Xcb!68s5V zXVyRCCujstKm?|)H`4W6pPFWTg3!2T7+N)KoP%`oZAf8(LdwVD%7MNAF?>8eDd!bjw@x~ zF(n|DF-IsH#z+v(CD%@*8E5U?NlDTp%4*^I<2p<2f~bU3Eb3DC`Jl{bZVkkARULWN_g?6Y(j8?t>b}6hMU?O&LP1m}g zAocXZR^r}m@o`@->~f3)frZJ0C%S0zt37ydev?MIx!)356Aj^q0lu+Agj=DqsuzdbGa3jUF=cictcURc;rXI8(#gsW+x2LeljP)i!n zERJP_3nANe=qUB=FBu3QR$KcSwyU8)VB!V;H=z`vYIw7?#EPg#rR`ERJ1p>r2`^aR(RjLA0thiX> z`FLWi%Z9X>yQ6d5OY1HS3Pj5yBF5uU2z^M5e{S{Yrt=sR!B&*gll+-~Kj0i6dyc>m zYz@H6X?!5fn49%EjzMZx*)&l&=|#frTcPO|482EUZ_}B^2C16O@}40d8l=;W-A{ ze${-oQ_-*BYR)vaPB~UMI*O=ro`z=fVK3aW3KzR4ImHfE&=I{aRWV?Vn>;~%xhtHW z1Kp2?Rqpv)WV1J&k9-=}rHsv};(h;s8bfZ{g)gjW=@H$iX+R1jYE}&rXWR{ z%T-awN>(&)-H-Bf9t%8L^$~Slv@%VD&t7ZsBLK^D;Khl&_0JD0_E{avasj-YUPr14 zMsL!i4ui129S*pz0@C(k7VEy~2nb|$d*O`QN6-DH7MCRGbtQYKAi5=(#P(;jQ8l

<97g@cX-LyEe{Mi5T1oM9y#UIH&^ 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

-NE11M%(xn?UHszIQ4R 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�p|8@0u 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 i6wP>1l&+pYPt`w! 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 sSRSi7Ig5WmQ`VZOl{G0&O1u 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
cGyk?NnM8NIBP=y}u8? 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

LW;Z9&0AZ?E%Fo;g_i@FnywZ$m z+|Uc0lxxo&VN|l+mS~9XsvcThm(>|V&7<$e`x}#u0P zIO$1DC%w|kYVSULhRaFTfmzE)i#);d#Ba%1S#|UbYQ&Q_aV{F7;^JUHpq5enyYMO% z9Sh>PZn2f#ElisA9jdRI?BBq*znKRazaDn{U?8hgMWB<}K^SsdaXn?S2$v<>(T^;Q z$`1B&IguVJys{EKImsVeeO5kRYOT^9`4!w8cBf^64iZ1q;@g-{Pbru(B_DZ$0xJE{8LO7&tn2q$e*ITAHJSr44rq zMC^p}SByUOJ~;6c1d9~LjZ0{gxqfv}=lg4T>G8{IdlB|8DP-?M>{m`QQHwI`DOC4M z_@YOsE9+5+Zxu=?VhJN-&@Y>koYaJOz!ud=%O$=kiu`*}2f%y?cKYJrkJ7{{i8eu+2dMGQG&J{L1Lo6TzZb{+CbtOBu;h;3lI98qyC0&0%Q0V7k zhRU)3qx4^nu%8=Q=HtBnk3I{)sJ1{M^9L2QyKkQH%QRF{No^v;P)YZ=Zj&ohp9*3{x-qb=X;KCvoq# z_#N^U62_swxwO(36Hp_Go?&H?;*W$P&2=~KcD?;Z`=jT#2lgQRFN8_Hd3ORgC4~)) zD2tNV^oYd?zLc0dGVv8Mk9A*=>{= zu~MsToZFfqgB>$?@=t@=eN0Mk#4e`JpOM$(TM4aZ@_@aHGzzqiDYFsd9|0Oi&;y^r`Swilq2 zVCkKJR&a>_r3;Yp*RfJ- zr%EI5hhc=`J*)7s<%`T&_bQ~>&&=EO*X#|NbS&6UtjQhO^`3%BwmvyRkiMXU z#7Q!H!0sFNPi8ZbDoz1xn@d4N4P)7>@66G@+P;~4W)J>sau=W{ZqffDMhD_%Wd>1& zbzo)ue8j1|0wUqUvcz+r%tVG&a7Uh3YCJCLJ9LeSqvX}7boSQnM0^RUPMEnRMffFJ zXcTA}99@`Nk-#%E01xtlDS#gaXdhwEuIV(cwkLh$r{Lb> z`~G9Ss2UgcJdLlb3|*)_{g`?;kKJPr?w#q9*(`~C!TZ}UNzalD6qXtH!Dl%C=SsT3 z4YIX~s6XU(efukw^M@UYv6pCV)E}knMkhc!Bq$SfBh5IoG{LServ>x|_S$mn%67ed zXh5I49WfyJOgx@%jU5nRgF4;v%vcmg7b{}QXIYf3JW*O<(DH67 z%&<>+vH@t>eX`Q7yqBrW5rg3o*ZKfrjR2d+_>gh_?fb^{kE^fGYo=G5D5;bW%9;om z1``O|oX51!KN1xWkHJer0Am8Z@ZMekDY|;i_n)>5738nNl7}z1rfz==ZS4rXirJ&S zweE0!++myzz?-oiX6|gEy5PCc<8=EPg4&m_xHRfIIVuF3@>y3KH``~cW?dJL_i#e9 z^O}ObN*bm5x;tL|`cPG5n7ec%dAC;dj2LnReHD(8(ML!{`7d&0;eN0cs^7YV$P(eK z5T1J4il#%&_@yYEsN(TO2f-SOV|YO>Q7(7r86rv+HgboMfK2KNNP2gFWlZX4E2yI<4Sb$m--E(J_V zQ)aUWqHN{gq4)nR)WUjUHWipzxX7*@)4I6b^N;-YH+fAN+9OW88!GBHDcSq+*Iv6n zw8utOTEiQuXJfBoi3DyNaV1q6qh)zxH)^f<;svr!osD8@Y)P9v`qe1QirDEtesFo^ zZWW+Qh1>Z08+Qwa5bHEgLSzy(|4qy(I=N2#Y|t3JMMkzuE*y0rWEZe49<x1`jvd3yDV&85e+mFnI~)=b1JS>8Y*34i#^^}8Rm z=&hLqpD^r9&wb;8Lkl$gvpO64kkAZ7Thes0y_F@cg@S@Ff$3DmSJx z*~lSeXrOrPewUnlpDT^9=MCBF{PvQ=ca|C{I=O|)+N#p6`IPdGhYW8PlAwFO8&X%? z%|2=sG3mw^ZN4pL=Qt{cyKqA<3wfzRU-Ju$yI40jR7Ue!LGupgeo*(S#T=iSq3EG2a_d#_UmFr($Xf2%_WHH+_6KRf`K0Wx z8OZhjzm}hfviL8|>(N?Xt)M@CB3fi;@5@e8Pas28OrWX5V849XlEL4^8zV2aAP(Qi zk&~~m2rhBIaUXsUOS4#|tjqZ$VFnWPMV9XPq{H2B5=9w$cuI|UWXtLIdzAGIRZeD= zmiwzFvz)%{_#tLI>@QM_XAXp=HwUk8ivCpl>hs`_xSa!Dkc_EMVDB2;tccon&8LOE zH*vzuR^SG+MyEeT+_`&K3}UD@j>Tqlo2_JU&UufukSEVbSD#Jv8?Rbd*lG57B_fK) z)UEINC~v>JG>#@4XKwVUFPfYeOY`)b=01lMxYLIM85~=Z;2C@$)xdnw-cnLY=Fgsr zQkl;O>&X7V(cHq_4|_L6x|EdrH!Nvq-Gf3F*H7UevU%H^SA^Ps-K}GSC0tm{Cwtu% zSeb{gNR*;;`_YtjXN0~FN4q;Xr1SmZjv7zDz02wtym-~u^v7i9;IiWPZt#??xxff2 zIm?9W$A_}}T#aXkPeNH1=XWYCN4xfFt#;|1mbY1Ceysx+J&EmZ2U~M^*AzegnlW$D zxA!UZwT`7oP_@eB4N1zCza_g!YjNlCs3Df$l%+{G-}HJ$(VTiaYv{ zDCEr0g=+R;{Uqt0>a!%xy;qJ#Ry~IXsMRb%l#e6vb)s!w2UU;uvqW+>>EABCbbywKeBM6L5h-RE3+4FQ+G0UsFC@JZy58blbn zT4dk#|K~zrg55JY`<~J24}axq;XnEzS}pi{t&A`t`$bnC?pvyH8alonccyfHbOpvs z`Q-8CcyrCD*=yTB`Fx@Gi?+_MD{fnxmk!jiIb1%>?$o`i*dH6-dpwqy_~rCZq%VTW z&+{>5DnI#4(dmT24Tn83S(nz{fZ>qkzG9CV#lKGm(`KH(`WGAB%V!LmSsiIorAL58 zhKB82zFe+=4H2XH6SSB=oyMBWX0aKaNERzoPAH%XZi`QgQW?b)P?gV$73-Li0+1mM z$!~iF!TTdv_{UIM*Q2;NTk-g0eM4j~O>M1G44K|wqI;kgj-sV$S`4Xvi3XGZone}J z8dY6>;zz{sStiFQBL8Cr-)2R4eGWCiK$ZKN^I*dpNcwDB1Of5%RoMt5!M)Tv@JTJz_i&(JL6ODT5mH9_W zi>u>McFK~^LqMHE-K7_}zm|*6U;Q?Sw z$v-rG^GCxvYxw|5r?FaMA@oSweji{2^b%q&4t^f{a&}hi<S8LRmDxI%j@ z+nIMhTS}(g;_WRHjj8~~-Ll~gOSmK%JMOw*URDVe{TFXZ7x7Y)vB!u; z{_s-czXSP4AYuUVE7@FinE#QK(1Ll$#D*01vE*H9gcIDE{=EtVXa=_bK&t_CSS)H{{p8yVaU z=z@Fy*(pvCr>Gc^qrwP6EB$4#U@FPTr(eup&Bg0-h|a~2jYLcE5#Y?i%=bYX&s0bK zlYV{+X|Y5Q6~6Oou7{}m7cv7jCz!!~z1;!46%bVfKtMBNv0axU2J>u3q|u)UatSh9 zp0|OFb>7_y?XFvk-E2%UZzy-~bcP-T1zj?WVX1heN>)VpCqhKrOH_Gn}TaqE2 zaoKr%kg!JkNjEK)kMt7jruiB6E-tjuL%q(SOxOA%+YNS8lA6tXh~9Lk zBlY?$+thfGDs@AokR1|(Y~8B_(GW1mhy4CSVl zn!9%yBKGuzT2594u*F0vj_#n@hPL^oM^gc?E7;QW-o;BR&TGBsB8kc}VBcmdqY_}m zp#o5Q1e=sL3hD73gtgnv>Gfgr0>s$u2aH~WDQbuaIwp8O`4zPv9{*sf zX8^3;8X@Qx#e^&U;zR@v`GfaRFG3U}6B7{F=XR>uP^rZASYo9c&J3*lKFUdB^EqF0 z2=4H2LEv3xjkavq7l{;2Rnl z>GUGL=W!(Gz6hp8d3uEWqn<~*tbGqI_6zKLa@w^+2|=Vm`Wfz&+7D@*2$Y-+{jQe2 znOwbm9%r7}WLp+NIT0HVl*x!>&{;?d<%;16eRxgDC#c_B9axPienpQY4Q2GnR@_s9 zP*AcDlwxy#IdEc5ZPn)sor}v=%ixn1jbD1VJWiy_mdwvY3R1M7kKe*~PGdLM|K>GSM|#lYu9De7s#YUN2qL zwovYp<~&Uhs**m+vbnr@_)>PS+H@f56WEEvJgS_fp88Lv4cu-+Eb4Nj&{1noe1tcO{{j&&>YP4uHZ+th1p{U??=m8a03u$>a`b#cR;hL7@xaDn_) zHH+w^MlD0r=d+qW;&)qTf?mN}3-YZFteZh+cOJ} zBT%d`cHojTXrHq}=OjegOr`yHFIGMyFj#7=F|lFeGs%gY?(gFTiOdWotr4S)rImI} zo|XtWo8Z`$T&w*;d7w6-*6Aeeuj}qyr!vG#-H2^w_Bh!oi>%se+dhMRuN`F`dpC`d z|N2Pt<{4~B+ZjDS#dD?NFS>SO$65S!RLf%aUh0HXWmqv)cilGD;R{Qg{Gh7i3b6NwBc%NM zg+9f-VW7)n5Bm%dmBgKdyygR$ccYwlmB~aZXjETpjP+W~0Sd?gVpoMyCx!r53g~N_ zu%O_+Ib&b(y%va*Q|`Oc8404KN6=ZGT>!rT0Ip8xd{6)YTj)GNNYL)h>e62CyYyu; z85o+B5NnW$DxGH-|OqI?R^J)+AG2+(LnFx&w10 z=S}k^#O-FWsawNaJ*-xdKkyMOwfSG3E@!xT-l}&+XW_Dntn{TQ}5NkKrz!QYzhU;J_rQ*l(f-w}Rt| z#r8sg;+ZHxnaTm4IyRV<+Nx=tG9<(ND@_b45Q~9zBgbFY=p35L6u7-@YuHEj-i41C*ew`M}>b*-rhXmxw#d`*!LNr(&4fWc; zxszC+Ubn1>g~etiuwW5cvJO1b; zeKxT$NLs#eQBPwj!t`bmWrKptDh^#prgBnvBu@=ZDo?`^TeW3hw$t2d4ZlB=UMfEQjCxrl=&d?y!dZ0o zx%Uq$IPVQ@3(q&1U6%u8F{X9whbpI+Y`#q8BSqhCm3Z|5ouq60=N$ffa%tB<*$U_8 zxI#iyPc(3mVp1bIG1@0=zP~ZdpCDaB(rKVYWvcq^A1|4Yc7GE9u~}C;YMmkJ=uyc0 zt0`4q#;M$V8O zyLM1k|M9=yMq0b8?F`w~{FO{E;)Vg_vqX;^vmCQ5xpzi5te%M65scma-Tt`Tn#({j zG^pNhpAO>F=5O9DoaB7pW&sm$md^|fonCxgMe3zERIC=>VL;aL!otGlimT-DmVn%= zty5-~CQiO+Un)c*mHv`#h%dR;9$O(p%S+63UDGnYZ8#u?!Pbl&KVVqp>=oelbbp%} zL316=4yZ=YaejMyLnTKBZBpSMC0DiPeNqi}!MPEpvb~tS+?gH2q1g4=Ds9gBQe1|k zb}J?X@u+M-s;wxDn_altW={MRw5^m;iIMRiN1vEqEsAitL|!sLw-*WaoL9OB^4gbz z>*Q9I3vjQ%mB1Skv5> z8h9(41z$7Gqxf{$$S>YGFcdygiz2sQ_^df+E{vU%!Od`oG0se2Vwen;ExnTXu^7AxgP@qUkw^%-_r zUhd^}HVH(%y$$fu6tz|Vt1z2v7@6!yE|?n$KZ2sftun;l+f72qYbJ-LZy;i4-Rq#M zxdCNC{dzG~q3z~BKk3s@k;H&PlfM`M8xof`XyCj)aat4@6@2{~QV9)NpFVtAgW=}@+XFB(N z8Sp*5s4HhWSoKQK)-jJ}QP!67PI|}jk_gA}#sIx~)pJs)feO5BuwUX+ms6*cQ5P)2 zRj+uXGY)0$t7Tnfw&CV;zR!7wC|{a*X}agt;WO{pX=E_k?dVd?x!cbCR&e}= zY=6aNyr?smMQp_nVfbp@l+en_hnjp9eg?694Q7R)0j1gk=8AnfIYQqzI|U}%6c=wo zxDA8|=DxrrH>1ZwG|djGQwVusd8_S_q%cXKQp+^=jQwwWD~(=WvdFGfy6qVq$*&}t9YJAi~ z)04(3Z{K@8N;>;?%+cix4%=Yg$rRY*q8}NuL8XIia(k5a>*29)mZhhHBE|AF{`bT- z4j+R=HjSiPaDfo8c-cuE1Z0BUpYyQ(+GL_QFz|UVIH3?)`^AE0krZn`H*2z1%}JK_ zMcEq>uiKr}ZK~tbt*hw8j-fEJ0=1Q+mKTR*yvuI+al-XlWEt+~IE58@us6SxuuH1|IW@Ez%<(&o}1e6U#V{;+6-3rOl)xuZTc->g8I|e_Cr{X#p zyw&X<;&o&GsD=<>*|;)V&AYz)6Z!6cIy0(3SLsNde|OTf{zQN0RD|cpuQ+wZNE&Xm z!cVQ#v_!)uf;@jPPvvWriF(9DM&RG(Q1}@cq!?q@4QM4Z-X-b_A*Ieg1$d04)j)*` zzFxmV`tRDZZkLSEx%3kGv7aeD4z|*@&doMg6GZk#X#Z=z;3GAST_&3yeQXYm(<-d^ z_r7CH0YA$;uDz2(h4C^FejXGbq=L>u%u}%ixeav+`Hi%!6HS?>olG^W=p+*O zl&xRA)J?d>BK7x+!*?htx~8~MqDKvAtvjuO5o#^%BI+@@=g*?IWTy; zYj!haJTa$`)BJn;nX#d<$>%s@5E@7evU2d%*_vVgHROHgG`AQ$8P2&TVYVeCJDzjs zuG?T~jUueTjR+u1mBq%oA9)zbh!I*OCAsKkJEs@hyK{#85(GCXKfDIZYIb_*P%+t( zATR+IqDajU#uC;qN3KkrKylB5@6PDE=IS4Yh+a_n?WjjENp=nsN+DjdJGriyD*>G1 z849z`4;dTmPSzMZXFt-nbQjUz;>b8SI$!6<{0mZVM(+ehfX6Jest{PgD}XM%qQ8Sq z+7_J}+aYamK$od!*bB5cP&S0b?)RSr>9l1a1HUPe=Cq`Qe~vW*IXqmXz_(eDV?Lafgh~%E}UXLf-7t zAMft^fE6#)FwvI$9)3ZwSMseEgf@eSx^fbmY3z5GnE_QxWmo8LAz8vNGao6Rb?cf` z-kk*j8sc#kbFh<>*9+d_05Y3tVES)X4DAdL-tZ>=-WA;3<2~;8CNx2U0B~nXev5TTvnB;6tJAezWCn#XEGzhUEPTP!9yC3wq5}bh0OMJ zcZlhW@8#|dl5vz%f%#||7D>Yyip44>E8(^MQM=H_QQS{5q>|XaT^7j6=65J;m;;A& zL^c>n>ScFf=dW@zSZL~MK5OF2=EbV#n|K{9X)e?wx!V3Lwxo{mEd)*?#l6Xl?!tDX zUS4(rz%%_A^D~-^3ai`RVTVHUa~-~Bhv0~xS2h?jdVit=g>0)yG2))NP>((`_~RXW zkv@^ljcR>xH|-Tvw~PoM-xc9ZHW-h{V%Jx-7AFBhOk4jwl*3`l)`it=16_1*wvT0e zJ7@4EKg2<7_wM1*vL6HA3uO;bf3>`D;?E(zcV_8vxe7_DV{Ya4`rMRG7r@O-=p;OwwzHzm6bkPpRa z1*~y3Z9OEZlkHQ$qU`SHo7;}w>gM}n$&$!Ml(jMuDG1=>IdLUI+4q!axx7N*(89NM z^QdJ{K9=(H4yHh(!51wXM<)L$WMSLBo!B0icdTIR_Psv{Yq1{VL-Wg+m*fAB64HnY zCB&frr*GZ9QjN4Q64StzKwk1l{471Sg_SvcimPE-J%t$mw}jn$!?4=u=n|(u)V8+w z_HfzY>xifq-d2x%@?SpFtMh!2W^X1*SFE5|iAxpsOKIvQqtAr?NO}${EXFx^q{;ZG zNmniqzFrt|FvgxGigQm*musio zY){lyz&0kIJx*t;^AES1>&S_TSSU)8=aBW7Hz!VHk6d1JLJ_wGd_1}>h(>=;t z`>uw0vyaHcwQruWX_@8A#&;FoMrC(S>b@C{BVo@{(qJ4us!HPMaG#_jNLyyDzFN(l zcTUmtYr-Dw&!F(-QmRex%KfKa(SEgng=fql6J|`&)o;RA_PB-1El;n^ zm-_5A`u-1kViJYUtlbB-hX)uEL24-){G_^g)wCL%FBJ|oxF8bEx-ow#WyBzs)@0-x zS-d2D`RHyWIz(R+KDZrEd2E3Co~l@R8~oB09xy)AUj@m@R3F!Y#7fUYPzAK_aHzVb z0Qy*(!+6@;V?3-GF_y_Bfpf-8h4_SYu-O{@dmW!HE|gy`@TS60oJWV8L9>1dC*pFl z-S?OzA5FzuK;uF_u|eju`|k(P)&jDLIRtfB3_Cv%m!V2ry%I#(N4ZrEh9nF8QB?(o z&!W%Qh;=B7&?p14+}v{l6YSNJc4tV$rK*p*Z?$Zaiq{&T7dkHu^gJzc`}_YRK3bIn zB5&v0R`m*g2ytERRkYYJ)lUa~XB#qG*Cck?C)5O;Q|$8yRFy62+?>o-dh)bywCZZi zZZOAUZ%#$+Gmjxhr)UR}-*u|IFEaF8s;+U{`r!1X(9Ldb*U^&jbyrYCFd7 z<@2ihO^Uw!bFcnZU3i18!$pptuJk%rf;4xj+Hber$W7%@dCs_dEs|mpLKAMT63o@;;8YG z79cE{rSJ1d^BsSZMOY*yng?;M&RQV?5;4&?-7ZN_Vf0Kxs0nlAi=e z**>7Q1|QLWJHVwUvu~j9tluq!K{@aSmRw;Ns!7tb6++Ddj}MdO}Y4x zI5ob2?2a$6R4_z!0-1JT{eZQgf{i8j)9dkl#@ln1&Lz^yA}=(^Xp)emNA~W)vV1#@ zd3nsdZx-~$hjWvodfTG`7wFkhl(!lEtS?vC(XN3-cv$eWU+v}TWEPZgDQip0M2v<9 zRpB6Yx$LRE@Md;=;H@CwVY`!Y9tu!tLnpp_+-OnQH`idXvi51RI?i6Q_u%}ZRbou} zkHn1E%S>)F&3!AqcWpkl1)_h<{4n(R*?>I&{EVuPuD4h|MFmzNGQAmyH9(QiW=L`> zUsn^Xp{6{^D4NA?+|7Mbg4-Z6&O(vUm=PXK!qBb0HvtV-$p-E<(fLN>42zh2s>5Cs zAD2znmbVH<68ty+Ja{H5D=vM3AbMNKzd7F)0*+L&E>WyOVkB4wRN!0oOI5=H6S#E^ z=Fn7q2C+?ogU9XJ@cw?Xks;LgW_Pa8@8KVMTVhtV+h@N)8O(I@baIF4>(odZyRDYN zf3dqZt~rWUQ=yxu1K4{=|B5|P`Wz!QZKVuEqZj1gvnU6Q>ND25i80FDntBy9D&O7} zvQ3G{J`gy{nxgROHkvNziKCV8lvR9r81MEKw*4)@5Xzje?;zt=1JuA5ERUJ>Je3^$< z#_<-B!bc~HfVtvY;td5FX|X+dMET<Z5`MgPb+?aC3hoNUiU!b>QM(&+iQRkS<(Qoj3Ly5G{EPoze^nwY4ABHtdb_M7}y`;y_Kl#8zWby zweroRS{@r*D$hBWpWb5^nO}yuy10Cq@wDjLX-;ObD7Xnd+}}9;-h9N^u0Eufe61{;rCp54X+%60I27 zz5;g1W8vo&-yF=lYA8=&CMTR1vjh!qe4eKnVJJ z8{V48h=^H6$z5^{ylcx{^?Sw9b~Y$KXu$ua6-V4aeO1$ZwPdP(lx`@&vJj!uZCck_ zU7~3P8z0az%%}W6sF005_&-Xv9!DE?&>uxl-aGH#?+c60o7bU;&W1>fT+ggrV3off z2w);{*oSRc`%UhiVS0;80-ez!w=kr-B;5Y)iGYqB#Qnq3tWGh^Y7c$Hu>!vYBdx^! z6<8H}lw$t&4U{nV)+h&bn(1ZkY!HYeTnKd%7sSN^*bPS3!oszPmq0=eIid8*c7VIB zK#_ym?KhuPdPl&#lc=j%RejSe=~ zN|vn1#8$jyTW5it;QePr+@P=)No~>g_#JZy2dR7evBQWq`ej%zwFYa4m9=e}lDM#p`b~w1wH9`#BqUMRG(YKPaa0muavRbei@Vt+`iz z4d+Auhm%cok`SMx1(8K@`H^Ar4(5=?6Yg{GvTWBZEnGUAFDpe_V~paeO9AD^(-rKO z$hsiX<^!P`eAr{MA99ncUjZ67m=l|Uoamy@PE04}HW`r#Rjt&jyQm(@cMNMcN~VE! z+6EI4tEi9G_*wc>WKXIMcMOhWw=`fKm+}z z4aaiZLfV!*Tn?%P@tT1hQX4V+e=IQSEwF(G6Topyp z8i_lJGRkbUYsL>x;@30UBO~CrC8pvQlmEpcJjwP2f#kOOD}Edn4K*WFhF{%AlO2hbi3Z2!u$8(Vc+EnwCFG7}~R_t~fY3iuH`;98F%TomKxzwb^Eq zuX#P)`3~Iur{mx!*X#MF3sW;dww*Fn$0D})-0abgv&J`I2*yNet(7T$Ba$H2YJs?sQw)fE1=E_YW|rRc+D@!hd)3neCTW| zUT*ioWhWm&)Cogfb2B^MUo{g%`wnWAZdlJ#7PY6c=~9MNal6U#*wu%-h#`wpn&YNm zbxPA(hP8=i;iYx9*1C6kO?7Jv?fRXI%(jEPKotev$ax?R@rz6@8~ckFbk6T-Y7ayB zR%3-G^uF<4J{6OH6!i?k`r@nn?YG}=12O^wxK^0W++sw|VvqXX?(;Oo!J;r{mweFe zBl!19^cPF!q-NcYBF>qN77wcOpEI>8VQ&77zm8LkAv-h&IkQYe2T50Srco5~9m=Ha z1kKINl)d3UWlH5tZ5A={uM3XGC)};WboJ(CqR*LH^Vu7+p9>|umd`TeJMg&n#k3l`*EA-vOIcc`9eXX)Ezb=+F zCWFU5>7Ij2iW!sXb0GEPOcPIg8#2w2HjAnk1z+Z^bZuvb4A)lhqCe?o`XeH}-*?j1 z_b!R7V=!x%?X|5^e$r~HJ{@xnf$y$tm7DV&>YTU!KhoZUEedFB{~iX0E@d z3ZFm1

5j0=qMz^JsNISBzG=Mqm2R~s0JJr^W&ndI01{t^6 z;0<%&=)_#>n`S~9`efp61wWI{v}-fcm|%-;oO#AmJhrPgv!YGudzrRkY53KI7%;c05-%>0?DZzhVW$+8&hg z?n6=)O@j@OBXs}&bLROY=KF?H!^dB5V!$v_b%ZuFWsLUh+YG=xpDUd!9g>Bc#q};= z+lDU0(APa4@%%Af(>-Q;Px+P}KxEiFkHYor8j#wkipy zgkTRjN6)SMuY$i2V$XpRo()?D6Aifcxi(_VivrZE>7sfh#j$l`Zt#1aAI8*hnka*4oKYES7d-zE5SkHA z#&%sZ9Olf(LABh~h){;BP&_&V^oXZMRgAo@vfiX1?BP91jNi~YKE79}aAJYWY87Z> z+{7Hq|A7eaOTcIvkx~(7uNb)fxOseUX&Eyr)(p@5+=mV3r1pFkzloK%2KUZ8V-e7h zXCR~>pESMrxaG?~OZTZQ4_`NE22R+0EKJ%@jWT-`JfG|$fBeJ11U3DSuH*g5dXPjk~N z$@VVgY3lEOk&imTY8aWSByIfI+cGr}4lVkQP@pUNRIkV$G~6FcTygT>H4g)pNV<48 z>vRWS`()idK~~P2+A3`8n@Qqd#hIvo0$t$%K@2Nb{y~)U!9ky`bgBSdET{6)YAj7=Haa`JidL!gQc=@ZdFuKf#!BPg3E;lYo@{Jqv}aq z!1Ath)SNL=D%{novx#?xQSxq)m>#&3HJEiB;$L2D2M$X6ZEZ-rDBX6Jcw6-^&1Gq% zZZ+{SUAQlb?oAnR>lHvJfgqN6=)r*sZGATY}Db(W%-9oF2c4^{OYzy15CnCe+Dng2)cPUMre@oN2T0z6PZ$aPGBKG5 zr?f3rf5DNzj-Cws2<@Fymz~fbEn08uQECrG; ze5Th(Cdn}U3`x2wtial8@6_pRyOP+gvs%%wQhe+DfxJG$w}|c?)svk|)j#OSfh2|* z4&He+Hzs);i(v00kzxv7aLy(RMt>ARp)LCM#!_7L4?z3SIMr!=@MG^O19>K;7T@Db zhlXvSJmqH^>&_Jyj}Ap+eG>RraTZ6xW?vG?~i##x;Cg^%+3~+|pcpz==Snnx(nF*8xtY0{HTDlZL z9ccmL_Scs;xDc61D6v{Oz`prdil6IH3i{5@*qh`+nPvv7VMh1%dd7TT{ZRtz@kAqM z7xJPCW&?nm_d;0(muHZA6S>wkzTOr`34H$iue^}tPo^W;R+s6e&Ef!QHv0ITsSdBa z`<(vCo&*)+%6mE%6szj-cgrZnB0H{5-J_{qsFam2Ex5MnkLT(c%j04xahUBK7lKDJ z)Ep-OhD|057(dn}ld7y+HnS9Wf3^~eLpR+z2U5`Oj*Zz27jThOND;7o#w4{d>Z2Ru zP-9>a)oJgt%O)GxK|vO$)PX}Xl2$~as+P5>6jh(Ux#%VLxNgNsAadW3@e@mWyvXt* zE%V3N!@km08i7T`-BLq~QjDeE)5PtK;Ce;-86@$fRQs1OQ1fnCVe@O14EvcYRA^|HJkn_d-3N_)~;&* zL=<9n<7!ownxA|u<`Y|HB`QSvnO}?omR8wCT>ET063beYGo^g2S$l{iqSlp~jo#1J zt}aVxIjxsR0dWejj9eLuuGQME$Rqk2I*Aaj=F0E=Et}K4B{9CgqGWs859qF=zz=)S znZMH$ZfO}4N^25blKUiqdc`F#FQC1|7~`F*pozl!A3-7jTwzI<$h6WR{}}dnGt)1{ zzZuR=fNrKC)l!4;f@|m~J?h^MWVvxl>EBA|EHjMzuHRC`P%i>;VG}mUeFXv)MYxQX1TU*mvrFhrR(o zTSN`{ArbfT?`JpuKySj-pIH_$2gCtv_c832ZdUU~=QQUrg23Ui(wE1Sp-!x0bRxPp zX1l)xL~NGI9(+9po|>wARUR)Ma@msZu)_7{Vk=s?MRY7D_k1Pc&%QejY6?a+KA}c) zq-7x4h{{fyjH1UnmF6^ANoK8jsZ0;a!pE|H#N=}s@ORIvwd5~QTHz>M>?aN`7R6HW6s7@ z0wy);&bm25wNxKvuy7?dPwXePDK9Vb=$G<_b0sO=mRYB;tg8;}P`j77=aC$Lrg$Kc za@*Pp4#fJ;q0L_kYs3%zL(AfE)!qEs3j1XCbX4Q*@ktODCMF&LydmXC9uQr@qL zYHHXm#Gj2@rtD|-az6Bxy?CliFTB1Gmq3RGmXv$Ck)Jt2pG@@s6C6=cGP!T^y+0CX zX1%#Xxk2o}`@M<+7atf583UI%zkIpfJ@21V6|SH}F`>_y%nT#Y`A%n4_6ED^QHu{oc_bwaKI0?-f)| z?=fuSGw`z&*$i8H^9tS@U&h-R_i-VqJd9QFf_p-tGLRmv`}U+@Jb*rjkzCfC@acQM z6s2>r!bu~?vMr`G#XIxz)Q>kJP-jNN8{T#zs-wP!*1i8(oBAiA_K~YQwP+4YAH@mT z;Vu<8aS)*KW7M%F(5{B4-kyyxHrpi8`g88tF9%1-p(&Q}t%kO!odoc$ zvRL;>#)GL<0Ykfq$N|*gmYPX^C@rzYUd19HLBKh1H_>Ab3As%!S@T&ZKJyC*TW<5~ zXSgy58`9{76!LH~@ZywQuPhF!<}#7IxZnoSM{l@xrVv(VB{}u7H>!IhPXexxl4hli zD_38K7!fWTB!*bSbpl<2-lIvxfr(ON*d+VOOJgl2u`M zlY|`03zkK83fvA4jv;^h?J}oON`g8Ee>JyL0f^6L%#IReb}+6U*AT3rRNJpP8Ir=Y zWu!&7M zyl)_gEP3dy{aegc_kxOvIE;CXfJxXwrGk=|&4{r=M?T%swKKMtG!625H2FDCAzR=> zkR)2rvT?ybkyq5-U2Wb!xHlW~u(qf>>?Q6ngqyj?OIHW{xxZ+xo9?3kX&s0{+Yx4L7ZK^^>)@B^r*-~{Km}BTq1qy0}IO0%t-%fHL;n$I>g61Dc0-2 zGQ9-a7LM4ct;h%crpA zIsKgUyLIvN^#GIfL%`3|n%vL~iPfsyY5OH5$a-Ti2IfM=&teZR5#-PQmJZ6_k4^impb}XGT&-V*ff{+(n&e zEi@Lnib1}JPekz4SFqNKvlZ|<;d~8w zy5hGaaUUTdxS5@vn(^m07O{+1V*;e@Sh%eD&t$ioTLxppF@%jZ{Jeyn4fOoODMPgI z|B8)z{u7qK_R>hXpt|R<)FSJehr0UFwYh?fJ$gcX*2a$~rOwQJI@vNMzXJ0>o}-)e_tTEDSq?$Kmr4#x&%bx|gja)y%j4i&CCGLP{T@7W*PyGXKYu`d;N+Roc=k5$-AU(?op zeb+Uyue$oUfwbs7uZizORP7w*Z|UouSzIft z*ZBL6C&I5Hh6<>vUlBS`4(AyRX`{y%+y8)i`g!G*`I(k`=Cy+c;_*0gY+)L&{Q7g+ z2m0Q=dAt61xcXsvmtn0bd03D09}Ajq346Ydf&aq1y#M*J6y=QQ@8BrcE%4YTedpp6FVCm)QZF~PAM$~kTpEAmnr zGTZMwM-Y@-+KVFSQP19@gz7 z5xP0Ze~O(jX=F|9eOfm{ox0u`wEer1j(26vj2n~seTdbDX3PB!dr5iN=Y9_d^)6on zml)CITlT+H+J>nfPM2Im=P`AOwr}T`%d@Jhu*5urPW}XA%t0>1Sny6AZ@R0-7az0})**0dIeVH@#|3WHga7ujqpeoS zpF!AP62r$e9iV#UVb;AtDqn1|hVdfUU1<%pkq4HiEj;AfmRyHN`mYxry)1@KtX!^Bx{n*q<;MLU=QGg~vgjbazM6d>QXY2AB8$ z|60`R3MWVNXS#O&ygN1zeK<7{*4!K4z6Wy|2oEc@0k1%PtU zcb*A?1+0lgdQp4UN_++b6dyHQfy0v!;r%yr{hm>Cf_3yEc>pC%IuIyIlN?R)9tp>z zi)=_G{~9?yq9>r2WE1VTt+Z9!x1yZRq3r=FiMfG}#K4#v|lLnihq-Hjy^Oa7{j> zi(eOt2>6hnqNI>z#u4RP3Y%%BT=dFw@ysI}{#ZOFs=c(|PuY-C)kjtSsujHlazn^B zJ=R31WkC7%ow=nNwkO|ea=d_=uouIetB7Q}gKxTeLN)QpV+`45W(xiVURSO5IEF9Y zgpMdCz%>R}YDMC4I`OBm5rFC+R{$Owjq>wA=FlNVm?~=88uF zK#udx6qw9$?FsXJ3dTHlsdZ{F;v`#l+_U;Pk@c|pN)sR4yG-^f;Ne5-L?Qum3VRo1 zh!x1j{nWHI%{!x+oYT8F|8Vg7>9oivRu0aS1Id4qk2YX431N3`D+L#ktfz&3koE|( zrADO70rI5PK}3G|5k2iMgpsQ~#+-Hq^{c17;_1;Bc$_G85?L+RiK`Dz05Rnss(F6v zWz2wI^q*J_J5yYy@(w6Nt?*`k&bu({nuhYVzr}r)9G$n(O@qm&N0!(9dHLsIE@?cwFJ~qd`mi5 z2nPX<5i_tqCBsuX0uH3WjdbX#j1GOU`M=O;a%gqEn;qH(g8rY@IJzq3qAWKgPz;0m zn`obraQ}VB^#iX1Cvqbd4=U-T3+>Rvix~X;jL_w}HOHZF5@Ht7HA=mUM?V zAjf~miVlr}3h|MJ{w}oMtB=O*;xtP07pm|3<=;0So2LTbhwW>s-dTcZF8=QHC4y!@!=c=5m}tUZH)a-$5=#uC8WD!*U0mF z6(dla;70D+m|Fj)EX4(?%!d%axb^`|rr@l*NH6c33X5qAoz1BA#u>l3kx7uCi7^7! z>ZI3OF84lb3TTwt=yghl`Mq710j_&&beYw; zZ8RNMZh;#PaqFHyT2P26m%TNNe>Rc~p16}?+@5gV$wHOnB{O&R7YF5WOjMIbMnV%~ zfQSsxeD`-$JckYQ(NS4|YV*3re>g`MouVuh-8VpF1IY z!~+04l}gq75_btxi3}-sX@*puH#@uMN9-G1s=#_$CNcIV$T-xdj5Dc{zPntZoU2ho zuHyYZG~^&p$sI>w31=jdad#{`^1uE_a%qAhDfFhvz?-s}@1nZBd4wXLfC>3&So|@_ zao$Eve@ThoHBq?2*6>^!;5#YqEJ}Sj(u?;PoZV9W5>i#V^>GTW;yn8AYT{v8aXnGF z@ej1bNTV2%FRr+2?D}brl#W>lrho0R!!nQQg;0~pkD?(!7uOND9@o-uw}rQ#i#AYC zJUXnqJg@s6%JY<8deN36elf4)tyUKlO_L1w!6N;yVdOY+!owyY3iB|}C01>ThePYDP)z(c?1rPmi z^kPZB=+>8NB)Bpf!=)6Hab3#yVGY|;9vQrP?A zzk?*Fk!GHaIJ90|3jA@FpQ~9+2x+&-@ZoDFa_5xUeQXbsR7V<>jKFo~|44b3?0oGM zPxO0)ZvJ%YX}0diflWup$+6zqPr>byhxPL9&tYul<|}tsd|6KCl0C((@S0PpX3645 z&F0rnJBgi#4K-FZ)}n{hlI_>tqbF$&Eyc!=m`D6id!t;KqgxBr5Ospff-DN(wcCbn zhRQB$}v{~nC2 z{C4&{N}jAJc$#5lcM>eky$HoD@XckMQ*_KJgxTfIw*N2U3nK5}zO6*8Uv73$RiDRL zVS=jj_fNV~lq}n)VKc7zs^^pm!Ulue_pI;ZvUs(ga@ePy=AE^Hi{8R+%VQRaHD^0D z)hR}gN)xdM#c6O$Ub@h!R83!%Z!)9qqov@$iDLTs|D3D;H`jXh+rV4KdpgUFJH{Kq z9o&pQ%k#yS?H6|D#k}VLZ0+{Dgi?i#Dn*Z1kD%)m7PM2N8QquM9m51KRhmbP6~#ND zF>KjtGPMo;H93m(oHa;(8ZbWHWA>#S4<$wFWLzqnxX3Bd3AkTBY)vbY<0+iVO-4e2;}qSo^QJc9jq9yRKRK0pLq;|Jk3$~qD=BORlU zvDmkMp170kvI4&!>Bqj=-GPgubC-=!((zXfnT$Xu!?TQ!=rnwyp-=lhVS()t>$#GJ(N22;NdfMWls!|8{ers7!~wtz%GSn>x&eN zP4b(TJGo2hWWVe)b6Vyb7(H0rEw+6Vqd%t_OT@F*#(M-9ofM&}35#svn-Jtl$hZeg z?Wae0Ia)h%6h6i=5v)-DD*pkRtLsL0ycEqJUNJ7-Roj$4CSkED?MJL9vHZ6^v z%YB>&j!mW&r?sFElhTjC*jv#eKu?)^IH~h$?#Lm@h)H86It&8`{DY~ zVLK^%yz^5j_|8>DnxC+B_&1kmT*iD*S#fn0Zp3ZV>Ex?fl#J8TqwRDI>l&-*e5V5k z&Yg65Q8rhkfT4X%o~sMb0Txti>8*y$8xOy(f#hw~I}O0z1=4b&yKfKSC8>eqTr#LM z5;eLe8HkJwZ2V48Jd9mg`vfQ2)fJZBMY;H0RdyDIA6ZpSKVN?8yw7LL$G!Ze#q|Ck z&7+cr7d1d}V+5PU#CWPTp+6eapl8V)$SZUR+bVp<9E)bCI?x5t7FS>g;V!&_no=`< z5~2!HR}ZN-5((UkA-JERxd5N`sNaAj-j-j|Lz~~4Ef=;~^Q4h7C8GD$zXrDQ3ZZ`Y z-akL*w)@c;h7{xyjZoU%NycfUp5ebVI}c&Khpb(35buVTjojo3k#pUy(2y=2A{^fg$9vvKxb;!Ji4Sz^7v*{}08BhQS9x&# zh%e}?pUqd+y1setxbW7iNI2H>k^P)p0Huh9*k$tdB{qn?C;i zA+Qp5RTO30HyGV|mTq^Yeg75k@q!ifolNnsXH~~wz^3dI_7E500=Uj=$y)sb&hrrA z(uT0^uWnQ*r{`zH>wVfSM_7z^lY;#EujBV7gwJeOx8`qmwtv%K z@DsM>@N|6mnc^yVm3=uZk7u8NO{W(V%20lhB`oA8(gwh{CJEcI-o+RhZS71=r+0lF zeK%Qk++Z9qu~(d>|6t|U*rA|)UCRJjShF`a=W1#T&er#a;XOh}ms(fR6(n-}svna& zV3mut3{;Ff?gjmj47J&n-O`-Us1M6He$LBtXCGQD^Ko6L;-5a6gg|;?Y$z_pF8@@{G{*8RndvX>(Pp3FKE?2kSAc?-ST1=`TgW7JY0i%+t)@y{2I%5ztPWa`83O< z%5DIB*#6rD4(ToHuzlrb;+LNJ%fg|Vh?FapDwB@SjmUN(bD9;hqq?)?Yt6%IJByj{&ug5e~Ur*eYbfSJOktLx!d44ec^}oCknkR1r|k- zgE&$EET3zlHe$qM>{BV7cSFcXxDTz6)fhC^<_d^-)W=sUe#3~`VSwPa@pDrG_x+V3 zK2i!hhqyoP5%T{;I)@gXv=?TW8aEc5Dl=T(;kFE%{d4}_BUqDpKH};&{tpjDKc-yH zO0y``gi58_;HtUFz5WQpaGv-+T+WtMyKQGs7tmVozn67@ziKP>go*1p z@9+&6Nd$iW9REz$>y~vyIa~Q;_<_*-`Pi)Q)zq2APjQZO#dE?7g^ERtr>}QEk1N;$ zrIsoVWAZ)zwfMFqWjfJSxGXGQ^Nw!ckKZHGBJsugeHH9%#N~@YDcAUK!bH6iS1){f zJ~SxEp8Vb|GV??E15-%lNDEyi{LO1#(0VSM32S*W{2|46Bdpra+d9Mi1YxIIiRbkv zVfHYGB&Rb>*+rE%Y)6q}Bdk0_^4V|h_O8`0!fYOK21kTfM(j_p7BYJ`bbkNx)AoBO zr{_j@%^~H)*D+{QN#PfzQJn~e4O>GXLvGWA7+HJI&d>}B`|RKMR^-Pcwi9a|D%H}v zKBgSc|5=3oZ>|n)SULHr<# zj7MY^W!io8uBN7)UnG|)<6+!8jcePal{Fh93I_$ffp!JMp^GP;N-pUGE}VIwOa@bH zpe#_abWX6BP%f|4Y}b+osqHKMFCi{%tjr6IBO|`3aB!}%f*`x_ys%+KTRw-dwS^(|75HxkuW0gQX{LE6n}t^3BkATE9B1v`e2Bo3i7IwOeF@#5 zi26c*LFy@LOkkNgOVjR>xr%MN*`d$>#!J``o*wzIughB17O75)?#_!GbGmA&w$&Z< zMk~Kt?)`IDc-riNf@fPuhdA+r^&`!8PoTPB!Wep*Uo4)+pZC>OuTR0;wG%eUp zW~XVSPW{S@Ze6U?4m@%`_S}YZ;X)JFWM3*i75+vRkLZIB)_d3Ek^`I3oy3VknRfsa z?f`pY)`;7k7?A{{K>q0pSO6Pw|enH`A`x~w&?Z38`#WSkkJcflywmA3K zku)up-#pY;l#QAPMgv)=U5DM%gfzmCg3w(@zgG#DW~0`Lhb83~W5JO1d{G6mv?oMA zJq;*zVt~B;NWVsQmG_m;s03JnB)KtH6DNPR{H&My7RR2`>t?uYZ+AhWsZnG&dk2T3 zF)r9?S-AbgIigvY0O~p1%%sj|crS@}E80erNf+80+?5yWp$#bBA~HW7EA7rVmy7H2 znJ8 zyGrf?_3<1L(sLZ`_kk8~g5<7{sRo>g(H&t(m^&foqzRr2ze?jHbBgr$DPr7zV;6JQv^a2IAnt&X=?g+untrRCC%GK#cOxjxCGq1I5cNw zv(c_Ur~L70MHOs_or3!n7f5Mn@BciNGukGXRLwTH(q8W5wT6lM7jNlX*E5u7B&|*( zSidYyPreXc?36Q_-|_XcK)zRoOxesLuJ}Rd9w;?-0q>jjEf~3}#j<#&Uk@_)J9`#y@Lppnyb`_C2lxs>03lCb z2)*G!_D0pQSbA3Kas*$(_ebdUHxi&MRn{GaG%eH5E-n&t9PUwofKgfUi1VMeB`3j~zKFiCxG=+z}9(lU)68*K! znX$+DB{N@#?0g!gK9564cTs#e%U#pK5@p3BRqEKKX7JcnTAdQ(cZr;!lZtgc*_xuO z=C5AGZ1`UZz>5Z;26Zfb_b`LwP*zOGHHPx^UA2LxpQiI{u=i{RTm~awVRmpbg)ugr z&)?eWIAU&Vxz}qN9E>97=ODLq8F`@ALDoF?y}NE@qQPz_**RfdV@Bu6y6G&b6+TQo z=o<5k*PQHP<~LZB=kTr9_a}m$Us`@GQ(P~0uJGJpcmQ2@^1j_C_HL+8%IU+}7R(cN zdu>V=nR>gmeDF}-VS4s^0j|HxeCb(B38zJBw)TgduQK&y`zG!Wh?(!<80qWkyHCdg zL+rcMfO&^(EaENgS9u*oY^$O>W2qMvrF97bp0Wn4O@0ni)Mm<3TA!Z^9(8Ljjz%IO zDGu$Edcp6;4Sz*ITYMBSkp~$7L^}0aG2<2RPsch{$DPG~VB>y}Y7yu3zr5m`v19ma z-^dc-hcH#6gLj~*Nf=n=*$4>w;SqC%5KPYgd~!c5JRL54?nPn+xzhQErFfsUS@?QP zs2OV@pXn+@was6uICO7Sr7c_QxH>zrgPZVE`_Q)F$7xA>#uEc&}qQmGNeReFOdxYQP9$ka8MjPf0?^}@07xUO4xY64b zi>`5@3QtRHK)-8T5-z}XABxSbmnW$iaZcQ^a_!Cl!ubfnOuF2Nbgq-&s&0Xx9?}v4 zb3%0qRPrqWu>ffRV@$u#z7rV=?=~SViWT4lIUD&^Ex9OFyp1AyTri)xuexqCrN5VQ zhTV1;0p6AmC8ac$#_M7d)^23!lv5j^eXH)=6=bI(z<5F_}W1pV$+ zN|1oEXq%a07)|u`RT|4i*Bv| z06ucVxgM-g-dM`s`n#$@WwUP8(%^7WF@w%^H;tQktTaDns>GDqC0SYMGP}=1uv2BB zJPIcb(Zl^vlM~qq;x0-EZag7|OBJRg&AdGt?IjDAcBzA^b+rgQjIQ361pK=_oB( zKL1or=mPoWq||d|R4db@#pY7wux;?RyGqFR1yu2;+;cHIh%KTXrun-z56Vy3S$2ir zs(!}5Pnx!xu+ONI+z>K_WWq9fJbZu0;-JXx?I@wo&ggGyJ@i@vL#z|9NI%M#504a? z0&QBS1rKS3823<_^{x-7mT~a8^J7U?R3LoK>1?f^dOmU1}RO>Fx~mttaKwv@kMLF=R>|ErB#Z-Kg6@TQ_@` zvq_C=S)q37wc>M&WP(>_@i`!F3T6BwF>%X{JAO_uj(kEE-2H6R%2=Hn)6$8Y#$Sm7@AgT%>B3gQzO(TNx`1-Pt$U2rCF z&)F}e&tAgMS1=a79&~!UxErr@j{JJ_1Mk77Iy>HQp9wt*Qw^pkzmoVT@n?zCyKbWN*2^^FoczSDIQdEg-xkTpnGwFLn5hvB zlbv#~w*EIiif4hE+^_Lh)=;9$?i<>4{pwqVllBIB*X2ks<#q|OW-O|2PMel=Z6%tf`%$>K z;}*0#&@wO_@v~POqj=+-0EmI!CuG|Jl=L}W3XO0(xGC(cSDq3+8_n8yxxA}zL&D+y zPvFdZN=^ zc(eXub5j$0Ypi#+zVfnMM!nz%NIV9bS+xw5pVd7YNmv>^5h{fc;PodzK63#s?gM&` z4#o>~JeH+Ui=NLKFgkCMCvRKOGSXM72hs08S#YGES!6q@o(=rYxw2V$T4%uyYr&%N zo>pUu$yAr>kL4R#0cF=3r*}XU>4A?~YGl<)sOHl`8bM4y4mqvMsa#R!A}mxB@I}`W z{$^T@;>miRWjH=g2{p!|SM2=HZ?6AO)YSESthCKPwRx{gs95tgEq+z+${2?QsXPn_ zPg7!)`FeZ^Ki_ZNmZ(W!HtN>c*3DNDKj0_*w1YqqNt8NvXf)T|=6>~F+iW74-Nr66 zdzzyEqtukZs)f9Bxz}0NKkA-`Lm;19yJgFt8t&C zZ`j_L_#3?IDb8ml>%Wo){Li@1hg&26f-V(5+1XD=x(@us6ph+_?eq`W{PT`GwqNnP zt+F;6Pm9#=tOM*-y345fqib^!Jpn}-uzuIDnZI^}E!%XE5y$f6p5|V;D?Ew>QijsJ zY4{M6GgdgRApiem0aU?%<0@s;eP-LXNnhn;Hh!cFKQQm`FSLX;j5m_D_POD`sq@3A zL>HIX@q=7uq(#3dp@v77)z85kPosY?|A^|Mz!4H$-rEhiJqZ1Z`iicdN{Zqam*P`Z ztnpJvJ!-w_V{AlMj70g>$i)K1g*exvKu5}EeOmi*Pls&fMM7OvXt8HL-oTiOE5Cn3 zYVX6CN$G9$kwuQ*Zp5|(-5ag=ujbVAG2lxLR|Z(pBi8{8`an7iQyE3(93Fg@A*%ET zI|6Y~5inY@c{#yC)L}WDo9j=m%0jd5z>Ln@k53O*Dibr}yR=UyA9=i=(JhR;5T5gv zPyfOzN^Y3ZzfaUsDv{j*4>9xAdoLtf(_};`g&wia{!SRUWyp6-slM1k2H$EMYlAiE zjMU;=jdyDyR~kV%gAYuG|CJyAZ-ylaFPdWH&5lFknHm2foUf--Z%x2F1@W2ka!t1l z?VU&>l(Lu4(LO|h(EMhTnJ9BZs|jsV4ZKVTOFiA054~VKRrKBLT zr;Uqr!T+6-&|>)t($XNzSB}H?xo&LNW=^Lu)!VdIwG6FSazyz`gv{1SX@1dW%G=++ zmr7=ej=k2gkqo=_(|t<*H~bGX3AQV=5?N}+BfV-z`M7dx$@1pVKxflCt)zK|^)j@6 zy&_=CF@K(OPW(dFd6-(6_|}!;YpKg8HNw;6i5!>szhZ*7$vbcSo)g~dXU`21k_;)B|@qV2}Gk%qAZ{(@8U935P1lBwWB0jfv?+o;-I(J1R*(*rQk za$Nq1rpo+z={~YP##A555~Etcf#m9EU!O#Q~w zQFZc_!0g}J?3W3o(2Wh41na!IRSYc0d=y>V-PBB#{ZT8_{E$0oFCBeD+N-s!PX1kh zv)ZEj_-PA2r90d7TEuvG3WF+|f6rlJ%?b>RyzcJB_wdPr7DeWYz0N4U2({dv`JlM` zg)&!7thH&{sy^cgR!`Vj5?^v;upSbfXWA+)Jty`8Of5k3_4RbaLm5EM_w~j*`a#rI zOBiSJVLlR+BN`jP;Ua~Fj`|9FmlV?^m7C{hJ5zx3MM|mpPeG|5Q#2xX5;_@{pZUP; z@M<`DecATvL&Mz9R6-Tf1sECUT?YEJ zv&3WQnGJ7=Ie3N?7nQM*22!$mD_u!%s*cKV-i+AKx2WwP;r<}1G&xLU9^g(h(QV(x_73IztNjS}tg8yP+|qhHOOi}#I-^R;dktdmG@-TMR{ zdg#Pbw>*rbKYfozGFo7wxApE9G&eA+DMoD_tgxNBVz|e!nP3jPZwsibo|C|mRL}?# zh`4i2B&=3MzRQjF74-BojX!vq9~l`VJvPJZo>f=vThLZyAzQMYlT?p@n6Gq0A1fGa z?|(xE>IcgX?J+EDjSrjGMD}_jk8ni$t454k9Ev4x>pSbRb^8sFOEzt?BUR(}%5uDH zZe@c;a%B~m4{&?b?g_8uC7F@}LTtj1J#ddm=$ku_k>rFkmBZV;KZ&jakV%ueMOSC$ z-RB5bz%+r=ez1?(dZ7D4dZ&(4LYzmWfClNWFKbgEcV}p8d=>7sN3EgRRK2LK0POp! zqqK>Wg9X!9kqpny{nDCEF4&u)F!AD&E)$`Xyl62G)rJjZ3p?w6oj&S1d?~`LBa^Jw zqiet6ZG6l&F)L_Dd8w+kq-}0h2URH>PRDDB-w!#o=d^AtDi#7D+3}*cFS3_x)VCf+ zd0(1>xV%Go<$vC;$vz`ldZIVf`eo`8@s;c~#&9Xe&%&liyU)@n3g@Z(P<^&Zrbndt$Vk`<)f z>F3E0hB&qMK%$i^!Yb^53bF}PwfMEWVsuuJlU+7{`cFoG+pAb3*@!XAW;k;pznE` z4D=l%8s(w&hh*l%RpH2{J0_(pKq`+p%iK^})PA7lfMEHg)i2L4 zo)cGt%o2+CrO&gP=I~mmG*kDpTin^sXD;`5{XuUuiVEWK60ViMz)0-QDB3{63G)HnF+?ao%>{aK5 zj0cxn41>Tybm^zG{0IF?FOCY+q9X|#T>^g>ww4Vg4cfW0^E#4gQZIO$s1-%ch;L7t zcZvA{HR;y$)NPilQ1{@JH`B-ikDTIE2!q^ z*oD$d^W9jLh!W5sn43$>N}^A%)1Z9ZfHlVT{toeYTTbEHE##}b5$2Y7U^hRJ%#9Kc z5&KcGt%S7=#+-UvVl8ZX#WqjPA=G3bb%QsLP?_JoLYtrRWv$}!vedjgdq)Tfm>5g} z@0@zP*3yEqlg!CfCPNF@&+~mPh5pdovE6vk`lLOmPrZI5OY9`;xdo|6>-`BDPYHAh zK$u(qqY#E&+Z^7}D5T`JLlmDkE;IdjtbCKyfD{=;nsh_ZTsHPY0S<=N4qkfj>MOGX zQ@ECX0K3OI){9ob&xBf~|Fo#+bPu>0Yo1X-xqCBOkgZw1R`qaFpn@H6xJZuUJy{a+ zJGPqb_xGN;8S%%pLD&Xr{ynF)7u*I5cmO7cMeY9D9pxbqz2zhu?2%JqSdqIihgfIy zNlm4zSQUvtmW_*!+^$CBaIVe!AX^_#?kfqN7@8}OjIm$r1pixI^9dp`;92we#{U#{ zr*P8|QFA+f_Ao5PT{F=f4GSG)2_r!E5J;VRD;0K2`BBRApI+Cps24#n0FMZo&EwrU zAWh{QeGl~m8a>`!8&~+PV;gzB&M6OV3=1?6v_4zR9&Ha0xt&Cp*2L3BHSlAJBp#T3 zg>L&%vj=-^1Pqk_D9VP%cnJ^$M0y6bG+Z_o)AsjBxb;+L))N8yy<%o$W$!8Xq+FT{ z`tI8M<;{|)o(4$*h%Bj!H&V9y0#wGlyn1rpPb6I65yXM=i39QINt~Brn$~ae0s>hl z20hhj)Y4jIjhl;J6EaMUm8^S5tqM*koK*Dw^^9}ZVBzJ77d{51@KrAm)q!e4Nx)72 zt##di69V$50>u?r{oFL)?{hcs_wq0*Nnx~Noy8boiyBHmV!5Vo+{2EB|G006`W2ln zc!Ety3BO5dq0bOk$ETk*R$pIQ(pJoYV5<7wVZNNu zavi!od_qT^_;u}$#ciLG0T-G86d!=YC?8FR)~VZ0-?PH2*xN}=e~13Q0l13+*kx`?EJ3QXD!x7Dke7i^+!{`*645?O`#|;6hr$1siK= z!M-_9IuXV_8$`mJ(}5bOJfZ@;mrh6dAMrZRX-rf=TGw2dtAfOq^2caI!{EwvY#rTG z#<=`W7fS_CR7C-uq2f&8xiYlSg^pbhU2g2R_1s89=&o-&r!kqWAr*TbYdR-B-V7*~ z`WPbN{=^J~{z;MY-AE$i?)*l#%qzya78GZZl_InkE^bBvh*jt8vvdzb2bHCV&hUH8nLmt~s z+yFP~d1%3-=krLPc5LZ3l9rZ0)Tbw;(?%hOWi|*!vhnomuci>s(;j+zb05NSPgN>5 z+v%b4U0LWi$UmQrP+kfnXaG^<1OJ9Iy8{ zv`hqiMP0$g3#32`Si;y#Oy)K4d21L|SRO~xoFO_X_!WPqUA*BrIL#J>$)@a&>_+#EuBM$C=DZ^ z0!m8f(A_CYcMc#WN=XdO5K4EAcPBk*lA^{(91u0{-P?KkKu~AgCs_tj4xyIkB&-URV*xm7=B) zGCRi0sdf7|GV>QSox^8z_xAapOzcectjVl5V+LQ{<>y@dgbw7~DI2f0y&D$`?C*Hh zPS6SGt3a%0j~&)N2K{-Rj`(j?62DjuYAg{daL8kTz)K96!QuU&Im`&yg$QL^r)m^n zDZy33dGPH8JWYY+wnQtX@CD*ENpooD$Uei}4~2`?XuA31el--y0H)zdr$Ec@g%3#r z${p0&@q-D_}-=MO~P73LP@ZfoZ1pSl7?U@fOQRE zLeKLU5hRvgq&ZprBRsZjt;J(JMcRb&SCgZA=1AU(@%wA3vRp~I=;6$2V^Gj=$_u>w z1Ei$>_J07c=_?F96|{&HkT!+zV~XzCg8nm)%Htglm7u=)EV0PFS?7}%ncoAsh66Y= zsW)!AWn9ffO3FB&@bCT9S6I202A|$K%-&a-&mff^sQ}9=bb|~gC*t;KI1T40hn+D9 z$#U&HU^H<(ZQt^407q+TOvY78N+8K*xOEaV;xpRKBC%1%WeRt=31hC5$z7YgvHZ85sT_ffuvj|l4a zF6EqC=kCCw-B}8WX1C(N`?>6@l}PZtbk)uC+0kj9-pD>HxMF6~6gR(QNiKupi{S@+ z0|C515`}yRLF4&199$eTHb%LxtP0rmpB=L&byN%74l^~w?>>KUUb$=thMYoaWjAu6M0dp+_L+$!151gcT6lM* zQthYS0yC{GY`q)^DCV!6G>6!syt zCC%BVx!&{w=S-vNy-nzB-0JwpHW->Qz#<3qU&**lJYOrjj>h&zb0iex4HGGMYgE{l zUgbUf@3tk$oe%X`z4A>KnbW}qV-K*g#$Ykrt>Dyz;Mp+@e*9czfbtEAPp}ZY`?Uh5 zGh}(TGY}IR%oFlqEr94=EQFl&E6X{Nf(H?asMMpM8t?RyDR=m4ZI=w8yiMEUt0vDJ zwPXLUzzPFt-6Xn?N4OSWm=0>9WiL9C+x=RoM0lZ^=R*h*Js7gH1UMcXV3VIc@s68Z z+p6zi_3RNPwBU~w|41ko0>rF$5ufaU?Gv&9AH-$m0974m54ic zw{PyfI-)EXJr{YaU`_kwwTZ$G?e{))B|}}PWjY1D?@!e(dWz{)kX+cLX%jZ@qT8^^ zUI=egSCO!ypqsAZZVwM%_|3RGF{+Ou$ z_syM7B`7N)>qSw`4LgOdw0CKTi$yurcTl``514yb@A76jNBF%fhG@r{js9V}h89b) zPJy52auN?HnRBdbfR_-7Zs%av%U80Q)MY8z_lt*R4HUBRvnErS+|8L*5*N;L9*_Pk z4aH!?V6<`W-) zrg&F{&!3f71v_e(YN0;qS{OuBm_bGIpdgCG&v(H-&A_HCwb`k0vHS?zE>wE?&l1js znVJRPB%{UkW4!g@mLrurPA;KZW~~x|wfh@E5~Rmk1JV2_cG)CqwV4_B`Bt_CwQs3_ zYRnCG1Th`(iLSol*YuC^6rMu^YoA!1KAL@I+_+8d_~Ee7aHlFp@gCpxbmodp>%HDw z*oQMVY1){#cpy=JB=fTB-lvp{h)b99;WiEZj|>F58>0#hB(6P(lTNoZZ^dZ9p5s*g zj}tqZst@r1-ReU=U7GRD`i782SjV)NCvVn+vcm4rz62Qq^Qs8*>*b~5J~3k=Bg$t{ zja7l$`6WLbf|;DOoA0@5x78P>#@An4O0>7$juDoldBR;sQ3d9S)2<0F|r{(dEouxgia#9>>1a|meHmTjHKWzOW6EpZ${6Xq&~@qjah3abU#>8bf?+mB zDt3Vs|2LHMK8pnUI)>khhC8KY(mI`nna5fC=pEF6>e@R6qw|~dN7u7d&u#9ukbG|3 z-sjWi{S@i{on`TE!{kqhrBGSp!@r; z6kaC!`|Z@v6$ZnJ;lBwa{Tt~*-fJ;2^?K*EZtNskXpZBBe&K}aV~^-zHY}#mG*!v# z3W&DRV(XjP{4mc?jr&vZbvMF!#y4+*v;!OrOU-PyFjPKCF*AFFm?w;l(Oix-jln7Q z0|)IK)|uS&Qi0f#7u=kv^8TVgby>yhqnGaRgvn7@uPj8V(Ny~IdMT|#hEX}5Kd)Uc2@h|tj-4h?xr@Omtcsz^68?cqD ztum@Xt2JDYetL=-hoD2>uQE3&L^2)ijJ9LAu@4SMP=rShA!O%iC!g6J&Sf@{+`r1F zyd&UFb^S7x^#{ECX<23ukH{SEZ_;^nW+`X$N=O-1hAexMTDZu(mUQJbw#TJC~8 z<)C-&-+7(v-a{$1kt#iqhl@wne{O=TCFfPs&k=^3Eykr&mj|MZlttk5V<1|t`|MN4 z&kB^z>zKdY0|_!=9qaTjW+Xv*EpG48T|)5z(k2W)oEgUt?~CZ1Vn?tRKdk39|6)Qi z@tX)ouN^8sj}H!8J+wZv=(500WP#!pS2t3s|q)IxHLf(Ps`zGzo!na=;9tQdU8kYvY+^bdB z>JkrcCGD&?T3u+(^K}N~SI;r!9vV$}@{4S|>&QA`5K$sO62d;Txv8sIcR3c)Bk2ao zb3)&t+)w|Yea_t*&boT=athT*9^lzy6`sahZnu}W6;^SO#*kiSif-ax)t_b~f0Z^8wUn|{ zqTXUC&!G>`kPL+6U_=_ISNWu5qKh`qMR#32oCK9V+O_;`a}`UNHGlRrL%I$a0v zju91i;)u%&JRoS2DUAHs;psH$4A1|5xphV$~fmVOqfBjeI&4txPJm*0kEkt z>~5D2xN*enC9A$6AlSMxgP%PkfJkvoa;dU+XR2w}F;~bp%Q+Kd<0eAHS??a)^KFPi z-V|WEXtr(MZZnr&R)ML_1o*t(%df=Rllp~JG$Pz?k_BpfMNExZ5WSKaf)M1$ttw+% zUw`$9i*=%3;RE@2N5ASe#fJC^7IEmz)-la-B zVde!mvBDeZudZ#U_d^8CXVlD6`5w#DJ^xZ_u>_qtBkf<05vQmje-S~m_C-han0<~e`A5~k!yr$?>YwULVUD^ zL^MsbUV3e{tNP+&e=g7DXGDsoXHW!Tye>i?RYus$z@`FILpd4tx~tsU3hzs-ver`s z$X|X>hXRD`2!AC1Ko6%wYj#qrp{%Rva#=o^)^cl3l&nP^Wn3p^2?ND4E^l$*Wp0%f zV^;f<3OvjtOTe$W3WIbDS>oy_Y5)WhJ;Gg=y6X222e406HePBF+TE{F<1$!6eX5T7 zJ{T>0D5V^(AM0YVt^Oj;q9;92FQiUEYd)_H_PmPa;oJK?70|5E5)J=j=QxAsJB}lk zKd%$36<*!mB%L3u0`QVM^25xujxMDOMjGj6p=Uhmn(f%r?@8$73owuti{@Ccig((j z&mMRJQ6tpEdS(I}x z+(Fwh5xCyjT0n32+Q7tI_|2CF+(T3VA)w=#cndpFU@J4=Z|R?#bHy88~m6c$8gTOSiTQtF)HR3(sM;H z$&&9@Izn7ul227tZmL6z+f{yrJ*v?MuSqZv2+bEj9ZaH%tJ@Z;D+$2ewZ>I58W#@E zDq69vl>&z|z#~@3F61Vl6U{9^9z{j&fBjkB!5eeG=t5kSAly^$Ea;@RU zYh9nHib&dcT#&8(z5BeL^-<3nEdVFS>wyrV(DC4q@v1!Km;$WUdwXXxL9Kr9enX?8r!ThhBi`W`z^ zUiNsT|8h-FVLLXk`%!DCk8R=arTz73efj=;9IMBY%!}r4hfX~6f>u+FC&pWsi&v#Z z%kZN8tSHWQ;TptY=(e+&*BAps_&U}^{v`|K${KA@QP>8@Mz^vo3}233uf16a*t>1dh1v|32Ce$kn|g$xMnu);g=*PBfn7hp zqMnjarrrX(OF_40%Io0MR~f_&?Puvs)Q*!d;EFK6OPi*l*9xU5NL_p2S3=NfYS7z6XK5q|Af z2CCJm$C3DiTFKkiCcWqW#poAP_(wrm4Z{?gEmdXQH&YE|mBGv9V!p=u&7!-)Q2>zN zYWnFQ+QK-y_{7awI4F3;%_E6RM^HskOW(HL1cibE^{Mm@iWal8_acxHm~RTtPdZym|Wjo5R zmE&SS^43+n9HMieu=3fD&Zu3tXGmi|NsFs6CHFkw8JWc)PT0qUH=;hJb0~r5&c3xp z!qJw--F^`sK}b9Wsx}jVAHop(b4$#ThcBD375tq&(h+%AcdK!*tl&f24#Or|7*X3e zQS~;cPu}$1>s^TId*bUmn5dSv(j}CAeBav(aoRIcHCSH7d(4_=QvT@)oikD4ey-hc zx+aNdu6yH~XIK=zk}6y_Ri~$67?S;YXCqH&vS(zdu*FQ?r_85Yf0-8+HB=8YWjw-H-(ex;5wI9jyP7KE~ji?p8zUQ1%U zxct=xaJ&NFW8qDABFYFlT(a{xTibosf>-OsLYZjyqTx{sKqlLj*K5*40s=?$aGNC| z^>IO#UQiBuXO?lmyqk~RRh7P3_)5BT+$45EaBE*pJM#_b1=X9f)%+)7om95`q|5=h z#jguq#9axsQHNj?NtGt0;n*EMl`3iaxhU$(ZsI*?6yD{~Nh)+w;IeVy2I#~97{jm* zf4yD~Xikx~`t$z8VY#0SQO48jk_>Obj)3W@Vt+8>ay!LmRym8=LC4I{2E|-ayE!S4 zH$1mq$v&TMXnUgVvzKNwS=slW(JfdT!(J!)%xl9cer(72=dokqZZg++cJpu5^e2p2^mf%NTR$C=KQ5$|^^l&vMdCqy~3;24=C|CyvYm)z$Kwys~yvX2V%P)hIf z054@ku!4nE198Qs{4S+31h=wj>L6(~?xpVJz7+tu)&scf6b!py7UBh5?zW%og2m`ju5`?+K{t~NdV`_B4VY38uoDkmM^@mSGAAd^D z;t%=mh&uGL;SW4|QZ8j{6gKslN6m?RSF+HvXsFw}ulbvb#ng&$D=ZOP%j?e*TQO4x zm~47D0F;avFHVxKhObY5t}jK0#jo;E?{()7X42{mVLER*o_6sGHVum+!k|3V1BYg6 zaGX|9BUaA~%c~n}A6-uI1dd-Cd&==QY(+ZH$INrT3$>Zoji+w$xN+4KITSiGhtS)q zl-pJuaH|TyjE1wTS6W1Trr}NP7Fw8z0-SelD7C&QVD(ygxNKE!Zzx;@j0B8gT73D-MS>H!w7FyC11Z%dtS_PUxahgDRb zMxr-(^1kkqjzi~*9cBQkKoWU*43qO;r%cpXxB;V?I7X~TnWbExGB@oWK57DDkHQ#w zx!O}sJQ=`YJhGs6PBN6uQUzg}lV$0ukP4Z+EMpZT=9y>vdl#+Q^6_FSW=^6)$uydR z%?Xd)IKZu80;VYCuzz3qB*vud+?>Qte@fJ_z}Re;7>`rba(^jKg}M93GlTF2SbA22 zFq)x&wPGIjF>i&LGe%GBn;EmFbbp?ddeFfiqm`T$3k7wovT98RJNffZ-jEOQ)^QBB zpR)g;#tsA_7s>MAKYsx2X9TSv-o$~ERCixNSvI)MbHZ@7oU;-;zDe&Ci0}7@CRuhp z!&k7N@QFsO_gh#V6ng%Hgrx5}j)p{*&kS!7iN-{8TctR_KRkLKHMIp#=5yR6UM&n? zD{E4vCRQv&{}b8vUZ4T{rF|C1k<;b4G09Wwt7F2&;*ix%A`^zSSQ`$e^PPOt?ze|;D6xNH!Mrz>Zg-GP}IFn#|EvOJY zEfb^uF?+mwLzdNM#9-QMw^R@9joI9WD?(EBoXA)z|rcpwTZyvtoB&s>za7B7)nU&9~nP6MJW@n1}%1N_9BhYgHvSXGq zbf_=XcZIsW)VW81Iqw4lG$om?CC_vnIklTdvQ#{llYTHCBV0!o7MQbeZ|l53slBR3 zL;K{b@nG^drpgyeGM_#LG_N>XLj(wpMdvu=M<(ZcY1 zx=tX6bZ801G3a-OSvjkAGfKx~d`<27#V0D3%YW@oNm82$NPp0s7U}J_COh=AUIn|m zx^{jRURTT7HM}LWxL6RcjR?(zLq1%|#kA!0o zZ{{>`k(EC%s)Lq;0_?+{g|1<9as6mRYxfU?HMd-Eyeob!o{*t-Iwhut5HNJ83Htjb zlDd0If{%dc6OZ>nKlyvgv=UN|%JFlu1a?OxCyk>}224KtKp5%wf&9vb{iu0tK7nYY zhVZ(267ALeAt**dwf9fI_h~^eqfB^^aKpc(fIg+6A)oNlHFk>}v%Xr3KOwQcXBifM z4sYU}?&Wqan_7q>&E-<4J{^Dunn0rMy!!H~`)DolrGg-dV}|EBwr$fNF?LVAm9}%i z5u%kF?zfj>uwAn2gzumG5F@WAEAWCJg;G3UOy~mowIqa>NGX9J2P4$84nLJeNh8xb zn4a4CiBw4}?Y&lF~NP`LVdB+-!`?3NuT|+6UkICKS#-wSZ?;my2a*z?CgXa3=x}oDXdzX*XlDw|I(5#rbuM6rT56gMz z{hsvT|3ovIa)<7AopO1Yi8~&0SWZ1kyM&?~*6csmL6m-=%>v&i`n|!nn|^9fF5<2t zK!~tpKRp(g$=Hbzy|+O=m8Z~ZqzR}Kt=_JD34R&WPHRcsQP_9VTjUp)8@0t@$m^)} zBR?nLTz%kZRXOO&Q)PTJdw=N1$vhd8;d9eE)#sjS_kyIROs9c|yI^Ge?kq8Al$E`6 zFINCRXiw(HYO~oW72xMsNK`8~fhuoHh{!92&K(l5QTZCR!Q+GdU08EP{C%>#(!fJ+G2MYGti zlE8(8Wr{0}sA%C$ZG+Nx5eAmjnu%3og5g0=?dPa7qJFLUtKpf}bG!QT>^YArOpo=>i%@x5yAO2zSSRTf1jHj-Fsk?)$ov&{Qd0#W^Dwz^0EZ)q)&Kjp@uBMUaTzk4x zrH~2vVq70#vv=Zr_yFU_T9Mzat^Pik5!$p|M(wV&hcTWgp17w^x9jK?afxkYuocvtSB2;IAQ{^y*eOS8g0^9|3{@RLfpwxs#|F%;W~U)`c6+y1PQl2ndPK+;z+*(C6(oL z@|~&Thqq!Kyv_n8CTE3**Unl=_mX*Pn{>}Q`%`lCz9v&^$pJirdD*+e20vDrZAb5d z2kJSRLZpVZ;|T8Db`CMWt4#`P8S!kctsnVk3THv#J1Z0zReqskS%10zDKLs=G|#8| zxhM~WW^uFL;+C#`5?ZqTQDGYh!yQi`WDp5SuFIlo`;#H9O_Ol+X3hP24X)We*-rms zcmDa!V5n$~B_xKCs1qkR_>-=DKM!Ivhb}HxaT|Mq4P+Ou*Md*Zp=jYny zP;kPn&^-tXy01I8z(Ns$E4~XXQsc|tDf3GPKt{}D<=3NcXIMjefUqsE3hTDV+L=n) zmg%>F8zqYjr;>59CpyYAm=oB8T-?5$2_pq2{T%q$=`WYhNPyk&oEuFB(9H5CNvP!J zJ^?)c^qH`dij}j3ptl5gE;zBoDcuj28J_R7<~+NS*&Hm}eMmM}lQiE@VB>q_9}p|M zPP)|J=74%L=6iEp*Amhqg@^T|txfLO_%_XTx9W$mLP zhC7*{$IZs~^Qi{WzfnJUh2tmOx3oyc!^aVNW91?P>aPe2QV9O2-P(LlW+-ji zF~ztmeCsK0P=LV2gM?mCR)<_UipByyQYui=ol1=k#jS0yHQ0*%102O(GzQm8Q*K zQX5e*R-Yo!(`KDbbKlz-MnE`aYQnuLY};WqGM#kEuRHo#p-y!Ur zn_)_udv)GxmKJ%gW^8x0EWxHJ=-La-I6m#pzorK}DHHDyBZ_&LCOHGv^~6~$T7A8Z z9kUHdmOhcX&O#K{2YL2tO6fckQM@nlGT#39r{yPm?^Ek)X+PdXn2}Iwt60oNXNUa- zUyccI=8NA#z-U#TW1@6_b;`mniRl#?TbtU$)V_Yor1qe)Q|0mC&tCBPNF7{#X^PIh zo9d9hE5hrmGWjZZ`7dd+Y?PWaCR+V@_a&*kE}v{5+A{Xv;A1Rq72(jA`Xfl5(4_L1 z6(QZ2gmSPX)#6cO0{H&^QIPU?F>;y44~+8O{(OCzO0g#3j)R?w=n`l9V1z)L_TdDe z(^%`#LFyQ8St!I9b^xq7>ac8^l1q8%JJ0Hf%sn60ow?x*|D%oB=E~$v_1jA>mbD=r zm`>tgr;)NcCxSICw4HxVV<n3J_|=1rCWn4(?9E|Ve!!TYBlPyb=(w$HVjC{b+pA5WWlXs!ExL7fU%LH-el8iMbZ1Q z!?=y|gQ2lLdfm`1Hni$bS8gs7#v_UI&3BeEtowv#=U*IfhQ7?iX>%+QLZ4=FZ`oXm z{4Ay(7z_ptpxPSXB#)P+Ml*!TooQQbi;3f@Q2Wm^89`G#O)G5D9-mN?mDcW08ZMPq7iT)a>~^tV@m7D|!2p6?B^Vn1>fcth6u0@GVC@)UhXNWa_;Tx>#Z3d% zg$Qj9lwXMb@z&;*5L~$gbYm5^I&-(vp7($}>*8~G3mnZ)7<@O(W!KFjR`K5x_NV^V z)*iQ7mcvWmh* z@y#Fwk}LNLnnOhM>ZGiLYUuQ1eAjJQB}-aEwasw{fZC;he8zq2rM;cmw}BAw zV>kie+Xb5-m|mC>s}Pe|Ak&yx-r*R!Cc1+Q*a6s*0Ln>V|0;!QqXy3!L5pv^C<-Kz z>lb37ITMoo%PF&mfAIG1iJNsVSniNdON^(?k5f829gO;T_qS;0)XPOW`J-+2avH#Q zQw`^0ZUsNpaH(a5YqWr$=SAV{VLz5xr8hc?s7y=?uN?#YRBskq6&xaNl`*HI{Ns6U zzstBzZLWSyEo#XP9K2^68=RjS+s_a3e|y1755q20kyTJ_sjiNFyIa*(<;gHNh`aIi zUbn9}U#s)W$zLk&qt_~fP%FwnjaIJ!wP>m21FvoGDJ@^s9v^izx_TMlI0j7eEu$RP zc0QW;vBhSpA>VBQIoMkJZ_sU-SmcBrj!AaFuw86r98P=bu$vzzd(k`rI}4pZ8e_-PTmA&QLHhvO;3$z!Iq9fZ#S^;?(T?V=0EqFGH@8*%)+nn zJw1tQHFI?326JwHLp{XHSDu`eZK;SGo-Ux11Sumk&P!l+DhmxVLyf|!bfDK`Uqf2v z`i3yjgMfzB6BK#1p&rd^wZt+D#RiKe%7TJ8HX`O&P~7K(!u6 z%XDUB?4KrIQ+1S&iIo+gUN;tTR_T4J_~SLlJ2tY3@v)lNtqAA3#tFSl6>|R%NBHw? zmJl~Jf#iDW{pws-74jaFB>GIu$fw0~jj-?2aZymE#rY@ny!Lb=eB+JZjm(ETQq|jn zAE`5^6m-Xb8kMU4Ql1;VGpb(DcnYGxIw56p(dpt z{uf}&vxesH(0NW|P<=i~Inb%r+K-gmYw1ta6;eSKnKsY(ayzW&?NN5Q-o4v@&&(ccF?BlKJ0wMBkwj-rB>NBun+9;#%H&m z{xAKn`HkT(R;0Z%CwYJyuNt zV{v}G0-p1~InB9{;QS4}AiPkcbwwJWLNe-*ao~-zA6lzwHAA<@^F~~zmyj`gNJ>o0 za`Tc%_}`Le(yDraymq!;DG~I51ai7w#p_j#rBRObusP^G(xkOm@Dh=$LQ~902K9d& zKAr_LPr59e$K8q0lnUCO*7t2ADhMeD-~CEtD~viG2LIgaTdtaH{pO-*&FcCArx8Uy zD@LjL=MyUz+15v0mGDQ4&C!@T^PMxY@!rrc|8$`o(efDXe%L#vU*18?(=)*MEO|bo zD0rEPThtg9FRdoV1&dmHD@fBPRq8(a^@-6Vo9XO30!G1C&6Wo+WuxJK-D6qkK+SgP zawjXYl}1|8JNd$3Ar}hCFKHd}<$7w#IDRK)*Tth??&tjF(?8x;W4+duBU6!@qAlR3 z^;0z|wwMeurtOZTXlZFvX4+q{5lAT(9Q!uz!dBEKXQF(V5&@4%T_`>m((4!1-%555 zlceG!y)on%`cF;qe{+tQQbQh=@6m@R`9h)(k#b83JH=;WVh^Em4|dDJzw@Vl%Y-6n zYm_ifN|B_d;Wrr11buK7PNJ|cu?!tLOLc-Ey;ur>#D2XA6u{Daiu4`6eSss<6E;34 zBY}~!yDdbnrUh<~O2}f(Z=|Z z*%`aL3G78yc@8#)d<1r3SYsO_NeXC#0X`*`ibi?om9SR{;4ie`jh7PP2?OD^TC{Y( z+t!H>eK%YW;ucz*I0PB8rW$R6v*(hhB#_t~QxOIgUpyJboCr4_ zJnG+RSU5_SU1~p6BeQG`rzT%~{0SAUjNA8?yF99{DR_IqE=3MKo<#6*(APtd>K2^o zMYiaLt3pS8w8A3bz1ztfkP3+c&z|3WXkqv6ED(~zJnA_3GUuiCy0ldap29EwOrH*pS$#ZnB53*!3LEv*l9p~-Z}5f!%Y!P)0hi5xVb0Jc`__F zWHs#$kNREs*f&$h&*08~?<8_~bV$H7HPwizvSlnjfYJm_q!@HZfG%9du1(!pL^C!O+wTc|| zMb!IpTGqZZomv>eu>%LB&D__$`g-x^Ld*f(o+Hwt81S%+pf8dp`>a74 zE44bGe$UC*u14GmUQt~A1i3hG#GOnfR7&sfd}Xx zE21z48I#As%L`L0qK`itsm~8gk(n4soBTGgpu|aO%Ag9Vw zWA5;?&Z2NM(~yJy&Kw`*HB5{02{10Oywt}Gz8o-}H{2C*PU30``PA%_734VkOg5Pv zHlFnkicm3Nsd%ewQ{L7lKalH;Pk9)(wf0aKXW5QIvwdOKsFya*&@7A9SyP0Dg&BJWV zk26w6(0Xca6^@?O_lsEcjH>s?xXuaxNO7ho*p3SVaDuFk)my(+?L)8&S{1R&@{6D| z``7QNLPkrOPChCEn?%OsrhHKn*XWH6(ABgEohIOgjUy9-&2}Z-jgNR*4OB3lVQcqf zm~z|awqbum`ME&#wAP6;?P`F6$pXtw`r%pt&jNHiym(#Zrk?3e{3fLS5bob18qjPP zc3j`=mIUqEx@dQ#e)b1_{Hv5@u_>WtdDr>xG~|TCNuckCl;#pSW$5rECt3O0?}dXy zCH-n8DzDX<%L~08I=V4Dh8?=pv-7AyRnzCJAnj}WQMHBx%_;J&1-A^WbgX+}@=q;S z-&n^`Q%%2MRs+*0aLO4c-ldgd+qRVnb2qizqMW_*aG z&dE6tG@5HI*>Nsx9P}36T>9a~C<#+_oXxXhO6HcTz3&x}%tkc7FAqR=uVUQ1mu2al z%&Ro2s}!3(V09%)&}LMCMMV0U-#SwtAgQ0*i$>co(75^V@JERZ*O|{ z?XqkJw>6dCpV;X7I>>Du4Ir`VTb0Fz8|wDD*X?_+&ZEXRIf?^}B8cu?2NM{PRinR` zXE8Hs&znS(pL5OID7v=D4<;cgsx6iz=5x=gRAnD^&fZH2NaTcBBar+39O%xQJL1i*d4Dx zmG4iMj(5CciWZp8U%*}#5>OBAe*8fe$ie`PzB-aVX)iUA-qR%iW;#89X4uR=q{C;< zlfpCJDW>VR_QDAxZ^07t_aqQHcRH&=r37q^9w<0mwe4N*ilLzQ zci%j}(O2Xh%J2yyxaAL9RVYLi7&1)l(7j?`(`MS9;|iCN438`3-t0)1!HzQyFbYiv zE!zK+N&Md&G%h=7PbkR-#3I!F2(5qRF(!0=eRy&_b~>{EFiTLdl7D~7w9l4PZb3?k zfnLk+%ziC4yDs##N1`$WpAhF`W(^OS(s$|}5dHWzVUI`Lm}n{7gcjmxr3gVh%#0x} zPrsr(JSn10^_Pc9Iio-$X0jjEOOt5YeDU8yTgbM1*!mkYU3W)xCiV42VR@Gr05y#EFZjBvYr(k6-=D9Ylb$A((!E# zubxXn$OW0q-bXLXQlYlmV!VHf5}v2IW#`cpwTm~TO@%r|QHKCVe2?QLiYGnV_>-No zAu)gme73OX=Co1M1rse;gfED#mc32=8V*+=uSWLY($rg>W{+0}Z|pZRrs}?6ORf>v z$E=G$4YvP%ynrp_)asiAKnX8t?LITHI6v-#;gu_<`&FsaM zxUQ_4ZocK?Kc>f4Q(UNpeNJ(G@f1mmruXM7CqWit?;s0?TBn?)PzjdqBHJoI}eQ4 z#+%=1&|0vONF-R1alUYuKRJl$pzb8-4hX{OX2lb_qH0W@YWFCFwtsR#@XiT8>rp089o`%B+}xeE#GuoGAovo>Y^EC#R zvjO8Td@$EOtwj z!isU*{fNN!dZ#qR9n3(g`Ml`@`^a9qp3zLLtD&kfAw>1iXm5%n^^gwW)7NV8BwFz5 z-Exx1erC!zuvNGUiuFC&51vu`GZz+gk8hv{XxVcg#I2#}#HS=B38{_DD< z{p>;X?Zs_0E_nP!FYs&80|%+`^4Fq)FS%0=)MFT#8;3J-_^CZfhxC@nj_?W*$21B% z72}h*C?grl=?_|E4h?I)T?xK3V2 zzn_a>?}{-o9YnwX_el9nQMdcC#j*mXBgv#=B(Cb5q_!~v$N`Ey}{vq`BRhW{k>_1W|i(YY@^NSMgV zEt}r0zyGB?Gv4fNE=dnPcix}=1V8n1$V z-JTSGi1A;cKvSV zIPxAef_XZzl-Dx&ohA52Mf_2Y^;&-}Dr^SgoE~bT;r|Tf0LJLgRAO{ybgebj;l2^0 zeZE#2oRQ>xGWTjKI+4|hG1A`0gV!^hx`eY*!J{wJ^D2#EJNWLL=5k}Vuew@a9#AOF z&%ons#%z9D9e9pq3U%aFxSaRVfu3_1-8Y#hDFmH&zCa(ZJh;%v3g}aq+}6v!Ke zbSzvpPgXs(3Fe~o&AbmB-7x)++Xx{5+=|%7VOl(XP?}+E`Q6s+tI}tsrKy1U!ltUI z=W4j!G`ydX)re-z_nld?p&cK2QR6!bQIifeG8tf+k2-X)zP4}Il*-7GYh#Z*C3lFW zo9%}VM_Uz&8Vsitokkhnl}ZX`dMc9A_) zUI2JooMFtTBn*8BRiRG$f@%_;RYAj!2`N1xVhT9(s_#DAv5XFk2`09=LUG z(sFZfhouM~?OT$AT0dwaAAQAa?7@FiD5{24IX!U} z0M@Z!47mKIXmcDV4GxnI1}jm}25rF*Sy{Lz`~Ser=YQcCqjV{d>nzO=jH@Rxb*+Sg zzchD0v~$p#cLe*2JsabW)*0epI`i^Xx}a#+A>MdgKWiHdwfwzf|9x}7 zLq<^iiTvXTL~t*u_X54;Q$6z9F+)rl(ve^(B;HQ|j6hzQ>2|X7Dm&0l97Arw6~rGi z1?|+MrN!UV>dnB*&7>u{Xa9U5Ryt`sF|1ujrVn$Vjctf0EG@shdVyB$Nm`k%I}4) z(!rTANmvbVkN?BjS$4G*b?rLAT?)nB0>#~}P#jXENb%yu-62r4xU{%C6e;fRZpGb# zy9GTw=Q-yGyzj^uJD>K4jIs7!Gjm?+zC0rWf&wqN+QkeHdvF2y0a_JRw)Eq!;Ywjj zU&7?Pt?~J0i1^RU$!(=bqQv3qJ2#?vhR4-O%gY#S3~XgPIf=&S?<-G0uhlx-*ox^_xYTO`ws~$PsuYR{Kywjm9HTM6TFWG>$hO1qJ=mP)rXN0vE57dx02I`K0wgz z)};SUlpy$WD|JmKVZ>xFFXxI+Mi#AR;$2}H59J|;wYN^En2Xvp`KC}> zt3@k*A!QPeyxTahb6Qo>5IY-p$;44%&gds0Xv3&;Hwq_;l4evbA|Cp?UFkGhfO3z? z0_$YVA7D$0)+AYH4qoe~gQgZ{Cjvfp^ z^u0-{ikL_%;!HYo&JR&5G6M~$9ys_2B}xP6KskoqBtl59ObKx5HTc0|o>!n7P4nTb z7Ly#4&TPmerUl7-<@r0iXVDUO$fUmF3dI{vkl`(#;4;4ZKqoo}ymyxG<^0x)s>l7- zA5q_Sef}|_?XzclUS88SZ>XafHi%V`Xl*mtyWPWM(7atQ0`Uo~?@`*{?)xX8&#e(9 zqeBs135s+Hsj0x(5e9#6N2EIn?^t+3)&N0-Y*7a9C}Owa-{M&VfVWJf`oj0fJK7Ms zjS)a5kW;PHr5d+O0QYNlzL99u9pZPdMI`48)!W^Xc2whsqNH*qcli#vrXU1>;%hT9 z(epXx-6QA5xVOZ04Q7Gy_*-E6G zew&fJ>t#I8H5nL-ML2C1N;)5d>vmfA#2jq5C`ngd3LU8on>ZYMA5b)>{BW8n;(*>g`+VL1o^oe zelFYAg1pK@K!^zWOM9XX)M==uQb7Jh6e>c`g#pCbD-+_14*2Z1EV6W>NM`MsgsOj9 z!|O9Z{fLffw3TBoj4-8IT+qJuRTtbd&&R}1Ao#AGe z67;7fP>GOhZpJ^I#9##}mHG4?rH*`XbIxxnbN;RKTRIn*>ck-Pm$6_uQ=lM?B5;32 z7Se?b=-LUhc}IxD)fzAGkR@g!dhwAg_tzw1V(j%1Uh>QOK8=$@qsSkK<=H?X7G zVA_uX`&+1oQ(9>;9}qRub{P3C54Z_%)e9A3bISN5>#@i^ghb)U;eNr-JebXFWc81B z1z(;H*(X+vCQI4bf=o43_CrQ5*j>3@3jk- z3TOa-;o^5EI0xQnGzPw9`(B@E`9ChRHzDS)tF{+qFOS%ex>*4?!1cOjhzqa!9HOA+ zmcKyp%Ih~#!Lz%2QQ4!#4Sqab4eSJYRlD;Rg)|&z!8JQ$NS@?SKK&WwtAGd>a1G|l z5lkrZt#bB#!13zD6js>})OsvP#+)_~jvguy`wWsPiton+`c2x^8}iLdRF=q!{9AKt zA7}WGD3fBpZ{_;MCC@d0o>^mZ$>AjQJUov?zSVZU?7`*1;@riebfO~2?@a9w>M~RL zhn#fSR{uYlpZ@fy0(<0-BA&$wen(snYKp}Jqx<&Hhw)94dq~pu$DnCl&mnu*bh47% zg%W=X`_?GW4Bu)yWIE2^;B@=CXdKnWuJzA9Ft;V_rg`+YyXjH?y^gy+w>ucziH(f?%-Wsg+BI=njMqoU`OUkQ&aO{txVA~Fml|ksw(~UNn;_Wc zkM244|Y5w_1b3}QYomv9&xKpIQU_CJ+q>lJh9`Z-i z&rmaDmfWY16rm+Gk=?zWRUQ``qu(88$uja9U_{8c`z5nK?FE?8t~ung z>l5-2o=}z0n>d)+HTGnOS+wzRNmN-tIVm9%z=)XcpsD=ju2J zQHA{0>faSVr?>I5`cC(LiPdIM6>a4!njVpaB@4&aR;tgFmf=~SnE{;Acme-Yu&gJ9 za(K2=VcQ!le5KKtUV6)qu`H-_l4R(&Nq6lXIe}ngBoS@wcT53j*$EQFxb}B&I>1%| zjmYm4wV^Z45Q)0hCC`p{efQY3)z(h^6D&w?5!8>@{Zse~`WAd9QXJJz(zWrpJ6h627mcR5B4nwrx;8XVN)|-?q#sH45 z&vZD3I8|84hVaW78^ko;?r+j&juB_L%}=#|l*6o=210SeLG(uE!lta2>)Tk$^r^SykI&s&bmb#<@ z&x3K&N{k@X6+MeLNgbZX(u_D}T@FGC(O+)rLv|djk0g2Uf%rjw=tR%G^379Mqo{D9 z@Gk^(pBVU^aUnOC7$T8^CC&-~KME*Yy1OVv?yVo;Gc$~opp`^%R~X5$T+RnyFw{I${XjlsXt;9JcDfF4UpaBplbSAv&P2#0ul@XlE z(b~wi-FIi&FQ3i>{bb;OBK&k)TI4Bfbh$l{vC5JyAE+l!mny6B`^V>e{!5f*UqmGw zSSnH>qVie>fUVuJlyjq9SOUN)*SE3$c1)h25EB@?jt$9^=t*b8qMlE!7(u|_cr%5*&GsRZC7Qo4*S)u{r4vk7e}cZ(#BzatN$W$SrR z19kYP-=r;`YJB`@M5rI{g6!?VgY|}}_vIqRwMexj2k4Og_fN+|s<1BKf0RuE46zg< z3t428_Ha$a+!`@|X(ixsnmf${Mt*1w4C0=bdi(?A$4PnR2}@|JWBdq~U4zq`h04et zNNMnHe3FdIzsh%z-0%FV4%PL*?JXm4uqE0E-_D?nirPJrr#1G#yFRe$7re-vJ|T7+ z`$j(~+4d{LmU%gl9kB_Wpn-B(5FSaE9YtbQU|9@bhx`?@4P`%6lQ=jW$7EFIW1F0t zpsgJh=9PMx^1l<7*auAd?`4GW9fa=(#;(R12TrPfn}kp+eHCaJ3Uv}dlSbC-XrQoS zubQq8eNAykUQT@0sfsPKpNZX^;QIhT^B>y`zP|De{F9AOjukr(a{aM7;x>;riAW3o z4hv;fy^(SV9;fDHcdT6l_SG&xQqYqWr}o(kE-dC6{Yt5cCE-kn!(V1in5$;J6m;Ey zL9X~m3-g9&=iOqE?7iY^jV~a1Pqb137MLrNqu!*$UxKH7d~i7ULx{Z%KZ%q}(Qq&W zv>Rr0A?0b};#{I_QkAdh$<3$3vYhM{lUW?Tf0DO9q1*LuRtl&mDyY(I_n8Xk`Cw>f zk{EmH3$_;3^>qo$Q-exklF+AY?xHqvpoGN??@g`VvfXR|GJo90^Zm;VXf0u}>SP8A z=^?kSS(IFA**z*b5siC4ccH2>+%5FkeQ{#vjG)&iz#-p+g(JFknP17O<0}6u6Y}D` zHucs>NqP%XMAZkhIytSpF^yxWS76Y+>o)e%HYuqJ%2NZy%xmmAH^2pKe77nZ!V$1l zPrN)t;hB1d;I_bK!(7jKq~Gpi;V0z}J4PbRm*`Nln2T$c7mc!u%$#SOtDC#LGgLiJrS@V?0Wq7H6<+mJbO%!bOTIh;IwM zi^us7@o07|R9hRA#O6f15C5M2r;t&Edrd^pNK@s_$!&*SFnb+-@KP6!ciz=6ZU&Pl z334}coT<(W;ULXMO|5I4ih!5qZIl}#%a~yOR=+*QHd1(QuaiI~Muy+&@e9kygs@A` zf`@tax9QpK@WWkL4U+??pTC*|H^vFS?}-#@STvEBWrr%=e~XQC`I}G_pf`V+Y$qku zFE}oE=OV(gzoP6sIxmuu&?Fx#f3vsKd}We?!`(r-#!Qg7LlA*7@eAO3!>wlGw4 znB?6rXtZEf+K$WkH%K`0dLvELD7*0};ir|oxK}2%tjVU-mT_3B?JnYreMamB<}Q$n}mwppgF=8#~iXs9fAz1M}woE$~Eqq4)(9Hl&i1d+)GlUCExs_BVui zE{u@mY{uq5+^dK-+OO$TDi$wL>*4%-*^(5AvzyCJUMs=zkJXm3$5Y*uaPqv(Pczkj z%Kzk-SMO*%4(+?1G>FYM~k!G0%p+%VtNAVipG%(NJLQ*Ne^3tcWUhKI^1+j~c-N z0Ta9%95_e!|ICBoPC(n3oF43)i3BZi0Oq_dCH(V43OgN2Gk_~XkZL6DK)<5YNq#*) z-`yKYQdeQXyWZ+{BNT1+P-xO&aCQPp6X<_tTOAT$TaiZbzo0-T9mN69Fr@2l-P;H; z7p=>jXQIffUJB^n5o9A8R}OafeGe2h0L*!+MT78%^Qf3A0cLu!Y-g83!yDb*`~WK1 z=@!P%R1alT#9ds&M6bPdYuQE1pL~DAy5|cpjQT&jw(sfvaZ&l;6*`=O1|#|B0AyqoRLr3s&4$ zcAISnOmyLN%#J$|VrR>hAd9fcVA|8z4{aVJFwOkm1sJ#>Gs;bFtXmhpA7V!q?Ye8+ z5xAav&n80C(g_kys5f_l`~&y&F?SahmdWHhr4!{zsc%4A&4zCz)4voqLNZx|TuMOD;;k3j{m~en#EmF`Gq=~U$sAXx7C!$QmGk(<<+8btaAjyzN#mKvzLJcum-wc2BuKCNR9~t%h9KuxBg^vX^AW$ z4Ym|*eSlw)8gKR;{@1^@)N4z8bD8ReD|cAi=8Eii!%DHCGBdMY4Q z3^$T754>(F$2Zc~lWx%{RFi#qquO@vY!B{O``sE{&@3)?#b-oFMMsg;8$~YC$A>Gy zu-GVJ@E~YRqG_Wpyz}AnH#lw^bwT?1`#}RhFi+!`=+QU?0Qx0fq@@q+H^FQ%Xas#D z<`z`7ng9_^(E@l$?2pod0KUkTz>x&)6#1dPXT!jsx$yX z5-Pvon~IJ%e}F)PainH-gLXgv!Xwx(`nvxmPCE-vn6yQ|#hyXa{Clj9O%8uASqLyq zm!hS%jua`kiTr8Z2rrfsZK$L49C&cO_@W>(jUrSh0vBkMZgDu-ILw}nbM@txK5eEf zycrMwMd4*9FYAH5D0_y%|0M;l6i9vqLRoz=@|H8jb4Y2^IrR8cVaK=;0wBck;wKIp zuOB;Elnc!F9KicyEuPa9d%TBFTyi#5my9W3nntZEp*VD>rg?zI_YOjF^=GW)A9O%Y zoy5wTqr_Lj-W%@Wxef3L9vXS5L0<@|mJj~8^#LV`qg$V<^2xvu%gThwMLI`mB+F`N zMxiEj8h^UoW*vyPTrZIFm8wd@c3CEGxxEB^pOWJ+oM@$>980s;mU3!K!{<7iGy?^x zdnbdHibgliEYMzzFTPgzG2;`hI;^I$OzIBsrs#_9;}d_3BQ47CwC81qd0k7pbZy&> zeoSk8mG)oavmmw|@!CyDfX;rJ&Q3qnxZ=g;pKm1W9S zc?h_K2H9O|Vjr99Fr}q4YlAhhJ0Gwh#1EZGpkUgczz^ck97*!&K26vjqPkFK8JW^hkRX z!4j7;T{STn;WU&^XF~2mtq?K3N2XcvtnKW%Ui86|pYcQX*6cQV(cgrL9{U*nj^`Wn zy^B5c(!AzObuNTxFS@#EnTwJ_9dUZ(BWx^ZEK!}9wl zUChlClE1}gY;Z5LN6bhsvnyKW|Jj86Ll8=}a^1~E79R-y-MjY;yA?GPd4^f7W<4XV?BrdP{R-Oa2v zbJSJmzo&b-H4L5jB`qiOA*I-bjp1O?eA>^a$ap-@&`cK=hk2#ErrU(U+1o<#ej{;d?xt_NItvzkb+f12ugviss!s%Qwa z-A1etm+D)0d5Q|>|G>W_foJ03ex0Wxjr2le*}lni%=;b*Z}H$oZL?20;=2^J zpLIUzbfMPa(Q^FVr+-Tf-qAeg3&HGF6)V6rt`lo}320@}l@PJ#U=iR;5cC&d%%aKs zIlPLc(AoGsI?vytaMD&$8n_NWy#~9h`W`)_nNKBDZm>dZKC>OLHG}T=^pj5 z#)yVsN+*O6oSa7nhvkL#0AG}CLXoK>aVl@e=3pZ;^qznA@N$dgo5>=>4~! zdnFkcPKOV4eD^fOLn;YIl#Of}4)o8zRvFiuu;EH7iOu#ny6dOM9ZrUvpV@9o{0W!7 zG>c)sgx3aSXuFUf0!k2^PP5!0Xm84B5vOBk;~bOnT4gBHxU34bXmyFaPK9B<$M;UM1Z`Z*xeyMe^9qgmF6ef&b3o@My?!SE&bv?}828b1p2;awrn5~x~bHEwE;(z-|`fYralgjY^+0coyucIV6 zL7++%4cN3WrA5&{Z{nH;XE%V_ihRbw>6G^{_Vw6;d8HVZqG`zJi2=WZ3ox6HA*|e&P0Q zDFq;q?)!${KMqkp_g-S(QJcZLpo0%1H9F3C?%Zc@3cuC=a|kguHjwFE-9ZK z19jD8ma(3Xi5Hb{9=P8SFTTfW*1-avnal!A@}o|7?k;7Lbw#&H2lar)lBASSuDFns zlHHnUYm7LzHN&baX*lP|oOMS3Wto7>TS;(%ooL}R&J5qK{J!jxr4paALu-wG!-}TL z63}Nb!tTAR%M2jr=$1=_5+U{N0hrX8w+gw8ki-%r;eVLq$|@%eOD61&T7zhTB%<$6t57t~OUFlao) zHgHnhn?#W6T6ow{sL;gb$AGTHCU9xHJ^-0R;uX)Ru%eco$=Y>=`^Iv)D$*`&a-Do5 zf@Yg_a@GY)s0vke*j-5aD#_(DS1M`+cx>c`p6?&uiZG;6~ ze{Y%Vn7d6(LG1)K`|(~Rs#_2T`3LCbJPX9!d-Z||e6)a8H6A5h?G2;=ZfDbfl&)VuqyCs?yz`N=T-9i zA$?r}Uw&?Jp`WI#F#AOYmosc%^yYSU{qR#}QRZm6#8)4M#-BLVrRyG;qNB8#Vn*o$ zyG{(7yqP9~5se-Pt$MV#{4|5=m5S++Fm7-$iCKG^lJBXHs0ohAzfTnf)NPY>>1i#y zPCjq$iKA99$z})!_o;CxR<-krquB5DV9(oO`@J8ptfTHU5-Z;QHRJFZMJ{ZFX@U_Q zq!Z1_uI@&sTgh2>#ffd{@?y?#0vylzH{E1;W;{NL^YL|^baT%4Cb}l$N$FW{&pbfi z!27{-MGTvky)ceKr>!M1>uftjxX)3^wTHBF76OZ|w}3s6dO@}1G}*!hmlV|ov;BVk zGAO|N$Yil+9(~4Pn(WyT527c5JsMai&k8rC!}iaDxOje1ceU#Z+8=onXe7f=B{~h6 zlqRQjM71*UUtO_frH}=ObEF=AP*0HXD1BlfoT@mijhpk8-#GgvvHUKIN%_I#yOb=R z%4C@Bkw4Ua17pTFqaP~%^q`4k-07sGGRr%|iaZ})@l2Z7ZpIW=D4gWmQo-3~e~%9_ zK4T&lN9dNBj+WDPudAG3R5~W|N<>&d}f3CZ%G(GiI1hoWP%YUK>GB`7uWPguyX#p!Kiok4_)3*})A+yTYF;}K z>VuIC-xv7T+}U3_4jg8kzpNT`j2K?r--o_%=y?367ogS zt2umIdzb+GnsS1uZP4L|ga4QUiONvl4%~0>61dkm1q3g{>7xC_<8gu>IAqp_3j=5* z33O_9yO{yV5Nm`_DDfVCdpp&R-?AXr5Cecsa-64jh>y$TrXZ+e<2LO*78id1^|=$L zlzx(TPvR%dI(iAOUJB131C*#f94)6w;c9ys9e{gD1RI6RW`=wwMLCy52|PnG7}Bj#up&l}&0`$kCa9geuCjzYtcFGUqsv|G>F=+4y(QZNyb{ z=3)(Ye-;)JV$c$s*O7k_LMQap62&&8_&O~XDilP926u?Zz8{_r!NrDbB8-hY(wI|?d(3EFC-~UuCQ%xYCm0*i$u%;gc}YvrB7AC#}L4hhmou%zO~?C8npxb zx4lnR6%-|vZ%*yIve=1KsDI3EDJju&u`ll;hW;_J9*HCu(2f2z(CuxX>|=kDu;0#$ zWK`ef48R#;??jPA$zwfFzwFdG%*AXy0K98=AW>J5rvx#}v=gAJyI(O%bUM|A(V|(q z>XN>dfVc1@RppDVGrh{OJOy|uar26ui*WRuz^~qc>@Vqi%!486j2*UuI5OUd+1RE? zqEW0ZqLB_ytllTo?pu*IGq*btY>hk*?X1;77hwAu!Y1gz!YMR zH;GHYB0prjT$gTB2JVgEJ9u2hhZC2`HBtixEt#W<6%kK@bvxw5U)B&9k?}LsC%i8` zceW1t&$omu7e2lW_g&JE?q++GGmGbC^(pJGlv-==h;;QKr*McJq(4%`fUOb&zs;K2 z+J`{|LUotkIF&TxR!1(nYpIEcJR?wNhXo(hA+BCjb)y=v0N6dzw?e3=mwMK~&e!jX zxSu7Dq8}q|;B71Ni#}I2?hf@eNbx>|WuUj@p&!TwPI^KeE7&`FRj|}9E}MYo(iAP% zAlDGUwKHV+Q0w651Z(z!M*rWa&zd_FDG6PfFD2_Ro3upmmF6q4j3SxHLLuDmyXVE2ZlbYgFf!yEk~W+78{_Yr z5!_p`1#aIi%Ouj-hpXKT*XHzo%bZvob^HbO{M7<5V}pPLxwB+Gc-%4JP2jnai&Ha% z^Z&!YTz->QZ`X3cJP~^o!3~2+}#I^DAsbxTz=M@>-N!V%b9rnhWDiN%{NMRo4f-jP!-PKDCCcZWR{2i@3Wr3fPr5zoDLm@snua?3 zSWTp&E@TadaB{~6^!}fN?>z5a7>TB@QDJ+8Z^2RF_=u0v>wcec9%-3y0d-v5RW0{g zZpvq6budZZQk*x^QFOZXqSr^|W^%DNe3Du;(WF)vdq$(dvP zsK1JjOc`g5&FLSx=RVUwZrCCBVl6Tw?_0M_&kz>U8eAa!_*+A^ozUB3v)rZU2h1XR zm@LH|43@^WGysS=SF~eyuctT4J8@LW4~>TS53^97Gv&Q4g(gfqKyyb72llWqiV|KK zLSxa^M~~rLAHGV0$@^snGz5L2aNTb~D6{|gVOUR@}WfRf<9lz^NzR;H$mzAGGo}C@Vcu(>0VaXGC?mstt+o%7einA0W$H= zEHV7^WhyKUD~ofj7&^$w6JGAM78xDME3Yj0)E15%(^SeEWuLo5=*c_2EB0>F_}cRm zqV;qD(#D|j zG}bBzNE=ZCg4O>>Ef8q$)^MViJEZuA7sx&?l-R|yn*|v>R1X zQUY_}rSEFn)6@IAUAKo~J4p!XO{d_GLyj&)VElW*o}To%O8$-U1RZl<6v(C$U&8*# zwL9MQ-k`uu*BLTurQ>K|r8fMfGC{7|x&=Ih_L2GN?SL#z*{U2a3U!z$CHD>31YG_a zACcMV@5EK(SpNQ_a3n8Xs3C*<&W<2dTaOeCYbL*a>m~yZ_y5%t3UIeIF5Qw2x5AG! z`sxKHudVGUmPxkuylb-?QMc4Du1k%|KumS{cI=Cmc3w>lDQ%a{yWsbApTr5l`*DLV z2|o@;txLrZ*PJ>OIEw9hf5D3sx14jI4XN)M_ZoNKD}^;g4l&mYj;+M&B)9)0op5|C zY$(C>kTs)AHr{sZ53mrQw~?pbY0xF(-t=B}n&|xI%K*?T)R$AH&AYc-$WihT{t@EeiVgBLyCkU$m~Sm(%;bs%>YAW zm@!LmL{Hm$h<@e=vn%S;P-YjTHhf*|c+@Qy+>&Zd%}d{GF+`STql}w}S$0V_JuOpB zf+RsX8rPKi&2AOn&A2ex!!E?vtLrAX33Lhfim91q#9jHZL|ekj^O`(cQ+W8!#+3E0 zb%Yl4s4^~a^SIR?Eok<*MK^xHZp%{V%kL^5Il zS!GNU>+Qc%jW;Ankcl)qYqZC%rq^+VNyR>JF-2GmwUEH_yD(*|z62MX6Z)aU%dC2L zU3di3KB3NKo%n)gv_>!ao){7im+e#7Yk-5e!kVVy!8f?i>=CH^vjTjDnRB49U#;Dg z^)Go8aKky+g?>yE;bX}wi9-pwtavy(*CP$r>R_8fq|?ESCma4-c<@!dt;0PP7e1uI z$BO;hbNT6FtvV%-EB6#tgFk%;da>zAB(Wa@Cx=SdN#Q_$AR~;~NoO}KB4i1mnPh^5 zygRaco8!#cD)OG;fw08IGwms2CK91>5496CiZov)+E!O+32%O8Of{eH+|t@^+}z0N z6xYmvj<_cInJzSaaxf}u`Ii!5w?<_W&IaxCBtIO4 zlD>$nTsGBu_alX!QWZ4gTT&DsBe&B?A$+1fv1dyCNtw%Nqi_7n_5j~UU9n@)gVdQE zM~faDbkw~2*Z-dd@ZC$hilu&Jj^6dRkmaM}725g<)qAhuk4@LLvaruatPPwtGx$>d z-5sPPC=%kR9|suiC2bAn?pQ&Zk1zW}D;+Nnu)U;YFu(Y#8pnfZQ3$j5jk-!$p2%B` zTm#ec`0KZiypPzM@$pOMKB45(gx6QGB|O_*K+K%K*_m+inWkpyO<-$waP5U~p{T(4 z;*ZuuZ&UViQ}dL#EE7J^I^}*+C0vx+{GrT_*?JD<@u?MZRt(^6*}fv+JmcA&2us{W zw=M3_n=oG>66d$xdcqQrF|Xrtny1@$N>bSq53pcs{%;WT{e}KRc4m!lY%kY#gS!^m zQWaPJ&OX9nze>#70xjv0jsyvlGgl_jeR}Oc2;h!AKX|5d{R~UDF&pIejrTMBEbMiNo_+P|h2X+=|KxJD+cD0M z{ThtbBl0Yh0{<~o)IBv@Z+QIjs}}GFb(zhUk-j!4mo4n7kX2lpkd&W`8GEjr>yL`# zk0>oZP}QknftAQI0#E(7I1&15hSl|^LOS!r4%`gO1V+vTqDP@^dCSHBw(7;H0$^)F zKc%3qKALy$eWKfY*deR9n*Oe}CLeAaMt^OopWL~lT$_q*6R+i#4>aO1KaoT*Hwf`T z^Sv+Re%|rBTwKewo-nnixZ_E+Z|3*JpQ6KuV4;mMf@#~W6!KI_=DlZ3M=45*%m(XP zxfq3^PA|Q#M@Uvz8T-R-d2{WuahXkN+rO?_B2yMpjyO|L7dR5b2*n;bpy%UKPFoLu zUFY5g@fUY(jAx*AxEyt#kVn0eUz?}N+=qBg^ppdd9*a%XDbAP!hSteZRC~Y_4w9^I+xo(eaR{6CqpBIo47*{_J;8=VL6^!y zJ11b>;K)mMFKd}u>djO6?QfQOTlo!px$KizIk)I?|MSRYH~z{uZO+uh`GIWNXisFB zXeF_Q*sBSjMn(8XQ*R}tF|$2;iuqI6AKOvIj=$?iM`jeaTougA_ftBgU2!3hNrk5q z^KXvv6%6T|$zvJ)auv@eC%~BP-H}dECCL@bl!fiFr^HjBIH8ldywn@_9*sSbeq+? z#wYh@u-O4g01Wy04h4q%@O0(V&-pm?(KZR9L}@Z6zVLT|^6Y^x!~Yad7`+nXHJ0y! zJp#mrfKlA!cHjq=Tq1dD`rDgfN}CaZqo=n=6AaW+n+-T0 zS)H=2ZPZxg$bymHmzt8zsIu+sf0Q7^QYg#DX6!8TGthZT>2uEv9F%v@dn!p_8E57t zJj6?Wk|{5G;`R$Ge*<>oJ3HjyZmlbo^R88WbIMk0{N6}7++tGaDK{^rRPL@?*&VeC zkSd<=Vwvj$=g+^Dm27}{i)k=T=AbBiie9pxk1om}hKb>8nnc2?-kXz6Y zO%xFu-qmrc_2gaoep7PL+ZR|*|JcXbSt*5Zim5?=AMQw--PlX5Hcv#bG9;kJQCPd# z3v^BF+$RP(qU@&|Nl7}ujli*`rHvXv@m>0#Ae_WU&Z(S6^(n7pKUmmw%m)A2M;Glu zzgRhdy9y^f=D1l>-yw$!W&X2U9&A3Hrum7^rGNN>bT~oHiGV~*=u7A`{)Cu$*J3ez zoa>@u;X38-6o(>`;4!TKamX+bf=<4tPAUTZ;hvSCp)%rr-)TP^Fk}I9UCu{mBtRI@ z14tABCbmT-z!JWhLe{HaCrZ@g^KD0voYBBT1QHp+_tSaDaDr#2#gr-{^W#HMDg%B% z^Dcd<85&ViU$NSaTBhL1Jf7D86mxb{5=ZiZGD0J~6gLc%eIfn8VyO(_VMN{t%L7%y zdx;oh5kaLhxtuC?X7nHdEy zsW5~#mtHVYINqY#d626vdVQ{l9h}}y$T4yX;(lF*0%h0$bA63$_msbaT;N2^{Eose zhA+gB9O%v7#roBrXfyTJMKMlTp=AakJzYd8?be?3HqrQUg!udgEzG;&Hle*K5v}Ud zQ>4%k{O1U8wnF9Lej{>lKP!@OH)Je?*nkf2Ad|r28p?M?*yzcTgIi#F+8@xg`GgyJ zUk@+tYHzrpmpA@1E(!o}Ozk+ZIZtFnKx5kse`3k|(0f^eYAc(9H&48Dz>-3U_tUl| zu43Z>9(Ifc&aqxJU`(TWd)?q$Tt_rB?$UY(jKc+l1bE*%l>9xske~t^E$Ds$+l$?g_2@^1PRwzYX`BFTQL|-@3Kx zYOf>aJld?L6|A`E#k+4igreVbqUcD{3EEMkKKS6_0 z8s;SZ?Xdu^%aJ9h*IwKuIH@}%6j^uuOL1dB5e2xc9L9KZx!=nUwhQ~c`1oHPu|pXA__p>K>+5Rehx3$m>ej?m_F-3G992h`-M7(6T%& z%spF8VjiB)@y^D?RSm$B*f?!e*MLcJTCD+qso22X?=N0+@@8a6-3TvLIL}h!#Vcvs zAn-@zuu~bNOoZ`5X2chDP*72n`d@`1W#QQrZ@$;6@<$ubF$O9)l^8;8vq!=pKUw|O zR^N}Am}zleQ* zXV}LxQ#NoHH$U@VYaTLcHai(HRNn$xun%^*M@TcY`Bs*e_eh54>qf!z#GY6mSvoNp zcXpjHA#*_xx+Gx5_M#p$x4zL_7@u~FvYwcQTWQj*%c#po9;A$&i4paBKH}^v`(Z{O z>iEmUfu}g=>wP-R20vd_tTuycw_$zr)^{iPV&?7x^v{dfslcy`gzDse0c1YhLz(Z9 z#`nUZ)25j@X4axinhBYt#(`VGno@_aN0>A=}4vwd|R+nbE!p1qzcWmd% zkx%m3g4^fJA`Oa`F*XgwZm)1=WjE)5rKgNRue%OB%|Id**x|Eh7T3gYA=~(fU=%=!23|b`xVrw(9TOO)>4%^^?fZx$&rCCVbK(a`p*N2$XX^^9YA>t# znt^XEE+E}`x!4V6m!2!Pwj05n$3}*Av0hIA`9N%{t=CU{7cerB;t*H`k8S zg)@R6clq68(Sz@!-Kdq=EFSe?y>f!e7_FHKAd!9b_=;1 zK`?2IOA07C0ZHse1n z>$3NJ?MD!Z9{O>oh@XpRRjb;T;3H;Vi)7mkPlF@@y{M^qkH|67m^$+%rNb0RU)CNc zjY+&lLpYY!`DK(MAwjjq;hob`1@yXCcM!d~D*4g|c{_9D-F+PG*#oH5aW9tq1vLqT z6B`ITjVrtowa^h$RoyAK!$*=^wHEPE#DWR(fE1BZHoJMNdarD2F_dBkjJ-MbW%f9O;PRv1}2-&Zc9gNv}r@a zCyAFCgZBRD8>{FqXulg?j&S*C@htBH%!iqd@*>|T-lCC0*e7U#eXb!ZyWz;UcSGNy zT;uO2eUKM!_=-|YX2xea5C;`#VBh(zmeK5%Km7J-=2&$PB3$y7kRAMH-||IekijY{ zGz;{bKKePh*V8=@(;|;!$&GIy9@xnGc}PM1brGBh&VnLQz>P;)c#blr=|(zcKhOt9 z;U}`vsUbJ#vG3LX5DI-vO1;B*KUKp!6EIjO^^9hQ#-1l0 z-%jiRpI^9zLK4t)F#bpT2Z>Yd*}}7 z?iqSOrI9W{dSK}84(T51;eOWpeu4k9_D8tRwXXBr`?rr{8$ohHcDyEjNPDp|U5-lr z3q2GE$kRDe{Ac&bGT#YRVmo43t?l=5KeGz9qph{iFw*u}GS+Qf z^09eAna9h9&(NHZqLWu}Dx=ID`Cu1;5W4X6X>&G{{4uW!p4%Nukaf9V(GG2^OM&4Z z=0{Dtf@WX4Ce)e1$3GlZ$XgZYuU!pzmy?d}=4!3;vK4r4600j5xu3ZQB&A#~o<2v` z%Lcv$Y}LC=sZmn*Uk4QNq6#GUR5!5_1?YeWqiGxiF4`c)Xn|!}P0tEkVjH=F@331% zDLj|0wxk33!f-`WM=4flrddV4>t9uzvQAtcYVd5UB#`m?!$zDb5?GocR@ej=mxfP^ z$kA%c-awtw6tvvX4sm(iuH9ROReF~C9Zd#BZ=*dL>iU{m*_*beVH<__t`QAV`)ld* zm|tJd!T(Zs)UUlb#oM*s??K-mAVDwWFthtAuEDjFQVYr|g$_5o#77nN)kWP*bwlJb z$O!URopl)o41)cf;L!I5dHqEJPZ!ma8{@-0GQ{Yr7iG$NQ;G85v{o}znd12j-uKaY z6L)(iiw2_t_Cv?(gCzKI0MMM$#| zsPF6!cfa74cu@f(4@7%wD&JENq`OMtJ<+xO?6!A1-ApW>3nx?yJ%YbGKt#hL|&Mg>3y~#KJPH`Lu5}ocph3c6=(bzMIlps(O5Jm!R2jZ z4%nm({3exvtL)o&RoeLOl@{%F+u)*T@Pc{p9$1z|dHH+%sm zN$o6QdIBg$3$GHRN!b<>>sufi2j`QZ54)$56YsR0Ou=6}s_FoNl{fo7-qbZcsAjL1 z5!8|Z%aaJ8>A4+oYp;paJ|Yf+^gClATljIp<4J2$`p3rRGLq$oP348b0~8Hwhq)0g zuRRg(yv=7!wR3rmb*gp-4#zh};(aiAxKj-gpaI~~y+=75=Wojnbk}{TzV|>#9OFhQloVgd<60c!1_MgLS&5jwFxw^bgj@mn zmtm0=X_$c9z~fD-CnEDr>c!vXwcc2Oy7c~_o-mD=MR?_TcJ=Wr@sda*=okUMuc$&( zrFqif+;>gT7`iOu&Iu5AV_4R7Kt_@9y%x?xRF|=0 z=wx96SOFK2bbQI&Axt3;>J6o@5VBw)@H=^KvbfTnmrQEJxP;BCtRk zJWfaQ>k*bWMdcYEaQmv3f88$Suj@3QhuaY|gN5>+uKF>Kn-GmQ2nvoMy4UfsAmu2r zIC~?BqaY+3-GVc$Zl|3_^R1XIkN|9G=H>`E+;;lTI1T*hQjcc|suHtU`B?w72ugA$ z{`PZPX~XOokP#oO85)=ZAe$Mj4^QvgrLUx|CX=Mu8^+{_HqKtSf{xw3j?X5sxK;qn zA0!T<(8?L}1LAqK)Q&q&$!AH9r9{vcYd?<@G0%8k(LQ1ml^>Up_<1D4HK=lY3pa5j zrz9Nc-9uVWzluxePxT@RH2!K79Ed!uY2V6%WSd=Z=$JZt1{OKWJDYl6V(wT`$XYK4 zAO??N?03D+La2l8cXlP8VKBj$bL!X7=p*a%>%K7)wI2VqgXHT2tg?X}QYVYsDS3>b zI2Ez6#xoB|qkW1Qo~^`n3F)M$qa1&@30i8Ax}uUgHWa`m3g;-m>a)qF5{M42rpIpK9u#l=w;I2MT=>$^buYJoT&NCcl?w8t3VYj% zFS}4A&0OEogcBaS(P$XwB}kqE@ja!gmRy*9n?TW9h|cALnRq3Br-OTVPV{t`t-U#u z>doXKDP~oS=u_(m913$hI=R7h%fn9$^lxJ_7Eoz1MM@SNP(|Jv;O_gRMhQlI6r}bX zOUiVs$K>^za-ui!@lMb_qdZ;+REkL#!Pdq&q+dyE>gn3ZrnIIuY^?w%>HQ7uH3pD z5l48u&|$qlq4>dgBng#}hv`tcHwEXR-QTJ)-ueSk&+CSGrFraK%~jtS?RTBt4E;M9 zSsTC+=Q4-DRITmZUZtPu{$<8=n@8=gTurVH+aAD2&Egf~yNqkQh!bD{Cal$+3g zR;Mz{g3IsPpa8scs%2oNyHLOAZuh0PrQpz^?SB3d%6lQwI$|CLZct8?JLAA@XA_1+ zcueIgrHZv_&84MWMAsw7#y-?1-%ZgZRlvN&{o3>*aM({Fu-GK-nx|=r%L}q)_mC$Tu$$jF}ntQkayM&G1c@z)PEpc7=5x{cToUEDNL9TU8h>Q)$hP zde0(QBubH6fgbN?udJOh=DGbAC{#SuuQ7~6ID8S`dfCwn>~g;k6~S;IF8nxy_f7gp zYn<3}TMxwk4&y(kfVxt*fMGCaLbZqG(xc)(l=r?sRpYtY#X~Up zptr%Q-8jKsoMIU!^{w&{xGUOt z+DB&XFL`o@q*0RN10ZIY23Ra96-9+*Lw0Fkqh5Ua z=k1Zpa=np{`TvX0@kIyYPWSO{;NjH5mBWB*L4o#XSwA5oF@i(TRiaY=5%`Qlt!D?( z&+)kg?DbsD1iC`v%8sp*>E+`fCS{DRc)aY&Ji;}=9^b+Fn>=>+H$4J_{sJBAc8K~c zq@V!-O2D>*0a6TBF_eOtAxl`RxeU>HkThYTP6A|te0;Xw@B)}PG^{oAv1XxD7bOlZ zseSv!%gFJ4I6KxHIW7oujT(_OmI5QzP?n>`%b}z+m)K>#)lnsh=v?O=$H%;jk6E#7 z1|BE;Y=|)f1C~T(O=8ZVi>Solc#>{(zhE2=Y>$a`RxF`zCqP$9r zNrHxR1}~)Z7`dPgpdb~}{XT?l?=C%ci5X@i?{qN<^M|DVTR)! z(3~hEk6SiZ`?evuV?b)~yar%K0^e9-E;08vWjk|I9Sn7LgrW-I1n44R=9wj%^?cor zdy$AmsrIpKcV)|`u_qz-9d6kIsyU&I@0Rf*P`<`y0lW!fp4g2Y-$25+Jzu|nc>y%> z(2a>HN}G{s(yEe^DYKLJb$o{q5m7t!pCB$|OVDC)8V}-WlrS-n=zPaUQO2x#d)L1? z*aU!`yCn;*|J5ZK#QN30iWZG4<*xrVh7DmOMlDmHLh&7Mf z3Ex9h%5TTPpEM{WBPRuWEQ_JzrwC_Qaxj+t#vq)4o|dmnA;&=#6eQOQGST1k8scZU z8;*$-`Vs+@v$79%hoLCFwwdVKS)r~d(Ii6lm9pwzg_TzThh6~lsj_5G54o$qfI_sc z?_x^dm6EY4Z1=`~ql6>-Bpe^?(M`3S;C8^XB1a;s9*W&=!C55T((R z4)`ZdFD1kyAY*oIzxNm(V%hh8fFTr-gsWvIK(0dSj9S2jwY^s>vK5)fhx(R3@=QTu zKkb*CFhwfC*Kgu*ZA%?rns6Ez=Jn~~;E)#qs){rjvxKyCqWkLroa}E=Y&;OjN~$Q?KP8irF#W z5Zc&tPqTJH&{cP1UK{K0@N=H%o+Lc+U^mBC&Ndpljh1V-d@FptzB;JexOf*E%6pP8 ze~0$+f}fA7yOtl}nP69UOgQJ2tiT@rHV5D-JD%M_DqE3x(ZJEZ0b#gm?6SdQrk=uT z`NvW^)sB{8sXC^eR^*lX?bRY0SAxNO7tKWzbib!Pb>Z|F9aY0=YAL}{(&>$BDD`zs zuED&*3R_4Xf+H5{t&W>rck0jti<6b>i|xSBUg)?tLgDKmR*m$Q%2yrKD7yo?vFKCO zvOtGz`vbpV4rZ30$vJH0Ov9Txmt+4Z$Qk?kxz`K!vkm{itqICs*KJYi7sCr35uP!S z;EC-1NdZ%0b=1yj;8&MbUo(AmwXEh+)T84zahk7A^B#H$q9Dwl^wWSEIil6RePg0h zVJpc&GECJfcP~#AbDv{(cClv*F<6ibK35#SzeEywZE-G$^-ir+k_{<6QJY zl!nedbI5moBclZoGzP5B`a9I@DiE$`k*?`XJR zvcSieU$X4i&pi48=0rAK`MPrBel&BsQLZy9vRN9mC!fXZqJ!wC#0u3)k;5^?} ze#j*S1euei?eA+Ui+ROq3TiNVvYrQ=UIbLXfM*qYw_CP`+v3jmfb^tAL(Hz)23s(( zT3|KQr-!s24}ay8#{4?=ld;6Qw1g5Xmqb@$(Sy;Q;V!f+!%oUF+r{YLD)J;R>R{;H zcC7=>ics%*FRO!NJUVDo)Jg2luf0p_*Z%`-PCYd^$g1lB=^Cim@=s`nDbNkyK?c5>u(A6Gqb z^baeuHn;sP!1j<{-j-V;RDE^UHohU`eb3UNoX6VP`as%sEI>%A2(oHP&#xPgtbLca z>%Koay^|LGO~{K?DOBVwI_-L|$c4^~4gl*$n>v(ykQeJ11qWZVJIn;{Xr+&`?odXY zWQ7GrqJA2S;6oqWEoLw{up@&H%$e(33H?kLX(VH{jA;RX352TT?C8Dr#+oyv{Kobb z_4dPV1dpkk1yG{pD^?<<|w2#LbP+r z{#UJcM>%{%7KcKUdkB}5-j&h!@=+lg&+ zFu}^-z37&<0fpIY6rv1^fI=_=>LB&6SCE1lXV+GmzIsN0=NOArUi&Ao8~S*Y0n}M;SOjvV z18iC0o~U)kq$F-&FF425cp!W5Ke2Ugl$SJe`o3;(MiyvLT-_ZKwfW-fUJ{r9EyeYA z*ff8rXKNJS)5yqecgm*dP}Zd>r-^5ZTlfD*BxOIEJ=G~LJts8Q_xID@mGqrQ6^b;7 zh3V>5G%~qPz<2mgAJ9qFt4Cb(1Df99KT_M70R;j3I?5Yc!ueHW+bN@DdKgcdZ#`8~|1%^0 z7kqd!T;#`K8q2!JHSYAnc(*TYoQM?`+9CFtev_9oi2cH zSZv25eyq$zSm$CM0f_R6G=N`%Zk5(PIV6I3$Z7-thP^A4>-@pTp#4vRgFa^p@P=~> zJg{x*IPG$37rpHyN?aUAx~YmeShUbwz(kaG7EI%OosYGElH*5r2Z4#dH8`v9KHlf< zK~XNn(S8Af>u?r2>3l8{p@XA=Ci*r0;q@;hmdU0|IoIK0^vHML9e$74!sFo-t92My zr{*BiAQL1^uroo{R?O(yhb(hNHD;V{I5Cx+B%y~~9!M!c?K56$3`(w^g?o}8U_Y)F zj9h&A>`xs~z3TLG-24IO{!^(oCwtgVjeOh}0?|i^VRz=e+C5#EVE-imF%78;FVA+# z$;?XZAvHH_{CE}z-tdr zfd(I6u{E)?&X@gN_XQd`npX45Esm!gI0nu^!;H3?evq%!1F7`3+-lkaPtOl{FVV8G z5W$nG1m}MJKAWe{;q0uG;duq$FJr8&`o;zyx*!}(S`T(-28okh>#Gu)U&^)Tx|}S< zq|4`Rf4M(h&h7A{53Rjl3})9ksj5!iTs#tWGB?uFZ~VW}%g56cDR{tiaE>C?tV;Dk zXNFQkKe5Ks#>zy2h|}WCD~Vg@b|+s>oOtC^VDDIoV73&BnWlH`hRyt3>yO7QR=u@B z*~GquZs%1hg!p3yinZ^r)&Nhy$u_=%k`PAt_wYl=He5fXkHRfao*#Nw81eHJK}5}9 zFCvkE#m#tO$GB4|(GOMt4>g^lkhKBV+=4H{mmC2755W|io@}T74DQ6JXX55#aMcV6djq@GN={c}?dPmV?+<`ThRKDIBbIQgOvf#_Y&t5O-u zNeJ#L@wsbfH-9Ln)~eYvv)xQ(Ee)=(s<7Eb-y|0|QeX%>XBz3*5tdn5Q@n4O{<%lPw6zF$V; z<#$7Y>J`PZC-n%gOcI_Y?Y&tbgzx1|ckaO@e$H;q>pClSa&YBr0ae7p>Zb2(|F8#rfF)Vsw*Mh2gKL!7~u>U2Zi8XLb*>_*k z$goK9l0$ZJewN|y#n8hWj@3E1Q^XQi@%mnKa?Ny5in5Ta-O=9VjF7pA$JaQxO0Qx- z+Bsncd!(%q&~#guM8Z3~80lVoGa}P1$lL{c@YG^r%gSEDJTJxw8*SBBc@8$d>mg}I>^Ky8LvNTN)&G-Xq&Y( z*k4$;j6j}xmxE^GZ7uR5mEU9iG%(odsYI@u&yk!kR9e&CZS{G222HI?)JlO-jzW8# z@E${g9-xbCUwja%25r-rmJ`W&eENeA4lpAu-d52pMG)myQVw!G4u!xtZbAheGW~a6 z0yil>ISk--|9Ku%4)=CuE4Stre_Tc?7)JEvsZr%f^F#IDQjSf;gXBhWzOMFhsRtXW zaZp+#0KNVxN^n?(*;x#Ww|huaI)Un&rJX_To=+jMZ~1&b=v2?@5=&at&Z>qvDBcqa zUUN_JSw70zmt?PMIY#Ef6fmh%8$L3L@5nD0@a-dt(OVFJ1Rh;^Kv)NcI;Upwx+HtG zq-!Yo#B6SO8Zy45o9F-{$^w5B!46EPVLCAcEgf#D94=E#+boHOGcNEDLuj%cePR!x zKtNN0kS}kbAbT_`i2X%1=q=Fw*=U~l0F83z28}L(If(U{x^FJgy}|7HG`V{g&LcEF zyB~Jon~Uxc#7o@am{^~KcJh?pu z!t3;4HTCYd(+jCCF{`E_`^l(mm~v(Y;oj=-ocx7P#DCBVm2Pb!@?e8+Ksd^T8*J|@P$3^Mac-MDH4oBQ=j1ejpPYwM! zz0OOZ8zT`n2oy=lkYnGTlcQu}zckR!M^`P#d!{O>pbMIg%i&)G; zUfLx=XC@0Nsx@+y$uA_^e{GgS$K59j{!6>AlEAllZ3jbRTWdLK!+Cd0R^4V; zyR{zLAm#2!52NgstEsQb0&lf2ZzFMw@Lu6ftWXhFjlD?!_dzNz4ac-1J~FThD?qBx zkGfoq7U4fspaH-qCClrwPcABkORE*$KDg{2z3j(JsfWfW*+>$SlHF5C!3$A_-TT&e z{s>SOA000ne|mAJsGcd3n^nao&bjg?CE^IzFfbh zItsG0&g}z(Nfw$M3ETXY3S=8Lz@q$eXNM+OXCDi2<4_b#xgyq0^rr~StQ}rf$ty0M z`R>92H(`YIK|MTJ^142NIkP>t=gWB7g?z47e_D(c@rUdsR^BCzZPT^CjxDx|orIn6 zO=;^EPq89G#(TV*?bT77KlD2DbFj2;L80y{axXnqz`2cM<;-&nvIxU?Sg;120<8Q9 zh0P!+78{I)*_ZR4~8 z+crhhtk*AyFV%xP*no91gtiUTw{N(b3S2x-usq9++}x9?V19>ZCT%ih3HM~$4xGET zr3wS{FfG&ZBE5O&=E2C(=vF2Pwt3tZ_U<`>hKR^6S$A`8Lf$AEAwz;xp{@RmW!v|p zq5fG%_lyX;!DRlPLj8gAr8grX&r+73)x)(aN@0U_Z?@kq-3=ec3s_`&;3`P3SCrX0 za60l5e@hh!>445K2X6&Sc}xk6iye>r21a)QVJce2#`PY&t4-z%E72+5mt^8lM?B+H zV~PLr2`_+X7Nc?&)+kX;P2bn{BNgR3GP^ znX$?2junfc$l-PNW;rJcc`VHr6jR|hr;jk~J(q2ZZGNs1z8r0`y-{7nB{k}CTFZ*9 zMbHuGS?GtB;T6(WCbSh(m5FbTL3}?XmT4#L8$|8j-W{qJ}bAMcD}!d6jb>BfNQ3-FBtWAo+JO z>rRS2F6c}@@g<*dUxAMLXvgpRwn^wo3341zmU1}9ZMBcLNZ4$~Ifh>vV2tLl%^_U2 zaw!z>vEQR!9V(|wrdn+p#A*Rwcv&-~FWxnY9yT?Y&?hlcB{z(c;5V|_P&`cx9DjTG zMy3m08eY#8=CDP8MzwN&$YW($-}s2SaXJmR|48q|d>i0UB2RKJ8oq^#AZwjmDyfwER-{SMe&FSfL7`RWF!)4em@A>y?(!KND z3}PuVG%Zqn%NOu8ps1(3YE43Hk&F=3gND2-gE&WL>O0viMWe3N)<*iJ(l2Kb0e~{d z``jL6CQZbeHujI}1v+HOHXIaSri@5)B`C$#e#G!+Pe_D0`CZmm zD1z|6+tb zXrn7{@mJ2-%_(S0Kj~Ks8XW$z;8STZ9Mq}eEwte4A(9yb`l-YtZt5YEh)K^--t|jySkt^< z+_^JgT--J5qf#=}p5xDNq~*{J$t@%?57YJw2HgHE9qf(PErRM%$DHs{7b1AaUq>xW z^KLJGgZsl=k>7#mCqfaqzD+DF6ieu5W`KkSGPMA4AGq)?pvDyqpe41D;L2A=(jD7< z8wz{NJHz-Lh8!I%h_IN&0A!yF=frzg%j~*Ib=BY&^_7o?)!TahL z3kkvV1Orep*<8;dkau0k{!aO_c)=w*wX1b^yw})I=%Ehl>t9yPR>};!qwXia+}?@m zme>rEyFnjp`cz}Gs(t0ScP=qC2wftiJj;Gp7!7H^->uE!UekF7;b%HokMaG=Q&2Ow z$HS*0x89Yv-t9#xAc}&&y;AdzHdyK2_=3cVsEYFEBug7ZXvPsJem<|AH?~d7HFYC} zcu~?p|A6Bl-Gbts!gYB`1)(@S5JlVFgsF=P82MV^8VHLr5!WzR3hp`v%B6Y8;#(F5 zYA3qzG$kVDqkMKFuXnuc?gyYD`~o$Cd}|$bjkURU4x4|ikN7-P7>d038L++swZ=5Bw-6h`Y^}k*d zh%@euVGrBSQ{n(v!HS$SDRsBdbJHNdFGxfL2hj9rEsLS$C)2_dg!Rw+jyUO`kZo?K z`Lb(0%4#H_-u?RxQGCix^%%a%qXIY(*g(~bbbKEvF&eAAm%5Jfxr1iE4DV`FWJ<=Ji9-yml5;-vj7a< z)=@-tpH?zEcrsmfli&)HVco3WZ8rx@L3rbgO;v0<@$XVxbDviCHhK+-{4Ti1fRY@(we zx*hwqLhn+>U2|1%r`o90NbLS`fqSQEYBY1LTy*um>vc7(-7*j84=AWoD_LEp3Ew_= z@Stc(z_{y1s}er>W9l3I%Tr99`9y_$N|>`j5;LtS5Jz1VnTR0LxqRYwT9FTXxM)K8 z53+ngoWs@_#H|My{sWo6BZ|}3)M4uITvrfLm610ko1Q`i1?M5FVpL$RfmVOGSMpq< zHHLc&M)R7oI_$bI9{Vpk^};}?!Cx*l&V3k`V~6^C%xii!}MfbG=XB+)HNLvJEHm^XGb-YK>;RtCKM{( zGhNjyy7y4l^wnI!cpv3!SQNNJ*8`R9#Q!+Iv}tJL;ZWF+z;6z%0K2l`Jdd5S>Dm}+ ze!Nq-vRCZK>_|;|Xi(I7M^&9CDUxviaPf#ofB6nE8`Ps1Bzt4X;!eEl;*_(^D2h1Y zuI0NtW_?vJR#vY%K4(!QD7!d|UY{=%rF6g+3uLIHTv8KBba@^nc0TK26hxd@Svf`r zP<1(kr?sSXS>msKuUpQJ8g8%;C1WK`rXP znFBPBZs z#Tq)x7vJe2V0#V{dTaaUWq*I={z6$0}tN5 zPV97J7Mqqbm%L|iVhqJgjBb&V_lX^^)<7?K^W->FxB8b{xd}+TIIYU;Zy1hp-|I1n z74{Y+ukoFySNTp~8U4`7){&>4PHvJkPo4F)?uR&RN4IsA{;9+!(iPc|y{05@A#k{V z9938m41Cdn*i8p*D2v?B^h5tY2vyDh053e-Q!1bdd;arEVpi-tZ+)x~p9SCxJu|#8 z`lXIx=fR#WPX{E2F~Yt+b5RExYVN*H{pikhC26q!Fd&Xg26cAbKTa{JNp_)akM{Ve zTbXO71VPc}-}^*(0t8=~mEY2WtO$I%T3CAAgPG2rf`C;%2*xEM5H8VfC$Lke9~8Cy9xqd;r~x5dFPLHwrc&P|8R;xbHL9 zj&cqMkPB8#EVFTY{4=i$i~hvBnBv?UX-yNJlX&|_S3TYRkSwFFCRGDm5o2+%8Ba=L z4CwSRH62($p#w2yHG=>P@*%VJ$5x0S|C@$R`r!8~9OQ4X>CvC$YY4mmREumEXMCSJ ztw1UgA6S;wo8(N&4X#?7^&oSwxpS+>*nk+|`9tT?WLMReQt`6U!*i!!Klmn2{BY-o z1Fyv^X5-}-a_hXER?DHRK2hfduim5LI`Ll0ssva~g}Q2q%cGPy`!|0sKFYd`9!az` zpD)v*wq#E{A(FVw%sLZ;C{-EO$`#hZ?gYdan}=QcRGGRbNohGzFd+Vj-(1!2Zgk+x z#6d-Gs?XwYJ!#I*5etrzB~`3wS8d6Bo#9} z$U6-GU^&K86I!u3n;|Bs+I`K2=1qyt{8{pT3=rDq(}@Kjm&$|GJ-1trZLs_XMV`b| zl83mb#Q>GN_r6+uM(UW(^}4X1u%NY{XKz-R$a2P>bs4JfR5;)_WQSexr@a2dG2lAJ zQ-cHkT9H;P-F5PwnwH;H1O15Iu*%!>)Rt21P%sXg(7M~r3k7BQjxnB9%WIbrZxzH< zmV8z4-Kht5K~-2CZ2!Egtf-i2eQ2Ey6CiA-762HX7o#o;0zR(PItt%`tpkU-u-oPj zTlVy9Q!GG$DV?Vk_shchyR%}fm-Ep_Y4XS}K%S=)ehpiD)|fWzRiZ;XPDFtu?#j~$ zDeMABd4+Ubs!PW>J8*gQvK+soroxvPo2|9n{Bxs}@a;|P*Stg_L4QlNqBb0Y92#1@ zVA4~e^IMel6IKZAjvP?x#qZAfK3cs6dALnk)55B_uif(_t*#!>Pl@T*kS)Inm)E#O#dKSD9!*WWU- zy|HyAab*PI1&`vERNX%m@O>9Kx<5-VfYOc`Uv)7HQQmUW`IBFmyO{p^Ew=1lA%D%9 z`p$0+#e#9>8BhhcpJEwkUFMhQu>5}9kzRnI!+*b^jwi2L(Ywyr$yu_7IyD1O@;RW~YiT;UU=(R^p7uRH#> z+!t@s9}jTHoJ9{OmnJak#^p))M@v{%s+`NPHkjhI{V;-MyYeVYHF2wCsr7CBP~NC` zBE0WRRT^vmLn2P*CA#3b9zbcZ2YrD#vFDSaS%;9(2dsy}T&}OUf=yaF9$3?Ffpm&? zV*aIFJwLC@r_%xsr6eWqmh$L0`y7omLfjQw?`;%M(HSKW6dJ2j2CxRtQYpaFlMKDbw8YsL-Ms9{hl{~T5PT{*p4B)rs zB-8jd=j(c0KPm-h0zCj0^l*%DNqV`{^t!9?dUn*aoTT=$E>&0Z%LNB0Jk!jEDI+IK z@f23!+Flflx_f@JtdTwfic96e?o>@*5E&_+zjnndeYCY%@M>|R|Fj)e8BSQWS}uB@ z;}nR_i9r>hlsA#d8%Wz8OskNeogM>~ek%B|k$co34#pO|5#(s^A_@(1qnWrh^AB6S z_%AYYcbg3OAoo;+8XfZ?YfC;Bcut+HZ~z%oPLYOM<^PYZ-;3FK*Zo>sK>}-B&yfy$b-W?QjFgCO+TD~(bg{rqeIikz~Ls*U? z=A&}op=I0t^3E1NVF3~4vW<_^$f@RbcgzV;eGlm|&GBY5u+@Fzm!Y2Pus5I6(s63s z>Bp@Im6HcW&t0S=%;QMPMXO?G8t?VFWhkyKXj8+bre3Jioar{NCa^@+oGu6jaczYf zmU?(WSaQ07PD1;S$<%0iFNHE}+8^^f&F%v(6jqyRpyPtc58%|N!z;h33rLWd?)w;}{jbU1#b3D|ky|my zGRU>}MSre|1Hbm3v9}!SRJ?xSfEVx0fzlBCK$E1Gx;BDU9uD*=(viy zCpYX|)JL+@{Zre0#3SQPy-V-o#7cxB$D8%kzQgD8H4{9N?-*Qxg#KesQ|iX2f$*id zHcoYQ3_O70hhM8A`ND?WSjhL9puY7z>tGcGA_Ax77;UAm&CYrwSBLUiZ1m_yzvYIb zHWk4H2#g9OY&wHKqzs7h(0y(i+)oid)$RjoW#)dRnJaVT{ft-;)0cpKBTZJ~X=s~S z`>H`Qx+AM*@T0cF3EapXgxuv&O^HjZr$AQ}rfD7e^+fySH)Sb_u2`M{%{=^b(?`ac z$x1Am+hCiza{MzHVmO8sNp$1`fJV+_v4d&6#s?PR-za!Q@3AeL64__YY7@}}+jIPS zf7$X2lgxChW$CL@A|WyP+dI*qjI@_;uMUx9Yu6w*9G%?X);KD97dpBlGCUo{d-2T# zwwwvxV%}$yByEzvzJNU)St~87_yRdf6dLIU_I#>(x^5R@pfk@Oa|;JUpE&mnzbBWH zk#5OMu;>*QdM~G3e!tdAcw=w!WqE!kmIl-dY;iUar< z_?iLr`;b*7+HS)ll5~pP> zQxM!w;e_=vRWKNS%O=)gt(_+>^|4y-Uxkq!f(<;&{p+DjO$%VELrP_&4@N#~5K4tR zfONkh+Z`sgjwGcp?9rL>$-`l4c;pE+QK*z;kK1}zWUwp{X&l!Ofn)gD#hdWG>eF;uwG+os6-A<+-X(B#n=(iQMpPd1k1HpmY z%T&)ig4*7pPuZ!GMQgphJDOjf$N9y;<$|6E3J6NceeJiPUuYUzAtLDDLVfshJ%(He z42X+P<+Q^0=iX;gFNEe}&@A{GI=YLvS{=M*Y-H48eAbI}iVbO}zXKIKt0>ws-N+B0 z+@i47EY`jQw-0Mq_V}cmb2$-us}d$AGF%Fbs}9*4UpBJmA(Vp@DLl=?c09}nG0`Db zT8bCPb>Lls~FhX z)p72XJGP6WBd^julF|Fl$eH{pQw-RM_XpyicsJ4fV+9;M;RH>_o z+Q{aplRgjFr&p41nsi#$20TuEx%cyZtkD4%r-hm-Tmg|o;&7q(g5CIl6!Wu zYilj+`~~j(y=HW{;V-_@(JrwT@Njc{a^>Cs^JT^T^aS+62?4)6`rlqQfn)p-d`{v= z&)YEmsS6Yr2ebmEJ8~zjNUi@-rsau3(zLDn^nTx_gn7g|!M@D`k6I*lAH6tkaG<6}<>0ukLoL` zbZ-22j+{Dc8!e&(_U!R`ww#{M4DU;ex3HPvR%e^-`L?Alaoex-fy{@ ziod(#qO{r`fb1^{Qx9|I>-t>IUc019+^3522r;RM)3ifU1gpm+m!p~7=^5Ds-MF3= zJfS4@z~oB`&m>+P4`}7>z1!s`_mOtC9Ui`+F!Ht6 z88fR)Z{iEVk48OV(;ZLwXvbPn*JCmoyAS?lcwBEkLv)&*Zu2tmW6-(8-6MVGt4EHJr7LE)6*Z#z9eeqwv?ehKazia_y?&Q|1|q%ks17( z^GerozJ^FIc1g10GrVZYHW#&a-gBU>4%RuKML48|>Z z4Z(s2clY4XxI=JvXryt6#$A5Ssk$$4Zq2Iw0=sIhxyBsx8+foCtiwi)XP+$SsNNvh ztXVDzfy&)4j5h5bO;xb^ljitO-=z8lG0LB{cj;vNSfNxomn`o7@Sqow*UQG%&gbk- zI4NZG-I}f4JxRB2s%bvy0J2R3MEG~2d*GTG6bBSZvaAPWuVJ8cvdn!n=W7pC_-0sV$%ixtd(Gx7niHLhrL#j9E5>Cfsrbg7_7=@Ww_xhijSkv zM8+67DE5-j!boXd8F!{|HudyH>p*(}+1|Lqy^T&JVq&t!pE={~^UP`~Ug}5kuV+CK z6Y7n4*ewp&Dc{fD9}=WtqZvw0&S3I0U**^#q@y_07RJWKWBnEULhqW-;2ctvuKnU>xW)j55=$L$;XY^~>cM0CHPoHhIQ_dPLu=D9;x=OoS= zneofaTeCJ^Y^#D;zWzED>!7k*P02^F-8ZGSP5kog`swoqvX>4f1p0fewckAj&1^X) z(krz4Pv)jm0AU%D-Jht_1+n8!HfAezv?=X_Cc15-R5@8#%(>bKZGDq(-{qaBl^1xu_;`D?wTerACAmvHAlc<7DgSE~ zAF%2O4L;mudN?AW98d~$`q#T6%5$AOyYcgRpZD9B0UYxS-HWFst{$j(GWf@mV8TC{ z0}s6EjR=>s^x!+KNZ6)_sJk~_8+^~r_l?M@tQ3idOf+t|PE`3780io#NXa1|B=I%w zO*HYSM0`2ac-UnUH5yitTv4}^KXp4c%MfnQ0_b|RFz#@43cOce|8Y3yCjW3|uixb0 z*jk*n5a|0fetCD9pod@JU0d#Ro1J5FuZ#w)V;TS0My~*P7`c=YotdxN>7eIv`2!?k z%!A4#I5EZ7B6-0gj)NDll`peI!H=&Yj#pn4n_; z^Ah#Cx>2RboK&*V1{dXE5ShH1^KR^7u06PYh(Jgw`od`3x zG-M$dF?h=#2C0kAZM9RUTi(3{3d6G}8?FgRz4kB9<|L}%N9Ebw2PN4~Hhkx|4DEQH zaVH1A_hGhW1i4&N^!*VJKr^w+)`Fd(c3M^)KyNB?v-eAnt?MP13k2z$uOlKEZj>qk zEyv|>U`=ofzvG)&O?RLvdfJLFo?Gmr8ac%&b7?YzuJSmZ%Vazg_GUm>yaC%ou#VJ) zmh6t}u)IY#uX1gySh)qm^bA$dPeP0%4Pn2w!g%~nwqpeyetWeg$`DS`H*$-%D&8O> zRiHt(vf^j6PW@UQonA#2ak#Nr6}Z<4T&wJ)vdlthZRNbbrKTJ%el{JJ%eM6!Mq+K~ zGo#F>{WqmyRCu`dP*hR+XqDViH6sb9a`1PVab3fGs`+}e!bvi76S6`SX7`AusJAp4 zzw9+5kse(;3>CIyS((SN|W?~l34+7_;s50HxEtv`jp|M&%WZ$-BgXM z^Y_+Kl&q}D+Zn1Cs%zSqVc0E36=;<28qG(PGT7L^Wv$cM7(xGnB|n$n|p zGvHNNS#EwWM&sH)v_<}Z_FwRNYKfEst&lslR~&0xMt zSeGyt;exH-HZ4V#q~0^8BSfouCq=wE`K2YA3;gkJ^wV2V9IG7!o0JbE=qLS64F%)U zzdeaa@0Z`82K$Gao5sg)2xfVu0i^*VHAu%Nrkkx$YrSeVNYi4MS`#~8@7cO)^uuhH z>7Ou}tfmknj`QU9WdxS9ww`bL*F~D(qbea$`;0vNip!HhfDtmf=YzJ8ix& z~yOBPCPaiG+i~(Ptx^BVVFDW0AEs zX_>D#GzTqq=$qSzEeXGIV10rG zF8w*_hJoQGTT4@5l7o9Y=I6?$ssz(woGr;@0avNmvu*OLd`JRnEbKa%;hWR%w3$(2 z%9QU=^xm24cl!`jBr$iGrrjdj8E~kk+KTwks_mMLM$Fn@JX(^!UKC|ir!(;X za_gw-S*5PSC0h=ybjIHMG*=^QAR8107JFBqhQq^Zy;A`` z`-&?Ny<$yy%jjv|C(h1Fz>m?!W3ZMoMzArBrc zo)D}rI*i@zx%-N{I;xl?2d8`R){RDn-mRWRX$W*04+_|9e!+mFFBATeM<5eHs2zqS zeDs_i#qKF0a;cXFeLs7PbYcZ$!41mdPbE`<|KH009med$CNtnH`Rw8Pb=7`Ti18r2@^nqxeLGcEv6AdgeH4j$EP zE&Wid;a~Y$m)HbaR@4-W$?q%$ftBEPJ&wXz`bs?Ew*+&XPj)Y0vMGul$I^kM!3Sj# z=DU&+M3Xn4mPt2zT#MJFls*+p##~RGysJn7PeL2uy0eebajS=RT||IqrT9X=V32c0 zcaW2{OnvZV@t+jA6;RU8*U=fodjCX?>>Ag^D%+pMKZx=Yn7Q?{+Titj^{rj*rR;DP z77u?qe04%RLmR}puPlFxzXepcs6s^HGPV6o-TCT`tG3!rwagO9^eSZIzXVI%5rs0P zJHc%U0lED*w=b&<_z?8~{4elbbwb3O+yAWaAPbbg4W)T;@&l`S+3Zl;Bn z;N@mlfmiNh+}F5zA>{#sS1Nccgks^epow4aoAclJPv=W6dh>r`kC7q~o^;;4|j97cnO zH-Y|e``@=3{mz3V-xnY`^%_xEy%gyKNm1UB@F5DZXy;e@ z5%t1wrlf#i7_amjBFR=RP>U>PU_}cY5jh}qaqcd}#^SfpFVrr&jGHi|;>uu(H<$N~ zCn-#?_7zRF@KXRWVpgB82`2Mfq9$J`m$$hSvH^AgDZs_0NPjisOFHpn7DDjuu3ons zrrpuR#eKw&%lCxV#*ul}wwm8vBCWMhCZQU8w3}uKY)trbwapSFL~+BhEhi)CX~pCx zI$c+^(|K4dCpz%cmhZtbwqz%%4U6dnn(lT5a?px?Uq7xY`Vy+}@QNF~%D}hqqB{KaanJB|BZ>b144R{8&aJHc+N4ngEKjO$WTKE zS%DGh38I(dw`i)Z9x722xFN}|xRy!w zLn0eEnI}(LNXwgnsyKh>TrVval?v&{w+Xri&Lo@x-aVr`++%ou#b3{p<+p{OGDf3s z?H3zRVpt`r?OZlT&=i<&RtkD<@bGZJMYYLrn;GL_9QO;4qx`ngCb?uDi`ZQqR{ClM ze^+|6sNsXYbB|hnN7{>%;HY>wVW8AJ?k}8T` zd9j4CE(6c0p7ZLMUQZTQ*Qf^8qZ~uoJO);#JniLTtRh`I(hd#BV@38z^)!e{6R3?> z9MW$igVCq^Rel6aI`&rGQ;-5$Uea>N^$$`Ro5LGgl*z`a33WM&O-Og$G4EvT^G}(q z2bY~UIf$K378S3b-66sPH`*c<3?oMjKE%?0PNL{N4|>_z7hEd)!GWB1ULY&VbghJ9 z>530!8EdosftP;j&3GLc&URaEmDgW!Ha(rf=Mcv1Gjmk*Z)C+L{wO$a7{92sRJ9GO z32GS(cI|T8%aiw3JC4RrhF&pAdip)Zl`f-;s|#`VipZZd7Pc|nHtuAW79+hJ6WuM| zuF067Rs^bxsaTUuB+5gusL%(%Bf*W?F1V7}m7HbPw=pDr9*V-llNM!Ifu=HH!ehfn9-*G$Og>ONknF7nR z57<}82z$9cT4=2@=-3W*gTu+-=@+xdqdccg=@y4(3EpJP3?TfhrV~NimD|9&f8m{w z^c;=~c^r5V{9G}#EzW~Bt9kVS@DUA>RB%bz;XT?g0uBn@Vc$DuCk9bnrm!#TVYCpG zTElZOyzb_(oI5%jilZ_*5q#M6W^fi;j0dXJm@rFZh0>1qoL#Tl-rc72JvLx zp3*#50ARSg&CTx~iD)V>j*c|-P83Pw;$!OsRP6A=1wXIk?s`6*mrVm)VSp9@j&+oANco2Fr)R37=9(96rTM` zRShnDI_c{$y8T%qE*P#3)leNt1E;k_S7B&9a)-JfK#>+8=H3#0Nly|PY=tfJSbwEdua6%eUFm3l&sMvw{B#J8*M1$YBM ze!CrjmC@hBARekBXHIn`eg%d}erGNhlhrq6?um{TDZfg&#<JA27kP*J=*(g?6-?spNt*^vdOZW+O|OD{sHDS+(^k2w)@l2~9SDq)xTE zSen=kt%tc`YSoWOoahS?hgVKTQICr_6ro!w$TGe~CQ|efe(r6r)A$uiwAWy&zyY5S)4L(uSp} z?B`x1rOe?T9(|@}_>d~ahv}fLu|5q7rwP zSeDFT9}&QxW82+|5yEPpQm^MJZnPueUGr5sSE`gD{<_}e+shBN>mzjNRSVjb()XR~ zT$S~h%uA9$8mV}GG^9cy-cGJg+wTr7zQcJyq_3FE&L;5HE3P!yP^Svy%ToKtTGx#t zR67Dg1FUNJOlY0ck`@?%)%^unQI-JU&w9b2RGNbk#nUL|KYdiKo`} z=XtEd)(^ULVfURlzPMq@Q)HMI!}m(L@n1Pv?_EB#SXQl#bD_vEd-kR!L!7=PKb9jn z9{h19UynY&3TwKSs1M`{zOBGlU9*o>Q|lCQ~ta<+C{a=jyyqvm40Te+8Hw=MS7>6Z|VTq%(2s;kkPX zytbl|;jis*@7fZ6;R`ddoLDTPGU^>*E>~dAm`Q~ZSVBAYM9jVk?!6I~oW5;7gtX7A z4UBhRtip3g316^-_8@Gfol!4;V5pR6|KgLMuo)aa5fL~}1z#JMeb$5UR5mQLiM3{q z@Vabfzi##7sO*ubpWn-RshE8N$;zvT+VumEqG3E1X)8f&A!eV7D=dD^+5f1MUFMH0 z96Q=Ot&S^RtQiiEQ4OKi0M(M)FOp_|f4UI&T9(yItji8RefR)<{0z(6+xkz3Vek!J zM?e{--{^!<=1hDiqX6Lz>+jf{lDD_jTN0Qlr%tXq)?GR@sv@ELIb81vu{$BX6zDNt zK5w*DIGyASMn}KAmuY+X`AIdrCS5iAz#>*$z?yUIOVMUgJ4ugcX=PMKLn7dEGhi2x6PYa_s{Nof>l{*q5W1UyGk6((nT4jF^!fu*P z_iq+l7{7IHs-~QjEbdRBHjc$c-=`D@54f%cKvjC%GwpXX6Opu>h(t_?OzHBBJhQ*k zZvX9^(v)o70*$w{%}Qn$de7Ekas?*ab=r!c?atVI^W9Fz&-{|)~M#>+Sbw>@=8m5ogxQq>YKf=(*T7u>!Qi@MNB z(oo(1Z4LTm>nVdM<$tU@U(QL9kv*O(f74j((U>P=Hc&xb%AmO2!!s4L$l@Ndr+-tG zxOHHNE;$P;BW(gHTriwel@H?|k^6k%2%zRwXGQ^GHA)V(!htcHzh9iYT7ap^*Z7E@ z9;N+31%qLIwg*{Qj~D*b91sqF@x)jEjZmAR>msRr8OO9Pf`Zvuoa0H9n?nO9gaKy# z-K-i>8`|k~*0SD>;P-T1d4{Gl0<1SMRC@p8c{tE*Y9wu8wq{XNzxqnkUmo!btw9}T~Nv_wgbP4Pa%H+`@;x?Vr1r(DW8 zO>s2J_3WyNtSA-&2_`{?z5$~4ivYkutX$&9*4X-Vv8(uCkR2n{9|*3(8&Qzoq=Q01 z08oEX;qZp2#pSuq6)q{R@B14w|4-X6j%hgQPh7>haD~^2!zw0=o@(#`*@X1^fE4jg zuN)T9MJjj}`2}=Vl76mSt0Wsjd_-9HyNn&fwSsB$&d-Ps&5yOkv6;K z$_%)(A|30vdHeZnZB+8CW$Pvw*9QS-&<>D+A&Ok`^mKW&%JUYr07yDgHJ_fOPRPH? zS;W@(7>*SS7XN!VEH*`AIM%-I7_dLM{%KsMs~k!h9l!g2XEbgjl%hWQo={r))^nEXGT>+5S#>%$jna95)5VHdR!@=ka+oVaQ*fMSj z(K?UsdSb2M`!(Fk`xrklEJEo1-tnHpj2HMrt(?;DcTFo^n@i-b75<3Xy=d1TbiVmK z{1!!c#D5Bk0WczJdF3_n5W}+$jF*lMnx&r?7+#{52#I(SQ6vU@n#LUb$d@>Un^QUI zA>3i+OuyrR)G9zy^84 z6X5Ur1ez<*+>ioJK+1oT(^z(f0~@`#-gPz_S?afhOZ*`_e`I5hhp_y}?xkKcKbpJ4 z?oH+l+TnX&uvJ>JK{X!#QmI;dsij%8hM*-i!X4I8U7L=CG4Va{EO3~Hi&Orm25uz#KK^gj!>>Qio`Gf^i)gKHxHU&QO5F^45lVFlix72 zSwhr=mm}sAT*6<*T#|S@i5uW@7nqt_o{aX_bL6$g;}b=I@5Dcr=gQ2eFKXunUViw7ZNOPlsOk0r>vFm5dNS z9~`i4Yej?Gg-}8p{>U)>RR?Tp_hw}>S(59o>|7&4kuhvCir8<_KWjxb;8yps1QvPW zRWxnzyKy40oU#fAu4i?KU7vWSP04GlXnj-zOLcSHB5fke?N$}n+)p9Yckdx%zI+4x2?7_(MO!vdg|V;!uvk*xiStBHM-YDk%yBEP{ddo={X7vkxS}b z20MhLXwfE>UYl^LcdI(hnepo_j&YRNMW*jjQe_s@ST43uy65W+r%vlismgvJ;Rywc zSrDlnvL82)?z~DQBRQ1DK;2v)zPOJqZ&(jr$zjN1tXPq^7x7zCHni{8br7^xUrljc zAO4cvM46(Q!kEF8Bpo1%n9)91aEYrPt_(F{?XV5kl8dda3Ft^p_MN7&>g2`9lzU1V z!;+Juh}QCJ&k!({4=22e*fnGjx9r~@f*H1xG(HRG+n7y_q)6Q%+epd*=7<{-g=JObqu%P?Lx{)Bh2u4O@9`0lzfeOJlfhRTlbiRDycs0`n5w*N*ZR*E%be=Wsde*BD(5ARQZh1Ffda3&R52 z)i4`j85SfOBl9tpqpw*EZi^R-76Znhh?Vt#-r{PJuliOLPuS<#Ow6GMl$ z;>t#wfTK+tSjlQB+Cw#*kDth7@SMJo1|8P%*b+Cw;)_|A?Fd`)Z>rZrDU(JLqL2*M zy8();2>~jh`b#B-6Ppu4m6m#j{zi6t9-(FnPVew#)ekF&;@*j6eLYl zryBXs63%x0qUwnF+sur3ex2GB!(IZtn@ zff?i_r1St}T^M7_*i%wIe1EeMOS_{~e1@GP{zO54{f}N{RgkuZ8mC_p(%@O<$msH= zFGSNxQ^_Q--yvLHm|9ApK$%6g;hfg>>uPZpcAdOf75^XqouMK!FvVV<-TG9zez5Sh zYFEtr=uECP?!6~X)M+zlfTK=Bv0HxpnG^yri|*(n=z3%0qx=-t$*WV1u!ZzIE`LIH z0w4N!7oOoxi^kUMnR#D#XGZ6lYA^q3zpf}$IoiM!4b5_H1zAu}F7e0yVuV&Q5 z-lK0-N519$_4fSic$IqQ?VRR*6oPk6^;6JOHs|7|*Fe?0uYR#nna#8nu7yh&=_d~% z*?5*pUy8>foUOl3sWHHK<4eG9w0O9k(659b{C`_bl);-qP7USZ^}520H8T5$rT_eH z^g0yvHjGS29(TygaPEddP_!Q%-u9dx0 z!B+B<@#K-7L2g^A2*=D(1ETC04Zqg`&pysi>&l-?WA#Z}rO}?a+Le2|=M_uMx4 z6Wm7gU+r%SlD&r4!z1LvlrZe#puUKs4aL3Q(d!(9KUE+kgp7^Q+n=E|-#Umz z&JgcXVL9U(9b3k@aU_@F5GgGET!u!?AG%D|sG2tF%ax0U!_I;cdUDY}bV8bswZ4b0 zzd@RrX-R^xo*-HKVW8y;)THebZKW|bQ4 zUlc_xz@FJh0;S@S_F96KJq-mxtEe&T_C`T7N| z2C%71IOh^%YDM8I|CJ1IC9F0hZm2(&A5qgeKIcJGi&$ zcYv0*KL%)NW5Q;L5F2!#*lf%|MajwO;&0NDRe0|H<*B!Aw0frKPWl$x@r#;DPp(Mr zIqJu1LCbZL?3>x|J3;-j{QqMCBsZe$&b$eGitwvSE}@;RV#GZP1*f`X`Cl&Gw|=iB zBphoU?oc{Bf@>W6Q`YMU+az3se4dU?$c+6M|2=8hHBiioTVa81nDbmq?F}jmnH;D^ z75h7q?OEKR1o3Y`6O=u>LB|d!Cr;yhkUsvX^&`SNQb2vCbg!~a4+0UULtL<3)z8R3 z(V8Prn&pR)7gzp^d#pMxJ1EpqQNZ}i-(ZEL%^KEdHnlUl*Xa5ztMd11WR;p&nja?} zKm*JnNT|9+Yc*fjV&iMn91c*r?8Yc`dF4`rSPc7T9i;~PwIw_%urBT+U)o4;x!qYP zuMmizvz*Fx2}LMOM`fzETYtPSnd#(2V{vF;g60JAem*$7Ji4Q)7w$3w4EeMVg&(yjWu?o5dZSmlqg+=>x-50Yy2|0h^3& zk_{!Rf%{6Sw3@Gi#Xi>~na!lOl*L@}?3aH4JaDN%$QmpLjwMa^>{@H`y<8@6O4X0|=oeEbHW<14@Z$?I>V$v0Q(l~?5% z*HE0*>Co9T!Qh3vqIp>fq~;wMUO^8w;N1}P9p}7}bYZMsyuUvRh)=s;M!~^yzy&mU z)tKB>ansx>i6Y5%#dmq{Eo6m-{s11f;jcuZH^aN(`HW|BOL&9F?-k9PRTG_?R%T;F zLpjrOdlT%_8A(l~ge%A_`A!)0zQv1#6*JG)wXtX%Lhqg~zrMthD@ick#+CJycgkhZ zbCB-SI8W8uf!*ys#Wk3v=R^vT&AYmoApaTwf1k#;(D1crl`__{yc6dTH9cJ713&RW zF>(+w5e{@ZO$`u}#S-~DutXal?%&4HIXb4-L8lz)>4J(ZaDV&9Ws;<>X*51ZR)}IQ zFQqn^c8-|g!iU)r%Lq`eH7GkfnJ8rq7U-s%_k`qykp7NDl4!CVGPF3H!F+oi;gOUP zL)1V`fn#KR=V|fdF&Q2&cRLQZS671^#W~(U{&R163H$;(FV!?R(~n>B0mYz>sntIB z1kj|bwcr;i^xwU)Cn$uM9l6o<><|J7ui%pZ39jUDeD=%%+c_{}F3R`O14{&yL(EC4 zRY7(2)7OjvQrd^Pb4B(vg{-HJ{QfDU%P_@-BXQ3@^4id;L2Z5|{+DS zg;8Fx&C-~<1$~knnVcVrklwsxt(<46sbPuh#>5Nr(!_f2d&TuMb$Sl@wh)c2Cg?Ap z*Dqw*L(vBkh~D6uh@LSrb-{>oDKEMw6nY-y4!B@xFa@WW|Lkvz&a_Mf*z}-6>P5K4 zOT*4zLXGpEXZ-o4+m3xG+&x8!9=%_%-XDb5|^gu}eKC5xpGK{CHPX5DCO#?e&< z?PV6*f08%6cYRDMx`TFUs7{AZ_0%Q5SQQHp|^$$1#>K?dY}o~?tI zXS8)!aXca;J6U_y7{2nOoY4H;;o_lg+sbGklXr8$fpcRTHKnPmr&_~~QYEw_Qrgg^ zOR)IsKH~JAHzgEqMfA5j<_s^sM!(fAuhm(aH zuTGr%xAn#jl07rZa{y^%e1!v!`(M4Lt{*1oqp$Ah})(1oTap8Lx*uI)E&qKzwpo$UMa+8<+I&NO~!3m z-}q7Sh8kAk^ZEz&uDYyvhT$fj)4olToxAybAj|y$nJ3IuSL!s+($cio#3)EzvR2)`J07zs z{3D%g9g))12xN4JSZaFS-er$hrBfYIDY*Kyv$J!$XJ60zgFkSh)A!NswyujBt?1V?v?s>mJmRFjYv#khPZz0( zo?41szyMITt{}}iP%#=%0%Fiq1Vq#ywWAGgHzW$Z>G}qqcfEe#r~35`s&o>lUGWGo z`m>~4<^ER|jGo}z25Mi#B8rqI?UYbq_^kJ`H&*g^2JdKCs^yBy9vmpLBe2UNwXgOz zMWxXy{H9~vx$Z=t&@1z%H&aMgc4{{5Vs%NN%?xllMmgWgGiil4?; z^iKGp|BkkLS=^==cdLlJo(Hcv^N90`0S$_ff;f2-7Dbg=9KUMJ@BV2*8*M%Z_SzHy zPnuNrWEz{?<(?Jy`6d$UDF3+LCpJ5Hm{}IT0O=Tm6u*3^t46lF>H4lC8~}5iHVJOl&cBj;+*h4DFRrX(hlpPU?KJo3 zp{MLIIxzDkc+?EN&*pqG?a15S9x9}2VnZ#z6FgUv_W8TtM-}`1$KqXV5l-a9L=Qil zBm072P$$g{R;JtMM3Ir)_u?^UlUsT8)E|X-v9_51c-(uha4I>QuJPXkyoU!(|hw>Pr%#8VV*bT1NFKCyK~x# zkXL2=G-W^bpC$@z;MIuE_BV-(F3Lt{_Pw7Vww(yhz>EV%#5eM0sm>w0?NHwd4o&Ch zfUB<>tYoY%AWsax2I)efDdd?ppa(kbLFaDApP?7S%P*pLyqm-q7xUWb*Hiw7me@?(B1XBcr*zLP)&tB7Zm1lV{{HcYs zf=^^1xO;;8fqq-`dw~-M2q<+ZqVg5o7>f9S>uOhM(&!+!siAQ_5Z#l{#&|w#+BP z)bt@TP;;E0W-x9R(!483(#^NKYi*A3> z{e3jPPi5OnxC18z7rD=)yux?|oo2`RlvHIWu~AxdGNc_D*~+v@p8RCr;-lq%8^EIN zV;FTCVNG4Dl_eb&#nUR+GQ+ehDPXLAfBf=gEV7%4novYKF_ zUjs{yAfe%I?THHh4dLXzSyo}+x(a+6&mRUEx$8i#l3A&`jBK5^pOpXT~>a9 zA;wG(Re#HGMe>xmC1RcDGX$woC)OG=EjOkin;*?zVcfK3-EK{|SI3>b>^R=c^x|fZ zx+ZLM#|WdzneoLj`8Xmo=IAv(c(=V0*t({6$4tJ}v?cs>jv~{XZ;;JIb#+gWN1QBY zK$(5cmGqJLS&N_BHkh-nPh*fD=499JLH|ea!Qm7IcdYdi_T0g(dg8D(x>dobgY6ZX zT69{!awAJpgCPcW1@phJxAA0Gn=dwvcchvr*BqkMeqtd7Ew$2_gqzA|L z-ww1B-<`j7P85ZAuvjOzs}sqY9~G@EZV^%ae604rKMlEUZ-a{UF0_08XPEN8)ss1A z7YppU*SnRy_S)aX=1JF{vOQZ5g=Ldw@K@Qy0;EK_&FB@Pv{9U-E5?%j>_wLZ6n1H+)qotqIs? zyu%{PIt?{g)BUs#yz1x`@noD4K&yt+LJ-Y8qI9skzu!g$(F`VAXO0Usp zKwtCx`{|p1NrXQ6dDhgvC5md15^aR__MeZmX-2W_M}3+Cl7F>VEWWswGrI$@DzIj= zg1zdy#viDkOJXXlJ@6#*oWBC*plLB=)9n43niaicYVg8<%IG_*NdHV-8 zCu#fmEF8l}ws|^w6^?qNL>CixFZHu8gz{!on zp3oSkIVm9OR7*-sO1(XVwf{|}qenB)P9&_P(Rvx-`dlPTY4P|`9ADu#En9q_H1S33jZKS36i0U}11Gu;<@MDw z^LdqUoWi+SnNZ8t4-b(92LB5_?fbBD!9uKliNhSgeEqw2fb%vX+Fts7WQA%_aYwB@ zX&U9U!xYn(@g@v**U5K<5d7<13*5c(x4MAYYV~UFEJxIUvHzQ z1|jlZ#3!cO3%&|=g}7D={j;(YT|Z0vQO221iQjU(K{bOLC~x`Y&oaJWd-=rhT)h4= zp4jct?=T-u=wc`HS&!*aS#1-`21}g943{5ok32cPp;(Do5Wb_v-24(w)MQpM1sgv) zLAbiU5fD9qN`5$`1tK?aKnrAcT572fL#a91kwjaN&-{Eg|LG_gNbcOvL z>HR49w8X3^+@=1|KI@iXN{oSxCyXe|%yR?$U}|Stamdtxq6W|J`kp@9mza8>^cSb> z(`@#MrKEAQvNTo}rM-X>ep=c_@!Y>8K9YaD!=Ia6~wZfVJsw^h;AbhlM#-EU;^ zVZHujMjKM6zb3C_)pfH=S;FMs8cIosxfY9WXornf-h(;QPUJ_ldkUhqw_g&drigV!Ekv-Z)S`0shgEQw-AFerJ#`0Xh-0=g^$n)$A z71r?ysLA-INHx51035K2H?KzFM4A6G&WGN;fH5k;LdX>)9QYx>3k`HOqH!wulu?w# zEko!MX%(+-cA%rz{62(x0>k2J72a>)ciuF29?x<$tPylrLxP1h*e0?oOiDUq7Y#g* zeSJUqlFSmAEd5K~%_eM}h9jAW8!H#aJHQK{u%P}Ic4p!ko2mYuY=jSda{$LiLLJVNwgq8eC&to(=d{uegQ=~Y<^}RPVCjnR2bnq-KN#SytAT0*P>4& zL=^-}Ckt8J`ftl1$LFv`7f6lhBWRqE(;#(assiPk#9?4s0CBB0`N46qFY3dL!a!@) zTiE&Y)inb#v(FF>ydN2^A`$UuTW#*t5d*9r|F{9t*xIkFM-e;V zM`R9WRwzXlKD2U=dWq_oUl)ooRCC%7YLjVZ$3pNB+4B|nc&GYqX8_wK}*;c_cKrx+7< zW;tza=}qq&1=$|Bc&%oc?_0gPK5|<>^^z34FvdR7~;p5>M&WFO7Eyrg#9`!qgI z&`MK=NYB+{2rkSUSjL%5l?vIlQyQl`G1F>6KPCZZ>&+`-qE<2JTOj^U}stn9YH{0ZdJuIFSrQc zwGDN%Vl$GU#X6141v2K_q(RZn>3>;UMsVX!hdp)s*ByQV zl(kra`DqapukpFz&)B&fLnpb+&6eyQ0O4R*-R%HmWHJE8&TH?*Cl*!zlvK3x=ll&k z0t6;uQPJTowAY(GK~QMtX^DF0XJTz$EEK1ldea~p3}%;v7xF9b?CGNdKYVBJ@zSU^ z2-d6JR~XUP27n-?tC)n<3oL-7bB|L9X?~VzH*=Ux1Kom37(Lv;+i3SyASb^Wc^+Id z+cW`WHcKGFfgxdOFA)CoN5cqh1XqZ3BIeTl=PFB0Yj z6g<}FoP#x)d*O>o43)T&zB3)Y zJH7dF@SaAml$+EfeolW!NEHDXe?uV;DkGy5RS7}7mvIqu&~dbX-1!XF3Ps+(X;gM_ z`SCCrT`Q=1gb)%CXV?U23u=w+Lx^C6u}6b3DL|8c7;z09@!W{U^nxrQdHWH1SCdiv zR&nsyoBOSbG2k!@HKc|h+hFdV$B|njQrh6j~Y3$@9gKhxfjt|}b} zooMzp?a^NUI;@U<55&q6?8qMrog`$#Jz~)`%_c$W*=#*1{zRGIa468>#4@G}x z^qco};K%~SBQ<4YJeht_+WZ!#F=yCWh`l))mNR{F{39o}ADSq|Gr+Jmx0q48QiJ`& zuTW_?;JF#@*u>iIu94|U=XC7h#Q1f2U%VTh3u%7|gvH~Re4O*MVhZuosv~HpzF$niP842Sc&Q}m0WdNI~ie|OQChV>J7p%yrt~QLwJjzH{+AH zl>4lpXz)Ve&Dk=l`>y=OheW*e4Q7B~pV|~9EeaALLzy}y%X+lvT3D5i$W2K%bi@fZ zug|PlgWx_JDRF`6ADX?DTQqQ9_TVBJ`wCT{1~)3F$exc!;lQMW@d3snC5tjP?TPpl&?R-(IkRDnYRBb10~3PR;y6^h?`eRb8zHH#zCC zDK5Wsz8VNA6it-KcRW6;8xHn9t1^t{QOkn9ID@-lz-?&MXgyz%^~;$I-?kGD@;C-s zC~I7UjwW}=20G=Jt}CovGiy(|+y@&@8qlGZ@0qlJG`Uf+xh@5R1%5dcZV6lnwT=?{ z>o~Vjx{4pdP71?p%uTz)i4ph-1CA_678ooH5Ft$}Vm}l;k;@&QvghwUUN)lHtM=L< zTT=K6*NG1(I`kHFr)*#lb|xFG`tro{ktXAi&jg~qTB0xzS% zxKXL&cpssnmSwsdo$$dn>x8qGr)La48f)b7I01KiZ;at+wtnhS#ZQbbb}d|%VA_B@ z>ZBUhT&SVdSipa&-$#)2gQ-}@wy%Lb`Y~%a?_AM3@+Tfm+UwE{O#Xff2_h|t5dC{& z^I}%Q^M+%4r(dZ&A}4+I!lB(Na>XLnx)Ev3b`QAIGRSf*67XnVwogQeU27f&xq|5P zJ1}Hq@u%{d&Hg`G^sJX~D>MX)OATimU%ngUcz)0Hu=jB--Y6lrDSR&Ph{jM?L8=tE zqj6jNrmZ-zf2@~!(UBxwAlrs~rIQRqM4ZZnMojb=$U zLuy}dxmj}OhIZtaJe3a}{ZAZqt-`iR!6%;5lQ#2cX_q2kudf%ELJq@EOOws^LN`^@ zp_VfgphjZny=vGB2NIr(zrd-5#plM0lODsv(mQak9}9u^2czB+qo(dVUOF7Z$M3#t-0sPxm#-Nj~W&(CGG4?4Sir!ZL_4=+O)^vJ;@S0mc-Ug!qO3;(dhK>2j86&r8$acTyYl zx4t#)nG(>T+gBE1>UGg)(=w0;+15eJEZ<3+Ds^r#f8H#**jP+S#-opw-^h=qEuw zZeI!*RY1C$=Ge&@aF1vjS=2 z4?xgL!2V~@xa18v&u}=bvukzKwqF?}GRaYWUX~?z4IbU;rV*R84L65FMm$?YswH`A zhV*5J7Lxi`dEW0`FnZzs5+z>Akmf(>Qc^)FJbkERfxHR+-LVz2TT z{vZg-d(`WZm#t}BV3A#&X0>eDJ5oR6S;0;Jq;bO7g@Q!pkJ^_qMVUISFhnve1*o&1 z=s)WJikDe}em|~5A5+#p!yTRfId(bD<$k~0Z6&RIOgu%4Kz~!Q(gvN zmD3epC1e$1k^qc}6p&>2FYCPdu{v5dzEJS@EwXb43~SGQ5YLool7uJW=j z7}A^ZM6oC*fgh^6Wk!qfcXAnwohGKuMW!qqAQKI0^~{GvR#!R_>_&JT z-~L-PI>_(Qe+5}nYq7~j0x(x>qRk@czNT~Ka2LYoER^QBD=y5ESKff5;HeF+pjTr$ z$f{+YJDHD;uZ`=%1uDE$zyrCbS6YpFBj75^ z)FHU@hR(khsOppPMaFf^{D6jtLcu1eWK}x*q@Mva=_zTt5ES2pN_W2O$8^P zIM0tGUkJm9K)a4LD=wn0gy@x~*)Y!ODGm0rXDT*FzK8lY&uzxz%9_(`OH4(+6toa^w&?!| zKLy?V07}73LJ`h`%Pc-`*xd1}4PoK2T4sMm7MWtf&R$E(zl)ZKDTJHE0FdeYao>cq z875McO^Q-Z6#j+iqU>F8Wud`e;XiIK_9fUljF1k#1ycpqcir$`jAfj@SZYef=9r~KqYFOqF`{5rNW~@Dg zcr@+@V( ze4D=iFq0Tf5s5I_uG1ySTxg+=CNn$cyLnyU2mLKH zC&Tkk8i3))P7+f^NuAXlChAnf&@^=8_k&>6Tp-A5Xi`g@$d%NS(K@-~3MrTRyteJ1 zS+U*g+WGRor#p4^~GzshlNZUwO3@V5~Oa9?c|{AhMCcv|qE zGA`?RJd};8I+j`g1LyrXO(hDyj=g@n&6qUxhy@paFeZCubQT;|z}nSTTvU3NHl*=% z5>_GDEje>LY%KpJfLBNd$P7GIJiwTuO}iLsIx6|tPBX0je_F)<2C?40n-P1Pu6}w2 z(_K#WZ(vu2!A7HM>+1|b8?2WuR z{##(zYjZDbR1;8KOR=N3q#5N4hU-#OV5gyCP`r?2rSifXmxbGPMHQgyAwwYlGa>@0 zX2yQkVGDmC0(&RR9f4(sBEA(QL9tn37oQkI%ib_lf2LXV^~qt^T)hCTkCU!Xjv!W8B?zEfC8N>j3WGPnYOc;bUvB>qBs``{=hq-K_BE zKk_CY!}{WPSl}q(N2!PN=!YChQ-HgmI+hO=#cq*lrsG{ul82pDTCVzB+5UJSW{Y0K zHV>M~X7Lv7@7H(8+E2uGl;Ni8+Vif$b`#BL;doJUmpIZC*L_|2Dq_Op%5()``r^Nn ziT6$0+<|`C6+erwHvVQG4PhXN{GMHusXvYoLJWQ4NubY^4u*<@!h^N$WyagkN9)teaR^*G0ULcY2VUHT8U`-gdbb ztaTHZjuJapt@4Y@fma$c6HA?BG5rJ4b&;Oh8EpRohw_8|8vEmXWz7Y`y;^g?0JUoo z(E|rB6{~cqaDS3OblG%)Ci#BdgWCm)gZSWdav)4iO;Nb37fujm-bYF%f3-j!GUPYO zyP^=ol+qk^8Tl{0a&u}1)2P^#FY4GVt|pHcX#B~SkTLo8906sDT7I{gGy>hYZVyKR zixqGIc@-Xfqo1l|fPsoQ((}<4$vJw)x*?@cv{KO`@QY;^EBZf# zhc%{5@2-Q5&klpy!N%l#9NuvkSb#4zS&%b^b&jIV%ayji;dck&C4HyZQTv~-5ADSJ zkf`05sSyb!ujVcJLb1w3+AbISnnur@6!sTb|00StmDU*g8-9rV>N4l)&++C8<^-gT zJly+%k6Baqtt0`w(kR$neMhmmaPdIE#+^7!Cr{eE0b~AHqSd%O8DpnV{>aHxBNcJw zW!ZPA>zd3~u#5!3$Oqv(v^mmS*)cH9^$F9^N?QLG z3{FX!K&#^O+^e|{AY&n}SzZ`fEO5cNGm9T*Q~BU1$}~@2p+{Epgsxe#-f#oc9?gcA z*#18HIpA-NF70)ZTI@31ITeR4mSa11TY`}cIsJFMbIPXO6 zwjc?@2$MzW6rht%76O$CtSnIy9o}l%rr838Jn*9rM^-WGVU0M1L?f+0Bzo1~Z z-=zi3ycYs-Gv!-0e4g|8Rs*PKVRY*zXHUm&jjnIopLqoR>sO^$u6}7SJ0-d>m4_hSMyFb=d+Y5LYzQ^m(eIPr{7SPpRk#;b@+2!jsxv%UcrfzT7 zwh`EO^(>skzaY$WLFRjA|gD!>^7CypcKWIa6?A9=ELi^BVnW4Du#%ja-4 z`ro~-UUwTG`{`V1tNxU`!~(&fZ7-I)1B1ZfkT1sgj%|NL(YB55m(WJ{+OelY zjgr~MR8@{fJH*$drC#~Aq8gOG73lYBqGcE=H6b}WW0O)xNCNub|+Qx9!Of!!CO z;mn$z34)#3fP6IC5tPKMc{Ds*J@J%7*x+_IUL~wc+;@pci}ciIegrF`Xh_Zp6ca`W zy}|(~sK`+vpgsGlZF;DMIh47nK7FhEeLir=g(i}S1Wc_s@<<5NjFESA2#4Xo&OvY> znOy25vp(G&8`j@58q7X%<;{>u{#={J-a=)!?sWU282NHAYZdk;fl>4nKznyY7o}`% z)>iaFr|)0y_%tu!cAZ{ik1itMx)L|zpQhQfpU~eN{lSOiHowOonGm7MZB^&C*t)we zamk{cA4X?deTa~t2M?9)C`P)^sRr}Fd{UqomDMZ*Er>I9ccnr1&hM)soc(hzLo)&p zNW=kD2uujSVhgTx>@Utei7e|YqWX6^Z8<|J4%k~hZXLKsz3+ergvESrpyV9ZRh10f z{;-Vp?t(Hm7Z5@#a8qk^(OjunoRdNWcl^Eebz!ieEM-8&2K8ICRSYgx=~yB&blahS z8v8&~u319P&q#@r!0wX%CBVqu8g{&%XP4pA#3(%gY| zpX1}=F=Ih}vkGIRz+~%@#1K1D!#PbB`i5vx@z$(5pNbE&xNw9y zfM4{g8?iUswVc51q{&JuG}WRMgEu|p6HCls*kk>bc`C7h*@e#30Q{k>!h+ffJT!C$ zJOb<0QemRD!<2eUVPr~tct0?WfJ%24M;y*{`XYw3_0BdDFhvu>h?51n^`@;f6P3dnx9f(Bv5NpQ?6*vYcjJ0WE@H zCu-!_PJ*idgeUd=4LBdcz9gG})Dd%SrR(zyz0%aC&fcs*WiSaVC&XdV02aLK;PvX4 zXZzK+h-;R__@!BQ z6PPOXJWv=_Lf?8#4!)R550)HZo(~W96_;DzdLsx?L=bb(a)j>=&+QZ|EREA&W zw!r-r<~UJ7q(voEbGaZXf#&1%YXXDxj%S~zfHypQP{{g1^H>xA<7TN)@XUfuDp?8Fccf2>|Kyo_#@F`?v#k_`c|4 zU%iLd9Ops4-A1z1<=n$KZ0q!|aI*+(sdr^?`7DGK67a1d%&EL~3F!pKxjV>h{J7@( zcj`QyGyWi_L-Tza{msW_@{`+aZU;68+`w5)|K;XWy^~uk?-`Y^aT+D~i!rdIs_+dk zze+bmV5NEt$s-3G)d+kcwA0TZ1n0+NT^pN$6X&<#%kvvyT!VhrpTG4nw=JhlB zXu%R{nXc&P#hM3^9!9N0vNop_aw8ennPRtoYh(eSk9+FGA+9<_b!82f(IKGv^7gbQ z4ca`*)p_Ie!cBVvv@jsE`NeCO;X@o)(RUXx?z}Eas(50L+e()`AWDLEzU*exv*$~4 zK<7A-867km6QC0X8q40NLxnmwX8eI--I6~C_W^$p60u}-j_nJ+MGpI%u4=)7A;$E zXV*>BF6#sy|2ii^*|Z2uJHNp+-~kC*pa-uebuM}E3zfbdwt)qnX)|t%w~!)jS@|CW zsVscP@22D!xQ$-IY`-XLY}Hvi`5O6&0^zGMSI zJa-_rKSLzfkK`U4B@PGP zWqSiShfRFooNz@PbG0|&k=I}WAcA|y=}mgd72K7s9ZwKtRZM#y zPh}Tvm168`sV4|ivVN)Biirj6F~V1tCDE!GWYr1fjXTMM{IGIOF&wg~)T=``l2Pqd z3o0dTAtGHnu#qgm!oNj!BnE!Qp1?Pv>haJOz>03sQAJAWZl@C8 zI^ZyHP4j8m)VTY&T2E7r)hxX}Qmn|IBe4#1GFJI8HE>^+4xB1gbK#thOuF(nl|6}V zrA;pju=dP+UEC51({lUPfjnw{?(#iGlHert?R&@l`e9B~+jPQV2$8X;*c5;X{tGI7 z)r0E4Q4Z01V`isyEvFCtuDL=RE&}AfW2Y$wynq>VE@2RczvfF5Jr3=)!7+xB`Akn2 z5^I<``e!*khih397Ux1Up>w=_xdZ16oz?dJjw5+42Xq)Xb zP;pJoLER0u_xQP4g>(}|;q%M!0!FmT{W%je`M>8IWIpU}J_nI9n1gaURoGJ11zOoF zGFlK)&V$A=%*6E6%P?c1aToOdbUX=<>~zX${$!j-;eG?hSuT2OomY=DDhP^ z#RvgedFl7crQ0y1~v9#f><5=05tQJ0K2vDmsK7|L21T)99`zTdxSqH4E{tvP&IcW z=L`7yDV2V+evI0ph553awCmZ3$3}n5gf)ZYkO^Kle2*E83jo`{VCpYkrW``tiL`5* z$%Yy}<|)qXsTV;P4@1}52P$(?80+?^LAv-Pzja@Nzk1yJ-UJnVm0BTHmGPXz9nKz-uV_^EiRtEyJ&*HVszR3ZBBDIg3f%j%|gQv&#eI6n{0M z8Lx36bW_j^?Pjse_~zFtVP;C7M+9%o<#OJmB`@lrv4E?$WnYrPAqR7VrNy}O3;w&8 zt(zQSoAK1M8~@5LG{wz=*%N0IG%yywD8JBuKn;bp{)%%;Vyqi~I>F=J7xi+g6LbWaMaYw#DzfPT zr>GdOCb3Gg`@`fTp_{z^y@rG7H!vDZ{bTpT@o|Dz{FGP%qD7w9AL~lG&oGV_1x`aX zEojdk{!itf@9hzX^Z6aU0@V_sjg=9d>n`!@9@v2uixV{y8h!KfANu;re~=o>nx?ZW z^c2`D{K`J`5xHwf;*kH^BbESM%L?26WtbFpMD1e!s^{G|+zv9IVMxD3_Z?VD4fn9W zVH3s0(g$^6`Ud*`uH7RYw^ic2Z6PlXuNkmvGbmB^DNl$<6rGBRtbEcM{uvSlB_3EV z+e^&8C$_VD74MW+Hf$MUen=~e_OA4^al$pQIvFi=`)%5-u2S)j*=&MfNzV#HZRwT84e-00dGuy2 zrIB;IjWbYgSQ+QmHd;XroC{f<3Nbvqt1=vQ9;1c4iL`nU2OWYt21KGz^w(X*q? zD<*#YKEyUI3*Mn|DgQ%VNOTkESG%5i$+YRS#3*seA3Lqt`jP>@{)4Wh7xyn|`b;_U&A(gE3yE{ui4(40C z=nv-hlqNU>s|c1d*k3Nkps^a|Az=jri(Ic?GRQ8Bxn(@!m{%Gzq03_=8A=M7_{z+t zYkG@S30>ujN6H8Fo65M*634|ePl`A7gDsJ`Y&5IUM-E`?X^CkX8Bx^YZOyBZ<+{W-CBtieAV zS~8Z<%Y1JiLC1VWZpzDjOOfU2W?^kDb>A?_+!g>Bas%@iSd$xCXg6j_O*))dHNV+q2KxX>~}hWGRTNNJA+r}@us(GBY5VPzV`f|2UM{x)z8 zL&?;HAWDzSfvjkafd+1fEjBx!haXdVqR$2G5CBGzTOn>D8hK%`guMyq%=1Xnok(sz zMV*NGQF=wlV~GTm&M+f0a{dO*3UU;(b9tSS&$ zNDA_Ih-j;i`rS4JOn(~Ew-NQsTSDaKesS~=wn}j|9h;2=(3%)`Z(l^fb^bOgF~csg zQ_V`p+d#N_$9qG|{E_!R0ijJXGVayj+lg!**`nVVU+F13+gq5cWuCMwy@!5{{8Ul+ zxlc9Y(j>j==l^P^RA2juo0vSD;BlNk?vu9vhJ z9hoffAx9;wi%Ru8K*jqW{t`~95Zhs~@kk@*Df}p_bXGU6-=N!NE~%Vgj~?9rsnqmj zyyJR4N#mhKjMS5jRWy;YsB@mm;=#~xs=Jinocsj^I7D|;s}ht>!SCM}>eGIe7`@a9 z_<88!7a>59Yy@z7evg)8FQ_@nKD06Knm&Xj4BS2$XCsFxfIA7mjv!}ie2nZ@d|Qn| zj4y0mk+GqR>eX(rW8pv*3G_yY_7~VeLdffZ*R9z&?U}EAuMuPpk2qZke>@d2EuC^g zyR+1DEARw{7b_3v;5Pklrr;pPTAhJovLt4%3rpcx$~`(Lw+1%t%Jc)17UUl|j8H^s!YK30%t(cceAu4S-=f`|!$S?$nh zhDGs~gVE(#mt(6vJf2F;UC7wcGRLe=eWykmx%VFQy{ zOtk3ZlXMh^gxo!4-Ct~Q^>|#dyf``YQjK4-{}QOiv;phpnHyhwgk2o=WBHIl3xyX< zhFc{>f@6hBr`tj$IR<`+mY7JP4QriWQhnw*fo)kWSL_`Y28}xI$F4tK1YLKI7WoKA zDYA>U&^KsAVCAQWd4zVO)mg|xwA8k=_n&kAYqC@m|H1N~Uh6h{=cEL9Ajc>YuS2kI zJ@+8)*Lp3V^|0Zg>=XR(XMWrliCaZQ7;o8~AN4ksdkT9Jq}gq@sNV!0S39GP2tnS_ zxm-8PdIKR7eu&wWko;cnjQ7X?FYErl{k*Ls;NZe3@YCBF+thIR&}NY%nw%|&OF;A_ zj^YlKp7knr*&i5S}dyntD6D+!?cQibfFSk>0RYkd76UQhX5K@t6ScGSC_Q|e~ zNY_5fJsc5iH^)B&7%h4tnIGd##f-!&i(h+{ z#%IHD-j-U+;+kp}%TElVMDQ&(G@IRZ^cgC|Go_u)t`R4U8do}DX$aAvW0vr%$WlmW zUky4aV?9bic$jy~!RO?1$}?Y)nsjUNOP0%4bE9O0I@>907F(MQ$IM&Jv8?^7WDE#W zXrAs9FB%FT>}Ns#)g#Kg=qeb`p4QvpXuUEzksVK83{i$w8e80s&1hVzUzUDPy^^_6{;vBq4mfY-oo+9-DOP@7u@@cKJ_X3?ofP+vFF+uNd zVSL0ZYj&QH-Y4J1VrCN!%Y&p{RPO2h@>6%Tl& zMGmT!RmzsDbG%qMd9=LyJkJzu^?CGc(=&buful3yN(8q z$es_=aV`eGrAs9@zv5U1|9#Y7F5~uFJ>ZBVbxA*wU30@E>}`1YJLi7-Rm-nR)yvl| z0ww31o^XaNi-e15nytgWLy@fm3&~u~_ohqVKAY4h zsZu4IX{{rf!c^TNaRd|rXO&oyBFP@&G-CAtarnqjRTSz&LU_nx1QFz4upi%by1Gr| z!igq1-8H6OsgSS^Q}4qdZ@utd6$p{&hs;0U*n%@e2IDyBD=FSl`+JQ-{m<_9{bMlF z=*-EB@=s}F13uc;5459}tK`@6BeO2|xbP5<>}Bnv-LmvLxc-zPkemxtHGgKLj0IZ&en#H|ZlCbWF`n z6n25eun#y{8w=Mjtg9k)k47QCq!JWHPc)}-C~9uMo=)rZgI9*TK!5|a9zD4@h^&8P?z^FDt^8q!wFn|ig)Ly5O zd|Ivwb_>5*A}|#-BUnCT4)AGxa)F&dzt{Ux|2m(2*g*2gvpO2 zFEO#TyPY8eh}2*e-R#By9{h%eO)Lp6W(ZPP9yM(0e&ic4Welzk(BRvRA3{bMV8Gzl?vT`O9Z)r$*;^80(s8GBHg zM*DP953hlCE}}2|)PnMShqF4PCA$WsaxVB-O_fLrQLLGUB1gnFhyYbBOREcpnY^_t zDOFEBJL|Mvt3y1XBIbsoj4I1|tjBpEe{J`#8=kS~Y%7u;3`NfWXB5j0W4;XENxFYA zZChj2!EM@i5}n1&ujY+{0~2bfj_nh46s<*_#n1oM#x^$Ya@=o%3KPYpf2qztnJAa= zO&15W1l{l#MXs&9$-G4D$n*oLj>m1$3@!s1M&ny$ z!?Q>Zk;n zrtDI}80&#Zci9-h^*+(&pBr+~pC*;UcacAXAEMth?%Xb}CrMLBVcW98dl~6mtW@l1 zzbRcEJ&NT990Yc&cSov-bS>7+|ClJRoHIGF9=jWQ8}qx^$XBeDRun4O=kN28ivJys zkt*sgzt+@Q9h5u9ANhaQL?dF|_H8HAvRv?$t>}f_pksHYw~6GbvP^$<2jkYqQqusG zDAgBL{l`;PyzL#z5WZJ=tNj^-Fb)6D3Zfua(KoChMpk)}*SR;qgxUYd_j{l3$C~Ta z)o*->=?J(m4&=N$yxH=0o9tFc7tLC-0W|Abn}3y_Q+xGz+sNQv1rAjp=)I@N{YasN zFgNnLzUitSyyx-Apy48$4omAkf9o;(-VwFFG5U{F5D7L6uP9+qey5c5YInl%FcTqC zFKFc3Wl)llW|<#DY&0|Fp91J8BYi)Z-AB$+x3AT+FOckjtgmN(Fx2EEvbMOOY1z?W zW44gJIJc-gZC~+hwp)5lfrKaDaw9aYe=-O$^_KMZK;oIXNP}=ZD6NuM(N_2lVIrLmeO)@%NH+PvKubIbxy}6Wwq!($%G} z&(;ST@gGmcG>nEPJIa3qYQc#%ccgUCzIAEU9`}%QcxHokpyjvns_LCQ>7}0{l4MS> zX*x)GHduvXZjg--MBh?BmV+o9eUEw8#@r*x)L5oC5QjzSKL&)WCKM-+C`?Up2ZUNz z5cgWe{M<}h3T;yiPz;izpfQbUYKI|Hn8j$Gi8`^}svl{GFQ{%GxK1}3t$IjIME2*m z$%&1mWyB}g>m(u=Q?Joat}=vGYWugG_0c_>H-jnH9cv(2A;23w-{FarVWiJ1%UK(5 zJRCB()n9X?W7$oem-d-$858q>!d#mDC_2WAgKS+%zn{8m6loMHP0W#8Uy~HzWktob z7mZiAU1l%Ov0X0GL0+6|%Qed^=-%13+LG!8sUhP_o84ps4o;$&9O^UZ5UJ|1J#qP}V=J|vjvWl->7|MmU`s003v;~SCBU8W$NT9MRs z{o$YxD(0NC`xHzSXC0-0a8~(>oYwOL*~ek!27Rg_NZG_5UhXbN1KSb|1K$cjW_{A; zdB7Rg6Xcy!Rm-`-^K2C+wTnH0!tOl7;sAD`h<6jj=sWXI}aX}#1Jg$1_CWhaA7HX@0#;dGEAeHw*EKgw-YysA5O$MCTK zmKVm;*Nk_w5mKiDa!hr%~KoXT<^L}!6MhaOngUhV+-_C8XwQFriUT-oumdVBiUcTh@DYy zYm1L!#T{Mb16~j$-UB8hs+NfyD3QH}NjG9|Zx#3(Q8MezL{AL9>JM{Y;{PF?c5@3O zPfpT!JMY{O(P-8IuWqg;nL>O<(>+W)7PfthXG{5Ou_%9YtY;XQHf{%;4+f;xN&DVJ z-zIU4)s{6z`c0R1S#fl$dytfOqQ zfjEFy)v1WWmw@v^x4ik_nXQd{5^bjpJic}pML0zC5Xb$kCi$S+Mj0o#pCOubK-KA9^4-HPas)bi3Vc`hLcup2)Mszc2eG#oO{4SG~!@It%Kij zftS`jT-d*z4jK!MyB3E%QUi;HPH=lPu=2EcQ=vI=-1#2~a}9X2x%TcgP~gVx&{ydL zS&!!(6qd{TF!|^4Bm%#kvF?q}4SvvCB`Er##zJ6eJ||m;BX=sp*Fpnmq4forBYe33 zb?UHfLa|zO#Zv^UcMY%nMlE0<%BW95Wgt z8p)dRL&nYeK)9au?tq*nxm2cv_~)NHSh0zvE|@zadB4^E`+whlA(E;Kxv}%N^70kz zxuSc56Ti&sY@J8d`qO%+O-LIDG!G1fvvL`69Iv%XN2v64;dkj(DUR2J%fL0=1h1dX zVvLm45QbDwZXQAmDP&E9rwvqZ1pe2Y!eQ%Pih;DmXSI&WN(#_Z3ncVn3ATlZ=PtE{ zpEp#4%Pohr&HQ@P$BCZKgo-Q*F@{TIJhp10)r5e(E~3{BU%$>?6m+?6kuZ2Ji(i2D zn-6yADErAU+9FJUVsO{_-olEFfIL<@JlKc$3Wx1SJ-t*)G6LdQQYfNkx`it3LA;8* z3r+Qf#3^h!cT@SR`qFHYmMu%8V<+$W^{jLBexuXil~H2~ms@TbY6tREXx>sYt$Vi9 zMJrS9=-@qWj70tRr0-Ho-3W_(s?2sjkfS@I*XR^>+7xCDn<{Ql4Hh_x3 z^e0&sW)}_B2PcA|Mj>^F<4(Jml=Y=#z4ciM`A$-q8mM7`)q9~6HxHmqm$neK__d&8 zAmlp*uET4U<0`F#C>8={kZmhPT1#phfy_-<_?Mw3-_8|>-p;=Mm@Z+>v_e5g{8w2^ z{abNtMaom<$<0sPOT1}1d8=%&=gZp6xV##-?SQTxrQ4kC_nM3a$!B;q;N!oQP-!nz zG$+zgd!z@g6)Lq}zOnz)sVijAM&oDeK8Uy+U zW!6X1w53?LQDs~W-TK*V96SAB-@wIThEMMsWP)xCXy=}UpodlonqWj-5o`5bfopr-6 zWM2BcwQwOo=Xmb}K`~EHm9)6qGQeuB5N?{c^s&e+^0d~6J{Yr_-`RZ##c*sfwg>=c z_(n{C(H9U^kb-y$7L2aGj!{9c!jG;qkZ+4@nEz##UDq{1NA``0haUciJhZ0SEPr?7 z{YiINJK`!0o6oKgdwB zZ0*@*Gl?`X0+i#ehKE+q8ecE~( zKjvJ=8C^7OhK8|Uj{C{FCngL=OV;_fq3+Vd+v6GcuF)^6NJJ27#LKiyQ5idFq$s?U z{W!!nu$%jSp`j;iTXZO{Bdq9GHG7 zG)?8eX-1Hrs8-g>05Ks%TgC>=5wjNou7r0_0Bv0zZ$zrAihKW-fPd4-k_^-vQJ%_? zVibi)5}4}lGb8BtfJiU?As@ranVF4Kswte$P&^K;<{j25&@X68b3!70@Wv8Z38|}3wZB*vj zFyRjwQ%TyMDgcRB-$S!kM-!UmqccPs#M@%|yXx~Xl^1{7kN0A;K9T&@9G4=O9XyNo z_xpJ1oQ*J%W)zl|Tr5kAV@Yj0(+5z)n>`j>VdxZRgmAi`#|9gmX5?Akn{_R6vG3f<@gfS~nUgBv zeRkH+>YmZsY7l){!<9AS+B$rXDP*|<8v=DZo?+YCGAe=wa99s1^>tgfUbTbN9uV;e77sP74*M;dd!YQA%y&Nw{x#h9@~=hk=hh`w5fjeGap z3O;$XeVm`N9-O1I@aT4Mi&b+x;)BQrWO&%+Fm!kY589Iu{1Va^ zD7`fi5yE|lV)~AsFgX3H0ixj9)I9d{7-GEies5A)TxEMaD~dG460+WUa589{925lE z8rk+3kA}$oQ=jf9Z`DKDX8DOjk(;V5DXa!+`a>-0h4RHZZP^~Kl8*B4L1|ZLia+X~ zYkq?krDH7)hBz3~@OvplC7WI{b*w;cG9-l2|2JJZVdpLPuP<^FnE#>D6_M86SY%q4 z_NN7USk6((;m~@aTcdm7t_+MBFPA>7SlmzetMt?)liA|>y`}v)wT=~go|+JZTSVPT zTc3yt)U?A&!=ua>Nc(hIJyEqftsfxs$PxbY@_#cW|DQE?n;+P{cAss1@vp7->u(^- z7FWBNS=(2$=i5Q{UQpg6oP&BNE8=Hq99JTstM$z95KK0jfzdOJchu^mctwtYX9kfA z8{NTz0QGt0P-Jyfr`W-NRzy=G2;$CCRX;<@E8Oyl1k1k*hk$h3UNwMsdM5hy^^t2LagTw)!E z;j6?*G8<^DMYbg&#P^Y{wKJsGo2#ZGr`r4KRwK>_u}pjURfpjO&VNR+k@vwvJo;q0 z5Poe})Nik>4bh60(=?clv!nZcyitnZCd-G)d_5-uKPrKFP}zww>s7~ zq3Mld&lPVydb`(oNQU#s!56z zFqjz)MU~{cH$`l2Xj|P$Yv-33Oy-{L%GgkVieqK2VZil2ejCCjF_HR9e5MQ-%J-)A=H<<9si7*0*|lOl{@?_Q=0gw;Eu3qV6O5fzyN*r z4w+yZuPFzYeAiL_`NSU+*RNu%@#>poLKcy_KfiP~W-Ab0J~?mKjnrlge0tYmMdSU^ zrhnm`i!Mw?A&SLVGK)y}RvG!QJ>fj!YP<5!A-m+)&F_CBOXcQth#T1`O8)SXs(LP- zeldsZS5X9-5p4fT&J6uCw3D~M^I1R1shKI)KyVkpj~oYpDu|SqV%ij~L7m%1*ycj7 z1$VuDik*FAm+~K;r#X?V?Fs4jQClca^5U4e-8!)$ELEsUeS_uz0B5U*r{TIMpkjy+ zsPp~SbYWTI%4D%Bzz(HK#qN8pvGV4@!t*eR4Q(K^2GvEy0Mr1+o&Q8@?{tDf-hgWJ zc8qnKbfsLloQaSHF(xtK#g7aay>k^Hzf2U2ba3vIjB^xR5^Tn}urJz-G~F=i@_J#@ zQM%JQ1>~+$P|-N-u90j3Agn1@FhNXxuGP}|utnSXY*LaByrw>ri9H|D7eDh?cDf#+ zr6|`gkd(q*eyV%Qd=Z^Ks<2F=mUNJm19Ez1>K0KQGr5T51(?e?=07iy)ceGbP)uW8 zzYjN2zr;1WWJuCaM(9*ta1K&dnNqIuR0mu|GCFc*+gm35v3aZZi<6s9gJ#XpQm1U{ z5Y)C74X3Aw2>Kg!C%IMm$8&uq+D%mtd$%q(lWH2}-}^*}W5F|nd$2AKwPI>)S?N*E z3HBZiU<~+A1PV0}$ubN{t*M@m^$HFvdZ*a`iR8rpNwg7a_sTf(%J@^I=%6Q|Eq5GG zpdY5x9Sz^s4C8T_y%uUk&myU*M$R}*P511$0os5n@bU^pZ-jkE5aMn}x2i=XG%Q0> z>Of_mi#a+D!e_);r<`?z+;CZ2=+`~~%`;n-)~|4myFIEZM=3()88Ol+uP<`E$6B{W z?Sy-=XGu4vGuQ@#Vp^HO|1gDL7|GOj1Y(w;t-aVnmG({tAm*(+Q%!Ah_n>gm*J}AF zpdPCb)$sx@`_cx!vMR+X#W6z^o21NEKZv0i13s<2UEPoFiJxDet!tH%YI)Q7OraF5 zHc;8xpc-@|vGqs4q{ULMsMyCln9H%h`B{|go2m5!z%C7tA!6S^3rF6ZP@n5@f1<29 z@nS=3K34ZUxsGnZ6^p<>rnx(NLj~VI@qT`pT&~q0!^%5;aM!VozrsDOu`LyGRJCO1U|c>>E_7}J;!0Z|IR&YFT-Z7aYEI9Pvm-7kBK37 zeSh`>;Zq_4*rp0>=}R1guB&dJ|2_c5djEbMCEe+c?@T{gPjdJ7^Zu2SR?6|9)sK-w zzU6$PKf-T&T@+)`A^;<^UOIlFM}0g{AiWnv&8w?kiuEERCGlRkCGz}ZVUeI67WjFU za?$;|l9E|uQvjwt8+$YRvDOX#i#)<|dw%D~vgKwAPW(veqEx}})|qZtbIt9to?b2d z0DqCK>7^|7x7>GEw;g2t&~^9rwUH#(uWu!R>*>BNF7FCH0MiQUR}XT2(4;*QNHsYr z6x5D5r)cumI!_EPmA&^ai$q=kA-kto&O*9@sDoR0N~Y3*ALsuQ|neA5xnewf|S z-yaVM$ZtLRMiE+$5eV@QSD2%WQHe16-iKV8a~QA|6qUKQh{VZ8R-xSr&xa-i5lt^|Cbln}h8)l{pA#<6_;O5bbgT0+(A zgN&z;%I+^GW0=H3$US_QEwG{;W|s$Qm6tkE3QR=Xoc;T5KNpcR_OHdrzz_bFhvlUQ zQjC!0zVEsnhQFc+I4bYmlal zAN0>Q^~FYa-=6S+*e_WC1aw>4MBA!PK7M`OA<;|cnu3H#M@?ByCq$xF`nVbbSM=U~ z2b-8LM|57|4k5LxXd0;gBj~_5= z#JH-FQ42#m~Rt?1D4rK;WR>dZpJw2u_>{T zlSC|Oejjgq8OdLd^*=eJEV(b87AMdSUjfqr>2~eI5U1T_E0t1(=@2Lt)fIpaH&9D6 zh|7tlj=PO^VKxP_nxL+=JBNt1pZjfyyNZJ?LdMmebP{S+$6ifmEptN@}!73c(}(mQys@7p`wf=owj`Eth~9Z!+EJPq%9B(gcLFyi!(BX#I| zzh>f)v5dZQoglO3nzjNa^C?LwxGfln4S3!g?C#-OQGTHJ7!4s@q1;!*nPY`#(3@d9-XKWN zCpr7p>^o0^g`rSo5hsUQXxvY;uAEK5x^nP?%==0Dqxn6-O@%fd*2X zB7GkGsH8Bj9Xs11{Sr+~de%hi^WY%RjXH-7PjKT23X&D`dRoO}cX0TODCM<*B$k`D z^}Ez^ELBq8d)Xui<~Z8yR7{DEnib;)K(A%Z^mZ{rWmkt+{dB8*hZH*5x>^Q4zLiKo z&PYq8ZA^D~a>nqTZEP$OJ~{B1;(U&~UaCRkTwse(SHofJ@#ajsEt}U+`4BvZ zek3BeRd8FVlGZI;VkCj9RkrmqA;He09qHy!PA(=bOCU0RS4mhTq}l9!=73V}!2%ko z%y4X@0vni;NiGpgG4bDf*tOFf-wSLw;S;1rm~j$#}uA?i9zrNspQv=^UQtrUODfD3|YtuLL-~Zijao{**pi z^uQMA<+ci`r50lK5=Sq-JZH=@)O_=L{H*=OQG`xz0`4eLxur@x_&L(j+I__?mY;WO zuz+RAXqBd&PYj&fby8w8n43+=oEW03Kkdy3(1Y9Jy09C*+H5R(jq_F4==Y~gI&TY^_cE87T z3%#uTavsR%&%eZSQ&upgL>S|8-mag$KeQ;3MUHJ^3=*${Ye(JJRa#gy_%h-tVo7aEX z@Q;#MmQQ*Q_1Z*>cdSowCp=zBrerHS`)545;Ptd#9{JUX<{HuDwce;gfpNmKaSM|L7O-{~Ij)pEAM!+oyLl%E9hAT&BruNg=e=10oWr zKe#9owTo)&tu*RKO-xyywb(8895IrRf~O0J0dF2kxj1uPF`V%a#%Qp95vd>pnSMnt zzezxztwxjemu27cGRdgTwwo!)$R=->JfUGsZ(AM}MoxYGN5%aC!0l064K`)1B#Tu9xMr)Ixg7(v~KJj@s8vaOl!N)|0hInRs;rER2n#Peci|RUxQzlJw*wA*ZxV>+~N3 z?=abZO1NTg;p9naCe4W6$FZR3;TJ&rQBSk;@EA@%>4DzbP7D-^B#9T`Ws;f+{pN{} zbL?VmLe=#!{~&Tfx5A{QsOk7iha|{EmBvx?I~D7>5gzv?DzR!KbUvV;k@Wj?O^vWq z`$*2huVF1v$wy4EtE)gR0IjdOa|JceV(Bjd->l6E9)WVqlsZ^stEl5u+bq2;o!~{H zy^Tfvhw86?WNPpyeQ&HptQX^8E%`%-k^_26h7gO6&4n-Y&XnBQMV{Ec#Py9Ybe+q(+zbERS$&qCu1*3`GPzKwc}rt4b(B2y(i6OADaJSZc?#ZRYoOSa9m?&=dKU_LLhx zFrWbqoH~Mzd{^&&!X{{?4>$nsWi1I`=!B|%743k@CBlxcgtw6RJ&6p^aA$~D>iAYA z&i@*V2D>9j0ooK50q=o|7$`*SsrQ8k5`4Q%Kx%=Y*ffJh+i7ANa(mK?x+E*7GA~Rp zFc2Yn`xe`V*Uc#CX!eN71ECC*XaCY+wQ=hVIndHYlU~zHS=``$DfVI%@y$=gg5ub` zrTPKb8)^mfr-Yo&54-hR%{e2C=NG2Sy6z_;qqh2D61un=2E96Io&c77h|f*_fft$f zfStaGo2p@?5N6_wZel*kxdE0rh5c^jWNf6uk?%`n=lLBCKlrEQg0ydengyhbxdqo1h(F4KEC{3vL=XcBT&7Q#UKFX6=WR56dYG2xnYrfEBYz0+@4- zbyEvD!c6sQiaurj3vGitph@9Hw)rh>8Na_MdG)usN=p{cw@HJ8uaIuCo362j16No7 zSgH71sJ2pl=L$tNGKs44-N0l^%p++&$K6EAXxZESbxN&`BZ9<~$80PcowXYxf3`mX zs^8~pL}9<%^^fNX^_~U=9eqZgPeL$}Slvbtrb>#Vi-?Cog-8X8v~DzFFKa#>IDzNfWp-i3y8=A0n(u0K;SE~ZP6bb3ydp$|qzv#f;ACER< zyj0oJHRZc!X;dq#OgpRQtYwr(K=daPYWL&)|J!-GBl{uxqVl9anmA8#^31HUO&%@ABdB9H?qxEG=|j*)!a^e&<(Qx z_C1mc?q z4&pvg(U#y2Lc#(&iBeb-d&F++tom-k(4tFE(m)QN`&>|(92brp z-s!3*dbVVtJn=<0cHEU9H#$mQTjq0XRqAHh+VC;{=SQiU>3zLP?5!t9xs{#7_5U>0 zaJbXM8)V8LixT^maeMlXTfp-w$X_azHd{F3v3c?-|ZeFmOtyT)06K5Oo| zqjd}Up6<Hx_m$kv)$@) z2?JZ2jw)K6U`n|rATa^vLcnZL{WDGEG@EXM7Us7nIbsU^5b@AS^QzQiTMG3rIUfmc zs;~sAVLEh)-HvC(@&;LLe|UWu*;a7)FdlI8wmhp1S(WTrlLTKV=6ugYTYN-l(cTS+ z9E;rbM^8RBr@zCGEImp;7^|46KH%_=dyJ-yr8&NVqu-;BA0TkQ4qIL`hN21D-tkYX zM0!taJVZvHc^-nEzibZov6ryPZf7C#)RhtaB%TXS<(ojE0Mu;7;k*}4II++#E^O(@ z9FVc`(6jE?NnfeZ`6xBMJb8`y2)cU44G~X)|Dw)Z^aBd_i7OzZc&qcG&KDb zcG!5zSX1HT;=UZ%YD&_~I&Sfz?T{9*-s$UV;Gx~F}XwSF{^v*c%m zr(N7W+SZrg z%|y5u0pfJ%T~0hilP7dQS4z0EHcIuMWoc}a^k^SVf(a=c>IkNT#0^fi68OLPj^DcC z#Xx>C0c@Shzw5mgmmZP83&y={*XxE!@aX^=%y`_UG*(tFt{$A4NcI%^^q#X@H2R zqvMe|r3&y#;tdd6t(=M-C;%9`UdT3a8?TbssF04T5P!EtQQkisx{cI{=wk57+`b2h9Ta1XzZN5zW`n6 zTM{z9qo53D-0~+Fs{UV^x>UTN=a-H_e$L#00II9I$0m6|8Ztl_GPUS8hL}eVd(Bqp zO`S=;HWC$CjZU9&x;gqV@-jg;nYz6#C<(`Z!(pAdhth40+rf%p=brE*3poBlj>o5Y z{|T7f-kod8f67&Y=A!xsd<(P1*c)nr7U-|ZT;Bnb%C}sXG$bWY-YY2oc*le~>#aoW zoqJxPk~@3}yGl__o%+<0b<3(7a*5Y3*p4r~p4R?%`b<-<;%Y6-ddO;zw}wU~j3GOf z))o=?mRn$o$W3>@kr&SaZ&Uf6`Bf^d9m2iu%`+M)(DlLOWP`ijrJiwq$YnuN3{A?- z@!%AvVkqTw-6g5Q1%Gb8nTFerqMoPIk||r)mmX85lLzFl38SV^)AfF7%KL02eC57v`mi65l%MSWlM7tlS+bEg$zct0_n(EY0tGPO7Ec>YME~Ak>1* zx8Io&PVK(OP*cD03h;vqi7sQQ2C}@}l4;H*7qQ@nC;S`z+taZavE5iVGp9<$ zVyN~5s8#}%Y~J~}!V)$5d;J^kNX;J;%yJb0eYK z(ixqTqZAeDL)obdXS}LBa`#p4NP;>fRvK#c&bPz5)m{Z3PQC8cXCr?Et8&Eai^yYn zIR~BH^9n=HLkV)~HME^aGfKA71hR_Jb{YoEgd3Kl`kUgqrM%S0S1+Q{X~YvT-g0dG zcJ9VTh>~YXG>_Y0z6{T^6#Ty|0Gki<4KwPxSi55l*_&u&$N$=hfO;6oNdA8-eg6)% z%D$4&hit;|gDEWw)7p6_6QhdtAOF$8$Qb`_v6Ou2;Rp+)zgg~>bh{WiI8%Q7r#@M* zb`{dDv-T~bfC$VpGZhYPKi9864B*6I!o6LO{8QUH6Y~A-6EF+G$_dp0u-Eug$&SYu zv8mac@}i4sZ8VqoeADV5>Tp-}A)Hc-#o7n_PLR{vg^zrmk@&lxS%PU@XUP+HET#_u zNs4xq)YR>9!em!bvnO8^u23J&OdT2q(>ARzf^ql)d}AoQm&EB68b z04%hjBG)K2j?m;OUO}s9?`G0na*%GFJGY>m2vErTk+WoIFx>yP30IT~p1NEWt?mOb zx1Zew9jB8+j^>^aN=*kQF_2`FfviY>g?8+rQhc);es_0(L~(;_VKj~1Q&Y#!$(MIo zox92gWdIN3H~uY)!X1n8D$4YufcH+I+LAb81D3z4599~GUg~=RRlq-Dm!W1-;`}oug7WoYXMb-lCG>noUBmFNp02X>P8?mjYO3vh>hI@x zj-}8B73as2q6lD0P=q<4H%0Kh7KZ_IkPotl1tKLMO&u7a}VzH?S52 z1iMiEOLSvl9QbW=Yk~$QF3C$0I^a)Izgm@WA_~IivOfRPdzBkSHH$9jj%G`rmUl1L z!n73+BRGednZ;(EzA$4!hU0kUlyYD`dN7NL%U0k%<7(xWf<+AZ72$)u==~Hm1ki?~ zoqCDA8}tMFCHoj$9`u1BnLHu#&M_U(enGK|j-C^pZJI~TT2erz9!o40;V~#+^ zG|02%W>kcR(M53;<+V4$$zup7%;G?H`H4;>zJ^y{;6l{<+GEQE8RyQm{Q}jPShUo# z&i(?#8UAgMaw_RK80!b6&0-LB6oT>45f?z81J^~D7Nm&uPl9x*8iCBQGeq#Qj{hI3Erwc?ufIQ1m^Lk>v3H)vxOf;$A&{2Gy5K zN1ZOPtpHEAdicX{BORl){<%08lG%K9c~?EFu)7z$*~`QMmydVMwc5~D@kHZgTMD^! z+Q77CqMO=t-clo~*{W{t(j>os^qbHOtv>I!Wh~6qnD}5N@2DIUtHH3ZHCL;J`#%w$ zZ!^87unV7b25n<|1^9(B3$sV2hRa0)Q}#RGy8jxyWVpPefY=ng?8a~l#$%ufKcP77 zOrHFy^;RB9b*%^*r8Fbmu@+22buGE#jTbiD{dXPuNt3vquTM$P z`KUPR%I=Xu;gZH5M4&{=-_VjEqJg@@JR;_tFnb@Woa>OfBGsnxZ90WS^Wf0qWtodG zuR*&fmj6>^2v0~nQ-sa5-HBCehjIsVU`5@pwTO^GLKy;(yb_v-mB z7T)^nA=(K-B+ZAPK=UUz1ifj!FxWnyjF_-rQ7_hXo#P_0bs~E`$aTJqYA-cFhV9~n z;uQ#!5z#2Ov!)VKQ*X4cIsy;SN5QD-Gv$;E4wm{##8cmCbo}>r`{Z!;eFwl9z~O@Q z*xB*mOytmLpf}ck$*bV=9)z}xd{yE+z{Eg|TbH#Q4oWHrte4_wHUOT-fBYR;*7L5y zK`Sx|6EvO1#a?iea#pQlJmti|P_0mBZ@ZlLZ(=wz&|bz)wUn1ul>mlj>@9;Rn`uH+ zd^m;x?Il{ezc?PIcvDUxO6oY)G53{d14yGyT+#4W(VakN{hPN_e^H=f%>nH@pL0zB z@11l6l-;;YkJ2lgv@2}3QC63D-tAQRo~g;pIX*&jma8eSg*OJ>YFls8i(KXTc>K0C zXf|#X^JV_Sm}mZxSV)nT5~u{jMz-}2t9g0nOnl1(`c1ZBDGI{(4(+xa{i8@05)exz zQ4Wf9kGKXf9VlF$eywYe8zLk2CSf(&_uVZV++5paPBN#_-k|30|BjnS@4`ucNcSQ! z!-Z8-N-b)_pRv$IW2XauVKH-nD`Q_5>F}Gj2}#4gJo{nznmDjVm?mM->n}WVl|#

Wvh>+84i) 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 Date: Tue, 17 Oct 2023 06:28:14 -0400 Subject: [PATCH 050/111] Updates to 23.12 (#3905) A couple PRs were merged after `branch-23.12` was created and contained RAPIDS versions that need to be updated in `branch-23.12`. Ref: - https://github.com/rapidsai/cugraph/pull/3838 - https://github.com/rapidsai/cugraph/pull/3852 Authors: - Ray Douglass (https://github.com/raydouglass) - Rick Ratzel (https://github.com/rlratzel) Approvers: - AJ Schmidt (https://github.com/ajschmidt8) - Brad Rees (https://github.com/BradReesWork) URL: https://github.com/rapidsai/cugraph/pull/3905 --- .devcontainer/cuda11.8-conda/devcontainer.json | 2 +- .devcontainer/cuda11.8-pip/devcontainer.json | 4 +++- .devcontainer/cuda12.0-pip/devcontainer.json | 4 +++- .github/workflows/pr.yaml | 1 + python/nx-cugraph/_nx_cugraph/__init__.py | 2 +- 5 files changed, 9 insertions(+), 4 deletions(-) diff --git a/.devcontainer/cuda11.8-conda/devcontainer.json b/.devcontainer/cuda11.8-conda/devcontainer.json index 8cfe0e35990..83078a304ed 100644 --- a/.devcontainer/cuda11.8-conda/devcontainer.json +++ b/.devcontainer/cuda11.8-conda/devcontainer.json @@ -5,7 +5,7 @@ "args": { "CUDA": "11.8", "PYTHON_PACKAGE_MANAGER": "conda", - "BASE": "rapidsai/devcontainers:23.12-cpp-llvm16-cuda11.8-mambaforge-ubuntu22.04" + "BASE": "rapidsai/devcontainers:23.12-cpp-cuda11.8-mambaforge-ubuntu22.04" } }, "hostRequirements": {"gpu": "optional"}, diff --git a/.devcontainer/cuda11.8-pip/devcontainer.json b/.devcontainer/cuda11.8-pip/devcontainer.json index fd3f8d22f89..d59742575b5 100644 --- a/.devcontainer/cuda11.8-pip/devcontainer.json +++ b/.devcontainer/cuda11.8-pip/devcontainer.json @@ -10,7 +10,9 @@ }, "hostRequirements": {"gpu": "optional"}, "features": { - "ghcr.io/rapidsai/devcontainers/features/ucx:23.12": {"version": "1.14.1"}, + "ghcr.io/rapidsai/devcontainers/features/ucx:23.12": { + "version": "1.14.1" + }, "ghcr.io/rapidsai/devcontainers/features/rapids-build-utils:23.12": {} }, "overrideFeatureInstallOrder": [ diff --git a/.devcontainer/cuda12.0-pip/devcontainer.json b/.devcontainer/cuda12.0-pip/devcontainer.json index abff840ebdd..3eacf726bf0 100644 --- a/.devcontainer/cuda12.0-pip/devcontainer.json +++ b/.devcontainer/cuda12.0-pip/devcontainer.json @@ -10,7 +10,9 @@ }, "hostRequirements": {"gpu": "optional"}, "features": { - "ghcr.io/rapidsai/devcontainers/features/ucx:23.12": {"version": "1.14.1"}, + "ghcr.io/rapidsai/devcontainers/features/ucx:23.12": { + "version": "1.14.1" + }, "ghcr.io/rapidsai/devcontainers/features/rapids-build-utils:23.12": {} }, "overrideFeatureInstallOrder": [ diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml index 55068b16cc7..362e826ef65 100644 --- a/.github/workflows/pr.yaml +++ b/.github/workflows/pr.yaml @@ -131,6 +131,7 @@ jobs: secrets: inherit uses: rapidsai/shared-action-workflows/.github/workflows/build-in-devcontainer.yaml@branch-23.12 with: + node_type: cpu32 extra-repo-deploy-key: CUGRAPH_OPS_SSH_PRIVATE_DEPLOY_KEY build_command: | sccache -z; diff --git a/python/nx-cugraph/_nx_cugraph/__init__.py b/python/nx-cugraph/_nx_cugraph/__init__.py index 227caea29d5..886d7a19f74 100644 --- a/python/nx-cugraph/_nx_cugraph/__init__.py +++ b/python/nx-cugraph/_nx_cugraph/__init__.py @@ -24,7 +24,7 @@ "backend_name": "cugraph", "project": "nx-cugraph", "package": "nx_cugraph", - "url": "https://github.com/rapidsai/cugraph/tree/branch-23.10/python/nx-cugraph", + "url": "https://github.com/rapidsai/cugraph/tree/branch-23.12/python/nx-cugraph", "short_summary": "GPU-accelerated backend.", # "description": "TODO", "functions": { From f0ea63475b3932e90f1133da52b9041b4462e19f Mon Sep 17 00:00:00 2001 From: Dylan Chima-Sanchez <97180625+betochimas@users.noreply.github.com> Date: Tue, 17 Oct 2023 07:22:59 -0700 Subject: [PATCH 051/111] Add nx-cugraph notebook for showing accelerated networkX APIs (#3830) This PR adds a notebook showing how to use the `nx-cugraph` package to accelerate your NetworkX workflow. closes rapidsai/graph_dl#277 Authors: - Dylan Chima-Sanchez (https://github.com/betochimas) - Rick Ratzel (https://github.com/rlratzel) Approvers: - Rick Ratzel (https://github.com/rlratzel) - Erik Welch (https://github.com/eriknw) URL: https://github.com/rapidsai/cugraph/pull/3830 --- notebooks/demo/nx_cugraph_demo.ipynb | 672 +++++++++++++++++++++++++++ 1 file changed, 672 insertions(+) create mode 100644 notebooks/demo/nx_cugraph_demo.ipynb diff --git a/notebooks/demo/nx_cugraph_demo.ipynb b/notebooks/demo/nx_cugraph_demo.ipynb new file mode 100644 index 00000000000..6e50370ed80 --- /dev/null +++ b/notebooks/demo/nx_cugraph_demo.ipynb @@ -0,0 +1,672 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# `nx-cugraph`: a NetworkX backend that provides GPU acceleration with RAPIDS cuGraph" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This notebook will demonstrate the `nx-cugraph` NetworkX backend using the NetworkX betweenness_centrality algorithm.\n", + "\n", + "## Background\n", + "Networkx version 3.0 introduced a dispatching mechanism that allows users to configure NetworkX to dispatch various algorithms to third-party backends. Backends can provide different implementations of graph algorithms, allowing users to take advantage of capabilities not available in NetworkX. `nx-cugraph` is a NetworkX backend provided by the [RAPIDS](https://rapids.ai) cuGraph project that adds GPU acceleration to greatly improve performance.\n", + "\n", + "## System Requirements\n", + "Using `nx-cugraph` with this notebook requires the following: \n", + "- NVIDIA GPU, Pascal architecture or later\n", + "- CUDA 11.2, 11.4, 11.5, 11.8, or 12.0\n", + "- Python versions 3.9, 3.10, or 3.11\n", + "- NetworkX >= version 3.2\n", + " - _NetworkX 3.0 supports dispatching and is compatible with `nx-cugraph`, but this notebook will demonstrate features added in 3.2_\n", + " - At the time of this writing, NetworkX 3.2 is only available from source and can be installed by following the [development version install instructions](https://github.com/networkx/networkx/blob/main/INSTALL.rst#install-the-development-version).\n", + "- Pandas\n", + "\n", + "More details about system requirements can be found in the [RAPIDS System Requirements documentation](https://docs.rapids.ai/install#system-req)." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "tags": [] + }, + "source": [ + "## Installation" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Assuming NetworkX >= 3.2 has been installed using the [development version install instructions](https://github.com/networkx/networkx/blob/main/INSTALL.rst#install-the-development-version), `nx-cugraph` can be installed using either `conda` or `pip`. \n", + "\n", + "#### conda\n", + "```\n", + "conda install -c rapidsai-nightly -c conda-forge -c nvidia nx-cugraph\n", + "```\n", + "#### pip\n", + "```\n", + "python -m pip install nx-cugraph-cu11 --extra-index-url https://pypi.nvidia.com\n", + "```\n", + "#### _Notes:_\n", + " * nightly wheel builds will not be available until the 23.12 release, therefore the index URL for the stable release version is being used in the pip install command above.\n", + " * Additional information relevant to installing any RAPIDS package can be found [here](https://rapids.ai/#quick-start).\n", + " * If you installed any of the packages described here since running this notebook, you may need to restart the kernel to have them visible to this notebook." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "tags": [] + }, + "source": [ + "## Notebook Helper Functions\n", + "\n", + "A few helper functions will be defined here that will be used in order to help keep this notebook easy to read." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import sys\n", + "def reimport_networkx():\n", + " \"\"\"\n", + " Re-imports networkx for demonstrating different backend configuration\n", + " options applied at import-time. This is only needed for demonstration\n", + " purposes since other mechanisms are available for runtime configuration.\n", + " \"\"\"\n", + " # Using importlib.reload(networkx) has several caveats (described here:\n", + " # https://docs.python.org/3/library/imp.html?highlight=reload#imp.reload)\n", + " # which result in backend configuration not being re-applied correctly.\n", + " # Instead, manually remove all modules and re-import\n", + " nx_mods = [m for m in sys.modules.keys()\n", + " if (m.startswith(\"networkx\") or m.startswith(\"nx_cugraph\"))]\n", + " for m in nx_mods:\n", + " sys.modules.pop(m)\n", + " import networkx\n", + " return networkx\n", + "\n", + "\n", + "from pathlib import Path\n", + "import requests\n", + "import gzip\n", + "import pandas as pd\n", + "def create_cit_patents_graph(verbose=True):\n", + " \"\"\"\n", + " Downloads the cit-Patents dataset (if not previously downloaded), reads\n", + " it, and creates a nx.DiGraph from it and returns it.\n", + " cit-Patents is described here:\n", + " https://snap.stanford.edu/data/cit-Patents.html\n", + " \"\"\"\n", + " url = \"https://snap.stanford.edu/data/cit-Patents.txt.gz\"\n", + " gz_file_name = Path(url.split(\"/\")[-1])\n", + " csv_file_name = Path(gz_file_name.stem)\n", + " if csv_file_name.exists():\n", + " if verbose: print(f\"{csv_file_name} already exists, not downloading.\")\n", + " else:\n", + " if verbose: print(f\"downloading {url}...\", end=\"\", flush=True)\n", + " req = requests.get(url)\n", + " open(gz_file_name, \"wb\").write(req.content)\n", + " if verbose: print(\"done\")\n", + " if verbose: print(f\"unzipping {gz_file_name}...\", end=\"\", flush=True)\n", + " with gzip.open(gz_file_name, \"rb\") as gz_in:\n", + " with open(csv_file_name, \"wb\") as txt_out:\n", + " txt_out.write(gz_in.read())\n", + " if verbose: print(\"done\")\n", + "\n", + " if verbose: print(\"reading csv to dataframe...\", end=\"\", flush=True)\n", + " pandas_edgelist = pd.read_csv(\n", + " csv_file_name.name,\n", + " skiprows=4,\n", + " delimiter=\"\\t\",\n", + " names=[\"src\", \"dst\"],\n", + " dtype={\"src\":\"int32\", \"dst\":\"int32\"},\n", + " )\n", + " if verbose: print(\"done\")\n", + " if verbose: print(\"creating NX graph from dataframe...\", end=\"\", flush=True)\n", + " G = nx.from_pandas_edgelist(\n", + " pandas_edgelist, source=\"src\", target=\"dst\", create_using=nx.DiGraph\n", + " )\n", + " if verbose: print(\"done\")\n", + " return G" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "tags": [] + }, + "source": [ + "## Running `betweenness_centrality`\n", + "Let's start by running `betweenness_centrality` on the Karate Club graph using the default NetworkX implementation." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "tags": [] + }, + "source": [ + "### Zachary's Karate Club\n", + "\n", + "Zachary's Karate Club is a small dataset consisting of 34 nodes and 78 edges which represent the friendships between members of a karate club. This dataset is small enough to make comparing results between NetworkX and `nx-cugraph` easy." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "import networkx as nx\n", + "karate_club_graph = nx.karate_club_graph()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "tags": [] + }, + "source": [ + "Having NetworkX compute the `betweenness_centrality` values for each node on this graph is quick and easy." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2.51 ms ± 1.02 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n" + ] + } + ], + "source": [ + "%%timeit global karate_nx_bc_results\n", + "karate_nx_bc_results = nx.betweenness_centrality(karate_club_graph)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "tags": [] + }, + "source": [ + "### Automatic GPU acceleration\n", + "When `nx-cugraph` is installed, NetworkX will detect it on import and make it available as a backend for APIs supported by that backend. However, NetworkX does not assume the user always wants to use a particular backend, and instead looks at various configuration mechanisms in place for users to specify how NetworkX should use installed backends. Since NetworkX was not configured to use a backend for the above `betweenness_centrality` call, it used the default implementation provided by NetworkX.\n", + "\n", + "The first configuration mechanism to be demonstrated below is the `NETWORKX_AUTOMATIC_BACKENDS` environment variable. This environment variable directs NetworkX to use the backend specified everywhere it's supported and does not require the user to modify any of their existing NetworkX code.\n", + "\n", + "To use it, a user sets `NETWORKX_AUTOMATIC_BACKENDS` in their shell to the backend they'd like to use. If a user has more than one backend installed, the environment variable can also accept a comma-separated list of backends, ordered by priority in which NetworkX should use them, where the first backend that supports a particular API call will be used. For example:\n", + "```\n", + "bash> export NETWORKX_AUTOMATIC_BACKENDS=cugraph\n", + "bash> python my_nx_app.py # uses nx-cugraph wherever possible, then falls back to default implementation where it's not.\n", + "```\n", + "or in the case of multiple backends installed\n", + "```\n", + "bash> export NETWORKX_AUTOMATIC_BACKENDS=cugraph,graphblas\n", + "bash> python my_nx_app.py # uses nx-cugraph if possible, then nx-graphblas if possible, then default implementation.\n", + "```\n", + "\n", + "NetworkX looks at the environment variable and the installed backends at import time, and will not re-examine the environment after that. Because `networkx` was already imported in this notebook, the `reimport_nx()` utility will be called after the `os.environ` dictionary is updated to simulate an environment variable being set in the shell.\n", + "\n", + "**Please note, this is only needed for demonstration purposes to compare runs both with and without fully-automatic backend use enabled.**" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "os.environ[\"NETWORKX_AUTOMATIC_BACKENDS\"] = \"cugraph\"\n", + "nx = reimport_networkx()\n", + "# reimporting nx requires reinstantiating Graphs since python considers\n", + "# types from the prior nx import != types from the reimported nx\n", + "karate_club_graph = nx.karate_club_graph()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "tags": [] + }, + "source": [ + "Once the environment is updated, re-running the same `betweenness_centrality` call on the same graph requires no code changes." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "43.9 ms ± 222 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)\n" + ] + } + ], + "source": [ + "%%timeit global karate_cg_bc_results\n", + "karate_cg_bc_results = nx.betweenness_centrality(karate_club_graph)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "tags": [] + }, + "source": [ + "We may see that the same computation actually took *longer* using `nx-cugraph`. This is not too surprising given how small the graph is, since there's a small amount of overhead to copy data to and from the GPU which becomes more obvious on very small graphs. We'll see with a larger graph how this overhead becomes negligible." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "tags": [] + }, + "source": [ + "### Results Comparison\n", + "\n", + "Let's examine the results of each run to see how they compare. \n", + "The `betweenness_centrality` results are a dictionary mapping vertex IDs to betweenness_centrality scores. The score itself is usually not as important as the relative rank of each vertex ID (e.g. vertex A is ranked higher than vertex B in both sets of results)." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "NX: (0, 0.437635), CG: (0, 0.437635)\n", + "NX: (33, 0.304075), CG: (33, 0.304075)\n", + "NX: (32, 0.145247), CG: (32, 0.145247)\n", + "NX: (2, 0.143657), CG: (2, 0.143657)\n", + "NX: (31, 0.138276), CG: (31, 0.138276)\n", + "NX: (8, 0.055927), CG: (8, 0.055927)\n", + "NX: (1, 0.053937), CG: (1, 0.053937)\n", + "NX: (13, 0.045863), CG: (13, 0.045863)\n", + "NX: (19, 0.032475), CG: (19, 0.032475)\n", + "NX: (5, 0.029987), CG: (5, 0.029987)\n", + "NX: (6, 0.029987), CG: (6, 0.029987)\n", + "NX: (27, 0.022333), CG: (27, 0.022333)\n", + "NX: (23, 0.017614), CG: (23, 0.017614)\n", + "NX: (30, 0.014412), CG: (30, 0.014412)\n", + "NX: (3, 0.011909), CG: (3, 0.011909)\n", + "NX: (25, 0.003840), CG: (25, 0.003840)\n", + "NX: (29, 0.002922), CG: (29, 0.002922)\n", + "NX: (24, 0.002210), CG: (24, 0.002210)\n", + "NX: (28, 0.001795), CG: (28, 0.001795)\n", + "NX: (9, 0.000848), CG: (9, 0.000848)\n", + "NX: (4, 0.000631), CG: (4, 0.000631)\n", + "NX: (10, 0.000631), CG: (10, 0.000631)\n", + "NX: (7, 0.000000), CG: (7, 0.000000)\n", + "NX: (11, 0.000000), CG: (11, 0.000000)\n", + "NX: (12, 0.000000), CG: (12, 0.000000)\n", + "NX: (14, 0.000000), CG: (14, 0.000000)\n", + "NX: (15, 0.000000), CG: (15, 0.000000)\n", + "NX: (16, 0.000000), CG: (16, 0.000000)\n", + "NX: (17, 0.000000), CG: (17, 0.000000)\n", + "NX: (18, 0.000000), CG: (18, 0.000000)\n", + "NX: (20, 0.000000), CG: (20, 0.000000)\n", + "NX: (21, 0.000000), CG: (21, 0.000000)\n", + "NX: (22, 0.000000), CG: (22, 0.000000)\n", + "NX: (26, 0.000000), CG: (26, 0.000000)\n" + ] + } + ], + "source": [ + "# The lists contain tuples of (vertex ID, betweenness_centrality score),\n", + "# sorted based on the score.\n", + "nx_sorted = sorted(karate_nx_bc_results.items(), key=lambda t:t[1], reverse=True)\n", + "cg_sorted = sorted(karate_cg_bc_results.items(), key=lambda t:t[1], reverse=True)\n", + "\n", + "for i in range(len(nx_sorted)):\n", + " print(\"NX: (%d, %.6f), CG: (%d, %.6f)\" % (nx_sorted[i] + cg_sorted[i]))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "tags": [] + }, + "source": [ + "Here we can see that the results match exactly as expected. \n", + "\n", + "For larger graphs, results are harder to compare given that `betweenness_centrality` is an approximation algorithm influenced by the random selection of paths used to compute the betweenness_centrality score of each vertex. The argument `k` is used for limiting the number of paths used in the computation, since using every path for every vertex would be prohibitively expensive for large graphs. For small graphs, `k` need not be specified, which allows `betweenness_centrality` to use all paths for all vertices and makes for an easier comparison." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "tags": [] + }, + "source": [ + "### `betweenness_centrality` on larger graphs - The U.S. Patent Citation Network1\n", + "\n", + "The U.S. Patent Citation Network dataset is much larger with over 3.7M nodes and over 16.5M edges and demonstrates how `nx-cugraph` enables NetworkX to run `betweenness_centrality` on graphs this large (and larger) in seconds instead of minutes.\n", + "\n", + "#### NetworkX default implementation" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "downloading https://snap.stanford.edu/data/cit-Patents.txt.gz...done\n", + "unzipping cit-Patents.txt.gz...done\n", + "reading csv to dataframe...done\n", + "creating NX graph from dataframe...done\n" + ] + } + ], + "source": [ + "import os\n", + "# Unset NETWORKX_AUTOMATIC_BACKENDS so the default NetworkX implementation is used\n", + "os.environ.pop(\"NETWORKX_AUTOMATIC_BACKENDS\", None)\n", + "nx = reimport_networkx()\n", + "# Create the cit-Patents graph - this will also download the dataset if not previously downloaded\n", + "cit_patents_graph = create_cit_patents_graph()" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "# Since this is a large graph, a k value must be set so the computation returns in a reasonable time\n", + "k = 40" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "tags": [] + }, + "source": [ + "Because this run will take time, `%%timeit` is restricted to a single pass.\n", + "\n", + "*NOTE: this run may take approximately 1 minute*" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1min 4s ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)\n" + ] + } + ], + "source": [ + "%%timeit -r 1\n", + "results = nx.betweenness_centrality(cit_patents_graph, k=k)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "tags": [] + }, + "source": [ + "Something to note is that `%%timeit` disables garbage collection by default, which may not be something a user is able to do. To see a more realistic real-world run time, `gc` can be enabled." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "0" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# import and run the garbage collector upfront prior to using it in the benchmark\n", + "import gc\n", + "gc.collect()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "tags": [] + }, + "source": [ + "*NOTE: this run may take approximately 7 minutes!*" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "6min 50s ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)\n" + ] + } + ], + "source": [ + "%%timeit -r 1 gc.enable()\n", + "nx.betweenness_centrality(cit_patents_graph, k=k)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### `nx-cugraph`\n", + "\n", + "Running on a GPU using `nx-cugraph` can result in a tremendous speedup, especially when graphs reach sizes larger than a few thousand nodes or `k` values become larger to increase accuracy.\n", + "\n", + "Rather than setting the `NETWORKX_AUTOMATIC_BACKENDS` environment variable and re-importing again, this example will demonstrate the `backend=` keyword argument to explicitly direct the NetworkX dispatcher to use the `cugraph` backend." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "10.1 s ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)\n" + ] + } + ], + "source": [ + "%%timeit -r 1 gc.enable()\n", + "nx.betweenness_centrality(cit_patents_graph, k=k, backend=\"cugraph\")" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [], + "source": [ + "k = 150" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "11.6 s ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)\n" + ] + } + ], + "source": [ + "%%timeit -r 1 gc.enable()\n", + "nx.betweenness_centrality(cit_patents_graph, k=k, backend=\"cugraph\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "tags": [] + }, + "source": [ + "For the same graph and the same `k` value, the `\"cugraph\"` backend returns results in seconds instead of minutes. Increasing the `k` value has very little relative impact to runtime due to the high parallel processing ability of the GPU, allowing the user to get improved accuracy for virtually no additional cost." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Type-based dispatching\n", + "\n", + "NetworkX also supports automatically dispatching to backends associated with specific graph types. This requires the user to write code for a specific backend, and therefore requires the backend to be installed, but has the advantage of ensuring a particular behavior without the potential for runtime conversions.\n", + "\n", + "To use type-based dispatching with `nx-cugraph`, the user must import the backend directly in their code to access the utilities provided to create a Graph instance specifically for the `nx-cugraph` backend." + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [], + "source": [ + "import nx_cugraph as nxcg" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "tags": [] + }, + "source": [ + "The `from_networkx()` API will copy the data from the NetworkX graph instance to the GPU and return a new `nx-cugraph` graph instance. By passing an explicit `nx-cugraph` graph, the NetworkX dispatcher will automatically call the `\"cugraph\"` backend (and only the `\"cugraph\"` backend) without requiring future conversions to copy data to the GPU." + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "7.92 s ± 2.85 ms per loop (mean ± std. dev. of 2 runs, 1 loop each)\n" + ] + } + ], + "source": [ + "%%timeit -r 2 global nxcg_cit_patents_graph\n", + "nxcg_cit_patents_graph = nxcg.from_networkx(cit_patents_graph)" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "3.14 s ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)\n" + ] + } + ], + "source": [ + "%%timeit -r 1 gc.enable()\n", + "nx.betweenness_centrality(nxcg_cit_patents_graph, k=k)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Conclusion\n", + "\n", + "This notebook demonstrated `nx-cugraph`'s support for `betweenness_centrality`. At the time of this writing, `nx-cugraph` also provides support for `edge_netweenness_centrality` and `louvain_communities`. Other algorithms are scheduled to be supported based on their availability in the cuGraph [pylibcugraph](https://github.com/rapidsai/cugraph/tree/branch-23.10/python/pylibcugraph/pylibcugraph) package and demand by the NetworkX community.\n", + "\n", + "#### Benchmark Results\n", + "The results included in this notebook were generated on a workstation with the following hardware:\n", + "\n", + "\n", + " \n", + " \n", + "
CPU:Intel(R) Xeon(R) Gold 6128 CPU @ 3.40GHz, 45GB
GPU:Quatro RTX 8000, 50GB
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "1 Information on the U.S. Patent Citation Network dataset used in this notebook is as follows:\n", + "\n", + " \n", + " \n", + " \n", + " \n", + "
Authors:Jure Leskovec and Andrej Krevl
Title:SNAP Datasets, Stanford Large Network Dataset Collection
URL:http://snap.stanford.edu/data
Date:June 2014
\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.12" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} From 9efd87d3479f510978fa61402879a051ae11d8d5 Mon Sep 17 00:00:00 2001 From: Joseph Nke <76006812+jnke2016@users.noreply.github.com> Date: Tue, 17 Oct 2023 17:16:21 -0500 Subject: [PATCH 052/111] Temporarily disable mg testing (#3940) This PR temporarily disables single-GPU "MG" tests in CI until the dask related transient failures are resolved. The PR to re-enable them, intended to block the 23.12 release, is [here](https://github.com/rapidsai/cugraph/pull/3941). Authors: - Joseph Nke (https://github.com/jnke2016) Approvers: - Rick Ratzel (https://github.com/rlratzel) - Ray Douglass (https://github.com/raydouglass) URL: https://github.com/rapidsai/cugraph/pull/3940 --- ci/test_python.sh | 3 ++- ci/test_wheel.sh | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/ci/test_python.sh b/ci/test_python.sh index c7687396ff0..1690ce2f15b 100755 --- a/ci/test_python.sh +++ b/ci/test_python.sh @@ -63,6 +63,7 @@ pytest \ tests popd +# FIXME: TEMPORARILY disable single-GPU "MG" testing rapids-logger "pytest cugraph" pushd python/cugraph/cugraph DASK_WORKER_DEVICES="0" \ @@ -78,7 +79,7 @@ pytest \ --cov=cugraph \ --cov-report=xml:"${RAPIDS_COVERAGE_DIR}/cugraph-coverage.xml" \ --cov-report=term \ - -k "not test_property_graph_mg" \ + -k "not _mg" \ tests popd diff --git a/ci/test_wheel.sh b/ci/test_wheel.sh index c4adb638437..28f59f0209e 100755 --- a/ci/test_wheel.sh +++ b/ci/test_wheel.sh @@ -21,9 +21,10 @@ arch=$(uname -m) if [[ "${arch}" == "aarch64" && ${RAPIDS_BUILD_TYPE} == "pull-request" ]]; then python ./ci/wheel_smoke_test_${package_name}.py else + # FIXME: TEMPORARILY disable single-GPU "MG" testing RAPIDS_DATASET_ROOT_DIR=`pwd`/datasets \ DASK_DISTRIBUTED__SCHEDULER__WORKER_TTL="1000s" \ DASK_DISTRIBUTED__COMM__TIMEOUTS__CONNECT="1000s" \ DASK_CUDA_WAIT_WORKERS_MIN_TIMEOUT="1000s" \ - python -m pytest ./python/${package_name}/${python_package_name}/tests + python -m pytest -k "not _mg" ./python/${package_name}/${python_package_name}/tests fi From 34dce3da87f5583d421e7b0ab30421afef581366 Mon Sep 17 00:00:00 2001 From: Seunghwa Kang <45857425+seunghwak@users.noreply.github.com> Date: Tue, 17 Oct 2023 19:46:52 -0700 Subject: [PATCH 053/111] Implement the transform_e primitive (to update property values for all edges) (#3917) We have two versions of transform_e, one updating property values for all edges and another updating property values only for the edges in the given edge list. Both were declared but only the latter was implemented. This PR implements the first version (necessary to update edge masks in testing the neighbor intersection primitives with edge masking). Authors: - Seunghwa Kang (https://github.com/seunghwak) - Chuck Hastings (https://github.com/ChuckHastings) Approvers: - Chuck Hastings (https://github.com/ChuckHastings) URL: https://github.com/rapidsai/cugraph/pull/3917 --- .../cugraph/edge_partition_device_view.cuh | 31 ++- ...ge_partition_edge_property_device_view.cuh | 39 ++-- ...artition_endpoint_property_device_view.cuh | 33 ++-- cpp/include/cugraph/edge_property.hpp | 8 +- .../cugraph/utilities/packed_bool_utils.hpp | 7 + cpp/src/prims/transform_e.cuh | 165 +++++++++++++++- cpp/tests/prims/mg_transform_e.cu | 182 ++++++++++++++---- 7 files changed, 382 insertions(+), 83 deletions(-) diff --git a/cpp/include/cugraph/edge_partition_device_view.cuh b/cpp/include/cugraph/edge_partition_device_view.cuh index d34d639f4d9..213f9b9497a 100644 --- a/cpp/include/cugraph/edge_partition_device_view.cuh +++ b/cpp/include/cugraph/edge_partition_device_view.cuh @@ -109,6 +109,13 @@ class edge_partition_device_view_base_t { __host__ __device__ edge_t const* offsets() const { return offsets_.data(); } __host__ __device__ vertex_t const* indices() const { return indices_.data(); } + __device__ vertex_t major_idx_from_local_edge_idx_nocheck(edge_t local_edge_idx) const noexcept + { + return static_cast(thrust::distance( + offsets_.begin() + 1, + thrust::upper_bound(thrust::seq, offsets_.begin() + 1, offsets_.end(), local_edge_idx))); + } + // major_idx == major offset if CSR/CSC, major_offset != major_idx if DCSR/DCSC __device__ thrust::tuple local_edges( vertex_t major_idx) const noexcept @@ -291,8 +298,19 @@ class edge_partition_device_view_t= (*major_hypersparse_first_ - major_range_first_) + ? (*dcs_nzd_vertices_)[major_idx - (*major_hypersparse_first_ - major_range_first_)] + : major_from_major_offset_nocheck(major_idx); + } else { // major_idx == major_offset + return major_from_major_offset_nocheck(major_idx); + } + } + // major_hypersparse_idx: index within the hypersparse segment - __host__ __device__ thrust::optional major_hypersparse_idx_from_major_nocheck( + __device__ thrust::optional major_hypersparse_idx_from_major_nocheck( vertex_t major) const noexcept { if (dcs_nzd_vertices_) { @@ -303,7 +321,7 @@ class edge_partition_device_view_t major_from_major_hypersparse_idx_nocheck( + __device__ thrust::optional major_from_major_hypersparse_idx_nocheck( vertex_t major_hypersparse_idx) const noexcept { return dcs_nzd_vertices_ @@ -442,8 +460,13 @@ class edge_partition_device_view_t major_hypersparse_idx_from_major_nocheck( + __device__ thrust::optional major_hypersparse_idx_from_major_nocheck( vertex_t major) const noexcept { assert(false); @@ -451,7 +474,7 @@ class edge_partition_device_view_t major_from_major_hypersparse_idx_nocheck( + __device__ thrust::optional major_from_major_hypersparse_idx_nocheck( vertex_t major_hypersparse_idx) const noexcept { assert(false); diff --git a/cpp/include/cugraph/edge_partition_edge_property_device_view.cuh b/cpp/include/cugraph/edge_partition_edge_property_device_view.cuh index f71fc167d12..18091567e38 100644 --- a/cpp/include/cugraph/edge_partition_edge_property_device_view.cuh +++ b/cpp/include/cugraph/edge_partition_edge_property_device_view.cuh @@ -33,18 +33,21 @@ template ::value_type> class edge_partition_edge_property_device_view_t { public: + using edge_type = edge_t; + using value_type = value_t; + static constexpr bool is_packed_bool = cugraph::is_packed_bool(); + static constexpr bool has_packed_bool_element = + cugraph::has_packed_bool_element(); + static_assert( std::is_same_v::value_type, value_t> || - cugraph::has_packed_bool_element()); + has_packed_bool_element); static_assert(cugraph::is_arithmetic_or_thrust_tuple_of_arithmetic::value); - using edge_type = edge_t; - using value_type = value_t; - edge_partition_edge_property_device_view_t() = default; edge_partition_edge_property_device_view_t( - edge_property_view_t const& view, size_t partition_idx) + edge_property_view_t const& view, size_t partition_idx) : value_first_(view.value_firsts()[partition_idx]) { value_first_ = view.value_firsts()[partition_idx]; @@ -54,8 +57,8 @@ class edge_partition_edge_property_device_view_t { __device__ value_t get(edge_t offset) const { - if constexpr (cugraph::has_packed_bool_element()) { - static_assert(std::is_arithmetic_v, "unimplemented for thrust::tuple types."); + if constexpr (has_packed_bool_element) { + static_assert(is_packed_bool, "unimplemented for thrust::tuple types."); auto mask = cugraph::packed_bool_mask(offset); return static_cast(*(value_first_ + cugraph::packed_bool_offset(offset)) & mask); } else { @@ -69,8 +72,8 @@ class edge_partition_edge_property_device_view_t { void> set(edge_t offset, value_t val) const { - if constexpr (cugraph::has_packed_bool_element()) { - static_assert(std::is_arithmetic_v, "unimplemented for thrust::tuple types."); + if constexpr (has_packed_bool_element) { + static_assert(is_packed_bool, "unimplemented for thrust::tuple types."); auto mask = cugraph::packed_bool_mask(offset); if (val) { atomicOr(value_first_ + cugraph::packed_bool_offset(offset), mask); @@ -88,8 +91,8 @@ class edge_partition_edge_property_device_view_t { value_t> atomic_and(edge_t offset, value_t val) const { - if constexpr (cugraph::has_packed_bool_element()) { - static_assert(std::is_arithmetic_v, "unimplemented for thrust::tuple types."); + if constexpr (has_packed_bool_element) { + static_assert(is_packed_bool, "unimplemented for thrust::tuple types."); auto mask = cugraph::packed_bool_mask(offset); auto old = atomicAnd(value_first_ + cugraph::packed_bool_offset(offset), val ? uint32_t{0xffffffff} : ~mask); @@ -105,8 +108,8 @@ class edge_partition_edge_property_device_view_t { value_t> atomic_or(edge_t offset, value_t val) const { - if constexpr (cugraph::has_packed_bool_element()) { - static_assert(std::is_arithmetic_v, "unimplemented for thrust::tuple types."); + if constexpr (has_packed_bool_element) { + static_assert(is_packed_bool, "unimplemented for thrust::tuple types."); auto mask = cugraph::packed_bool_mask(offset); auto old = atomicOr(value_first_ + cugraph::packed_bool_offset(offset), val ? mask : uint32_t{0}); @@ -132,8 +135,8 @@ class edge_partition_edge_property_device_view_t { value_t> elementwise_atomic_cas(edge_t offset, value_t compare, value_t val) const { - if constexpr (cugraph::has_packed_bool_element()) { - static_assert(std::is_arithmetic_v, "unimplemented for thrust::tuple types."); + if constexpr (has_packed_bool_element) { + static_assert(is_packed_bool, "unimplemented for thrust::tuple types."); auto mask = cugraph::packed_bool_mask(offset); auto old = val ? atomicOr(value_first_ + cugraph::packed_bool_offset(offset), mask) : atomicAnd(value_first_ + cugraph::packed_bool_offset(offset), ~mask); @@ -170,8 +173,10 @@ class edge_partition_edge_property_device_view_t { template class edge_partition_edge_dummy_property_device_view_t { public: - using edge_type = edge_t; - using value_type = thrust::nullopt_t; + using edge_type = edge_t; + using value_type = thrust::nullopt_t; + static constexpr bool is_packed_bool = false; + static constexpr bool has_packed_bool_element = false; edge_partition_edge_dummy_property_device_view_t() = default; diff --git a/cpp/include/cugraph/edge_partition_endpoint_property_device_view.cuh b/cpp/include/cugraph/edge_partition_endpoint_property_device_view.cuh index 1ff279fbdca..7578c646175 100644 --- a/cpp/include/cugraph/edge_partition_endpoint_property_device_view.cuh +++ b/cpp/include/cugraph/edge_partition_endpoint_property_device_view.cuh @@ -39,12 +39,15 @@ template ::value_type> class edge_partition_endpoint_property_device_view_t { public: + using vertex_type = vertex_t; + using value_type = value_t; + static constexpr bool is_packed_bool = cugraph::is_packed_bool(); + static constexpr bool has_packed_bool_element = + cugraph::has_packed_bool_element(); + static_assert( std::is_same_v::value_type, value_t> || - cugraph::has_packed_bool_element()); - - using vertex_type = vertex_t; - using value_type = value_t; + has_packed_bool_element); edge_partition_endpoint_property_device_view_t() = default; @@ -77,8 +80,8 @@ class edge_partition_endpoint_property_device_view_t { __device__ value_t get(vertex_t offset) const { auto val_offset = value_offset(offset); - if constexpr (cugraph::has_packed_bool_element()) { - static_assert(std::is_arithmetic_v, "unimplemented for thrust::tuple types."); + if constexpr (has_packed_bool_element) { + static_assert(is_packed_bool, "unimplemented for thrust::tuple types."); auto mask = cugraph::packed_bool_mask(val_offset); return static_cast(*(value_first_ + cugraph::packed_bool_offset(val_offset)) & mask); } else { @@ -93,8 +96,8 @@ class edge_partition_endpoint_property_device_view_t { atomic_and(vertex_t offset, value_t val) const { auto val_offset = value_offset(offset); - if constexpr (cugraph::has_packed_bool_element()) { - static_assert(std::is_arithmetic_v, "unimplemented for thrust::tuple types."); + if constexpr (has_packed_bool_element) { + static_assert(is_packed_bool, "unimplemented for thrust::tuple types."); auto mask = cugraph::packed_bool_mask(val_offset); auto old = atomicAnd(value_first_ + cugraph::packed_bool_offset(val_offset), val ? cugraph::packed_bool_full_mask() : ~mask); @@ -111,8 +114,8 @@ class edge_partition_endpoint_property_device_view_t { atomic_or(vertex_t offset, value_t val) const { auto val_offset = value_offset(offset); - if constexpr (cugraph::has_packed_bool_element()) { - static_assert(std::is_arithmetic_v, "unimplemented for thrust::tuple types."); + if constexpr (has_packed_bool_element) { + static_assert(is_packed_bool, "unimplemented for thrust::tuple types."); auto mask = cugraph::packed_bool_mask(val_offset); auto old = atomicOr(value_first_ + cugraph::packed_bool_offset(val_offset), val ? mask : cugraph::packed_bool_empty_mask()); @@ -140,8 +143,8 @@ class edge_partition_endpoint_property_device_view_t { elementwise_atomic_cas(vertex_t offset, value_t compare, value_t val) const { auto val_offset = value_offset(offset); - if constexpr (cugraph::has_packed_bool_element()) { - static_assert(std::is_arithmetic_v, "unimplemented for thrust::tuple types."); + if constexpr (has_packed_bool_element) { + static_assert(is_packed_bool, "unimplemented for thrust::tuple types."); auto mask = cugraph::packed_bool_mask(val_offset); auto old = val ? atomicOr(value_first_ + cugraph::packed_bool_offset(val_offset), mask) : atomicAnd(value_first_ + cugraph::packed_bool_offset(val_offset), ~mask); @@ -203,8 +206,10 @@ class edge_partition_endpoint_property_device_view_t { template class edge_partition_endpoint_dummy_property_device_view_t { public: - using vertex_type = vertex_t; - using value_type = thrust::nullopt_t; + using vertex_type = vertex_t; + using value_type = thrust::nullopt_t; + static constexpr bool is_packed_bool = false; + static constexpr bool has_packed_bool_element = false; edge_partition_endpoint_dummy_property_device_view_t() = default; diff --git a/cpp/include/cugraph/edge_property.hpp b/cpp/include/cugraph/edge_property.hpp index 8904006a2a2..d46d4e52fd4 100644 --- a/cpp/include/cugraph/edge_property.hpp +++ b/cpp/include/cugraph/edge_property.hpp @@ -72,9 +72,11 @@ class edge_property_t { public: static_assert(cugraph::is_arithmetic_or_thrust_tuple_of_arithmetic::value); - using edge_type = typename GraphViewType::edge_type; - using value_type = T; - using buffer_type = decltype(allocate_dataframe_buffer(size_t{0}, rmm::cuda_stream_view{})); + using edge_type = typename GraphViewType::edge_type; + using value_type = T; + using buffer_type = + decltype(allocate_dataframe_buffer, uint32_t, T>>( + size_t{0}, rmm::cuda_stream_view{})); edge_property_t(raft::handle_t const& handle) {} diff --git a/cpp/include/cugraph/utilities/packed_bool_utils.hpp b/cpp/include/cugraph/utilities/packed_bool_utils.hpp index 9557b11e8e0..0be5711d90c 100644 --- a/cpp/include/cugraph/utilities/packed_bool_utils.hpp +++ b/cpp/include/cugraph/utilities/packed_bool_utils.hpp @@ -47,6 +47,13 @@ has_packed_bool_element(std::index_sequence) } // namespace detail +template +constexpr bool is_packed_bool() +{ + return std::is_same_v::value_type, uint32_t> && + std::is_same_v; +} + // sizeof(uint32_t) * 8 packed Boolean values are stored using one uint32_t template constexpr bool has_packed_bool_element() diff --git a/cpp/src/prims/transform_e.cuh b/cpp/src/prims/transform_e.cuh index 7950df58a3e..edacdc8a970 100644 --- a/cpp/src/prims/transform_e.cuh +++ b/cpp/src/prims/transform_e.cuh @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -35,6 +36,70 @@ namespace cugraph { +namespace detail { + +int32_t constexpr transform_e_kernel_block_size = 512; + +template +__global__ void transform_e_packed_bool( + edge_partition_device_view_t edge_partition, + EdgePartitionSrcValueInputWrapper edge_partition_src_value_input, + EdgePartitionDstValueInputWrapper edge_partition_dst_value_input, + EdgePartitionEdgeValueInputWrapper edge_partition_e_value_input, + EdgePartitionEdgeValueOutputWrapper edge_partition_e_value_output, + EdgeOp e_op) +{ + static_assert(EdgePartitionEdgeValueOutputWrapper::is_packed_bool); + static_assert(raft::warp_size() == packed_bools_per_word()); + + using edge_t = typename GraphViewType::edge_type; + + auto const tid = threadIdx.x + blockIdx.x * blockDim.x; + static_assert(transform_e_kernel_block_size % raft::warp_size() == 0); + auto const lane_id = tid % raft::warp_size(); + auto idx = static_cast(packed_bool_offset(tid)); + + auto num_edges = edge_partition.number_of_edges(); + while (idx < static_cast(packed_bool_size(num_edges))) { + auto local_edge_idx = + idx * static_cast(packed_bools_per_word()) + static_cast(lane_id); + uint32_t mask{0}; + int predicate{0}; + if (local_edge_idx < num_edges) { + auto major_idx = edge_partition.major_idx_from_local_edge_idx_nocheck(local_edge_idx); + auto major = edge_partition.major_from_major_idx_nocheck(major_idx); + auto major_offset = edge_partition.major_offset_from_major_nocheck(major); + auto minor = *(edge_partition.indices() + local_edge_idx); + auto minor_offset = edge_partition.minor_offset_from_minor_nocheck(minor); + + auto src = GraphViewType::is_storage_transposed ? minor : major; + auto dst = GraphViewType::is_storage_transposed ? major : minor; + auto src_offset = GraphViewType::is_storage_transposed ? minor_offset : major_offset; + auto dst_offset = GraphViewType::is_storage_transposed ? major_offset : minor_offset; + predicate = e_op(src, + dst, + edge_partition_src_value_input.get(src_offset), + edge_partition_dst_value_input.get(dst_offset), + edge_partition_e_value_input.get(local_edge_idx)) + ? int{1} + : int{0}; + } + mask = __ballot_sync(uint32_t{0xffffffff}, predicate); + if (lane_id == 0) { *(edge_partition_e_value_output.value_first() + idx) = mask; } + + idx += static_cast(gridDim.x * (blockDim.x / raft::warp_size())); + } +} + +} // namespace detail + /** * @brief Iterate over the entire set of edges and update edge property values. * @@ -84,9 +149,103 @@ void transform_e(raft::handle_t const& handle, EdgeValueOutputWrapper edge_value_output, bool do_expensive_check = false) { - // CUGRAPH_EXPECTS(!graph_view.has_edge_mask(), "unimplemented."); + using vertex_t = typename GraphViewType::vertex_type; + using edge_t = typename GraphViewType::edge_type; - CUGRAPH_FAIL("unimplemented."); + using edge_partition_src_input_device_view_t = std::conditional_t< + std::is_same_v, + detail::edge_partition_endpoint_dummy_property_device_view_t, + detail::edge_partition_endpoint_property_device_view_t< + vertex_t, + typename EdgeSrcValueInputWrapper::value_iterator, + typename EdgeSrcValueInputWrapper::value_type>>; + using edge_partition_dst_input_device_view_t = std::conditional_t< + std::is_same_v, + detail::edge_partition_endpoint_dummy_property_device_view_t, + detail::edge_partition_endpoint_property_device_view_t< + vertex_t, + typename EdgeDstValueInputWrapper::value_iterator, + typename EdgeDstValueInputWrapper::value_type>>; + using edge_partition_e_input_device_view_t = std::conditional_t< + std::is_same_v, + detail::edge_partition_edge_dummy_property_device_view_t, + detail::edge_partition_edge_property_device_view_t< + edge_t, + typename EdgeValueInputWrapper::value_iterator, + typename EdgeValueInputWrapper::value_type>>; + using edge_partition_e_output_device_view_t = detail::edge_partition_edge_property_device_view_t< + edge_t, + typename EdgeValueOutputWrapper::value_iterator, + typename EdgeValueOutputWrapper::value_type>; + + CUGRAPH_EXPECTS(!graph_view.has_edge_mask(), "unimplemented."); + + for (size_t i = 0; i < graph_view.number_of_local_edge_partitions(); ++i) { + auto edge_partition = + edge_partition_device_view_t( + graph_view.local_edge_partition_view(i)); + + edge_partition_src_input_device_view_t edge_partition_src_value_input{}; + edge_partition_dst_input_device_view_t edge_partition_dst_value_input{}; + if constexpr (GraphViewType::is_storage_transposed) { + edge_partition_src_value_input = edge_partition_src_input_device_view_t(edge_src_value_input); + edge_partition_dst_value_input = + edge_partition_dst_input_device_view_t(edge_dst_value_input, i); + } else { + edge_partition_src_value_input = + edge_partition_src_input_device_view_t(edge_src_value_input, i); + edge_partition_dst_value_input = edge_partition_dst_input_device_view_t(edge_dst_value_input); + } + auto edge_partition_e_value_input = edge_partition_e_input_device_view_t(edge_value_input, i); + auto edge_partition_e_value_output = + edge_partition_e_output_device_view_t(edge_value_output, i); + + auto num_edges = edge_partition.number_of_edges(); + if constexpr (edge_partition_e_output_device_view_t::has_packed_bool_element) { + static_assert(edge_partition_e_output_device_view_t::is_packed_bool, + "unimplemented for thrust::tuple types."); + if (edge_partition.number_of_edges() > edge_t{0}) { + raft::grid_1d_thread_t update_grid(num_edges, + detail::transform_e_kernel_block_size, + handle.get_device_properties().maxGridSize[0]); + detail::transform_e_packed_bool + <<>>( + edge_partition, + edge_partition_src_value_input, + edge_partition_dst_value_input, + edge_partition_e_value_input, + edge_partition_e_value_output, + e_op); + } + } else { + thrust::transform( + handle.get_thrust_policy(), + thrust::make_counting_iterator(edge_t{0}), + thrust::make_counting_iterator(num_edges), + edge_partition_e_value_output.value_first(), + [e_op, + edge_partition, + edge_partition_src_value_input, + edge_partition_dst_value_input, + edge_partition_e_value_input] __device__(edge_t i) { + auto major_idx = edge_partition.major_idx_from_local_edge_idx_nocheck(i); + auto major = edge_partition.major_from_major_idx_nocheck(major_idx); + auto major_offset = edge_partition.major_offset_from_major_nocheck(major); + auto minor = *(edge_partition.indices() + i); + auto minor_offset = edge_partition.minor_offset_from_minor_nocheck(minor); + + auto src = GraphViewType::is_storage_transposed ? minor : major; + auto dst = GraphViewType::is_storage_transposed ? major : minor; + auto src_offset = GraphViewType::is_storage_transposed ? minor_offset : major_offset; + auto dst_offset = GraphViewType::is_storage_transposed ? major_offset : minor_offset; + return e_op(src, + dst, + edge_partition_src_value_input.get(src_offset), + edge_partition_dst_value_input.get(dst_offset), + edge_partition_e_value_input.get(i)); + }); + } + } } /** @@ -177,7 +336,7 @@ void transform_e(raft::handle_t const& handle, typename EdgeValueOutputWrapper::value_iterator, typename EdgeValueOutputWrapper::value_type>; - // CUGRAPH_EXPECTS(!graph_view.has_edge_mask(), "unimplemented."); + CUGRAPH_EXPECTS(!graph_view.has_edge_mask(), "unimplemented."); auto major_first = GraphViewType::is_storage_transposed ? edge_list.dst_begin() : edge_list.src_begin(); diff --git a/cpp/tests/prims/mg_transform_e.cu b/cpp/tests/prims/mg_transform_e.cu index 127eddd43c7..24deaad810a 100644 --- a/cpp/tests/prims/mg_transform_e.cu +++ b/cpp/tests/prims/mg_transform_e.cu @@ -51,6 +51,7 @@ #include struct Prims_Usecase { + bool use_edgelist{false}; bool check_correctness{true}; }; @@ -113,8 +114,9 @@ class Tests_MGTransformE auto mg_dst_prop = cugraph::test::generate::dst_property( *handle_, mg_graph_view, mg_vertex_prop); - cugraph::edge_bucket_t edge_list(*handle_); - { + cugraph::edge_bucket_t edge_list( + *handle_); + if (prims_usecase.use_edgelist) { rmm::device_uvector srcs(0, handle_->get_stream()); rmm::device_uvector dsts(0, handle_->get_stream()); std::tie(srcs, dsts, std::ignore, std::ignore) = cugraph::decompress_to_edgelist( @@ -154,24 +156,41 @@ class Tests_MGTransformE if (cugraph::test::g_perf) { RAFT_CUDA_TRY(cudaDeviceSynchronize()); // for consistent performance measurement handle_->get_comms().barrier(); - hr_timer.start("MG transform_reduce_e"); + hr_timer.start("MG transform_e"); } - cugraph::transform_e( - *handle_, - mg_graph_view, - edge_list, - mg_src_prop.view(), - mg_dst_prop.view(), - cugraph::edge_dummy_property_t{}.view(), - [] __device__(auto src, auto dst, auto src_property, auto dst_property, thrust::nullopt_t) { - if (src_property < dst_property) { - return src_property; - } else { - return dst_property; - } - }, - edge_value_output.mutable_view()); + if (prims_usecase.use_edgelist) { + cugraph::transform_e( + *handle_, + mg_graph_view, + edge_list, + mg_src_prop.view(), + mg_dst_prop.view(), + cugraph::edge_dummy_property_t{}.view(), + [] __device__(auto src, auto dst, auto src_property, auto dst_property, thrust::nullopt_t) { + if (src_property < dst_property) { + return src_property; + } else { + return dst_property; + } + }, + edge_value_output.mutable_view()); + } else { + cugraph::transform_e( + *handle_, + mg_graph_view, + mg_src_prop.view(), + mg_dst_prop.view(), + cugraph::edge_dummy_property_t{}.view(), + [] __device__(auto src, auto dst, auto src_property, auto dst_property, thrust::nullopt_t) { + if (src_property < dst_property) { + return src_property; + } else { + return dst_property; + } + }, + edge_value_output.mutable_view()); + } if (cugraph::test::g_perf) { RAFT_CUDA_TRY(cudaDeviceSynchronize()); // for consistent performance measurement @@ -183,24 +202,42 @@ class Tests_MGTransformE // 3. validate MG results if (prims_usecase.check_correctness) { - auto num_invalids = cugraph::count_if_e( - *handle_, - mg_graph_view, - mg_src_prop.view(), - mg_dst_prop.view(), - edge_value_output.view(), - [property_initial_value] __device__( - auto src, auto dst, auto src_property, auto dst_property, auto edge_property) { - if (((src + dst) % 2) == 0) { + size_t num_invalids{}; + if (prims_usecase.use_edgelist) { + num_invalids = cugraph::count_if_e( + *handle_, + mg_graph_view, + mg_src_prop.view(), + mg_dst_prop.view(), + edge_value_output.view(), + [property_initial_value] __device__( + auto src, auto dst, auto src_property, auto dst_property, auto edge_property) { + if (((src + dst) % 2) == 0) { + if (src_property < dst_property) { + return edge_property != src_property; + } else { + return edge_property != dst_property; + } + } else { + return edge_property != property_initial_value; + } + }); + } else { + num_invalids = cugraph::count_if_e( + *handle_, + mg_graph_view, + mg_src_prop.view(), + mg_dst_prop.view(), + edge_value_output.view(), + [property_initial_value] __device__( + auto src, auto dst, auto src_property, auto dst_property, auto edge_property) { if (src_property < dst_property) { return edge_property != src_property; } else { return edge_property != dst_property; } - } else { - return edge_property != property_initial_value; - } - }); + }); + } ASSERT_TRUE(num_invalids == 0); } @@ -278,13 +315,13 @@ TEST_P(Tests_MGTransformE_Rmat, CheckInt64Int64FloatTupleIntFloatTransposeTrue) cugraph::test::override_Rmat_Usecase_with_cmd_line_arguments(std::get<1>(param))); } -TEST_P(Tests_MGTransformE_File, CheckInt32Int32FloatTransposeFalse) +TEST_P(Tests_MGTransformE_File, CheckInt32Int32FloatIntTransposeFalse) { auto param = GetParam(); run_current_test(std::get<0>(param), std::get<1>(param)); } -TEST_P(Tests_MGTransformE_Rmat, CheckInt32Int32FloatTransposeFalse) +TEST_P(Tests_MGTransformE_Rmat, CheckInt32Int32FloatIntTransposeFalse) { auto param = GetParam(); run_current_test( @@ -292,7 +329,7 @@ TEST_P(Tests_MGTransformE_Rmat, CheckInt32Int32FloatTransposeFalse) cugraph::test::override_Rmat_Usecase_with_cmd_line_arguments(std::get<1>(param))); } -TEST_P(Tests_MGTransformE_Rmat, CheckInt32Int64FloatTransposeFalse) +TEST_P(Tests_MGTransformE_Rmat, CheckInt32Int64FloatIntTransposeFalse) { auto param = GetParam(); run_current_test( @@ -300,7 +337,7 @@ TEST_P(Tests_MGTransformE_Rmat, CheckInt32Int64FloatTransposeFalse) cugraph::test::override_Rmat_Usecase_with_cmd_line_arguments(std::get<1>(param))); } -TEST_P(Tests_MGTransformE_Rmat, CheckInt64Int64FloatTransposeFalse) +TEST_P(Tests_MGTransformE_Rmat, CheckInt64Int64FloatIntTransposeFalse) { auto param = GetParam(); run_current_test( @@ -308,13 +345,13 @@ TEST_P(Tests_MGTransformE_Rmat, CheckInt64Int64FloatTransposeFalse) cugraph::test::override_Rmat_Usecase_with_cmd_line_arguments(std::get<1>(param))); } -TEST_P(Tests_MGTransformE_File, CheckInt32Int32FloatTransposeTrue) +TEST_P(Tests_MGTransformE_File, CheckInt32Int32FloatIntTransposeTrue) { auto param = GetParam(); run_current_test(std::get<0>(param), std::get<1>(param)); } -TEST_P(Tests_MGTransformE_Rmat, CheckInt32Int32FloatTransposeTrue) +TEST_P(Tests_MGTransformE_Rmat, CheckInt32Int32FloatIntTransposeTrue) { auto param = GetParam(); run_current_test( @@ -322,7 +359,7 @@ TEST_P(Tests_MGTransformE_Rmat, CheckInt32Int32FloatTransposeTrue) cugraph::test::override_Rmat_Usecase_with_cmd_line_arguments(std::get<1>(param))); } -TEST_P(Tests_MGTransformE_Rmat, CheckInt32Int64FloatTransposeTrue) +TEST_P(Tests_MGTransformE_Rmat, CheckInt32Int64FloatIntTransposeTrue) { auto param = GetParam(); run_current_test( @@ -330,7 +367,7 @@ TEST_P(Tests_MGTransformE_Rmat, CheckInt32Int64FloatTransposeTrue) cugraph::test::override_Rmat_Usecase_with_cmd_line_arguments(std::get<1>(param))); } -TEST_P(Tests_MGTransformE_Rmat, CheckInt64Int64FloatTransposeTrue) +TEST_P(Tests_MGTransformE_Rmat, CheckInt64Int64FloatIntTransposeTrue) { auto param = GetParam(); run_current_test( @@ -338,11 +375,71 @@ TEST_P(Tests_MGTransformE_Rmat, CheckInt64Int64FloatTransposeTrue) cugraph::test::override_Rmat_Usecase_with_cmd_line_arguments(std::get<1>(param))); } +TEST_P(Tests_MGTransformE_File, CheckInt32Int32FloatBoolTransposeFalse) +{ + auto param = GetParam(); + run_current_test(std::get<0>(param), std::get<1>(param)); +} + +TEST_P(Tests_MGTransformE_Rmat, CheckInt32Int32FloatBoolTransposeFalse) +{ + auto param = GetParam(); + run_current_test( + std::get<0>(param), + cugraph::test::override_Rmat_Usecase_with_cmd_line_arguments(std::get<1>(param))); +} + +TEST_P(Tests_MGTransformE_Rmat, CheckInt32Int64FloatBoolTransposeFalse) +{ + auto param = GetParam(); + run_current_test( + std::get<0>(param), + cugraph::test::override_Rmat_Usecase_with_cmd_line_arguments(std::get<1>(param))); +} + +TEST_P(Tests_MGTransformE_Rmat, CheckInt64Int64FloatBoolTransposeFalse) +{ + auto param = GetParam(); + run_current_test( + std::get<0>(param), + cugraph::test::override_Rmat_Usecase_with_cmd_line_arguments(std::get<1>(param))); +} + +TEST_P(Tests_MGTransformE_File, CheckInt32Int32FloatBoolTransposeTrue) +{ + auto param = GetParam(); + run_current_test(std::get<0>(param), std::get<1>(param)); +} + +TEST_P(Tests_MGTransformE_Rmat, CheckInt32Int32FloatBoolTransposeTrue) +{ + auto param = GetParam(); + run_current_test( + std::get<0>(param), + cugraph::test::override_Rmat_Usecase_with_cmd_line_arguments(std::get<1>(param))); +} + +TEST_P(Tests_MGTransformE_Rmat, CheckInt32Int64FloatBoolTransposeTrue) +{ + auto param = GetParam(); + run_current_test( + std::get<0>(param), + cugraph::test::override_Rmat_Usecase_with_cmd_line_arguments(std::get<1>(param))); +} + +TEST_P(Tests_MGTransformE_Rmat, CheckInt64Int64FloatBoolTransposeTrue) +{ + auto param = GetParam(); + run_current_test( + std::get<0>(param), + cugraph::test::override_Rmat_Usecase_with_cmd_line_arguments(std::get<1>(param))); +} + INSTANTIATE_TEST_SUITE_P( file_test, Tests_MGTransformE_File, ::testing::Combine( - ::testing::Values(Prims_Usecase{true}), + ::testing::Values(Prims_Usecase{false, true}, Prims_Usecase{true, true}), ::testing::Values(cugraph::test::File_Usecase("test/datasets/karate.mtx"), cugraph::test::File_Usecase("test/datasets/web-Google.mtx"), cugraph::test::File_Usecase("test/datasets/ljournal-2008.mtx"), @@ -350,7 +447,8 @@ INSTANTIATE_TEST_SUITE_P( INSTANTIATE_TEST_SUITE_P(rmat_small_test, Tests_MGTransformE_Rmat, - ::testing::Combine(::testing::Values(Prims_Usecase{true}), + ::testing::Combine(::testing::Values(Prims_Usecase{false, true}, + Prims_Usecase{true, true}), ::testing::Values(cugraph::test::Rmat_Usecase( 10, 16, 0.57, 0.19, 0.19, 0, false, false)))); @@ -362,7 +460,7 @@ INSTANTIATE_TEST_SUITE_P( factor (to avoid running same benchmarks more than once) */ Tests_MGTransformE_Rmat, ::testing::Combine( - ::testing::Values(Prims_Usecase{false}), + ::testing::Values(Prims_Usecase{false, false}, Prims_Usecase{true, false}), ::testing::Values(cugraph::test::Rmat_Usecase(20, 32, 0.57, 0.19, 0.19, 0, false, false)))); CUGRAPH_MG_TEST_PROGRAM_MAIN() From 8877464a094370421d82a04b95d9618eff647ce5 Mon Sep 17 00:00:00 2001 From: Erik Welch Date: Tue, 17 Oct 2023 23:06:00 -0500 Subject: [PATCH 054/111] Add multigraph support to nx-cugraph (#3934) Authors: - Erik Welch (https://github.com/eriknw) Approvers: - Rick Ratzel (https://github.com/rlratzel) URL: https://github.com/rapidsai/cugraph/pull/3934 --- dependencies.yaml | 3 +- python/nx-cugraph/lint.yaml | 14 +- .../nx-cugraph/nx_cugraph/classes/__init__.py | 2 + python/nx-cugraph/nx_cugraph/classes/graph.py | 28 +- .../nx_cugraph/classes/multidigraph.py | 30 ++ .../nx_cugraph/classes/multigraph.py | 453 ++++++++++++++++++ python/nx-cugraph/nx_cugraph/convert.py | 167 +++++-- python/nx-cugraph/nx_cugraph/interface.py | 12 +- .../nx_cugraph/tests/bench_convert.py | 16 +- .../nx_cugraph/tests/test_convert.py | 39 +- .../nx_cugraph/tests/test_multigraph.py | 72 +++ python/nx-cugraph/pyproject.toml | 3 +- 12 files changed, 752 insertions(+), 87 deletions(-) create mode 100644 python/nx-cugraph/nx_cugraph/classes/multidigraph.py create mode 100644 python/nx-cugraph/nx_cugraph/classes/multigraph.py create mode 100644 python/nx-cugraph/nx_cugraph/tests/test_multigraph.py diff --git a/dependencies.yaml b/dependencies.yaml index 70fb54b4721..736aa2e019f 100644 --- a/dependencies.yaml +++ b/dependencies.yaml @@ -401,12 +401,13 @@ dependencies: - output_types: [conda, pyproject] packages: - networkx>=3.0 + - &numpy numpy>=1.21 python_run_cugraph_dgl: common: - output_types: [conda, pyproject] packages: - *numba - - &numpy numpy>=1.21 + - *numpy - output_types: [pyproject] packages: - &cugraph cugraph==23.12.* diff --git a/python/nx-cugraph/lint.yaml b/python/nx-cugraph/lint.yaml index 338ca7b230e..4f604fbeb6e 100644 --- a/python/nx-cugraph/lint.yaml +++ b/python/nx-cugraph/lint.yaml @@ -11,7 +11,7 @@ default_language_version: python: python3 repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.4.0 + rev: v4.5.0 hooks: - id: check-added-large-files - id: check-case-conflict @@ -26,7 +26,7 @@ repos: - id: mixed-line-ending - id: trailing-whitespace - repo: https://github.com/abravalheri/validate-pyproject - rev: v0.14 + rev: v0.15 hooks: - id: validate-pyproject name: Validate pyproject.toml @@ -40,7 +40,7 @@ repos: hooks: - id: isort - repo: https://github.com/asottile/pyupgrade - rev: v3.13.0 + rev: v3.15.0 hooks: - id: pyupgrade args: [--py39-plus] @@ -50,7 +50,7 @@ repos: - id: black # - id: black-jupyter - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.0.291 + rev: v0.0.292 hooks: - id: ruff args: [--fix-only, --show-fixes] @@ -70,18 +70,18 @@ repos: - id: yesqa additional_dependencies: *flake8_dependencies - repo: https://github.com/codespell-project/codespell - rev: v2.2.5 + rev: v2.2.6 hooks: - id: codespell types_or: [python, rst, markdown] additional_dependencies: [tomli] files: ^(nx_cugraph|docs)/ - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.0.291 + rev: v0.0.292 hooks: - id: ruff - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.4.0 + rev: v4.5.0 hooks: - id: no-commit-to-branch args: [-p, "^branch-2....$"] diff --git a/python/nx-cugraph/nx_cugraph/classes/__init__.py b/python/nx-cugraph/nx_cugraph/classes/__init__.py index e47641ae812..9916bcbe241 100644 --- a/python/nx-cugraph/nx_cugraph/classes/__init__.py +++ b/python/nx-cugraph/nx_cugraph/classes/__init__.py @@ -11,5 +11,7 @@ # See the License for the specific language governing permissions and # limitations under the License. from .graph import Graph +from .multigraph import MultiGraph from .digraph import DiGraph # isort:skip +from .multidigraph import MultiDiGraph # isort:skip diff --git a/python/nx-cugraph/nx_cugraph/classes/graph.py b/python/nx-cugraph/nx_cugraph/classes/graph.py index ded4cc3943f..f1e85c836e3 100644 --- a/python/nx-cugraph/nx_cugraph/classes/graph.py +++ b/python/nx-cugraph/nx_cugraph/classes/graph.py @@ -43,7 +43,8 @@ class Graph: # Tell networkx to dispatch calls with this object to nx-cugraph - __networkx_plugin__: ClassVar[str] = "cugraph" + __networkx_backend__: ClassVar[str] = "cugraph" # nx >=3.2 + __networkx_plugin__: ClassVar[str] = "cugraph" # nx <3.2 # networkx properties graph: dict @@ -58,7 +59,7 @@ class Graph: node_values: dict[AttrKey, cp.ndarray[NodeValue]] node_masks: dict[AttrKey, cp.ndarray[bool]] key_to_id: dict[NodeKey, IndexValue] | None - _id_to_key: dict[IndexValue, NodeKey] | None + _id_to_key: list[NodeKey] | None _N: int #################### @@ -77,7 +78,7 @@ def from_coo( node_masks: dict[AttrKey, cp.ndarray[bool]] | None = None, *, key_to_id: dict[NodeKey, IndexValue] | None = None, - id_to_key: dict[IndexValue, NodeKey] | None = None, + id_to_key: list[NodeKey] | None = None, **attr, ) -> Graph: new_graph = object.__new__(cls) @@ -88,7 +89,7 @@ def from_coo( new_graph.node_values = {} if node_values is None else dict(node_values) new_graph.node_masks = {} if node_masks is None else dict(node_masks) new_graph.key_to_id = None if key_to_id is None else dict(key_to_id) - new_graph._id_to_key = None if id_to_key is None else dict(id_to_key) + new_graph._id_to_key = None if id_to_key is None else list(id_to_key) new_graph._N = op.index(N) # Ensure N is integral new_graph.graph = new_graph.graph_attr_dict_factory() new_graph.graph.update(attr) @@ -123,7 +124,7 @@ def from_csr( node_masks: dict[AttrKey, cp.ndarray[bool]] | None = None, *, key_to_id: dict[NodeKey, IndexValue] | None = None, - id_to_key: dict[IndexValue, NodeKey] | None = None, + id_to_key: list[NodeKey] | None = None, **attr, ) -> Graph: N = indptr.size - 1 @@ -155,7 +156,7 @@ def from_csc( node_masks: dict[AttrKey, cp.ndarray[bool]] | None = None, *, key_to_id: dict[NodeKey, IndexValue] | None = None, - id_to_key: dict[IndexValue, NodeKey] | None = None, + id_to_key: list[NodeKey] | None = None, **attr, ) -> Graph: N = indptr.size - 1 @@ -189,7 +190,7 @@ def from_dcsr( node_masks: dict[AttrKey, cp.ndarray[bool]] | None = None, *, key_to_id: dict[NodeKey, IndexValue] | None = None, - id_to_key: dict[IndexValue, NodeKey] | None = None, + id_to_key: list[NodeKey] | None = None, **attr, ) -> Graph: row_indices = cp.array( @@ -222,7 +223,7 @@ def from_dcsc( node_masks: dict[AttrKey, cp.ndarray[bool]] | None = None, *, key_to_id: dict[NodeKey, IndexValue] | None = None, - id_to_key: dict[IndexValue, NodeKey] | None = None, + id_to_key: list[NodeKey] | None = None, **attr, ) -> Graph: col_indices = cp.array( @@ -295,11 +296,11 @@ def node_dtypes(self) -> dict[AttrKey, Dtype]: return {key: val.dtype for key, val in self.node_values.items()} @property - def id_to_key(self) -> dict[IndexValue, NodeKey] | None: + def id_to_key(self) -> [NodeKey] | None: if self.key_to_id is None: return None if self._id_to_key is None: - self._id_to_key = {val: key for key, val in self.key_to_id.items()} + self._id_to_key = sorted(self.key_to_id, key=self.key_to_id.__getitem__) return self._id_to_key name = nx.Graph.name @@ -370,6 +371,12 @@ def get_edge_data( v = self.key_to_id[v] except KeyError: return default + else: + try: + if u < 0 or v < 0 or u >= self._N or v >= self._N: + return default + except TypeError: + return default index = cp.nonzero((self.row_indices == u) & (self.col_indices == v))[0] if index.size == 0: return default @@ -447,6 +454,7 @@ def to_undirected(self, as_view: bool = False) -> Graph: ################### def _copy(self, as_view: bool, cls: type[Graph], reverse: bool = False): + # DRY warning: see also MultiGraph._copy indptr = self.indptr row_indices = self.row_indices col_indices = self.col_indices diff --git a/python/nx-cugraph/nx_cugraph/classes/multidigraph.py b/python/nx-cugraph/nx_cugraph/classes/multidigraph.py new file mode 100644 index 00000000000..5629e2c9c06 --- /dev/null +++ b/python/nx-cugraph/nx_cugraph/classes/multidigraph.py @@ -0,0 +1,30 @@ +# Copyright (c) 2023, NVIDIA CORPORATION. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +from __future__ import annotations + +import networkx as nx + +import nx_cugraph as nxcg + +from .digraph import DiGraph +from .multigraph import MultiGraph + +__all__ = ["MultiDiGraph"] + +networkx_api = nxcg.utils.decorators.networkx_class(nx.MultiDiGraph) + + +class MultiDiGraph(MultiGraph, DiGraph): + @classmethod + def to_networkx_class(cls) -> type[nx.MultiDiGraph]: + return nx.MultiDiGraph diff --git a/python/nx-cugraph/nx_cugraph/classes/multigraph.py b/python/nx-cugraph/nx_cugraph/classes/multigraph.py new file mode 100644 index 00000000000..499ca7ce212 --- /dev/null +++ b/python/nx-cugraph/nx_cugraph/classes/multigraph.py @@ -0,0 +1,453 @@ +# Copyright (c) 2023, NVIDIA CORPORATION. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +from __future__ import annotations + +from copy import deepcopy +from typing import TYPE_CHECKING, ClassVar + +import cupy as cp +import networkx as nx +import numpy as np + +import nx_cugraph as nxcg + +from .graph import Graph + +if TYPE_CHECKING: + from nx_cugraph.typing import ( + AttrKey, + EdgeKey, + EdgeValue, + IndexValue, + NodeKey, + NodeValue, + ) +__all__ = ["MultiGraph"] + +networkx_api = nxcg.utils.decorators.networkx_class(nx.MultiGraph) + + +class MultiGraph(Graph): + # networkx properties + edge_key_dict_factory: ClassVar[type] = dict + + # Not networkx properties + + # In a MultiGraph, each edge has a unique `(row, col, key)` key. + # By default, `key` is 0 if possible, else 1, else 2, etc. + # This key can be any hashable Python object in NetworkX. + # We don't use a dict for our data structure here, because + # that would require a `(row, col, key)` key. + # Instead, we keep `edge_keys` and/or `edge_indices`. + # `edge_keys` is the list of Python objects for each edge. + # `edge_indices` is for the common case of default multiedge keys, + # in which case we can store it as a cupy array. + # `edge_indices` is generally preferred. It is possible to provide + # both where edge_indices is the default and edge_keys is anything. + # It is also possible for them both to be None, which means the + # default edge indices has not yet been calculated. + edge_indices: cp.ndarray[IndexValue] | None + edge_keys: list[EdgeKey] | None + + #################### + # Creation methods # + #################### + + @classmethod + def from_coo( + cls, + N: int, + row_indices: cp.ndarray[IndexValue], + col_indices: cp.ndarray[IndexValue], + edge_indices: cp.ndarray[IndexValue] | None = None, + edge_values: dict[AttrKey, cp.ndarray[EdgeValue]] | None = None, + edge_masks: dict[AttrKey, cp.ndarray[bool]] | None = None, + node_values: dict[AttrKey, cp.ndarray[NodeValue]] | None = None, + node_masks: dict[AttrKey, cp.ndarray[bool]] | None = None, + *, + key_to_id: dict[NodeKey, IndexValue] | None = None, + id_to_key: list[NodeKey] | None = None, + edge_keys: list[EdgeKey] | None = None, + **attr, + ) -> MultiGraph: + new_graph = super().from_coo( + N, + row_indices, + col_indices, + edge_values, + edge_masks, + node_values, + node_masks, + key_to_id=key_to_id, + id_to_key=id_to_key, + **attr, + ) + new_graph.edge_indices = edge_indices + new_graph.edge_keys = edge_keys + # Easy and fast sanity checks + if ( + new_graph.edge_keys is not None + and len(new_graph.edge_keys) != row_indices.size + ): + raise ValueError + return new_graph + + @classmethod + def from_csr( + cls, + indptr: cp.ndarray[IndexValue], + col_indices: cp.ndarray[IndexValue], + edge_indices: cp.ndarray[IndexValue] | None = None, + edge_values: dict[AttrKey, cp.ndarray[EdgeValue]] | None = None, + edge_masks: dict[AttrKey, cp.ndarray[bool]] | None = None, + node_values: dict[AttrKey, cp.ndarray[NodeValue]] | None = None, + node_masks: dict[AttrKey, cp.ndarray[bool]] | None = None, + *, + key_to_id: dict[NodeKey, IndexValue] | None = None, + id_to_key: list[NodeKey] | None = None, + edge_keys: list[EdgeKey] | None = None, + **attr, + ) -> MultiGraph: + N = indptr.size - 1 + row_indices = cp.array( + # cp.repeat is slow to use here, so use numpy instead + np.repeat(np.arange(N, dtype=np.int32), cp.diff(indptr).get()) + ) + return cls.from_coo( + N, + row_indices, + col_indices, + edge_indices, + edge_values, + edge_masks, + node_values, + node_masks, + key_to_id=key_to_id, + id_to_key=id_to_key, + edge_keys=edge_keys, + **attr, + ) + + @classmethod + def from_csc( + cls, + indptr: cp.ndarray[IndexValue], + row_indices: cp.ndarray[IndexValue], + edge_indices: cp.ndarray[IndexValue] | None = None, + edge_values: dict[AttrKey, cp.ndarray[EdgeValue]] | None = None, + edge_masks: dict[AttrKey, cp.ndarray[bool]] | None = None, + node_values: dict[AttrKey, cp.ndarray[NodeValue]] | None = None, + node_masks: dict[AttrKey, cp.ndarray[bool]] | None = None, + *, + key_to_id: dict[NodeKey, IndexValue] | None = None, + id_to_key: list[NodeKey] | None = None, + edge_keys: list[EdgeKey] | None = None, + **attr, + ) -> MultiGraph: + N = indptr.size - 1 + col_indices = cp.array( + # cp.repeat is slow to use here, so use numpy instead + np.repeat(np.arange(N, dtype=np.int32), cp.diff(indptr).get()) + ) + return cls.from_coo( + N, + row_indices, + col_indices, + edge_indices, + edge_values, + edge_masks, + node_values, + node_masks, + key_to_id=key_to_id, + id_to_key=id_to_key, + edge_keys=edge_keys, + **attr, + ) + + @classmethod + def from_dcsr( + cls, + N: int, + compressed_rows: cp.ndarray[IndexValue], + indptr: cp.ndarray[IndexValue], + col_indices: cp.ndarray[IndexValue], + edge_indices: cp.ndarray[IndexValue] | None = None, + edge_values: dict[AttrKey, cp.ndarray[EdgeValue]] | None = None, + edge_masks: dict[AttrKey, cp.ndarray[bool]] | None = None, + node_values: dict[AttrKey, cp.ndarray[NodeValue]] | None = None, + node_masks: dict[AttrKey, cp.ndarray[bool]] | None = None, + *, + key_to_id: dict[NodeKey, IndexValue] | None = None, + id_to_key: list[NodeKey] | None = None, + edge_keys: list[EdgeKey] | None = None, + **attr, + ) -> MultiGraph: + row_indices = cp.array( + # cp.repeat is slow to use here, so use numpy instead + np.repeat(compressed_rows.get(), cp.diff(indptr).get()) + ) + return cls.from_coo( + N, + row_indices, + col_indices, + edge_indices, + edge_values, + edge_masks, + node_values, + node_masks, + key_to_id=key_to_id, + id_to_key=id_to_key, + edge_keys=edge_keys, + **attr, + ) + + @classmethod + def from_dcsc( + cls, + N: int, + compressed_cols: cp.ndarray[IndexValue], + indptr: cp.ndarray[IndexValue], + row_indices: cp.ndarray[IndexValue], + edge_indices: cp.ndarray[IndexValue] | None = None, + edge_values: dict[AttrKey, cp.ndarray[EdgeValue]] | None = None, + edge_masks: dict[AttrKey, cp.ndarray[bool]] | None = None, + node_values: dict[AttrKey, cp.ndarray[NodeValue]] | None = None, + node_masks: dict[AttrKey, cp.ndarray[bool]] | None = None, + *, + key_to_id: dict[NodeKey, IndexValue] | None = None, + id_to_key: list[NodeKey] | None = None, + edge_keys: list[EdgeKey] | None = None, + **attr, + ) -> Graph: + col_indices = cp.array( + # cp.repeat is slow to use here, so use numpy instead + np.repeat(compressed_cols.get(), cp.diff(indptr).get()) + ) + return cls.from_coo( + N, + row_indices, + col_indices, + edge_indices, + edge_values, + edge_masks, + node_values, + node_masks, + key_to_id=key_to_id, + id_to_key=id_to_key, + edge_keys=edge_keys, + **attr, + ) + + def __new__( + cls, incoming_graph_data=None, multigraph_input=None, **attr + ) -> MultiGraph: + if isinstance(incoming_graph_data, dict) and multigraph_input is not False: + new_graph = nxcg.from_networkx( + nx.MultiGraph(incoming_graph_data, multigraph_input=multigraph_input), + preserve_all_attrs=True, + ) + else: + new_graph = super().__new__(cls, incoming_graph_data) + new_graph.graph.update(attr) + return new_graph + + ################# + # Class methods # + ################# + + @classmethod + @networkx_api + def is_directed(cls) -> bool: + return False + + @classmethod + @networkx_api + def is_multigraph(cls) -> bool: + return True + + @classmethod + @networkx_api + def to_directed_class(cls) -> type[nxcg.MultiDiGraph]: + return nxcg.MultiDiGraph + + @classmethod + def to_networkx_class(cls) -> type[nx.MultiGraph]: + return nx.MultiGraph + + @classmethod + @networkx_api + def to_undirected_class(cls) -> type[MultiGraph]: + return MultiGraph + + ########################## + # NetworkX graph methods # + ########################## + + @networkx_api + def clear(self) -> None: + super().clear() + self.edge_indices = None + self.edge_keys = None + + @networkx_api + def clear_edges(self) -> None: + super().clear_edges() + self.edge_indices = None + self.edge_keys = None + + @networkx_api + def copy(self, as_view: bool = False) -> MultiGraph: + # Does shallow copy in networkx + return self._copy(as_view, self.__class__) + + @networkx_api + def get_edge_data( + self, + u: NodeKey, + v: NodeKey, + key: EdgeKey | None = None, + default: EdgeValue | None = None, + ): + if self.key_to_id is not None: + try: + u = self.key_to_id[u] + v = self.key_to_id[v] + except KeyError: + return default + else: + try: + if u < 0 or v < 0 or u >= self._N or v >= self._N: + return default + except TypeError: + return default + mask = (self.row_indices == u) & (self.col_indices == v) + if not mask.any(): + return default + if self.edge_keys is None: + if self.edge_indices is None: + self._calculate_edge_indices() + if key is not None: + try: + mask = mask & (self.edge_indices == key) + except TypeError: + return default + indices = cp.nonzero(mask)[0] + if indices.size == 0: + return default + edge_keys = self.edge_keys + if key is not None and edge_keys is not None: + mask[[i for i in indices.tolist() if edge_keys[i] != key]] = False + indices = cp.nonzero(mask)[0] + if indices.size == 0: + return default + if key is not None: + [index] = indices.tolist() + return { + k: v[index].tolist() + for k, v in self.edge_values.items() + if k not in self.edge_masks or self.edge_masks[k][index] + } + return { + edge_keys[index] + if edge_keys is not None + else index: { + k: v[index].tolist() + for k, v in self.edge_values.items() + if k not in self.edge_masks or self.edge_masks[k][index] + } + for index in indices.tolist() + } + + @networkx_api + def has_edge(self, u: NodeKey, v: NodeKey, key: EdgeKey | None = None) -> bool: + if self.key_to_id is not None: + try: + u = self.key_to_id[u] + v = self.key_to_id[v] + except KeyError: + return False + mask = (self.row_indices == u) & (self.col_indices == v) + if key is None or (self.edge_indices is None and self.edge_keys is None): + return bool(mask.any()) + if self.edge_keys is None: + try: + return bool((mask & (self.edge_indices == key)).any()) + except TypeError: + return False + indices = cp.nonzero(mask)[0] + if indices.size == 0: + return False + edge_keys = self.edge_keys + return any(edge_keys[i] == key for i in indices.tolist()) + + @networkx_api + def to_directed(self, as_view: bool = False) -> nxcg.MultiDiGraph: + return self._copy(as_view, self.to_directed_class()) + + @networkx_api + def to_undirected(self, as_view: bool = False) -> MultiGraph: + # Does deep copy in networkx + return self.copy(as_view) + + ################### + # Private methods # + ################### + + def _copy(self, as_view: bool, cls: type[Graph], reverse: bool = False): + # DRY warning: see also Graph._copy + indptr = self.indptr + row_indices = self.row_indices + col_indices = self.col_indices + edge_indices = self.edge_indices + edge_values = self.edge_values + edge_masks = self.edge_masks + node_values = self.node_values + node_masks = self.node_masks + key_to_id = self.key_to_id + id_to_key = None if key_to_id is None else self._id_to_key + edge_keys = self.edge_keys + if not as_view: + indptr = indptr.copy() + row_indices = row_indices.copy() + col_indices = col_indices.copy() + edge_indices = edge_indices.copy() + edge_values = {key: val.copy() for key, val in edge_values.items()} + edge_masks = {key: val.copy() for key, val in edge_masks.items()} + node_values = {key: val.copy() for key, val in node_values.items()} + node_masks = {key: val.copy() for key, val in node_masks.items()} + if key_to_id is not None: + key_to_id = key_to_id.copy() + if id_to_key is not None: + id_to_key = id_to_key.copy() + if edge_keys is not None: + edge_keys = edge_keys.copy() + if reverse: + row_indices, col_indices = col_indices, row_indices + rv = cls.from_coo( + indptr, + row_indices, + col_indices, + edge_indices, + edge_values, + edge_masks, + node_values, + node_masks, + key_to_id=key_to_id, + id_to_key=id_to_key, + edge_keys=edge_keys, + ) + if as_view: + rv.graph = self.graph + else: + rv.graph.update(deepcopy(self.graph)) + return rv diff --git a/python/nx-cugraph/nx_cugraph/convert.py b/python/nx-cugraph/nx_cugraph/convert.py index 1240ea71db7..e82286e5e29 100644 --- a/python/nx-cugraph/nx_cugraph/convert.py +++ b/python/nx-cugraph/nx_cugraph/convert.py @@ -122,8 +122,6 @@ def from_networkx( graph = G else: raise TypeError(f"Expected networkx.Graph; got {type(graph)}") - elif graph.is_multigraph(): - raise NotImplementedError("MultiGraph support is not yet implemented") if preserve_all_attrs: preserve_edge_attrs = True @@ -144,7 +142,7 @@ def from_networkx( else: node_attrs = {node_attrs: None} - if graph.__class__ in {nx.Graph, nx.DiGraph}: + if graph.__class__ in {nx.Graph, nx.DiGraph, nx.MultiGraph, nx.MultiDiGraph}: # This is a NetworkX private attribute, but is much faster to use adj = graph._adj else: @@ -163,7 +161,11 @@ def from_networkx( edge_attrs = None elif preserve_edge_attrs: # Using comprehensions should be just as fast starting in Python 3.11 - attr_sets = set(map(frozenset, concat(map(dict.values, adj.values())))) + it = concat(map(dict.values, adj.values())) + if graph.is_multigraph(): + it = concat(map(dict.values, it)) + # PERF: should we add `filter(None, ...)` to remove empty data dicts? + attr_sets = set(map(frozenset, it)) attrs = frozenset.union(*attr_sets) edge_attrs = dict.fromkeys(attrs, REQUIRED) if len(attr_sets) > 1: @@ -180,11 +182,19 @@ def from_networkx( if len(required) == 1: # Fast path for the common case of a single attribute with no default [attr] = required - it = ( - attr in edgedata - for rowdata in adj.values() - for edgedata in rowdata.values() - ) + if graph.is_multigraph(): + it = ( + attr in edgedata + for rowdata in adj.values() + for multiedges in rowdata.values() + for edgedata in multiedges.values() + ) + else: + it = ( + attr in edgedata + for rowdata in adj.values() + for edgedata in rowdata.values() + ) if next(it): if all(it): # All edges have data @@ -195,9 +205,10 @@ def from_networkx( del edge_attrs[attr] # Else some edges have attribute (default already None) else: - attr_sets = set( - map(required.intersection, concat(map(dict.values, adj.values()))) - ) + it = concat(map(dict.values, adj.values())) + if graph.is_multigraph(): + it = concat(map(dict.values, it)) + attr_sets = set(map(required.intersection, it)) for attr in required - frozenset.union(*attr_sets): # No edges have these attributes del edge_attrs[attr] @@ -254,7 +265,23 @@ def from_networkx( key_to_id = None else: col_iter = map(key_to_id.__getitem__, col_iter) - col_indices = cp.fromiter(col_iter, np.int32) + if graph.is_multigraph(): + col_indices = np.fromiter(col_iter, np.int32) + num_multiedges = np.fromiter( + map(len, concat(map(dict.values, adj.values()))), np.int32 + ) + # cp.repeat is slow to use here, so use numpy instead + col_indices = cp.array(np.repeat(col_indices, num_multiedges)) + # Determine edge keys and edge ids for multigraphs + edge_keys = list(concat(concat(map(dict.values, adj.values())))) + edge_indices = cp.fromiter( + concat(map(range, map(len, concat(map(dict.values, adj.values()))))), + np.int32, + ) + if edge_keys == edge_indices.tolist(): + edge_keys = None # Prefer edge_indices + else: + col_indices = cp.fromiter(col_iter, np.int32) edge_values = {} edge_masks = {} @@ -268,16 +295,29 @@ def from_networkx( if edge_default is None: vals = [] append = vals.append - iter_mask = ( - append( - edgedata[edge_attr] - if (present := edge_attr in edgedata) - else False + if graph.is_multigraph(): + iter_mask = ( + append( + edgedata[edge_attr] + if (present := edge_attr in edgedata) + else False + ) + or present + for rowdata in adj.values() + for multiedges in rowdata.values() + for edgedata in multiedges.values() + ) + else: + iter_mask = ( + append( + edgedata[edge_attr] + if (present := edge_attr in edgedata) + else False + ) + or present + for rowdata in adj.values() + for edgedata in rowdata.values() ) - or present - for rowdata in adj.values() - for edgedata in rowdata.values() - ) edge_masks[edge_attr] = cp.fromiter(iter_mask, bool) edge_values[edge_attr] = cp.array(vals, dtype) # if vals.ndim > 1: ... @@ -289,8 +329,16 @@ def from_networkx( # for rowdata in adj.values() # for edgedata in rowdata.values() # ) - iter_values = map( - op.itemgetter(edge_attr), concat(map(dict.values, adj.values())) + it = concat(map(dict.values, adj.values())) + if graph.is_multigraph(): + it = concat(map(dict.values, it)) + iter_values = map(op.itemgetter(edge_attr), it) + elif graph.is_multigraph(): + iter_values = ( + edgedata.get(edge_attr, edge_default) + for rowdata in adj.values() + for multiedges in rowdata.values() + for edgedata in multiedges.values() ) else: iter_values = ( @@ -304,13 +352,13 @@ def from_networkx( edge_values[edge_attr] = cp.fromiter(iter_values, dtype) # if vals.ndim > 1: ... - row_indices = cp.array( - # cp.repeat is slow to use here, so use numpy instead - np.repeat( - np.arange(N, dtype=np.int32), - np.fromiter(map(len, adj.values()), np.int32), - ) + # cp.repeat is slow to use here, so use numpy instead + row_indices = np.repeat( + np.arange(N, dtype=np.int32), np.fromiter(map(len, adj.values()), np.int32) ) + if graph.is_multigraph(): + row_indices = np.repeat(row_indices, num_multiedges) + row_indices = cp.array(row_indices) node_values = {} node_masks = {} @@ -350,21 +398,38 @@ def from_networkx( else: node_values[node_attr] = cp.fromiter(iter_values, dtype) # if vals.ndim > 1: ... - - if graph.is_directed() or as_directed: - klass = nxcg.DiGraph + if graph.is_multigraph(): + if graph.is_directed() or as_directed: + klass = nxcg.MultiDiGraph + else: + klass = nxcg.MultiGraph + rv = klass.from_coo( + N, + row_indices, + col_indices, + edge_indices, + edge_values, + edge_masks, + node_values, + node_masks, + key_to_id=key_to_id, + edge_keys=edge_keys, + ) else: - klass = nxcg.Graph - rv = klass.from_coo( - N, - row_indices, - col_indices, - edge_values, - edge_masks, - node_values, - node_masks, - key_to_id=key_to_id, - ) + if graph.is_directed() or as_directed: + klass = nxcg.DiGraph + else: + klass = nxcg.Graph + rv = klass.from_coo( + N, + row_indices, + col_indices, + edge_values, + edge_masks, + node_values, + node_masks, + key_to_id=key_to_id, + ) if preserve_graph_attrs: rv.graph.update(graph.graph) # deepcopy? return rv @@ -427,7 +492,7 @@ def to_networkx(G: nxcg.Graph) -> nx.Graph: full_node_dicts = _iter_attr_dicts(node_values, node_masks) rv.add_nodes_from(zip(node_iter, full_node_dicts)) elif id_to_key is not None: - rv.add_nodes_from(id_to_key.values()) + rv.add_nodes_from(id_to_key) else: rv.add_nodes_from(range(len(G))) @@ -448,7 +513,17 @@ def to_networkx(G: nxcg.Graph) -> nx.Graph: if id_to_key is not None: row_iter = map(id_to_key.__getitem__, row_indices) col_iter = map(id_to_key.__getitem__, col_indices) - if edge_values: + if G.is_multigraph() and (G.edge_keys is not None or G.edge_indices is not None): + if G.edge_keys is not None: + edge_keys = G.edge_keys + else: + edge_keys = G.edge_indices.tolist() + if edge_values: + full_edge_dicts = _iter_attr_dicts(edge_values, edge_masks) + rv.add_edges_from(zip(row_iter, col_iter, edge_keys, full_edge_dicts)) + else: + rv.add_edges_from(zip(row_iter, col_iter, edge_keys)) + elif edge_values: full_edge_dicts = _iter_attr_dicts(edge_values, edge_masks) rv.add_edges_from(zip(row_iter, col_iter, full_edge_dicts)) else: diff --git a/python/nx-cugraph/nx_cugraph/interface.py b/python/nx-cugraph/nx_cugraph/interface.py index 2ad23acd940..124b86a4af2 100644 --- a/python/nx-cugraph/nx_cugraph/interface.py +++ b/python/nx-cugraph/nx_cugraph/interface.py @@ -174,6 +174,10 @@ def key(testpath): ): louvain_different, key("test_louvain.py:test_none_weight_param"): louvain_different, key("test_louvain.py:test_multigraph"): louvain_different, + # See networkx#6630 + key( + "test_louvain.py:test_undirected_selfloops" + ): "self-loops not handled in Louvain", } ) @@ -189,10 +193,4 @@ def can_run(cls, name, args, kwargs): This is a proposed API to add to networkx dispatching machinery and may change. """ - return ( - hasattr(cls, name) - and getattr(cls, name).can_run(*args, **kwargs) - # We don't support MultiGraphs yet - and not any(isinstance(x, nx.MultiGraph) for x in args) - and not any(isinstance(x, nx.MultiGraph) for x in kwargs.values()) - ) + return hasattr(cls, name) and getattr(cls, name).can_run(*args, **kwargs) diff --git a/python/nx-cugraph/nx_cugraph/tests/bench_convert.py b/python/nx-cugraph/nx_cugraph/tests/bench_convert.py index 7e6278661c2..2eb432230eb 100644 --- a/python/nx-cugraph/nx_cugraph/tests/bench_convert.py +++ b/python/nx-cugraph/nx_cugraph/tests/bench_convert.py @@ -39,6 +39,8 @@ gpubenchmark = pytest_benchmark.plugin.benchmark +CREATE_USING = [nx.Graph, nx.DiGraph, nx.MultiGraph, nx.MultiDiGraph] + def _bench_helper(gpubenchmark, N, attr_kind, create_using, method): G = method(N, create_using=create_using) @@ -103,7 +105,7 @@ def _bench_helper_scipy(gpubenchmark, N, attr_kind, create_using, method, fmt): None, ], ) -@pytest.mark.parametrize("create_using", [nx.Graph, nx.DiGraph]) +@pytest.mark.parametrize("create_using", CREATE_USING) def bench_cycle_graph(gpubenchmark, N, attr_kind, create_using): _bench_helper(gpubenchmark, N, attr_kind, create_using, nx.cycle_graph) @@ -111,7 +113,7 @@ def bench_cycle_graph(gpubenchmark, N, attr_kind, create_using): @pytest.mark.skipif("not cugraph") @pytest.mark.parametrize("N", [1, 10**6]) @pytest.mark.parametrize("attr_kind", ["full", None]) -@pytest.mark.parametrize("create_using", [nx.Graph, nx.DiGraph]) +@pytest.mark.parametrize("create_using", CREATE_USING) @pytest.mark.parametrize("do_renumber", [True, False]) def bench_cycle_graph_cugraph(gpubenchmark, N, attr_kind, create_using, do_renumber): if N == 1 and not do_renumber: @@ -124,7 +126,7 @@ def bench_cycle_graph_cugraph(gpubenchmark, N, attr_kind, create_using, do_renum @pytest.mark.skipif("not scipy") @pytest.mark.parametrize("N", [1, 10**6]) @pytest.mark.parametrize("attr_kind", ["full", None]) -@pytest.mark.parametrize("create_using", [nx.Graph, nx.DiGraph]) +@pytest.mark.parametrize("create_using", CREATE_USING) @pytest.mark.parametrize("fmt", ["coo", "csr"]) def bench_cycle_graph_scipy(gpubenchmark, N, attr_kind, create_using, fmt): _bench_helper_scipy(gpubenchmark, N, attr_kind, create_using, nx.cycle_graph, fmt) @@ -143,14 +145,14 @@ def bench_cycle_graph_scipy(gpubenchmark, N, attr_kind, create_using, fmt): None, ], ) -@pytest.mark.parametrize("create_using", [nx.Graph, nx.DiGraph]) +@pytest.mark.parametrize("create_using", CREATE_USING) def bench_complete_graph_edgedata(gpubenchmark, N, attr_kind, create_using): _bench_helper(gpubenchmark, N, attr_kind, create_using, nx.complete_graph) @pytest.mark.parametrize("N", [3000]) @pytest.mark.parametrize("attr_kind", [None]) -@pytest.mark.parametrize("create_using", [nx.Graph, nx.DiGraph]) +@pytest.mark.parametrize("create_using", CREATE_USING) def bench_complete_graph_noedgedata(gpubenchmark, N, attr_kind, create_using): _bench_helper(gpubenchmark, N, attr_kind, create_using, nx.complete_graph) @@ -158,7 +160,7 @@ def bench_complete_graph_noedgedata(gpubenchmark, N, attr_kind, create_using): @pytest.mark.skipif("not cugraph") @pytest.mark.parametrize("N", [1, 1500]) @pytest.mark.parametrize("attr_kind", ["full", None]) -@pytest.mark.parametrize("create_using", [nx.Graph, nx.DiGraph]) +@pytest.mark.parametrize("create_using", CREATE_USING) @pytest.mark.parametrize("do_renumber", [True, False]) def bench_complete_graph_cugraph(gpubenchmark, N, attr_kind, create_using, do_renumber): if N == 1 and not do_renumber: @@ -171,7 +173,7 @@ def bench_complete_graph_cugraph(gpubenchmark, N, attr_kind, create_using, do_re @pytest.mark.skipif("not scipy") @pytest.mark.parametrize("N", [1, 1500]) @pytest.mark.parametrize("attr_kind", ["full", None]) -@pytest.mark.parametrize("create_using", [nx.Graph, nx.DiGraph]) +@pytest.mark.parametrize("create_using", CREATE_USING) @pytest.mark.parametrize("fmt", ["coo", "csr"]) def bench_complete_graph_scipy(gpubenchmark, N, attr_kind, create_using, fmt): _bench_helper_scipy( diff --git a/python/nx-cugraph/nx_cugraph/tests/test_convert.py b/python/nx-cugraph/nx_cugraph/tests/test_convert.py index ba3cd7aaee1..83820f7621f 100644 --- a/python/nx-cugraph/nx_cugraph/tests/test_convert.py +++ b/python/nx-cugraph/nx_cugraph/tests/test_convert.py @@ -18,7 +18,9 @@ from nx_cugraph import interface -@pytest.mark.parametrize("graph_class", [nx.Graph, nx.DiGraph]) +@pytest.mark.parametrize( + "graph_class", [nx.Graph, nx.DiGraph, nx.MultiGraph, nx.MultiDiGraph] +) @pytest.mark.parametrize( "kwargs", [ @@ -48,9 +50,10 @@ def test_convert_empty(graph_class, kwargs): assert G.graph == Gcg.graph == H.graph == {} -def test_convert(): +@pytest.mark.parametrize("graph_class", [nx.Graph, nx.MultiGraph]) +def test_convert(graph_class): # FIXME: can we break this into smaller tests? - G = nx.Graph() + G = graph_class() G.add_edge(0, 1, x=2) G.add_node(0, foo=10) G.add_node(1, foo=20, bar=100) @@ -88,7 +91,10 @@ def test_convert(): cp.testing.assert_array_equal(Gcg.node_values["foo"], [10, 20]) assert Gcg.edge_values == Gcg.edge_masks == {} H = nxcg.to_networkx(Gcg) - assert set(G.edges) == set(H.edges) == {(0, 1)} + if G.is_multigraph(): + assert set(G.edges) == set(H.edges) == {(0, 1, 0)} + else: + assert set(G.edges) == set(H.edges) == {(0, 1)} assert G.nodes == H.nodes # Fill completely missing attribute with default value @@ -100,6 +106,8 @@ def test_convert(): assert Gcg.edge_masks == Gcg.node_values == Gcg.node_masks == {} H = nxcg.to_networkx(Gcg) assert list(H.edges(data=True)) == [(0, 1, {"y": 0})] + if Gcg.is_multigraph(): + assert set(H.edges) == {(0, 1, 0)} # If attribute is completely missing (and no default), then just ignore it Gcg = nxcg.from_networkx(G, edge_attrs={"y": None}) @@ -108,6 +116,8 @@ def test_convert(): assert sorted(Gcg.edge_values) == sorted(Gcg.edge_masks) == [] H = nxcg.to_networkx(Gcg) assert list(H.edges(data=True)) == [(0, 1, {})] + if Gcg.is_multigraph(): + assert set(H.edges) == {(0, 1, 0)} G.add_edge(0, 2) # Some edges are missing 'x' attribute; need to use a mask @@ -120,6 +130,8 @@ def test_convert(): cp.testing.assert_array_equal(Gcg.edge_values["x"][Gcg.edge_masks["x"]], [2, 2]) H = nxcg.to_networkx(Gcg) assert list(H.edges(data=True)) == [(0, 1, {"x": 2}), (0, 2, {})] + if Gcg.is_multigraph(): + assert set(H.edges) == {(0, 1, 0), (0, 2, 0)} with pytest.raises(KeyError, match="x"): nxcg.from_networkx(G, edge_attrs={"x": nxcg.convert.REQUIRED}) @@ -131,7 +143,7 @@ def test_convert(): nxcg.from_networkx(G, node_attrs={"bar": ...}) # Now for something more complicated... - G = nx.Graph() + G = graph_class() G.add_edge(10, 20, x=1) G.add_edge(10, 30, x=2, y=1.5) G.add_node(10, foo=100) @@ -147,7 +159,7 @@ def test_convert(): {"edge_attrs": {"x": 0, "y": None}, "edge_dtypes": {"x": int, "y": float}}, ]: Gcg = nxcg.from_networkx(G, **kwargs) - assert Gcg.id_to_key == {0: 10, 1: 20, 2: 30} # Remap node IDs to 0, 1, ... + assert Gcg.id_to_key == [10, 20, 30] # Remap node IDs to 0, 1, ... cp.testing.assert_array_equal(Gcg.row_indices, [0, 0, 1, 2]) cp.testing.assert_array_equal(Gcg.col_indices, [1, 2, 0, 0]) cp.testing.assert_array_equal(Gcg.edge_values["x"], [1, 2, 1, 2]) @@ -168,7 +180,7 @@ def test_convert(): {"node_attrs": {"foo": 0, "bar": None, "missing": None}}, ]: Gcg = nxcg.from_networkx(G, **kwargs) - assert Gcg.id_to_key == {0: 10, 1: 20, 2: 30} # Remap node IDs to 0, 1, ... + assert Gcg.id_to_key == [10, 20, 30] # Remap node IDs to 0, 1, ... cp.testing.assert_array_equal(Gcg.row_indices, [0, 0, 1, 2]) cp.testing.assert_array_equal(Gcg.col_indices, [1, 2, 0, 0]) cp.testing.assert_array_equal(Gcg.node_values["foo"], [100, 200, 300]) @@ -189,7 +201,7 @@ def test_convert(): {"node_attrs": {"bar": 0, "foo": None}, "node_dtypes": int}, ]: Gcg = nxcg.from_networkx(G, **kwargs) - assert Gcg.id_to_key == {0: 10, 1: 20, 2: 30} # Remap node IDs to 0, 1, ... + assert Gcg.id_to_key == [10, 20, 30] # Remap node IDs to 0, 1, ... cp.testing.assert_array_equal(Gcg.row_indices, [0, 0, 1, 2]) cp.testing.assert_array_equal(Gcg.col_indices, [1, 2, 0, 0]) cp.testing.assert_array_equal(Gcg.node_values["bar"], [0, 1000, 0]) @@ -201,3 +213,14 @@ def test_convert(): interface.BackendInterface.convert_from_nx(G, edge_attrs={"x": 1}, weight="x") with pytest.raises(TypeError, match="Expected networkx.Graph"): nxcg.from_networkx({}) + + +@pytest.mark.parametrize("graph_class", [nx.MultiGraph, nx.MultiDiGraph]) +def test_multigraph(graph_class): + G = graph_class() + G.add_edge(0, 1, "key1", x=10) + G.add_edge(0, 1, "key2", y=20) + Gcg = nxcg.from_networkx(G, preserve_edge_attrs=True) + H = nxcg.to_networkx(Gcg) + assert type(G) is type(H) + assert nx.utils.graphs_equal(G, H) diff --git a/python/nx-cugraph/nx_cugraph/tests/test_multigraph.py b/python/nx-cugraph/nx_cugraph/tests/test_multigraph.py new file mode 100644 index 00000000000..a8f189a4745 --- /dev/null +++ b/python/nx-cugraph/nx_cugraph/tests/test_multigraph.py @@ -0,0 +1,72 @@ +# Copyright (c) 2023, NVIDIA CORPORATION. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import networkx as nx +import pytest + +import nx_cugraph as nxcg + + +@pytest.mark.parametrize("test_nxcugraph", [False, True]) +def test_get_edge_data(test_nxcugraph): + G = nx.MultiGraph() + G.add_edge(0, 1, 0, x=10) + G.add_edge(0, 1, 1, y=20) + G.add_edge(0, 2, "a", x=100) + G.add_edge(0, 2, "b", y=200) + G.add_edge(0, 3) + G.add_edge(0, 3) + if test_nxcugraph: + G = nxcg.MultiGraph(G) + default = object() + assert G.get_edge_data(0, 0, default=default) is default + assert G.get_edge_data("a", "b", default=default) is default + assert G.get_edge_data(0, 1, 2, default=default) is default + assert G.get_edge_data(-1, 1, default=default) is default + assert G.get_edge_data(0, 1, 0, default=default) == {"x": 10} + assert G.get_edge_data(0, 1, 1, default=default) == {"y": 20} + assert G.get_edge_data(0, 1, default=default) == {0: {"x": 10}, 1: {"y": 20}} + assert G.get_edge_data(0, 2, "a", default=default) == {"x": 100} + assert G.get_edge_data(0, 2, "b", default=default) == {"y": 200} + assert G.get_edge_data(0, 2, default=default) == {"a": {"x": 100}, "b": {"y": 200}} + assert G.get_edge_data(0, 3, 0, default=default) == {} + assert G.get_edge_data(0, 3, 1, default=default) == {} + assert G.get_edge_data(0, 3, 2, default=default) is default + assert G.get_edge_data(0, 3, default=default) == {0: {}, 1: {}} + assert G.has_edge(0, 1) + assert G.has_edge(0, 1, 0) + assert G.has_edge(0, 1, 1) + assert not G.has_edge(0, 1, 2) + assert not G.has_edge(0, 1, "a") + assert not G.has_edge(0, -1) + assert G.has_edge(0, 2) + assert G.has_edge(0, 2, "a") + assert G.has_edge(0, 2, "b") + assert not G.has_edge(0, 2, "c") + assert not G.has_edge(0, 2, 0) + assert G.has_edge(0, 3) + assert not G.has_edge(0, 0) + assert not G.has_edge(0, 0, 0) + + G = nx.MultiGraph() + G.add_edge(0, 1) + if test_nxcugraph: + G = nxcg.MultiGraph(G) + assert G.get_edge_data(0, 1, default=default) == {0: {}} + assert G.get_edge_data(0, 1, 0, default=default) == {} + assert G.get_edge_data(0, 1, 1, default=default) is default + assert G.get_edge_data(0, 1, "b", default=default) is default + assert G.get_edge_data(-1, 2, default=default) is default + assert G.has_edge(0, 1) + assert G.has_edge(0, 1, 0) + assert not G.has_edge(0, 1, 1) + assert not G.has_edge(0, 1, "a") diff --git a/python/nx-cugraph/pyproject.toml b/python/nx-cugraph/pyproject.toml index 98adbb439d8..2478a02df9b 100644 --- a/python/nx-cugraph/pyproject.toml +++ b/python/nx-cugraph/pyproject.toml @@ -32,6 +32,7 @@ classifiers = [ dependencies = [ "cupy-cuda11x>=12.0.0", "networkx>=3.0", + "numpy>=1.21", "pylibcugraph==23.12.*", ] # This list was generated by `rapids-dependency-file-generator`. To make changes, edit ../../dependencies.yaml and run `rapids-dependency-file-generator`. @@ -109,7 +110,7 @@ addopts = [ # "-ra", # Print summary of all fails/errors "--benchmark-warmup=off", "--benchmark-max-time=0", - "--benchmark-min-rounds=3", + "--benchmark-min-rounds=1", "--benchmark-columns=min,median,max", ] From 0fd58836cc54a7a1d557f0251d31eaa157043eb2 Mon Sep 17 00:00:00 2001 From: Jake Awe <50372925+AyodeAwe@users.noreply.github.com> Date: Wed, 18 Oct 2023 08:36:31 -0500 Subject: [PATCH 055/111] update workflow links (#3939) --- .github/workflows/build.yaml | 20 ++++++++++---------- .github/workflows/pr.yaml | 30 +++++++++++++++--------------- .github/workflows/test.yaml | 10 +++++----- ci/release/update-version.sh | 2 +- 4 files changed, 31 insertions(+), 31 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index c92c11941f9..ccfdb826812 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -28,7 +28,7 @@ concurrency: jobs: cpp-build: secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/conda-cpp-build.yaml@branch-23.12 + uses: rapidsai/shared-workflows/.github/workflows/conda-cpp-build.yaml@branch-23.12 with: build_type: ${{ inputs.build_type || 'branch' }} branch: ${{ inputs.branch }} @@ -38,7 +38,7 @@ jobs: python-build: needs: [cpp-build] secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/conda-python-build.yaml@branch-23.12 + uses: rapidsai/shared-workflows/.github/workflows/conda-python-build.yaml@branch-23.12 with: build_type: ${{ inputs.build_type || 'branch' }} branch: ${{ inputs.branch }} @@ -47,7 +47,7 @@ jobs: upload-conda: needs: [cpp-build, python-build] secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/conda-upload-packages.yaml@branch-23.12 + uses: rapidsai/shared-workflows/.github/workflows/conda-upload-packages.yaml@branch-23.12 with: build_type: ${{ inputs.build_type || 'branch' }} branch: ${{ inputs.branch }} @@ -57,7 +57,7 @@ jobs: if: github.ref_type == 'branch' needs: python-build secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/custom-job.yaml@branch-23.12 + uses: rapidsai/shared-workflows/.github/workflows/custom-job.yaml@branch-23.12 with: arch: "amd64" branch: ${{ inputs.branch }} @@ -69,7 +69,7 @@ jobs: sha: ${{ inputs.sha }} wheel-build-pylibcugraph: secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/wheels-build.yaml@branch-23.12 + uses: rapidsai/shared-workflows/.github/workflows/wheels-build.yaml@branch-23.12 with: build_type: ${{ inputs.build_type || 'branch' }} branch: ${{ inputs.branch }} @@ -83,7 +83,7 @@ jobs: wheel-publish-pylibcugraph: needs: wheel-build-pylibcugraph secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/wheels-publish.yaml@branch-23.12 + uses: rapidsai/shared-workflows/.github/workflows/wheels-publish.yaml@branch-23.12 with: build_type: ${{ inputs.build_type || 'branch' }} branch: ${{ inputs.branch }} @@ -93,7 +93,7 @@ jobs: wheel-build-cugraph: needs: wheel-publish-pylibcugraph secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/wheels-build.yaml@branch-23.12 + uses: rapidsai/shared-workflows/.github/workflows/wheels-build.yaml@branch-23.12 with: build_type: ${{ inputs.build_type || 'branch' }} branch: ${{ inputs.branch }} @@ -106,7 +106,7 @@ jobs: wheel-publish-cugraph: needs: wheel-build-cugraph secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/wheels-publish.yaml@branch-23.12 + uses: rapidsai/shared-workflows/.github/workflows/wheels-publish.yaml@branch-23.12 with: build_type: ${{ inputs.build_type || 'branch' }} branch: ${{ inputs.branch }} @@ -116,7 +116,7 @@ jobs: wheel-build-nx-cugraph: needs: wheel-publish-pylibcugraph secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/wheels-build.yaml@branch-23.12 + uses: rapidsai/shared-workflows/.github/workflows/wheels-build.yaml@branch-23.12 with: build_type: ${{ inputs.build_type || 'branch' }} branch: ${{ inputs.branch }} @@ -126,7 +126,7 @@ jobs: wheel-publish-nx-cugraph: needs: wheel-build-nx-cugraph secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/wheels-publish.yaml@branch-23.12 + uses: rapidsai/shared-workflows/.github/workflows/wheels-publish.yaml@branch-23.12 with: build_type: ${{ inputs.build_type || 'branch' }} branch: ${{ inputs.branch }} diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml index 362e826ef65..9d20074381e 100644 --- a/.github/workflows/pr.yaml +++ b/.github/workflows/pr.yaml @@ -27,41 +27,41 @@ jobs: - wheel-tests-nx-cugraph - devcontainer secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/pr-builder.yaml@branch-23.12 + uses: rapidsai/shared-workflows/.github/workflows/pr-builder.yaml@branch-23.12 checks: secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/checks.yaml@branch-23.12 + uses: rapidsai/shared-workflows/.github/workflows/checks.yaml@branch-23.12 with: enable_check_generated_files: false conda-cpp-build: needs: checks secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/conda-cpp-build.yaml@branch-23.12 + uses: rapidsai/shared-workflows/.github/workflows/conda-cpp-build.yaml@branch-23.12 with: build_type: pull-request node_type: cpu32 conda-cpp-tests: needs: conda-cpp-build secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/conda-cpp-tests.yaml@branch-23.12 + uses: rapidsai/shared-workflows/.github/workflows/conda-cpp-tests.yaml@branch-23.12 with: build_type: pull-request conda-python-build: needs: conda-cpp-build secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/conda-python-build.yaml@branch-23.12 + uses: rapidsai/shared-workflows/.github/workflows/conda-python-build.yaml@branch-23.12 with: build_type: pull-request conda-python-tests: needs: conda-python-build secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/conda-python-tests.yaml@branch-23.12 + uses: rapidsai/shared-workflows/.github/workflows/conda-python-tests.yaml@branch-23.12 with: build_type: pull-request conda-notebook-tests: needs: conda-python-build secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/custom-job.yaml@branch-23.12 + uses: rapidsai/shared-workflows/.github/workflows/custom-job.yaml@branch-23.12 with: build_type: pull-request node_type: "gpu-v100-latest-1" @@ -71,7 +71,7 @@ jobs: docs-build: needs: conda-python-build secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/custom-job.yaml@branch-23.12 + uses: rapidsai/shared-workflows/.github/workflows/custom-job.yaml@branch-23.12 with: build_type: pull-request node_type: "gpu-v100-latest-1" @@ -81,7 +81,7 @@ jobs: wheel-build-pylibcugraph: needs: checks secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/wheels-build.yaml@branch-23.12 + uses: rapidsai/shared-workflows/.github/workflows/wheels-build.yaml@branch-23.12 with: build_type: pull-request script: ci/build_wheel_pylibcugraph.sh @@ -92,14 +92,14 @@ jobs: wheel-tests-pylibcugraph: needs: wheel-build-pylibcugraph secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/wheels-test.yaml@branch-23.12 + uses: rapidsai/shared-workflows/.github/workflows/wheels-test.yaml@branch-23.12 with: build_type: pull-request script: ci/test_wheel_pylibcugraph.sh wheel-build-cugraph: needs: wheel-tests-pylibcugraph secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/wheels-build.yaml@branch-23.12 + uses: rapidsai/shared-workflows/.github/workflows/wheels-build.yaml@branch-23.12 with: build_type: pull-request script: ci/build_wheel_cugraph.sh @@ -109,27 +109,27 @@ jobs: wheel-tests-cugraph: needs: wheel-build-cugraph secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/wheels-test.yaml@branch-23.12 + uses: rapidsai/shared-workflows/.github/workflows/wheels-test.yaml@branch-23.12 with: build_type: pull-request script: ci/test_wheel_cugraph.sh wheel-build-nx-cugraph: needs: wheel-tests-pylibcugraph secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/wheels-build.yaml@branch-23.12 + uses: rapidsai/shared-workflows/.github/workflows/wheels-build.yaml@branch-23.12 with: build_type: pull-request script: ci/build_wheel_nx-cugraph.sh wheel-tests-nx-cugraph: needs: wheel-build-nx-cugraph secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/wheels-test.yaml@branch-23.12 + uses: rapidsai/shared-workflows/.github/workflows/wheels-test.yaml@branch-23.12 with: build_type: pull-request script: ci/test_wheel_nx-cugraph.sh devcontainer: secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/build-in-devcontainer.yaml@branch-23.12 + uses: rapidsai/shared-workflows/.github/workflows/build-in-devcontainer.yaml@branch-23.12 with: node_type: cpu32 extra-repo-deploy-key: CUGRAPH_OPS_SSH_PRIVATE_DEPLOY_KEY diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 6d2c1566a3c..a0ecb67712c 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -16,7 +16,7 @@ on: jobs: conda-cpp-tests: secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/conda-cpp-tests.yaml@branch-23.12 + uses: rapidsai/shared-workflows/.github/workflows/conda-cpp-tests.yaml@branch-23.12 with: build_type: nightly branch: ${{ inputs.branch }} @@ -24,7 +24,7 @@ jobs: sha: ${{ inputs.sha }} conda-python-tests: secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/conda-python-tests.yaml@branch-23.12 + uses: rapidsai/shared-workflows/.github/workflows/conda-python-tests.yaml@branch-23.12 with: build_type: nightly branch: ${{ inputs.branch }} @@ -32,7 +32,7 @@ jobs: sha: ${{ inputs.sha }} wheel-tests-pylibcugraph: secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/wheels-test.yaml@branch-23.12 + uses: rapidsai/shared-workflows/.github/workflows/wheels-test.yaml@branch-23.12 with: build_type: nightly branch: ${{ inputs.branch }} @@ -41,7 +41,7 @@ jobs: script: ci/test_wheel_pylibcugraph.sh wheel-tests-cugraph: secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/wheels-test.yaml@branch-23.12 + uses: rapidsai/shared-workflows/.github/workflows/wheels-test.yaml@branch-23.12 with: build_type: nightly branch: ${{ inputs.branch }} @@ -50,7 +50,7 @@ jobs: script: ci/test_wheel_cugraph.sh wheel-tests-nx-cugraph: secrets: inherit - uses: rapidsai/shared-action-workflows/.github/workflows/wheels-test.yaml@branch-23.12 + uses: rapidsai/shared-workflows/.github/workflows/wheels-test.yaml@branch-23.12 with: build_type: nightly branch: ${{ inputs.branch }} diff --git a/ci/release/update-version.sh b/ci/release/update-version.sh index aaeaa715434..ad6426b66ff 100755 --- a/ci/release/update-version.sh +++ b/ci/release/update-version.sh @@ -124,7 +124,7 @@ sed_runner "/^ucx_py_version:$/ {n;s/.*/ - \"${NEXT_UCX_PY_VERSION}.*\"/}" cond # CI files for FILE in .github/workflows/*.yaml; do - sed_runner "/shared-action-workflows/ s/@.*/@branch-${NEXT_SHORT_TAG}/g" "${FILE}" + sed_runner "/shared-workflows/ s/@.*/@branch-${NEXT_SHORT_TAG}/g" "${FILE}" # Wheel builds clone cugraph-ops, update its branch sed_runner "s/extra-repo-sha: branch-.*/extra-repo-sha: branch-${NEXT_SHORT_TAG}/g" "${FILE}" # Wheel builds install dask-cuda from source, update its branch From d9a8dc7165198d5fabe6e8462df3abf36177c128 Mon Sep 17 00:00:00 2001 From: Erik Welch Date: Mon, 23 Oct 2023 17:14:44 -0500 Subject: [PATCH 056/111] nx-cugraph: xfail test_louvain.py:test_threshold in Python 3.9 (#3944) This test is flaky b/c Louvain in PLC does not have a seed or random state parameter. This test holds the seed fixed and changes the threshold, but clearly this may not work if we can't set the seed. Authors: - Erik Welch (https://github.com/eriknw) Approvers: - Rick Ratzel (https://github.com/rlratzel) - Naim (https://github.com/naimnv) URL: https://github.com/rapidsai/cugraph/pull/3944 --- python/nx-cugraph/nx_cugraph/interface.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/python/nx-cugraph/nx_cugraph/interface.py b/python/nx-cugraph/nx_cugraph/interface.py index 124b86a4af2..a7b88b72ec5 100644 --- a/python/nx-cugraph/nx_cugraph/interface.py +++ b/python/nx-cugraph/nx_cugraph/interface.py @@ -12,6 +12,8 @@ # limitations under the License. from __future__ import annotations +import sys + import networkx as nx import nx_cugraph as nxcg @@ -180,6 +182,11 @@ def key(testpath): ): "self-loops not handled in Louvain", } ) + if sys.version_info[:2] == (3, 9): + # This test is sensitive to RNG, which depends on Python version + xfail[ + key("test_louvain.py:test_threshold") + ] = "Louvain does not support seed parameter" for item in items: kset = set(item.keywords) From 9b28458dae82e85270bad0f8abf806f318fc84c8 Mon Sep 17 00:00:00 2001 From: Chuck Hastings <45364586+ChuckHastings@users.noreply.github.com> Date: Mon, 23 Oct 2023 22:47:36 -0400 Subject: [PATCH 057/111] Some MTMG code cleanup and small optimizations (#3894) Added some missing documentation. A couple of optimizations: * Modified the `append` logic to keep the mutex lock only long enough to compute what needs to be copied and where. * Modified the handle created by the resource manager for each GPU to have a stream pool to enable different threads to operate on different streams. Authors: - Chuck Hastings (https://github.com/ChuckHastings) Approvers: - Seunghwa Kang (https://github.com/seunghwak) URL: https://github.com/rapidsai/cugraph/pull/3894 --- .../mtmg/detail/per_device_edgelist.hpp | 116 ++++++++++++------ cpp/include/cugraph/mtmg/handle.hpp | 35 +++++- cpp/include/cugraph/mtmg/instance_manager.hpp | 12 +- cpp/include/cugraph/mtmg/resource_manager.hpp | 15 ++- .../cugraph/mtmg/vertex_result_view.hpp | 2 + cpp/src/mtmg/vertex_result.cu | 4 +- cpp/tests/mtmg/threaded_test.cu | 16 +-- 7 files changed, 137 insertions(+), 63 deletions(-) diff --git a/cpp/include/cugraph/mtmg/detail/per_device_edgelist.hpp b/cpp/include/cugraph/mtmg/detail/per_device_edgelist.hpp index 8011146ee4f..7fd5bb726e6 100644 --- a/cpp/include/cugraph/mtmg/detail/per_device_edgelist.hpp +++ b/cpp/include/cugraph/mtmg/detail/per_device_edgelist.hpp @@ -16,6 +16,7 @@ #pragma once +#include #include // FIXME: Could use std::span once compiler supports C++20 @@ -58,6 +59,15 @@ class per_device_edgelist_t { per_device_edgelist_t& operator=(per_device_edgelist_t const&) = delete; per_device_edgelist_t& operator=(per_device_edgelist_t&&) = delete; + /** + * @brief Construct a new per device edgelist t object + * + * @param handle MTMG resource handle - used to identify GPU resources + * @param device_buffer_size Number of edges to store in each device buffer + * @param use_weight Whether or not the edgelist will have weights + * @param use_edge_id Whether or not the edgelist will have edge ids + * @param use_edge_type Whether or not the edgelist will have edge types + */ per_device_edgelist_t(cugraph::mtmg::handle_t const& handle, size_t device_buffer_size, bool use_weight, @@ -82,6 +92,11 @@ class per_device_edgelist_t { create_new_buffers(handle); } + /** + * @brief Move construct a new per device edgelist t object + * + * @param other Object to move into this instance + */ per_device_edgelist_t(per_device_edgelist_t&& other) : device_buffer_size_{other.device_buffer_size_}, current_pos_{other.current_pos_}, @@ -110,45 +125,72 @@ class per_device_edgelist_t { std::optional> edge_id, std::optional> edge_type) { - // FIXME: This lock guard could be on a smaller region, but it - // would require more careful coding. The raft::update_device - // calls could be done without the lock if we made a local - // of the values of *.back() and did an increment of current_pos_ - // while we hold the lock. - std::lock_guard lock(lock_); - - size_t count = src.size(); - size_t pos = 0; - - while (count > 0) { - size_t copy_count = std::min(count, (src_.back().size() - current_pos_)); - - raft::update_device( - src_.back().begin() + current_pos_, src.begin() + pos, copy_count, handle.get_stream()); - raft::update_device( - dst_.back().begin() + current_pos_, dst.begin() + pos, copy_count, handle.get_stream()); - if (wgt) - raft::update_device( - wgt_->back().begin() + current_pos_, wgt->begin() + pos, copy_count, handle.get_stream()); - if (edge_id) - raft::update_device(edge_id_->back().begin() + current_pos_, - edge_id->begin() + pos, - copy_count, - handle.get_stream()); - if (edge_type) - raft::update_device(edge_type_->back().begin() + current_pos_, - edge_type->begin() + pos, - copy_count, - handle.get_stream()); - - count -= copy_count; - pos += copy_count; - current_pos_ += copy_count; - - if (current_pos_ == src_.back().size()) { create_new_buffers(handle); } + std::vector> copy_positions; + + { + std::lock_guard lock(lock_); + + size_t count = src.size(); + size_t pos = 0; + + while (count > 0) { + size_t copy_count = std::min(count, (src_.back().size() - current_pos_)); + + copy_positions.push_back(std::make_tuple(src_.size() - 1, current_pos_, pos, copy_count)); + + count -= copy_count; + pos += copy_count; + current_pos_ += copy_count; + + if (current_pos_ == src_.back().size()) { create_new_buffers(handle); } + } } - handle.raft_handle().sync_stream(); + std::for_each(copy_positions.begin(), + copy_positions.end(), + [&handle, + &this_src = src_, + &src, + &this_dst = dst_, + &dst, + &this_wgt = wgt_, + &wgt, + &this_edge_id = edge_id_, + &edge_id, + &this_edge_type = edge_type_, + &edge_type](auto tuple) { + auto [buffer_idx, buffer_pos, input_pos, copy_count] = tuple; + + raft::update_device(this_src[buffer_idx].begin() + buffer_pos, + src.begin() + input_pos, + copy_count, + handle.get_stream()); + + raft::update_device(this_dst[buffer_idx].begin() + buffer_pos, + dst.begin() + input_pos, + copy_count, + handle.get_stream()); + + if (this_wgt) + raft::update_device((*this_wgt)[buffer_idx].begin() + buffer_pos, + wgt->begin() + input_pos, + copy_count, + handle.get_stream()); + + if (this_edge_id) + raft::update_device((*this_edge_id)[buffer_idx].begin() + buffer_pos, + edge_id->begin() + input_pos, + copy_count, + handle.get_stream()); + + if (this_edge_type) + raft::update_device((*this_edge_type)[buffer_idx].begin() + buffer_pos, + edge_type->begin() + input_pos, + copy_count, + handle.get_stream()); + }); + + handle.sync_stream(); } /** diff --git a/cpp/include/cugraph/mtmg/handle.hpp b/cpp/include/cugraph/mtmg/handle.hpp index f23bce5aeac..efdec3f0775 100644 --- a/cpp/include/cugraph/mtmg/handle.hpp +++ b/cpp/include/cugraph/mtmg/handle.hpp @@ -18,6 +18,8 @@ #include +#include + namespace cugraph { namespace mtmg { @@ -60,10 +62,41 @@ class handle_t { rmm::cuda_stream_view get_stream() const { return raft_handle_.is_stream_pool_initialized() - ? raft_handle_.get_stream_from_stream_pool(device_id_) + ? raft_handle_.get_stream_from_stream_pool(thread_rank_) : raft_handle_.get_stream(); } + /** + * @brief Sync on the cuda stream + * + * @param stream Which stream to synchronize (defaults to the stream for this handle) + */ + void sync_stream(rmm::cuda_stream_view stream) const { raft_handle_.sync_stream(stream); } + + /** + * @brief Sync on the cuda stream for this handle + */ + void sync_stream() const { sync_stream(get_stream()); } + + /** + * @brief get thrust policy for the stream + * + * @param stream Which stream to use for this thrust call + * + * @return exec policy using the current stream + */ + rmm::exec_policy get_thrust_policy(rmm::cuda_stream_view stream) const + { + return rmm::exec_policy(stream); + } + + /** + * @brief get thrust policy for the stream for this handle + * + * @return exec policy using the current stream + */ + rmm::exec_policy get_thrust_policy() const { return get_thrust_policy(get_stream()); } + /** * @brief Get thread rank * diff --git a/cpp/include/cugraph/mtmg/instance_manager.hpp b/cpp/include/cugraph/mtmg/instance_manager.hpp index 8bf62b56f4b..687c5ddbf02 100644 --- a/cpp/include/cugraph/mtmg/instance_manager.hpp +++ b/cpp/include/cugraph/mtmg/instance_manager.hpp @@ -54,18 +54,18 @@ class instance_manager_t { * the request. Threads will be assigned to GPUs in a round-robin fashion to * spread requesting threads around the GPU resources. * - * This function will be CPU thread-safe. + * This function is CPU thread-safe. * * @return a handle for this thread. */ handle_t get_handle() { - int local_id = thread_counter_++; + int local_id = thread_counter_++; + int gpu_id = local_id % raft_handle_.size(); + int thread_id = local_id / raft_handle_.size(); - RAFT_CUDA_TRY(cudaSetDevice(device_ids_[local_id % raft_handle_.size()].value())); - return handle_t(*raft_handle_[local_id % raft_handle_.size()], - local_id / raft_handle_.size(), - static_cast(local_id % raft_handle_.size())); + RAFT_CUDA_TRY(cudaSetDevice(device_ids_[gpu_id].value())); + return handle_t(*raft_handle_[gpu_id], thread_id, static_cast(gpu_id)); } /** diff --git a/cpp/include/cugraph/mtmg/resource_manager.hpp b/cpp/include/cugraph/mtmg/resource_manager.hpp index b4633626e7c..e1e1d7ffc9d 100644 --- a/cpp/include/cugraph/mtmg/resource_manager.hpp +++ b/cpp/include/cugraph/mtmg/resource_manager.hpp @@ -23,8 +23,8 @@ #include #include -#include #include +#include #include #include @@ -121,11 +121,14 @@ class resource_manager_t { * @param instance_manager_id a ncclUniqueId that is shared by all processes participating * in this instance. All processes must use the same ID in this call, it is up * to the calling code to share this ID properly before the call. + * @param n_streams The number of streams to create in a stream pool for + * each GPU. Defaults to 16. * * @return unique pointer to instance manager */ - std::unique_ptr create_instance_manager( - std::vector ranks_to_include, ncclUniqueId instance_manager_id) const + std::unique_ptr create_instance_manager(std::vector ranks_to_include, + ncclUniqueId instance_manager_id, + size_t n_streams = 16) const { std::for_each( ranks_to_include.begin(), ranks_to_include.end(), [local_ranks = local_rank_map_](int rank) { @@ -153,11 +156,11 @@ class resource_manager_t { auto pos = local_rank_map_.find(rank); RAFT_CUDA_TRY(cudaSetDevice(pos->second.value())); - raft::handle_t tmp_handle; - nccl_comms.push_back(std::make_unique()); handles.push_back( - std::make_unique(tmp_handle, per_device_rmm_resources_.find(rank)->second)); + std::make_unique(rmm::cuda_stream_per_thread, + std::make_shared(n_streams), + per_device_rmm_resources_.find(rank)->second)); device_ids.push_back(pos->second); } diff --git a/cpp/include/cugraph/mtmg/vertex_result_view.hpp b/cpp/include/cugraph/mtmg/vertex_result_view.hpp index 7a7070d6f2a..a349bb95333 100644 --- a/cpp/include/cugraph/mtmg/vertex_result_view.hpp +++ b/cpp/include/cugraph/mtmg/vertex_result_view.hpp @@ -21,6 +21,8 @@ #include #include +#include + namespace cugraph { namespace mtmg { diff --git a/cpp/src/mtmg/vertex_result.cu b/cpp/src/mtmg/vertex_result.cu index a669a127f41..97fcd291c87 100644 --- a/cpp/src/mtmg/vertex_result.cu +++ b/cpp/src/mtmg/vertex_result.cu @@ -97,7 +97,7 @@ rmm::device_uvector vertex_result_view_t::gather( return vertex_partition.local_vertex_partition_offset_from_vertex_nocheck(v); }); - thrust::gather(handle.raft_handle().get_thrust_policy(), + thrust::gather(handle.get_thrust_policy(), iter, iter + local_vertices.size(), wrapped.begin(), @@ -118,7 +118,7 @@ rmm::device_uvector vertex_result_view_t::gather( // // Finally, reorder result // - thrust::scatter(handle.raft_handle().get_thrust_policy(), + thrust::scatter(handle.get_thrust_policy(), tmp_result.begin(), tmp_result.end(), vertex_pos.begin(), diff --git a/cpp/tests/mtmg/threaded_test.cu b/cpp/tests/mtmg/threaded_test.cu index c5dc2d3c7ce..bc4d8cfef6a 100644 --- a/cpp/tests/mtmg/threaded_test.cu +++ b/cpp/tests/mtmg/threaded_test.cu @@ -94,8 +94,9 @@ class Tests_Multithreaded size_t device_buffer_size{64 * 1024 * 1024}; size_t thread_buffer_size{4 * 1024 * 1024}; + const int num_threads_per_gpu{4}; int num_gpus = gpu_list.size(); - int num_threads = num_gpus * 4; + int num_threads = num_gpus * num_threads_per_gpu; cugraph::mtmg::resource_manager_t resource_manager; @@ -106,8 +107,10 @@ class Tests_Multithreaded ncclUniqueId instance_manager_id; ncclGetUniqueId(&instance_manager_id); + // Currently the only uses for multiple streams for each CPU threads + // associated with a particular GPU, which is a constant set above auto instance_manager = resource_manager.create_instance_manager( - resource_manager.registered_ranks(), instance_manager_id); + resource_manager.registered_ranks(), instance_manager_id, num_threads_per_gpu); cugraph::mtmg::edgelist_t edgelist; cugraph::mtmg::graph_t graph; @@ -172,15 +175,6 @@ class Tests_Multithreaded per_thread_edgelist(edgelist.get(thread_handle), thread_buffer_size); for (size_t j = i; j < h_src_v.size(); j += num_threads) { -#if 0 - if (h_weights_v) { - thread_edgelist.append( - thread_handle, h_src_v[j], h_dst_v[j], (*h_weights_v)[j], std::nullopt, std::nullopt); - } else { - thread_edgelist.append( - thread_handle, h_src_v[j], h_dst_v[j], std::nullopt, std::nullopt, std::nullopt); - } -#endif per_thread_edgelist.append( thread_handle, h_src_v[j], From 629e63c164732fe8da690fa42d2bd9b990131b09 Mon Sep 17 00:00:00 2001 From: Erik Welch Date: Wed, 25 Oct 2023 16:16:00 -0500 Subject: [PATCH 058/111] nx-cugraph: add k_truss and degree centralities (#3945) New algorithms: - `degree_centrality` - `in_degree_centrality` - `k_truss` - `number_of_selfloops` - `out_degree_centrality` Also, rename `row_indices, col_indices` to `src_indices, dst_indices` Authors: - Erik Welch (https://github.com/eriknw) Approvers: - Rick Ratzel (https://github.com/rlratzel) URL: https://github.com/rapidsai/cugraph/pull/3945 --- python/nx-cugraph/_nx_cugraph/__init__.py | 5 + python/nx-cugraph/lint.yaml | 6 +- .../nx_cugraph/algorithms/__init__.py | 1 + .../algorithms/centrality/__init__.py | 1 + .../algorithms/centrality/degree_alg.py | 48 ++++++++ .../algorithms/community/louvain.py | 2 +- .../nx-cugraph/nx_cugraph/algorithms/core.py | 96 ++++++++++++++++ .../nx_cugraph/algorithms/isolate.py | 8 +- .../nx-cugraph/nx_cugraph/classes/__init__.py | 5 +- .../nx-cugraph/nx_cugraph/classes/digraph.py | 13 ++- .../nx-cugraph/nx_cugraph/classes/function.py | 23 ++++ python/nx-cugraph/nx_cugraph/classes/graph.py | 105 ++++++++++-------- .../nx_cugraph/classes/multigraph.py | 64 ++++++----- python/nx-cugraph/nx_cugraph/convert.py | 38 +++---- .../nx_cugraph/tests/test_convert.py | 32 +++--- .../nx_cugraph/tests/test_match_api.py | 3 + python/nx-cugraph/pyproject.toml | 5 +- 17 files changed, 330 insertions(+), 125 deletions(-) create mode 100644 python/nx-cugraph/nx_cugraph/algorithms/centrality/degree_alg.py create mode 100644 python/nx-cugraph/nx_cugraph/algorithms/core.py create mode 100644 python/nx-cugraph/nx_cugraph/classes/function.py diff --git a/python/nx-cugraph/_nx_cugraph/__init__.py b/python/nx-cugraph/_nx_cugraph/__init__.py index 886d7a19f74..965b5b232ab 100644 --- a/python/nx-cugraph/_nx_cugraph/__init__.py +++ b/python/nx-cugraph/_nx_cugraph/__init__.py @@ -30,11 +30,16 @@ "functions": { # BEGIN: functions "betweenness_centrality", + "degree_centrality", "edge_betweenness_centrality", + "in_degree_centrality", "is_isolate", "isolates", + "k_truss", "louvain_communities", "number_of_isolates", + "number_of_selfloops", + "out_degree_centrality", # END: functions }, "extra_docstrings": { diff --git a/python/nx-cugraph/lint.yaml b/python/nx-cugraph/lint.yaml index 4f604fbeb6e..fef2cebc7f5 100644 --- a/python/nx-cugraph/lint.yaml +++ b/python/nx-cugraph/lint.yaml @@ -45,12 +45,12 @@ repos: - id: pyupgrade args: [--py39-plus] - repo: https://github.com/psf/black - rev: 23.9.1 + rev: 23.10.0 hooks: - id: black # - id: black-jupyter - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.0.292 + rev: v0.1.1 hooks: - id: ruff args: [--fix-only, --show-fixes] @@ -77,7 +77,7 @@ repos: additional_dependencies: [tomli] files: ^(nx_cugraph|docs)/ - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.0.292 + rev: v0.1.1 hooks: - id: ruff - repo: https://github.com/pre-commit/pre-commit-hooks diff --git a/python/nx-cugraph/nx_cugraph/algorithms/__init__.py b/python/nx-cugraph/nx_cugraph/algorithms/__init__.py index dfd9adfc61a..22600bfdc2d 100644 --- a/python/nx-cugraph/nx_cugraph/algorithms/__init__.py +++ b/python/nx-cugraph/nx_cugraph/algorithms/__init__.py @@ -12,4 +12,5 @@ # limitations under the License. from . import centrality, community from .centrality import * +from .core import * from .isolate import * diff --git a/python/nx-cugraph/nx_cugraph/algorithms/centrality/__init__.py b/python/nx-cugraph/nx_cugraph/algorithms/centrality/__init__.py index 2ac6242e8a4..af91f227843 100644 --- a/python/nx-cugraph/nx_cugraph/algorithms/centrality/__init__.py +++ b/python/nx-cugraph/nx_cugraph/algorithms/centrality/__init__.py @@ -11,3 +11,4 @@ # See the License for the specific language governing permissions and # limitations under the License. from .betweenness import * +from .degree_alg import * diff --git a/python/nx-cugraph/nx_cugraph/algorithms/centrality/degree_alg.py b/python/nx-cugraph/nx_cugraph/algorithms/centrality/degree_alg.py new file mode 100644 index 00000000000..0b2fd24af79 --- /dev/null +++ b/python/nx-cugraph/nx_cugraph/algorithms/centrality/degree_alg.py @@ -0,0 +1,48 @@ +# Copyright (c) 2023, NVIDIA CORPORATION. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +from nx_cugraph.convert import _to_directed_graph, _to_graph +from nx_cugraph.utils import networkx_algorithm, not_implemented_for + +__all__ = ["degree_centrality", "in_degree_centrality", "out_degree_centrality"] + + +@networkx_algorithm +def degree_centrality(G): + G = _to_graph(G) + if len(G) <= 1: + return dict.fromkeys(G, 1) + deg = G._degrees_array() + centrality = deg * (1 / (len(G) - 1)) + return G._nodearray_to_dict(centrality) + + +@not_implemented_for("undirected") +@networkx_algorithm +def in_degree_centrality(G): + G = _to_directed_graph(G) + if len(G) <= 1: + return dict.fromkeys(G, 1) + deg = G._in_degrees_array() + centrality = deg * (1 / (len(G) - 1)) + return G._nodearray_to_dict(centrality) + + +@not_implemented_for("undirected") +@networkx_algorithm +def out_degree_centrality(G): + G = _to_directed_graph(G) + if len(G) <= 1: + return dict.fromkeys(G, 1) + deg = G._out_degrees_array() + centrality = deg * (1 / (len(G) - 1)) + return G._nodearray_to_dict(centrality) diff --git a/python/nx-cugraph/nx_cugraph/algorithms/community/louvain.py b/python/nx-cugraph/nx_cugraph/algorithms/community/louvain.py index dc209870c89..62261d109a2 100644 --- a/python/nx-cugraph/nx_cugraph/algorithms/community/louvain.py +++ b/python/nx-cugraph/nx_cugraph/algorithms/community/louvain.py @@ -42,7 +42,7 @@ def louvain_communities( # NetworkX allows both directed and undirected, but cugraph only allows undirected. seed = _seed_to_int(seed) # Unused, but ensure it's valid for future compatibility G = _to_undirected_graph(G, weight) - if G.row_indices.size == 0: + if G.src_indices.size == 0: # TODO: PLC doesn't handle empty graphs gracefully! return [{key} for key in G._nodeiter_to_iter(range(len(G)))] if max_level is None: diff --git a/python/nx-cugraph/nx_cugraph/algorithms/core.py b/python/nx-cugraph/nx_cugraph/algorithms/core.py new file mode 100644 index 00000000000..0a64dd71c69 --- /dev/null +++ b/python/nx-cugraph/nx_cugraph/algorithms/core.py @@ -0,0 +1,96 @@ +# Copyright (c) 2023, NVIDIA CORPORATION. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import cupy as cp +import networkx as nx +import numpy as np +import pylibcugraph as plc + +import nx_cugraph as nxcg +from nx_cugraph.utils import networkx_algorithm, not_implemented_for + +__all__ = ["k_truss"] + + +@not_implemented_for("directed") +@not_implemented_for("multigraph") +@networkx_algorithm +def k_truss(G, k): + if is_nx := isinstance(G, nx.Graph): + G = nxcg.from_networkx(G, preserve_all_attrs=True) + if nxcg.number_of_selfloops(G) > 0: + raise nx.NetworkXError( + "Input graph has self loops which is not permitted; " + "Consider using G.remove_edges_from(nx.selfloop_edges(G))." + ) + # TODO: create renumbering helper function(s) + if k < 3: + # k-truss graph is comprised of nodes incident on k-2 triangles, so k<3 is a + # boundary condition. Here, all we need to do is drop nodes with zero degree. + # Technically, it would be okay to delete this branch of code, because + # plc.k_truss_subgraph behaves the same for 0 <= k < 3. We keep this branch b/c + # it's faster and has an "early return" if there are no nodes with zero degree. + degrees = G._degrees_array() + # Renumber step 0: node indices + node_indices = degrees.nonzero()[0] + if degrees.size == node_indices.size: + # No change + return G if is_nx else G.copy() + src_indices = G.src_indices + dst_indices = G.dst_indices + # Renumber step 1: edge values (no changes needed) + edge_values = {key: val.copy() for key, val in G.edge_values.items()} + edge_masks = {key: val.copy() for key, val in G.edge_masks.items()} + else: + # int dtype for edge_indices would be preferred + edge_indices = cp.arange(G.src_indices.size, dtype=np.float64) + src_indices, dst_indices, edge_indices, _ = plc.k_truss_subgraph( + resource_handle=plc.ResourceHandle(), + graph=G._get_plc_graph(edge_array=edge_indices), + k=k, + do_expensive_check=False, + ) + # Renumber step 0: node indices + node_indices = cp.unique(cp.concatenate([src_indices, dst_indices])) + # Renumber step 1: edge values + edge_indices = edge_indices.astype(np.int64) + edge_values = {key: val[edge_indices] for key, val in G.edge_values.items()} + edge_masks = {key: val[edge_indices] for key, val in G.edge_masks.items()} + # Renumber step 2: edge indices + mapper = cp.zeros(len(G), src_indices.dtype) + mapper[node_indices] = cp.arange(node_indices.size, dtype=mapper.dtype) + src_indices = mapper[src_indices] + dst_indices = mapper[dst_indices] + # Renumber step 3: node values + node_values = {key: val[node_indices] for key, val in G.node_values.items()} + node_masks = {key: val[node_indices] for key, val in G.node_masks.items()} + # Renumber step 4: key_to_id + if (id_to_key := G.id_to_key) is not None: + key_to_id = { + id_to_key[old_index]: new_index + for new_index, old_index in enumerate(node_indices.tolist()) + } + else: + key_to_id = None + # Same as calling `G.from_coo`, but use __class__ to indicate it's a classmethod. + new_graph = G.__class__.from_coo( + node_indices.size, + src_indices, + dst_indices, + edge_values, + edge_masks, + node_values, + node_masks, + key_to_id=key_to_id, + ) + new_graph.graph.update(G.graph) + return new_graph diff --git a/python/nx-cugraph/nx_cugraph/algorithms/isolate.py b/python/nx-cugraph/nx_cugraph/algorithms/isolate.py index 774627e84f6..d32223fb3ed 100644 --- a/python/nx-cugraph/nx_cugraph/algorithms/isolate.py +++ b/python/nx-cugraph/nx_cugraph/algorithms/isolate.py @@ -30,18 +30,18 @@ def is_isolate(G, n): G = _to_graph(G) index = n if G.key_to_id is None else G.key_to_id[n] return not ( - (G.row_indices == index).any().tolist() + (G.src_indices == index).any().tolist() or G.is_directed() - and (G.col_indices == index).any().tolist() + and (G.dst_indices == index).any().tolist() ) def _mark_isolates(G) -> cp.ndarray[bool]: """Return a boolean mask array indicating indices of isolated nodes.""" mark_isolates = cp.ones(len(G), bool) - mark_isolates[G.row_indices] = False + mark_isolates[G.src_indices] = False if G.is_directed(): - mark_isolates[G.col_indices] = False + mark_isolates[G.dst_indices] = False return mark_isolates diff --git a/python/nx-cugraph/nx_cugraph/classes/__init__.py b/python/nx-cugraph/nx_cugraph/classes/__init__.py index 9916bcbe241..19a5357da55 100644 --- a/python/nx-cugraph/nx_cugraph/classes/__init__.py +++ b/python/nx-cugraph/nx_cugraph/classes/__init__.py @@ -11,7 +11,8 @@ # See the License for the specific language governing permissions and # limitations under the License. from .graph import Graph +from .digraph import DiGraph from .multigraph import MultiGraph +from .multidigraph import MultiDiGraph -from .digraph import DiGraph # isort:skip -from .multidigraph import MultiDiGraph # isort:skip +from .function import * diff --git a/python/nx-cugraph/nx_cugraph/classes/digraph.py b/python/nx-cugraph/nx_cugraph/classes/digraph.py index 72a1bff21a9..52ea2334c85 100644 --- a/python/nx-cugraph/nx_cugraph/classes/digraph.py +++ b/python/nx-cugraph/nx_cugraph/classes/digraph.py @@ -14,6 +14,7 @@ from typing import TYPE_CHECKING +import cupy as cp import networkx as nx import nx_cugraph as nxcg @@ -48,7 +49,7 @@ def number_of_edges( ) -> int: if u is not None or v is not None: raise NotImplementedError - return self.row_indices.size + return self.src_indices.size ########################## # NetworkX graph methods # @@ -59,3 +60,13 @@ def reverse(self, copy: bool = True) -> DiGraph: return self._copy(not copy, self.__class__, reverse=True) # Many more methods to implement... + + ################### + # Private methods # + ################### + + def _in_degrees_array(self): + return cp.bincount(self.dst_indices, minlength=self._N) + + def _out_degrees_array(self): + return cp.bincount(self.src_indices, minlength=self._N) diff --git a/python/nx-cugraph/nx_cugraph/classes/function.py b/python/nx-cugraph/nx_cugraph/classes/function.py new file mode 100644 index 00000000000..633e4abd7f4 --- /dev/null +++ b/python/nx-cugraph/nx_cugraph/classes/function.py @@ -0,0 +1,23 @@ +# Copyright (c) 2023, NVIDIA CORPORATION. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +from nx_cugraph.convert import _to_graph +from nx_cugraph.utils import networkx_algorithm + +__all__ = ["number_of_selfloops"] + + +@networkx_algorithm +def number_of_selfloops(G): + G = _to_graph(G) + is_selfloop = G.src_indices == G.dst_indices + return is_selfloop.sum().tolist() diff --git a/python/nx-cugraph/nx_cugraph/classes/graph.py b/python/nx-cugraph/nx_cugraph/classes/graph.py index f1e85c836e3..166b6b9dc6b 100644 --- a/python/nx-cugraph/nx_cugraph/classes/graph.py +++ b/python/nx-cugraph/nx_cugraph/classes/graph.py @@ -52,8 +52,8 @@ class Graph: # Not networkx properties # We store edge data in COO format with {row,col}_indices and edge_values. - row_indices: cp.ndarray[IndexValue] - col_indices: cp.ndarray[IndexValue] + src_indices: cp.ndarray[IndexValue] + dst_indices: cp.ndarray[IndexValue] edge_values: dict[AttrKey, cp.ndarray[EdgeValue]] edge_masks: dict[AttrKey, cp.ndarray[bool]] node_values: dict[AttrKey, cp.ndarray[NodeValue]] @@ -70,8 +70,8 @@ class Graph: def from_coo( cls, N: int, - row_indices: cp.ndarray[IndexValue], - col_indices: cp.ndarray[IndexValue], + src_indices: cp.ndarray[IndexValue], + dst_indices: cp.ndarray[IndexValue], edge_values: dict[AttrKey, cp.ndarray[EdgeValue]] | None = None, edge_masks: dict[AttrKey, cp.ndarray[bool]] | None = None, node_values: dict[AttrKey, cp.ndarray[NodeValue]] | None = None, @@ -82,8 +82,8 @@ def from_coo( **attr, ) -> Graph: new_graph = object.__new__(cls) - new_graph.row_indices = row_indices - new_graph.col_indices = col_indices + new_graph.src_indices = src_indices + new_graph.dst_indices = dst_indices new_graph.edge_values = {} if edge_values is None else dict(edge_values) new_graph.edge_masks = {} if edge_masks is None else dict(edge_masks) new_graph.node_values = {} if node_values is None else dict(node_values) @@ -93,9 +93,9 @@ def from_coo( new_graph._N = op.index(N) # Ensure N is integral new_graph.graph = new_graph.graph_attr_dict_factory() new_graph.graph.update(attr) - size = new_graph.row_indices.size + size = new_graph.src_indices.size # Easy and fast sanity checks - if size != new_graph.col_indices.size: + if size != new_graph.dst_indices.size: raise ValueError for attr in ["edge_values", "edge_masks"]: if datadict := getattr(new_graph, attr): @@ -117,7 +117,7 @@ def from_coo( def from_csr( cls, indptr: cp.ndarray[IndexValue], - col_indices: cp.ndarray[IndexValue], + dst_indices: cp.ndarray[IndexValue], edge_values: dict[AttrKey, cp.ndarray[EdgeValue]] | None = None, edge_masks: dict[AttrKey, cp.ndarray[bool]] | None = None, node_values: dict[AttrKey, cp.ndarray[NodeValue]] | None = None, @@ -128,14 +128,14 @@ def from_csr( **attr, ) -> Graph: N = indptr.size - 1 - row_indices = cp.array( + src_indices = cp.array( # cp.repeat is slow to use here, so use numpy instead np.repeat(np.arange(N, dtype=np.int32), cp.diff(indptr).get()) ) return cls.from_coo( N, - row_indices, - col_indices, + src_indices, + dst_indices, edge_values, edge_masks, node_values, @@ -149,7 +149,7 @@ def from_csr( def from_csc( cls, indptr: cp.ndarray[IndexValue], - row_indices: cp.ndarray[IndexValue], + src_indices: cp.ndarray[IndexValue], edge_values: dict[AttrKey, cp.ndarray[EdgeValue]] | None = None, edge_masks: dict[AttrKey, cp.ndarray[bool]] | None = None, node_values: dict[AttrKey, cp.ndarray[NodeValue]] | None = None, @@ -160,14 +160,14 @@ def from_csc( **attr, ) -> Graph: N = indptr.size - 1 - col_indices = cp.array( + dst_indices = cp.array( # cp.repeat is slow to use here, so use numpy instead np.repeat(np.arange(N, dtype=np.int32), cp.diff(indptr).get()) ) return cls.from_coo( N, - row_indices, - col_indices, + src_indices, + dst_indices, edge_values, edge_masks, node_values, @@ -183,7 +183,7 @@ def from_dcsr( N: int, compressed_rows: cp.ndarray[IndexValue], indptr: cp.ndarray[IndexValue], - col_indices: cp.ndarray[IndexValue], + dst_indices: cp.ndarray[IndexValue], edge_values: dict[AttrKey, cp.ndarray[EdgeValue]] | None = None, edge_masks: dict[AttrKey, cp.ndarray[bool]] | None = None, node_values: dict[AttrKey, cp.ndarray[NodeValue]] | None = None, @@ -193,14 +193,14 @@ def from_dcsr( id_to_key: list[NodeKey] | None = None, **attr, ) -> Graph: - row_indices = cp.array( + src_indices = cp.array( # cp.repeat is slow to use here, so use numpy instead np.repeat(compressed_rows.get(), cp.diff(indptr).get()) ) return cls.from_coo( N, - row_indices, - col_indices, + src_indices, + dst_indices, edge_values, edge_masks, node_values, @@ -216,7 +216,7 @@ def from_dcsc( N: int, compressed_cols: cp.ndarray[IndexValue], indptr: cp.ndarray[IndexValue], - row_indices: cp.ndarray[IndexValue], + src_indices: cp.ndarray[IndexValue], edge_values: dict[AttrKey, cp.ndarray[EdgeValue]] | None = None, edge_masks: dict[AttrKey, cp.ndarray[bool]] | None = None, node_values: dict[AttrKey, cp.ndarray[NodeValue]] | None = None, @@ -226,14 +226,14 @@ def from_dcsc( id_to_key: list[NodeKey] | None = None, **attr, ) -> Graph: - col_indices = cp.array( + dst_indices = cp.array( # cp.repeat is slow to use here, so use numpy instead np.repeat(compressed_cols.get(), cp.diff(indptr).get()) ) return cls.from_coo( N, - row_indices, - col_indices, + src_indices, + dst_indices, edge_values, edge_masks, node_values, @@ -343,8 +343,8 @@ def clear(self) -> None: self.node_values.clear() self.node_masks.clear() self.graph.clear() - self.row_indices = cp.empty(0, self.row_indices.dtype) - self.col_indices = cp.empty(0, self.col_indices.dtype) + self.src_indices = cp.empty(0, self.src_indices.dtype) + self.dst_indices = cp.empty(0, self.dst_indices.dtype) self._N = 0 self.key_to_id = None self._id_to_key = None @@ -353,8 +353,8 @@ def clear(self) -> None: def clear_edges(self) -> None: self.edge_values.clear() self.edge_masks.clear() - self.row_indices = cp.empty(0, self.row_indices.dtype) - self.col_indices = cp.empty(0, self.col_indices.dtype) + self.src_indices = cp.empty(0, self.src_indices.dtype) + self.dst_indices = cp.empty(0, self.dst_indices.dtype) @networkx_api def copy(self, as_view: bool = False) -> Graph: @@ -377,7 +377,7 @@ def get_edge_data( return default except TypeError: return default - index = cp.nonzero((self.row_indices == u) & (self.col_indices == v))[0] + index = cp.nonzero((self.src_indices == u) & (self.dst_indices == v))[0] if index.size == 0: return default [index] = index.tolist() @@ -397,7 +397,7 @@ def has_edge(self, u: NodeKey, v: NodeKey) -> bool: v = self.key_to_id[v] except KeyError: return False - return bool(((self.row_indices == u) & (self.col_indices == v)).any()) + return bool(((self.src_indices == u) & (self.dst_indices == v)).any()) @networkx_api def has_node(self, n: NodeKey) -> bool: @@ -431,8 +431,8 @@ def order(self) -> int: def size(self, weight: AttrKey | None = None) -> int: if weight is not None: raise NotImplementedError - # If no self-edges, then `self.row_indices.size // 2` - return int((self.row_indices <= self.col_indices).sum()) + # If no self-edges, then `self.src_indices.size // 2` + return int((self.src_indices <= self.dst_indices).sum()) @networkx_api def to_directed(self, as_view: bool = False) -> nxcg.DiGraph: @@ -455,9 +455,8 @@ def to_undirected(self, as_view: bool = False) -> Graph: def _copy(self, as_view: bool, cls: type[Graph], reverse: bool = False): # DRY warning: see also MultiGraph._copy - indptr = self.indptr - row_indices = self.row_indices - col_indices = self.col_indices + src_indices = self.src_indices + dst_indices = self.dst_indices edge_values = self.edge_values edge_masks = self.edge_masks node_values = self.node_values @@ -465,9 +464,8 @@ def _copy(self, as_view: bool, cls: type[Graph], reverse: bool = False): key_to_id = self.key_to_id id_to_key = None if key_to_id is None else self._id_to_key if not as_view: - indptr = indptr.copy() - row_indices = row_indices.copy() - col_indices = col_indices.copy() + src_indices = src_indices.copy() + dst_indices = dst_indices.copy() edge_values = {key: val.copy() for key, val in edge_values.items()} edge_masks = {key: val.copy() for key, val in edge_masks.items()} node_values = {key: val.copy() for key, val in node_values.items()} @@ -477,11 +475,11 @@ def _copy(self, as_view: bool, cls: type[Graph], reverse: bool = False): if id_to_key is not None: id_to_key = id_to_key.copy() if reverse: - row_indices, col_indices = col_indices, row_indices + src_indices, dst_indices = dst_indices, src_indices rv = cls.from_coo( - indptr, - row_indices, - col_indices, + self._N, + src_indices, + dst_indices, edge_values, edge_masks, node_values, @@ -502,8 +500,11 @@ def _get_plc_graph( edge_dtype: Dtype | None = None, *, store_transposed: bool = False, + edge_array: cp.ndarray[EdgeValue] | None = None, ): - if edge_attr is None: + if edge_array is not None: + pass + elif edge_attr is None: edge_array = None elif edge_attr not in self.edge_values: raise KeyError("Graph has no edge attribute {edge_attr!r}") @@ -532,14 +533,20 @@ def _get_plc_graph( is_multigraph=self.is_multigraph(), is_symmetric=not self.is_directed(), ), - src_or_offset_array=self.row_indices, - dst_or_index_array=self.col_indices, + src_or_offset_array=self.src_indices, + dst_or_index_array=self.dst_indices, weight_array=edge_array, store_transposed=store_transposed, renumber=False, do_expensive_check=False, ) + def _degrees_array(self): + degrees = cp.bincount(self.src_indices, minlength=self._N) + if self.is_directed(): + degrees += cp.bincount(self.dst_indices, minlength=self._N) + return degrees + def _nodeiter_to_iter(self, node_ids: Iterable[IndexValue]) -> Iterable[NodeKey]: """Convert an iterable of node IDs to an iterable of node keys.""" if (id_to_key := self.id_to_key) is not None: @@ -551,6 +558,14 @@ def _nodearray_to_list(self, node_ids: cp.ndarray[IndexValue]) -> list[NodeKey]: return node_ids.tolist() return list(self._nodeiter_to_iter(node_ids.tolist())) + def _nodearray_to_dict( + self, values: cp.ndarray[NodeValue] + ) -> dict[NodeKey, NodeValue]: + it = enumerate(values.tolist()) + if (id_to_key := self.id_to_key) is not None: + return {id_to_key[key]: val for key, val in it} + return dict(it) + def _nodearrays_to_dict( self, node_ids: cp.ndarray[IndexValue], values: cp.ndarray[NodeValue] ) -> dict[NodeKey, NodeValue]: diff --git a/python/nx-cugraph/nx_cugraph/classes/multigraph.py b/python/nx-cugraph/nx_cugraph/classes/multigraph.py index 499ca7ce212..3d90861a328 100644 --- a/python/nx-cugraph/nx_cugraph/classes/multigraph.py +++ b/python/nx-cugraph/nx_cugraph/classes/multigraph.py @@ -67,8 +67,8 @@ class MultiGraph(Graph): def from_coo( cls, N: int, - row_indices: cp.ndarray[IndexValue], - col_indices: cp.ndarray[IndexValue], + src_indices: cp.ndarray[IndexValue], + dst_indices: cp.ndarray[IndexValue], edge_indices: cp.ndarray[IndexValue] | None = None, edge_values: dict[AttrKey, cp.ndarray[EdgeValue]] | None = None, edge_masks: dict[AttrKey, cp.ndarray[bool]] | None = None, @@ -82,8 +82,8 @@ def from_coo( ) -> MultiGraph: new_graph = super().from_coo( N, - row_indices, - col_indices, + src_indices, + dst_indices, edge_values, edge_masks, node_values, @@ -97,7 +97,7 @@ def from_coo( # Easy and fast sanity checks if ( new_graph.edge_keys is not None - and len(new_graph.edge_keys) != row_indices.size + and len(new_graph.edge_keys) != src_indices.size ): raise ValueError return new_graph @@ -106,7 +106,7 @@ def from_coo( def from_csr( cls, indptr: cp.ndarray[IndexValue], - col_indices: cp.ndarray[IndexValue], + dst_indices: cp.ndarray[IndexValue], edge_indices: cp.ndarray[IndexValue] | None = None, edge_values: dict[AttrKey, cp.ndarray[EdgeValue]] | None = None, edge_masks: dict[AttrKey, cp.ndarray[bool]] | None = None, @@ -119,14 +119,14 @@ def from_csr( **attr, ) -> MultiGraph: N = indptr.size - 1 - row_indices = cp.array( + src_indices = cp.array( # cp.repeat is slow to use here, so use numpy instead np.repeat(np.arange(N, dtype=np.int32), cp.diff(indptr).get()) ) return cls.from_coo( N, - row_indices, - col_indices, + src_indices, + dst_indices, edge_indices, edge_values, edge_masks, @@ -142,7 +142,7 @@ def from_csr( def from_csc( cls, indptr: cp.ndarray[IndexValue], - row_indices: cp.ndarray[IndexValue], + src_indices: cp.ndarray[IndexValue], edge_indices: cp.ndarray[IndexValue] | None = None, edge_values: dict[AttrKey, cp.ndarray[EdgeValue]] | None = None, edge_masks: dict[AttrKey, cp.ndarray[bool]] | None = None, @@ -155,14 +155,14 @@ def from_csc( **attr, ) -> MultiGraph: N = indptr.size - 1 - col_indices = cp.array( + dst_indices = cp.array( # cp.repeat is slow to use here, so use numpy instead np.repeat(np.arange(N, dtype=np.int32), cp.diff(indptr).get()) ) return cls.from_coo( N, - row_indices, - col_indices, + src_indices, + dst_indices, edge_indices, edge_values, edge_masks, @@ -180,7 +180,7 @@ def from_dcsr( N: int, compressed_rows: cp.ndarray[IndexValue], indptr: cp.ndarray[IndexValue], - col_indices: cp.ndarray[IndexValue], + dst_indices: cp.ndarray[IndexValue], edge_indices: cp.ndarray[IndexValue] | None = None, edge_values: dict[AttrKey, cp.ndarray[EdgeValue]] | None = None, edge_masks: dict[AttrKey, cp.ndarray[bool]] | None = None, @@ -192,14 +192,14 @@ def from_dcsr( edge_keys: list[EdgeKey] | None = None, **attr, ) -> MultiGraph: - row_indices = cp.array( + src_indices = cp.array( # cp.repeat is slow to use here, so use numpy instead np.repeat(compressed_rows.get(), cp.diff(indptr).get()) ) return cls.from_coo( N, - row_indices, - col_indices, + src_indices, + dst_indices, edge_indices, edge_values, edge_masks, @@ -217,7 +217,7 @@ def from_dcsc( N: int, compressed_cols: cp.ndarray[IndexValue], indptr: cp.ndarray[IndexValue], - row_indices: cp.ndarray[IndexValue], + src_indices: cp.ndarray[IndexValue], edge_indices: cp.ndarray[IndexValue] | None = None, edge_values: dict[AttrKey, cp.ndarray[EdgeValue]] | None = None, edge_masks: dict[AttrKey, cp.ndarray[bool]] | None = None, @@ -229,14 +229,14 @@ def from_dcsc( edge_keys: list[EdgeKey] | None = None, **attr, ) -> Graph: - col_indices = cp.array( + dst_indices = cp.array( # cp.repeat is slow to use here, so use numpy instead np.repeat(compressed_cols.get(), cp.diff(indptr).get()) ) return cls.from_coo( N, - row_indices, - col_indices, + src_indices, + dst_indices, edge_indices, edge_values, edge_masks, @@ -330,7 +330,7 @@ def get_edge_data( return default except TypeError: return default - mask = (self.row_indices == u) & (self.col_indices == v) + mask = (self.src_indices == u) & (self.dst_indices == v) if not mask.any(): return default if self.edge_keys is None: @@ -376,7 +376,7 @@ def has_edge(self, u: NodeKey, v: NodeKey, key: EdgeKey | None = None) -> bool: v = self.key_to_id[v] except KeyError: return False - mask = (self.row_indices == u) & (self.col_indices == v) + mask = (self.src_indices == u) & (self.dst_indices == v) if key is None or (self.edge_indices is None and self.edge_keys is None): return bool(mask.any()) if self.edge_keys is None: @@ -405,9 +405,8 @@ def to_undirected(self, as_view: bool = False) -> MultiGraph: def _copy(self, as_view: bool, cls: type[Graph], reverse: bool = False): # DRY warning: see also Graph._copy - indptr = self.indptr - row_indices = self.row_indices - col_indices = self.col_indices + src_indices = self.src_indices + dst_indices = self.dst_indices edge_indices = self.edge_indices edge_values = self.edge_values edge_masks = self.edge_masks @@ -417,9 +416,8 @@ def _copy(self, as_view: bool, cls: type[Graph], reverse: bool = False): id_to_key = None if key_to_id is None else self._id_to_key edge_keys = self.edge_keys if not as_view: - indptr = indptr.copy() - row_indices = row_indices.copy() - col_indices = col_indices.copy() + src_indices = src_indices.copy() + dst_indices = dst_indices.copy() edge_indices = edge_indices.copy() edge_values = {key: val.copy() for key, val in edge_values.items()} edge_masks = {key: val.copy() for key, val in edge_masks.items()} @@ -432,11 +430,11 @@ def _copy(self, as_view: bool, cls: type[Graph], reverse: bool = False): if edge_keys is not None: edge_keys = edge_keys.copy() if reverse: - row_indices, col_indices = col_indices, row_indices + src_indices, dst_indices = dst_indices, src_indices rv = cls.from_coo( - indptr, - row_indices, - col_indices, + self._N, + src_indices, + dst_indices, edge_indices, edge_values, edge_masks, diff --git a/python/nx-cugraph/nx_cugraph/convert.py b/python/nx-cugraph/nx_cugraph/convert.py index e82286e5e29..d117c8e5c03 100644 --- a/python/nx-cugraph/nx_cugraph/convert.py +++ b/python/nx-cugraph/nx_cugraph/convert.py @@ -266,12 +266,12 @@ def from_networkx( else: col_iter = map(key_to_id.__getitem__, col_iter) if graph.is_multigraph(): - col_indices = np.fromiter(col_iter, np.int32) + dst_indices = np.fromiter(col_iter, np.int32) num_multiedges = np.fromiter( map(len, concat(map(dict.values, adj.values()))), np.int32 ) # cp.repeat is slow to use here, so use numpy instead - col_indices = cp.array(np.repeat(col_indices, num_multiedges)) + dst_indices = cp.array(np.repeat(dst_indices, num_multiedges)) # Determine edge keys and edge ids for multigraphs edge_keys = list(concat(concat(map(dict.values, adj.values())))) edge_indices = cp.fromiter( @@ -281,7 +281,7 @@ def from_networkx( if edge_keys == edge_indices.tolist(): edge_keys = None # Prefer edge_indices else: - col_indices = cp.fromiter(col_iter, np.int32) + dst_indices = cp.fromiter(col_iter, np.int32) edge_values = {} edge_masks = {} @@ -353,12 +353,12 @@ def from_networkx( # if vals.ndim > 1: ... # cp.repeat is slow to use here, so use numpy instead - row_indices = np.repeat( + src_indices = np.repeat( np.arange(N, dtype=np.int32), np.fromiter(map(len, adj.values()), np.int32) ) if graph.is_multigraph(): - row_indices = np.repeat(row_indices, num_multiedges) - row_indices = cp.array(row_indices) + src_indices = np.repeat(src_indices, num_multiedges) + src_indices = cp.array(src_indices) node_values = {} node_masks = {} @@ -405,8 +405,8 @@ def from_networkx( klass = nxcg.MultiGraph rv = klass.from_coo( N, - row_indices, - col_indices, + src_indices, + dst_indices, edge_indices, edge_values, edge_masks, @@ -422,8 +422,8 @@ def from_networkx( klass = nxcg.Graph rv = klass.from_coo( N, - row_indices, - col_indices, + src_indices, + dst_indices, edge_values, edge_masks, node_values, @@ -496,23 +496,23 @@ def to_networkx(G: nxcg.Graph) -> nx.Graph: else: rv.add_nodes_from(range(len(G))) - row_indices = G.row_indices - col_indices = G.col_indices + src_indices = G.src_indices + dst_indices = G.dst_indices edge_values = G.edge_values edge_masks = G.edge_masks if edge_values and not G.is_directed(): # Only add upper triangle of the adjacency matrix so we don't double-add edges - mask = row_indices <= col_indices - row_indices = row_indices[mask] - col_indices = col_indices[mask] + mask = src_indices <= dst_indices + src_indices = src_indices[mask] + dst_indices = dst_indices[mask] edge_values = {k: v[mask] for k, v in edge_values.items()} if edge_masks: edge_masks = {k: v[mask] for k, v in edge_masks.items()} - row_indices = row_iter = row_indices.tolist() - col_indices = col_iter = col_indices.tolist() + src_indices = row_iter = src_indices.tolist() + dst_indices = col_iter = dst_indices.tolist() if id_to_key is not None: - row_iter = map(id_to_key.__getitem__, row_indices) - col_iter = map(id_to_key.__getitem__, col_indices) + row_iter = map(id_to_key.__getitem__, src_indices) + col_iter = map(id_to_key.__getitem__, dst_indices) if G.is_multigraph() and (G.edge_keys is not None or G.edge_indices is not None): if G.edge_keys is not None: edge_keys = G.edge_keys diff --git a/python/nx-cugraph/nx_cugraph/tests/test_convert.py b/python/nx-cugraph/nx_cugraph/tests/test_convert.py index 83820f7621f..1a71b796861 100644 --- a/python/nx-cugraph/nx_cugraph/tests/test_convert.py +++ b/python/nx-cugraph/nx_cugraph/tests/test_convert.py @@ -71,8 +71,8 @@ def test_convert(graph_class): ]: # All edges have "x" attribute, so all kwargs are equivalent Gcg = nxcg.from_networkx(G, **kwargs) - cp.testing.assert_array_equal(Gcg.row_indices, [0, 1]) - cp.testing.assert_array_equal(Gcg.col_indices, [1, 0]) + cp.testing.assert_array_equal(Gcg.src_indices, [0, 1]) + cp.testing.assert_array_equal(Gcg.dst_indices, [1, 0]) cp.testing.assert_array_equal(Gcg.edge_values["x"], [2, 2]) assert len(Gcg.edge_values) == 1 assert Gcg.edge_masks == {} @@ -86,8 +86,8 @@ def test_convert(graph_class): # Structure-only graph (no edge attributes) Gcg = nxcg.from_networkx(G, preserve_node_attrs=True) - cp.testing.assert_array_equal(Gcg.row_indices, [0, 1]) - cp.testing.assert_array_equal(Gcg.col_indices, [1, 0]) + cp.testing.assert_array_equal(Gcg.src_indices, [0, 1]) + cp.testing.assert_array_equal(Gcg.dst_indices, [1, 0]) cp.testing.assert_array_equal(Gcg.node_values["foo"], [10, 20]) assert Gcg.edge_values == Gcg.edge_masks == {} H = nxcg.to_networkx(Gcg) @@ -99,8 +99,8 @@ def test_convert(graph_class): # Fill completely missing attribute with default value Gcg = nxcg.from_networkx(G, edge_attrs={"y": 0}) - cp.testing.assert_array_equal(Gcg.row_indices, [0, 1]) - cp.testing.assert_array_equal(Gcg.col_indices, [1, 0]) + cp.testing.assert_array_equal(Gcg.src_indices, [0, 1]) + cp.testing.assert_array_equal(Gcg.dst_indices, [1, 0]) cp.testing.assert_array_equal(Gcg.edge_values["y"], [0, 0]) assert len(Gcg.edge_values) == 1 assert Gcg.edge_masks == Gcg.node_values == Gcg.node_masks == {} @@ -111,8 +111,8 @@ def test_convert(graph_class): # If attribute is completely missing (and no default), then just ignore it Gcg = nxcg.from_networkx(G, edge_attrs={"y": None}) - cp.testing.assert_array_equal(Gcg.row_indices, [0, 1]) - cp.testing.assert_array_equal(Gcg.col_indices, [1, 0]) + cp.testing.assert_array_equal(Gcg.src_indices, [0, 1]) + cp.testing.assert_array_equal(Gcg.dst_indices, [1, 0]) assert sorted(Gcg.edge_values) == sorted(Gcg.edge_masks) == [] H = nxcg.to_networkx(Gcg) assert list(H.edges(data=True)) == [(0, 1, {})] @@ -123,8 +123,8 @@ def test_convert(graph_class): # Some edges are missing 'x' attribute; need to use a mask for kwargs in [{"preserve_edge_attrs": True}, {"edge_attrs": {"x": None}}]: Gcg = nxcg.from_networkx(G, **kwargs) - cp.testing.assert_array_equal(Gcg.row_indices, [0, 0, 1, 2]) - cp.testing.assert_array_equal(Gcg.col_indices, [1, 2, 0, 0]) + cp.testing.assert_array_equal(Gcg.src_indices, [0, 0, 1, 2]) + cp.testing.assert_array_equal(Gcg.dst_indices, [1, 2, 0, 0]) assert sorted(Gcg.edge_values) == sorted(Gcg.edge_masks) == ["x"] cp.testing.assert_array_equal(Gcg.edge_masks["x"], [True, False, True, False]) cp.testing.assert_array_equal(Gcg.edge_values["x"][Gcg.edge_masks["x"]], [2, 2]) @@ -160,8 +160,8 @@ def test_convert(graph_class): ]: Gcg = nxcg.from_networkx(G, **kwargs) assert Gcg.id_to_key == [10, 20, 30] # Remap node IDs to 0, 1, ... - cp.testing.assert_array_equal(Gcg.row_indices, [0, 0, 1, 2]) - cp.testing.assert_array_equal(Gcg.col_indices, [1, 2, 0, 0]) + cp.testing.assert_array_equal(Gcg.src_indices, [0, 0, 1, 2]) + cp.testing.assert_array_equal(Gcg.dst_indices, [1, 2, 0, 0]) cp.testing.assert_array_equal(Gcg.edge_values["x"], [1, 2, 1, 2]) assert sorted(Gcg.edge_masks) == ["y"] cp.testing.assert_array_equal(Gcg.edge_masks["y"], [False, True, False, True]) @@ -181,8 +181,8 @@ def test_convert(graph_class): ]: Gcg = nxcg.from_networkx(G, **kwargs) assert Gcg.id_to_key == [10, 20, 30] # Remap node IDs to 0, 1, ... - cp.testing.assert_array_equal(Gcg.row_indices, [0, 0, 1, 2]) - cp.testing.assert_array_equal(Gcg.col_indices, [1, 2, 0, 0]) + cp.testing.assert_array_equal(Gcg.src_indices, [0, 0, 1, 2]) + cp.testing.assert_array_equal(Gcg.dst_indices, [1, 2, 0, 0]) cp.testing.assert_array_equal(Gcg.node_values["foo"], [100, 200, 300]) assert sorted(Gcg.node_masks) == ["bar"] cp.testing.assert_array_equal(Gcg.node_masks["bar"], [False, True, False]) @@ -202,8 +202,8 @@ def test_convert(graph_class): ]: Gcg = nxcg.from_networkx(G, **kwargs) assert Gcg.id_to_key == [10, 20, 30] # Remap node IDs to 0, 1, ... - cp.testing.assert_array_equal(Gcg.row_indices, [0, 0, 1, 2]) - cp.testing.assert_array_equal(Gcg.col_indices, [1, 2, 0, 0]) + cp.testing.assert_array_equal(Gcg.src_indices, [0, 0, 1, 2]) + cp.testing.assert_array_equal(Gcg.dst_indices, [1, 2, 0, 0]) cp.testing.assert_array_equal(Gcg.node_values["bar"], [0, 1000, 0]) assert Gcg.node_masks == {} diff --git a/python/nx-cugraph/nx_cugraph/tests/test_match_api.py b/python/nx-cugraph/nx_cugraph/tests/test_match_api.py index ecfda1397db..a654ff343ed 100644 --- a/python/nx-cugraph/nx_cugraph/tests/test_match_api.py +++ b/python/nx-cugraph/nx_cugraph/tests/test_match_api.py @@ -31,6 +31,9 @@ def test_match_signature_and_names(): if is_nx_30_or_31 and name in {"louvain_communities"}: continue + if name not in nx_backends._registered_algorithms: + print(f"{name} not dispatched from networkx") + continue dispatchable_func = nx_backends._registered_algorithms[name] # nx version >=3.2 uses orig_func, version >=3.0,<3.2 uses _orig_func if is_nx_30_or_31: diff --git a/python/nx-cugraph/pyproject.toml b/python/nx-cugraph/pyproject.toml index 2478a02df9b..9fec8fa0242 100644 --- a/python/nx-cugraph/pyproject.toml +++ b/python/nx-cugraph/pyproject.toml @@ -81,7 +81,10 @@ float_to_top = true default_section = "THIRDPARTY" known_first_party = "nx_cugraph" line_length = 88 -extend_skip_glob = ["nx_cugraph/__init__.py"] +extend_skip_glob = [ + "nx_cugraph/__init__.py", + "nx_cugraph/classes/__init__.py", +] [tool.pytest.ini_options] minversion = "6.0" From c90fd9480644c2ef68cf67041b97160fc174313e Mon Sep 17 00:00:00 2001 From: GALI PREM SAGAR Date: Thu, 26 Oct 2023 07:40:03 -0500 Subject: [PATCH 059/111] Unpin `dask` and `distributed` for `23.12` development (#3953) This PR relaxes `dask` and `distributed` versions pinning for `23.12` development. xref: https://github.com/rapidsai/cudf/pull/14320 Authors: - GALI PREM SAGAR (https://github.com/galipremsagar) Approvers: - Brad Rees (https://github.com/BradReesWork) - Ray Douglass (https://github.com/raydouglass) - https://github.com/jakirkham URL: https://github.com/rapidsai/cugraph/pull/3953 --- ci/test_wheel_cugraph.sh | 2 +- conda/environments/all_cuda-118_arch-x86_64.yaml | 2 +- conda/environments/all_cuda-120_arch-x86_64.yaml | 2 +- conda/recipes/cugraph-pyg/meta.yaml | 2 +- conda/recipes/cugraph-service/meta.yaml | 2 +- conda/recipes/cugraph/meta.yaml | 6 +++--- dependencies.yaml | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/ci/test_wheel_cugraph.sh b/ci/test_wheel_cugraph.sh index ac18459128a..f9e2aa6d8da 100755 --- a/ci/test_wheel_cugraph.sh +++ b/ci/test_wheel_cugraph.sh @@ -9,6 +9,6 @@ RAPIDS_PY_WHEEL_NAME="pylibcugraph_${RAPIDS_PY_CUDA_SUFFIX}" rapids-download-whe python -m pip install --no-deps ./local-pylibcugraph-dep/pylibcugraph*.whl # Always install latest dask for testing -python -m pip install git+https://github.com/dask/dask.git@2023.9.2 git+https://github.com/dask/distributed.git@2023.9.2 +python -m pip install git+https://github.com/dask/dask.git@main git+https://github.com/dask/distributed.git@main ./ci/test_wheel.sh cugraph python/cugraph diff --git a/conda/environments/all_cuda-118_arch-x86_64.yaml b/conda/environments/all_cuda-118_arch-x86_64.yaml index 71a8d10b935..2f3a9c988cf 100644 --- a/conda/environments/all_cuda-118_arch-x86_64.yaml +++ b/conda/environments/all_cuda-118_arch-x86_64.yaml @@ -20,7 +20,7 @@ dependencies: - cupy>=12.0.0 - cxx-compiler - cython>=3.0.0 -- dask-core==2023.9.2 +- dask-core>=2023.9.2 - dask-cuda==23.12.* - dask-cudf==23.12.* - dask>=2023.7.1 diff --git a/conda/environments/all_cuda-120_arch-x86_64.yaml b/conda/environments/all_cuda-120_arch-x86_64.yaml index 9179fba788e..31ff503e682 100644 --- a/conda/environments/all_cuda-120_arch-x86_64.yaml +++ b/conda/environments/all_cuda-120_arch-x86_64.yaml @@ -20,7 +20,7 @@ dependencies: - cupy>=12.0.0 - cxx-compiler - cython>=3.0.0 -- dask-core==2023.9.2 +- dask-core>=2023.9.2 - dask-cuda==23.12.* - dask-cudf==23.12.* - dask>=2023.7.1 diff --git a/conda/recipes/cugraph-pyg/meta.yaml b/conda/recipes/cugraph-pyg/meta.yaml index 1dc5a75c41b..f4b2e9a4ee9 100644 --- a/conda/recipes/cugraph-pyg/meta.yaml +++ b/conda/recipes/cugraph-pyg/meta.yaml @@ -26,7 +26,7 @@ requirements: - python - scikit-build >=0.13.1 run: - - distributed ==2023.9.2 + - distributed >=2023.9.2 - numba >=0.57 - numpy >=1.21 - python diff --git a/conda/recipes/cugraph-service/meta.yaml b/conda/recipes/cugraph-service/meta.yaml index 2daf0438351..3d001e83e1e 100644 --- a/conda/recipes/cugraph-service/meta.yaml +++ b/conda/recipes/cugraph-service/meta.yaml @@ -59,7 +59,7 @@ outputs: - cupy >=12.0.0 - dask-cuda ={{ minor_version }} - dask-cudf ={{ minor_version }} - - distributed ==2023.9.2 + - distributed >=2023.9.2 - numba >=0.57 - numpy >=1.21 - python diff --git a/conda/recipes/cugraph/meta.yaml b/conda/recipes/cugraph/meta.yaml index f9bf54a2ef4..3691db508d9 100644 --- a/conda/recipes/cugraph/meta.yaml +++ b/conda/recipes/cugraph/meta.yaml @@ -76,9 +76,9 @@ requirements: - cupy >=12.0.0 - dask-cuda ={{ minor_version }} - dask-cudf ={{ minor_version }} - - dask ==2023.9.2 - - dask-core ==2023.9.2 - - distributed ==2023.9.2 + - dask >=2023.9.2 + - dask-core >=2023.9.2 + - distributed >=2023.9.2 - fsspec>=0.6.0 - libcugraph ={{ version }} - pylibcugraph ={{ version }} diff --git a/dependencies.yaml b/dependencies.yaml index 736aa2e019f..b127d9bd29e 100644 --- a/dependencies.yaml +++ b/dependencies.yaml @@ -385,7 +385,7 @@ dependencies: - output_types: conda packages: - aiohttp - - &dask-core_conda dask-core==2023.9.2 + - &dask-core_conda dask-core>=2023.9.2 - fsspec>=0.6.0 - libcudf==23.12.* - requests From 4e39f209392ad604c747dc92a8e28b398a4ef1c7 Mon Sep 17 00:00:00 2001 From: Jake Awe <50372925+AyodeAwe@users.noreply.github.com> Date: Fri, 27 Oct 2023 02:25:07 -0500 Subject: [PATCH 060/111] Download `xml` docs artifact through CloudFront endpoint (#3955) * local docs build workaround * pipe output --- ci/build_docs.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci/build_docs.sh b/ci/build_docs.sh index e774a6f9871..3f97f652d41 100755 --- a/ci/build_docs.sh +++ b/ci/build_docs.sh @@ -44,7 +44,7 @@ for PROJECT in libcugraphops libwholegraph; do rapids-logger "Download ${PROJECT} xml_tar" TMP_DIR=$(mktemp -d) export XML_DIR_${PROJECT^^}="$TMP_DIR" - aws s3 cp --only-show-errors "s3://rapidsai-docs/${PROJECT}/xml_tar/${RAPIDS_VERSION_NUMBER}/xml.tar.gz" - | tar xzf - -C "${TMP_DIR}" + curl "https://d1664dvumjb44w.cloudfront.net/${PROJECT}/xml_tar/${RAPIDS_VERSION_NUMBER}/xml.tar.gz" | tar -xzf - -C "${TMP_DIR}" done rapids-logger "Build CPP docs" From a57f779dc1c1ab8ea2575210747eb1066db5f9cf Mon Sep 17 00:00:00 2001 From: Seunghwa Kang <45857425+seunghwak@users.noreply.github.com> Date: Fri, 27 Oct 2023 11:59:57 -0700 Subject: [PATCH 061/111] Update the neighbor intersection primitive to support edge masking. (#3550) This PR updates the detail::nbr_intersection() primitive and the per_v_pair_transform_dst_nbr_intersection primitive (which calls the detail::nbr_intersection primitive) to work with edge masking (graph_view_t object with attached edge mask). Several utility functions are updated to support edge masking as well to support primitive updates and testing. This PR is necessary to implement K-truss with the cuGraph C++ primitives. See https://github.com/rapidsai/cugraph/issues/3446#issuecomment-1499649664 for additional details. Authors: - Seunghwa Kang (https://github.com/seunghwak) - Brad Rees (https://github.com/BradReesWork) - Chuck Hastings (https://github.com/ChuckHastings) Approvers: - Chuck Hastings (https://github.com/ChuckHastings) - Naim (https://github.com/naimnv) - Joseph Nke (https://github.com/jnke2016) URL: https://github.com/rapidsai/cugraph/pull/3550 --- .../detail/decompress_edge_partition.cuh | 138 ++- ...ge_partition_edge_property_device_view.cuh | 12 +- cpp/include/cugraph/graph_mask.hpp | 382 -------- cpp/include/cugraph/graph_view.hpp | 24 +- .../cugraph/utilities/device_functors.cuh | 55 +- cpp/include/cugraph/utilities/mask_utils.cuh | 150 ++++ .../cugraph/utilities/packed_bool_utils.hpp | 7 + cpp/src/prims/detail/nbr_intersection.cuh | 835 +++++++++++------- .../detail/optional_dataframe_buffer.hpp | 1 + cpp/src/prims/kv_store.cuh | 6 +- ..._v_pair_transform_dst_nbr_intersection.cuh | 50 +- ...r_v_random_select_transform_outgoing_e.cuh | 2 +- ...m_reduce_dst_key_aggregated_outgoing_e.cuh | 10 +- ...t_nbr_intersection_of_e_endpoints_by_v.cuh | 35 +- cpp/src/structure/coarsen_graph_impl.cuh | 16 +- .../structure/decompress_to_edgelist_impl.cuh | 56 +- cpp/src/structure/graph_impl.cuh | 15 +- cpp/src/structure/graph_view_impl.cuh | 79 +- cpp/tests/CMakeLists.txt | 4 - ...r_v_pair_transform_dst_nbr_intersection.cu | 128 ++- ...transform_dst_nbr_weighted_intersection.cu | 77 +- cpp/tests/structure/graph_mask_test.cpp | 64 -- 22 files changed, 1085 insertions(+), 1061 deletions(-) delete mode 100644 cpp/include/cugraph/graph_mask.hpp create mode 100644 cpp/include/cugraph/utilities/mask_utils.cuh delete mode 100644 cpp/tests/structure/graph_mask_test.cpp diff --git a/cpp/include/cugraph/detail/decompress_edge_partition.cuh b/cpp/include/cugraph/detail/decompress_edge_partition.cuh index cd8739114f2..4b256a0413a 100644 --- a/cpp/include/cugraph/detail/decompress_edge_partition.cuh +++ b/cpp/include/cugraph/detail/decompress_edge_partition.cuh @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -46,7 +47,7 @@ __global__ void decompress_to_edgelist_mid_degree( edge_partition_device_view_t edge_partition, vertex_t major_range_first, vertex_t major_range_last, - vertex_t* majors) + raft::device_span majors) { auto const tid = threadIdx.x + blockIdx.x * blockDim.x; static_assert(decompress_edge_partition_block_size % raft::warp_size() == 0); @@ -76,7 +77,7 @@ __global__ void decompress_to_edgelist_high_degree( edge_partition_device_view_t edge_partition, vertex_t major_range_first, vertex_t major_range_last, - vertex_t* majors) + raft::device_span majors) { auto major_start_offset = static_cast(major_range_first - edge_partition.major_range_first()); @@ -103,10 +104,19 @@ template void decompress_edge_partition_to_fill_edgelist_majors( raft::handle_t const& handle, edge_partition_device_view_t edge_partition, - vertex_t* majors, + std::optional> + edge_partition_mask_view, + raft::device_span majors, std::optional> const& segment_offsets) { - auto execution_policy = handle.get_thrust_policy(); + auto tmp_buffer = edge_partition_mask_view + ? std::make_optional>( + edge_partition.number_of_edges(), handle.get_stream()) + : std::nullopt; + + auto output_buffer = + tmp_buffer ? raft::device_span((*tmp_buffer).data(), (*tmp_buffer).size()) : majors; + if (segment_offsets) { // FIXME: we may further improve performance by 1) concurrently running kernels on different // segments; 2) individually tuning block sizes for different segments; and 3) adding one more @@ -124,7 +134,7 @@ void decompress_edge_partition_to_fill_edgelist_majors( edge_partition, edge_partition.major_range_first(), edge_partition.major_range_first() + (*segment_offsets)[1], - majors); + output_buffer); } if ((*segment_offsets)[2] - (*segment_offsets)[1] > 0) { raft::grid_1d_warp_t update_grid((*segment_offsets)[2] - (*segment_offsets)[1], @@ -138,49 +148,63 @@ void decompress_edge_partition_to_fill_edgelist_majors( edge_partition, edge_partition.major_range_first() + (*segment_offsets)[1], edge_partition.major_range_first() + (*segment_offsets)[2], - majors); + output_buffer); } if ((*segment_offsets)[3] - (*segment_offsets)[2] > 0) { thrust::for_each( - execution_policy, + handle.get_thrust_policy(), thrust::make_counting_iterator(edge_partition.major_range_first()) + (*segment_offsets)[2], thrust::make_counting_iterator(edge_partition.major_range_first()) + (*segment_offsets)[3], - [edge_partition, majors] __device__(auto major) { + [edge_partition, output_buffer] __device__(auto major) { auto major_offset = edge_partition.major_offset_from_major_nocheck(major); auto local_degree = edge_partition.local_degree(major_offset); auto local_offset = edge_partition.local_offset(major_offset); - thrust::fill( - thrust::seq, majors + local_offset, majors + local_offset + local_degree, major); + thrust::fill(thrust::seq, + output_buffer.begin() + local_offset, + output_buffer.begin() + local_offset + local_degree, + major); }); } if (edge_partition.dcs_nzd_vertex_count() && (*(edge_partition.dcs_nzd_vertex_count()) > 0)) { thrust::for_each( - execution_policy, + handle.get_thrust_policy(), thrust::make_counting_iterator(vertex_t{0}), thrust::make_counting_iterator(*(edge_partition.dcs_nzd_vertex_count())), - [edge_partition, major_start_offset = (*segment_offsets)[3], majors] __device__(auto idx) { + [edge_partition, major_start_offset = (*segment_offsets)[3], output_buffer] __device__( + auto idx) { auto major = *(edge_partition.major_from_major_hypersparse_idx_nocheck(idx)); auto major_idx = major_start_offset + idx; // major_offset != major_idx in the hypersparse region auto local_degree = edge_partition.local_degree(major_idx); auto local_offset = edge_partition.local_offset(major_idx); - thrust::fill( - thrust::seq, majors + local_offset, majors + local_offset + local_degree, major); + thrust::fill(thrust::seq, + output_buffer.begin() + local_offset, + output_buffer.begin() + local_offset + local_degree, + major); }); } } else { - thrust::for_each( - execution_policy, - thrust::make_counting_iterator(edge_partition.major_range_first()), - thrust::make_counting_iterator(edge_partition.major_range_first()) + - edge_partition.major_range_size(), - [edge_partition, majors] __device__(auto major) { - auto major_offset = edge_partition.major_offset_from_major_nocheck(major); - auto local_degree = edge_partition.local_degree(major_offset); - auto local_offset = edge_partition.local_offset(major_offset); - thrust::fill( - thrust::seq, majors + local_offset, majors + local_offset + local_degree, major); - }); + thrust::for_each(handle.get_thrust_policy(), + thrust::make_counting_iterator(edge_partition.major_range_first()), + thrust::make_counting_iterator(edge_partition.major_range_first()) + + edge_partition.major_range_size(), + [edge_partition, output_buffer] __device__(auto major) { + auto major_offset = edge_partition.major_offset_from_major_nocheck(major); + auto local_degree = edge_partition.local_degree(major_offset); + auto local_offset = edge_partition.local_offset(major_offset); + thrust::fill(thrust::seq, + output_buffer.begin() + local_offset, + output_buffer.begin() + local_offset + local_degree, + major); + }); + } + + if (tmp_buffer) { + copy_if_mask_set(handle, + (*tmp_buffer).begin(), + (*tmp_buffer).end(), + (*edge_partition_mask_view).value_first(), + majors.begin()); } } @@ -192,33 +216,59 @@ void decompress_edge_partition_to_edgelist( edge_partition_weight_view, std::optional> edge_partition_id_view, - vertex_t* edgelist_majors /* [OUT] */, - vertex_t* edgelist_minors /* [OUT] */, - std::optional edgelist_weights /* [OUT] */, - std::optional edgelist_ids /* [OUT] */, + std::optional> + edge_partition_mask_view, + raft::device_span edgelist_majors /* [OUT] */, + raft::device_span edgelist_minors /* [OUT] */, + std::optional> edgelist_weights /* [OUT] */, + std::optional> edgelist_ids /* [OUT] */, std::optional> const& segment_offsets) { auto number_of_edges = edge_partition.number_of_edges(); decompress_edge_partition_to_fill_edgelist_majors( - handle, edge_partition, edgelist_majors, segment_offsets); - thrust::copy(handle.get_thrust_policy(), - edge_partition.indices(), - edge_partition.indices() + number_of_edges, - edgelist_minors); - if (edge_partition_id_view) { - assert(edgelist_ids.has_value()); + handle, edge_partition, edge_partition_mask_view, edgelist_majors, segment_offsets); + if (edge_partition_mask_view) { + copy_if_mask_set(handle, + edge_partition.indices(), + edge_partition.indices() + number_of_edges, + (*edge_partition_mask_view).value_first(), + edgelist_minors.begin()); + } else { thrust::copy(handle.get_thrust_policy(), - (*edge_partition_id_view).value_first(), - (*edge_partition_id_view).value_first() + number_of_edges, - (*edgelist_ids)); + edge_partition.indices(), + edge_partition.indices() + number_of_edges, + edgelist_minors.begin()); } if (edge_partition_weight_view) { assert(edgelist_weights.has_value()); - thrust::copy(handle.get_thrust_policy(), - (*edge_partition_weight_view).value_first(), - (*edge_partition_weight_view).value_first() + number_of_edges, - (*edgelist_weights)); + if (edge_partition_mask_view) { + copy_if_mask_set(handle, + (*edge_partition_weight_view).value_first(), + (*edge_partition_weight_view).value_first() + number_of_edges, + (*edge_partition_mask_view).value_first(), + (*edgelist_weights).begin()); + } else { + thrust::copy(handle.get_thrust_policy(), + (*edge_partition_weight_view).value_first(), + (*edge_partition_weight_view).value_first() + number_of_edges, + (*edgelist_weights).begin()); + } + } + if (edge_partition_id_view) { + assert(edgelist_ids.has_value()); + if (edge_partition_mask_view) { + copy_if_mask_set(handle, + (*edge_partition_id_view).value_first(), + (*edge_partition_id_view).value_first() + number_of_edges, + (*edge_partition_mask_view).value_first(), + (*edgelist_ids).begin()); + } else { + thrust::copy(handle.get_thrust_policy(), + (*edge_partition_id_view).value_first(), + (*edge_partition_id_view).value_first() + number_of_edges, + (*edgelist_ids).begin()); + } } } diff --git a/cpp/include/cugraph/edge_partition_edge_property_device_view.cuh b/cpp/include/cugraph/edge_partition_edge_property_device_view.cuh index 18091567e38..e5b64b1e02f 100644 --- a/cpp/include/cugraph/edge_partition_edge_property_device_view.cuh +++ b/cpp/include/cugraph/edge_partition_edge_property_device_view.cuh @@ -33,8 +33,9 @@ template ::value_type> class edge_partition_edge_property_device_view_t { public: - using edge_type = edge_t; - using value_type = value_t; + using edge_type = edge_t; + using value_type = value_t; + static constexpr bool is_packed_bool = cugraph::is_packed_bool(); static constexpr bool has_packed_bool_element = cugraph::has_packed_bool_element(); @@ -53,7 +54,7 @@ class edge_partition_edge_property_device_view_t { value_first_ = view.value_firsts()[partition_idx]; } - __host__ __device__ ValueIterator value_first() { return value_first_; } + __host__ __device__ ValueIterator value_first() const { return value_first_; } __device__ value_t get(edge_t offset) const { @@ -173,8 +174,9 @@ class edge_partition_edge_property_device_view_t { template class edge_partition_edge_dummy_property_device_view_t { public: - using edge_type = edge_t; - using value_type = thrust::nullopt_t; + using edge_type = edge_t; + using value_type = thrust::nullopt_t; + static constexpr bool is_packed_bool = false; static constexpr bool has_packed_bool_element = false; diff --git a/cpp/include/cugraph/graph_mask.hpp b/cpp/include/cugraph/graph_mask.hpp deleted file mode 100644 index 2048d3692c7..00000000000 --- a/cpp/include/cugraph/graph_mask.hpp +++ /dev/null @@ -1,382 +0,0 @@ -/* - * Copyright (c) 2022-2023, NVIDIA CORPORATION. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include -#include - -#include -#include -#include -#include - -namespace cugraph { - -/** - * Compile-time fast lookup of log2(num_bits(mask_t)) to eliminate - * the log2 computation for powers of 2. - * @tparam mask_t - */ -template -__host__ __device__ constexpr int log_bits() -{ - switch (std::numeric_limits::digits) { - case 8: return 3; - case 16: return 4; - case 32: return 5; - case 64: return 6; - default: return log2(std::numeric_limits::digits); - } -} - -/** - * Uses bit-shifting to perform a fast mod operation. This - * is used to compute the index of a specific bit - * @tparam mask_t - * @tparam T - */ -template -__host__ __device__ int bit_mod(T numerator) -{ - return numerator & (std::numeric_limits::digits - 1); -} - -namespace detail { - -/** - * Sets the bit at location h in a one-hot encoded 32-bit int array - */ -template -__device__ __host__ inline void _set_bit(mask_type* arr, mask_type h) -{ - mask_type bit = bit_mod(h); - mask_type idx = h >> log_bits(); - atomicOr(arr + idx, 1 << bit); -} - -/** - * Unsets the bit at location h in a one-hot encoded 32-bit int array - */ -template -__device__ __host__ inline void _unset_bit(mask_type* arr, mask_type h) -{ - mask_type bit = bit_mod(h); - mask_type idx = h >> log_bits(); - atomicAnd(arr + idx, ~(1 << bit)); -} - -/** - * Returns whether or not bit at location h is nonzero in a one-hot - * encoded 32-bit in array. - */ -template -__device__ __host__ inline bool _is_set(mask_type* arr, mask_type h) -{ - mask_type bit = bit_mod(h); - mask_type idx = h >> log_bits(); - return arr[idx] >> bit & 1U; -} -}; // namespace detail - -/** - * Mask view to be used in device functions for reading and updating existing mask. - * This assumes the appropriate masks (vertex/edge) have already been initialized, - * since that will need to be done from the owning object. - * @tparam vertex_t - * @tparam edge_t - * @tparam mask_t - */ -template -struct graph_mask_view_t { - public: - graph_mask_view_t() = delete; - - graph_mask_view_t(vertex_t n_vertices, - edge_t n_edges, - std::optional>& vertices, - std::optional>& edges, - bool complement = false) - : n_vertices_(n_vertices), - n_edges_(n_edges), - complement_(complement), - vertices_(vertices), - edges_(edges) - { - } - - graph_mask_view_t(graph_mask_view_t const& other) = default; - using vertex_type = vertex_t; - using edge_type = edge_t; - using mask_type = mask_t; - using size_type = std::size_t; - - ~graph_mask_view_t() = default; - graph_mask_view_t(graph_mask_view_t&&) noexcept = default; - - graph_mask_view_t& operator=(graph_mask_view_t&&) noexcept = default; - graph_mask_view_t& operator=(graph_mask_view_t const& other) = default; - - /** - * Are masks complemeneted? - * - * - !complemented means masks are inclusive (masking in) - * - complemented means masks are exclusive (masking out) - */ - __host__ __device__ bool is_complemented() const { return complement_; } - - /** - * Has the edge mask been initialized? - */ - __host__ __device__ bool has_edge_mask() const { return edges_.has_value(); } - - /** - * Has the vertex mask been initialized? - */ - __host__ __device__ bool has_vertex_mask() const { return vertices_.has_value(); } - - /** - * Get the vertex mask - */ - __host__ __device__ std::optional> get_vertex_mask() const - { - return vertices_; - } - - /** - * Get the edge mask - */ - __host__ __device__ std::optional> get_edge_mask() const - { - return edges_; - } - - __host__ __device__ edge_t get_edge_mask_size() const { return n_edges_ >> log_bits(); } - - __host__ __device__ vertex_t get_vertex_mask_size() const - { - return n_vertices_ >> log_bits(); - } - - protected: - vertex_t n_vertices_; - edge_t n_edges_; - bool complement_{false}; - std::optional> vertices_{std::nullopt}; - std::optional> edges_{std::nullopt}; -}; // struct graph_mask_view_t - -/** - * An owning container object to manage separate bitmasks for - * filtering vertices and edges. A compliment setting - * determines whether the value of 1 for corresponding - * items means they should be masked in (included) or - * masked out (excluded). - * - * Creating this object does not allocate any memory on device. - * In order to start using and querying the masks, they will - * need to first be initialized. - * - * @tparam vertex_t - * @tparam edge_t - * @tparam mask_t - */ -template -struct graph_mask_t { - public: - using vertex_type = vertex_t; - using edge_type = edge_t; - using mask_type = mask_t; - using size_type = std::size_t; - - ~graph_mask_t() = default; - graph_mask_t(graph_mask_t&&) noexcept = default; - - graph_mask_t() = delete; - - explicit graph_mask_t(raft::handle_t const& handle, - vertex_t n_vertices, - edge_t n_edges, - bool complement = false) - : handle_(handle), - n_vertices_(n_vertices), - n_edges_(n_edges), - edges_(0, handle.get_stream()), - vertices_(0, handle.get_stream()), - complement_(complement) - { - } - - explicit graph_mask_t(graph_mask_t const& other) - : handle_(other.handle_), - n_vertices_(other.n_vertices_), - n_edges_(other.n_edges_), - edges_(other.edges_, other.handle_.get_stream()), - vertices_(other.vertices_, other.handle_.get_stream()), - complement_(other.complement_) - { - } - - graph_mask_t& operator=(graph_mask_t&&) noexcept = default; - graph_mask_t& operator=(graph_mask_t const& other) = default; - - /** - * Determines whether the 1 bit in a vertex or edge position - * represents an inclusive mask or exclusive mask. Default is - * an inclusive mask (e.g. 1 bit means the corresponding vertex - * or edge should be included in computations). - * @return - */ - bool is_complemented() const { return complement_; } - - /** - * Whether or not the current mask object has been initialized - * with an edge mask. - * @return - */ - bool has_edge_mask() const { return get_edge_mask().has_value(); } - - /** - * Whether or not the current mask object has been initialized - * with a vertex mask. - * @return - */ - bool has_vertex_mask() const { return get_vertex_mask().has_value(); } - - /** - * Returns the edge mask if it has been initialized on the instance - * @return - */ - std::optional get_edge_mask() const - { - return edges_.size() > 0 ? std::make_optional(edges_.data()) : std::nullopt; - } - - /** - * Retuns the vertex mask if it has been initialized on the instance - * @return - */ - std::optional get_vertex_mask() const - { - return vertices_.size() > 0 ? std::make_optional(vertices_.data()) - : std::nullopt; - } - - vertex_t get_n_vertices() { return n_vertices_; } - - edge_t get_n_edges() { return n_edges_; } - - edge_t get_edge_mask_size() const { return n_edges_ >> log_bits(); } - - vertex_t get_vertex_mask_size() const { return n_vertices_ >> log_bits(); } - - void initialize_edge_mask(bool init = 0) - { - if (!has_edge_mask()) { - allocate_edge_mask(); - RAFT_CUDA_TRY(cudaMemsetAsync(edges_.data(), - edges_.size() * sizeof(mask_t), - std::numeric_limits::max() * init, - handle_.get_stream())); - } - } - - void initialize_vertex_mask(bool init = 0) - { - if (!has_vertex_mask()) { - allocate_vertex_mask(); - RAFT_CUDA_TRY(cudaMemsetAsync(vertices_.data(), - vertices_.size() * sizeof(mask_t), - std::numeric_limits::max() * init, - handle_.get_stream())); - } - } - - /** - * Initializes an edge mask by allocating the device memory - */ - void allocate_edge_mask() - { - if (edges_.size() == 0) { - edges_.resize(get_edge_mask_size(), handle_.get_stream()); - clear_edge_mask(); - } - } - - /** - * Initializes a vertex mask by allocating the device memory - */ - void allocate_vertex_mask() - { - if (vertices_.size() == 0) { - vertices_.resize(get_vertex_mask_size(), handle_.get_stream()); - clear_vertex_mask(); - } - } - - /** - * Clears out all the masked bits of the edge mask - */ - void clear_edge_mask() - { - if (edges_.size() > 0) { - RAFT_CUDA_TRY( - cudaMemsetAsync(edges_.data(), edges_.size() * sizeof(mask_t), 0, handle_.get_stream())); - } - } - - /** - * Clears out all the masked bits of the vertex mask - */ - void clear_vertex_mask() - { - if (vertices_.size() > 0) { - RAFT_CUDA_TRY(cudaMemsetAsync( - vertices_.data(), vertices_.size() * sizeof(mask_t), 0, handle_.get_stream())); - } - } - - /** - * Returns a view of the current mask object which can safely be used - * in device functions. - * - * Note that the view will not be able to initialize the underlying - * masks so they will need to be initialized before this method is - * invoked. - */ - auto view() - { - auto vspan = has_vertex_mask() ? std::make_optional>(vertices_.data(), - vertices_.size()) - : std::nullopt; - auto espan = has_edge_mask() - ? std::make_optional>(edges_.data(), edges_.size()) - : std::nullopt; - return graph_mask_view_t( - n_vertices_, n_edges_, vspan, espan, complement_); - } - - protected: - raft::handle_t const& handle_; - vertex_t n_vertices_; - edge_t n_edges_; - bool complement_ = false; - rmm::device_uvector vertices_; - rmm::device_uvector edges_; - -}; // struct graph_mask_t -}; // namespace cugraph \ No newline at end of file diff --git a/cpp/include/cugraph/graph_view.hpp b/cpp/include/cugraph/graph_view.hpp index 2d10b435224..f30a8b7e2af 100644 --- a/cpp/include/cugraph/graph_view.hpp +++ b/cpp/include/cugraph/graph_view.hpp @@ -444,12 +444,6 @@ class graph_view_t local_edge_partition_view( size_t partition_idx) const { - CUGRAPH_EXPECTS(!has_edge_mask(), "unimplemented."); - vertex_t major_range_first{}; vertex_t major_range_last{}; vertex_t minor_range_first{}; @@ -748,6 +740,11 @@ class graph_view_t> edge_mask_view() const + { + return edge_mask_view_; + } + private: std::vector edge_partition_offsets_{}; std::vector edge_partition_indices_{}; @@ -856,12 +853,6 @@ class graph_view_tnumber_of_edges(); - } - vertex_t local_edge_partition_src_range_size(size_t partition_idx = 0) const { assert(partition_idx == 0); @@ -1030,6 +1021,11 @@ class graph_view_t> edge_mask_view() const + { + return edge_mask_view_; + } + private: edge_t const* offsets_{nullptr}; vertex_t const* indices_{nullptr}; diff --git a/cpp/include/cugraph/utilities/device_functors.cuh b/cpp/include/cugraph/utilities/device_functors.cuh index 501e74cf47b..3af8ed1dd19 100644 --- a/cpp/include/cugraph/utilities/device_functors.cuh +++ b/cpp/include/cugraph/utilities/device_functors.cuh @@ -15,18 +15,11 @@ */ #pragma once -#include -#include -#include +#include -#include -#include -#include -#include +#include #include -#include -#include namespace cugraph { @@ -44,12 +37,12 @@ struct pack_bool_t { __device__ uint32_t operator()(size_t i) const { - auto first = i * (sizeof(uint32_t) * 8); - auto last = std::min((i + 1) * (sizeof(uint32_t) * 8), num_bools); + auto first = i * packed_bools_per_word(); + auto last = std::min((i + 1) * packed_bools_per_word(), num_bools); uint32_t ret{0}; for (auto j = first; j < last; ++j) { if (*(bool_first + j)) { - auto mask = uint32_t{1} << (j % (sizeof(uint32_t) * 8)); + auto mask = packed_bool_mask(j); ret |= mask; } } @@ -57,6 +50,22 @@ struct pack_bool_t { } }; +template +struct check_bit_set_t { + PackedBoolIterator bitmap_first{}; + T idx_first{}; + + static_assert( + std::is_same_v::value_type, uint32_t>); + + __device__ bool operator()(T idx) const + { + auto offset = idx - idx_first; + return static_cast(*(bitmap_first + packed_bool_offset(offset)) & + packed_bool_mask(offset)); + } +}; + template struct indirection_t { Iterator first{}; @@ -80,7 +89,14 @@ struct indirection_if_idx_valid_t { }; template -struct not_equal_t { +struct is_equal_t { + T compare{}; + + __device__ bool operator()(T val) const { return val == compare; } +}; + +template +struct is_not_equal_t { T compare{}; __device__ bool operator()(T val) const { return val != compare; } @@ -96,19 +112,6 @@ struct is_first_in_run_t { } }; -template -struct check_bit_set_t { - uint32_t const* bitmaps{nullptr}; - T idx_first{}; - - __device__ bool operator()(T idx) const - { - auto offset = idx - idx_first; - auto mask = uint32_t{1} << (offset % (sizeof(uint32_t) * 8)); - return (*(bitmaps + (offset / (sizeof(uint32_t) * 8))) & mask) > uint32_t{0}; - } -}; - template struct check_in_range_t { T min{}; // inclusive diff --git a/cpp/include/cugraph/utilities/mask_utils.cuh b/cpp/include/cugraph/utilities/mask_utils.cuh new file mode 100644 index 00000000000..ab1403d019b --- /dev/null +++ b/cpp/include/cugraph/utilities/mask_utils.cuh @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2023, NVIDIA CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include +#include + +#include + +#include +#include +#include +#include +#include + +namespace cugraph { + +namespace detail { + +template // should be packed bool +__device__ size_t count_set_bits(MaskIterator mask_first, size_t start_offset, size_t num_bits) +{ + static_assert( + std::is_same_v::value_type, uint32_t>); + + size_t ret{0}; + + mask_first = mask_first + packed_bool_offset(start_offset); + start_offset = start_offset % packed_bools_per_word(); + if (start_offset != 0) { + auto mask = ~packed_bool_partial_mask(start_offset); + if (start_offset + num_bits < packed_bools_per_word()) { + mask &= packed_bool_partial_mask(start_offset + num_bits); + } + ret += __popc(*mask_first & mask); + num_bits -= __popc(mask); + ++mask_first; + } + + return thrust::transform_reduce( + thrust::seq, + thrust::make_counting_iterator(size_t{0}), + thrust::make_counting_iterator(packed_bool_size(num_bits)), + [mask_first, num_bits] __device__(size_t i) { + auto word = *(mask_first + i); + if ((i + 1) * packed_bools_per_word() > num_bits) { + word &= packed_bool_partial_mask(num_bits % packed_bools_per_word()); + } + return static_cast(__popc(word)); + }, + ret, + thrust::plus{}); +} + +template ::value_type, // for packed bool support + typename output_value_type = typename thrust::iterator_traits< + OutputIterator>::value_type> // for packed bool support +__device__ size_t copy_if_mask_set(InputIterator input_first, + MaskIterator mask_first, + OutputIterator output_first, + size_t input_start_offset, + size_t output_start_offset, + size_t num_items) +{ + static_assert( + std::is_same_v::value_type, uint32_t>); + static_assert( + std::is_same_v::value_type, input_value_type> || + cugraph::has_packed_bool_element()); + static_assert(std::is_same_v::value_type, + output_value_type> || + cugraph::has_packed_bool_element()); + + static_assert(!cugraph::has_packed_bool_element() && + !cugraph::has_packed_bool_element(), + "unimplemented."); + + return static_cast(thrust::distance( + output_first + output_start_offset, + thrust::copy_if(thrust::seq, + input_first + input_start_offset, + input_first + (input_start_offset + num_items), + thrust::make_transform_iterator( + thrust::make_counting_iterator(size_t{0}), + check_bit_set_t{mask_first, size_t{0}}) + + input_start_offset, + output_first + output_start_offset, + is_equal_t{true}))); +} + +template // should be packed bool +size_t count_set_bits(raft::handle_t const& handle, MaskIterator mask_first, size_t num_bits) +{ + static_assert( + std::is_same_v::value_type, uint32_t>); + + return thrust::transform_reduce( + handle.get_thrust_policy(), + thrust::make_counting_iterator(size_t{0}), + thrust::make_counting_iterator(packed_bool_size(num_bits)), + [mask_first, num_bits] __device__(size_t i) { + auto word = *(mask_first + i); + if ((i + 1) * packed_bools_per_word() > num_bits) { + word &= packed_bool_partial_mask(num_bits % packed_bools_per_word()); + } + return static_cast(__popc(word)); + }, + size_t{0}, + thrust::plus{}); +} + +template +OutputIterator copy_if_mask_set(raft::handle_t const& handle, + InputIterator input_first, + InputIterator input_last, + MaskIterator mask_first, + OutputIterator output_first) +{ + return thrust::copy_if( + handle.get_thrust_policy(), + input_first, + input_last, + thrust::make_transform_iterator(thrust::make_counting_iterator(size_t{0}), + check_bit_set_t{mask_first, size_t{0}}), + output_first, + is_equal_t{true}); +} + +} // namespace detail + +} // namespace cugraph diff --git a/cpp/include/cugraph/utilities/packed_bool_utils.hpp b/cpp/include/cugraph/utilities/packed_bool_utils.hpp index 0be5711d90c..b418d5afc35 100644 --- a/cpp/include/cugraph/utilities/packed_bool_utils.hpp +++ b/cpp/include/cugraph/utilities/packed_bool_utils.hpp @@ -92,6 +92,13 @@ constexpr uint32_t packed_bool_mask(T bool_offset) constexpr uint32_t packed_bool_full_mask() { return uint32_t{0xffffffff}; } +template +constexpr uint32_t packed_bool_partial_mask(T num_set_bits) +{ + assert(static_cast(num_set_bits) <= sizeof(uint32_t) * 8); + return uint32_t{0xffffffff} >> (sizeof(uint32_t) * 8 - num_set_bits); +} + constexpr uint32_t packed_bool_empty_mask() { return uint32_t{0x0}; } template diff --git a/cpp/src/prims/detail/nbr_intersection.cuh b/cpp/src/prims/detail/nbr_intersection.cuh index 2f30faebb3e..32247ca3466 100644 --- a/cpp/src/prims/detail/nbr_intersection.cuh +++ b/cpp/src/prims/detail/nbr_intersection.cuh @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -130,6 +131,8 @@ struct update_rx_major_local_degree_t { int minor_comm_size{}; edge_partition_device_view_t edge_partition{}; + thrust::optional> + edge_partition_e_mask{}; size_t reordered_idx_first{}; size_t local_edge_partition_idx{}; @@ -151,19 +154,28 @@ struct update_rx_major_local_degree_t { auto major = rx_majors[rx_group_firsts[major_comm_rank * minor_comm_size + local_edge_partition_idx] + offset_in_local_edge_partition]; - edge_t local_degree{}; + vertex_t major_idx{0}; + edge_t local_degree{0}; if (multi_gpu && (edge_partition.major_hypersparse_first() && (major >= *(edge_partition.major_hypersparse_first())))) { auto major_hypersparse_idx = edge_partition.major_hypersparse_idx_from_major_nocheck(major); - local_degree = major_hypersparse_idx - ? edge_partition.local_degree((*(edge_partition.major_hypersparse_first()) - - edge_partition.major_range_first()) + - *major_hypersparse_idx) - : edge_t{0}; + if (major_hypersparse_idx) { + major_idx = + (*(edge_partition.major_hypersparse_first()) - edge_partition.major_range_first()) + + *major_hypersparse_idx; + local_degree = edge_partition.local_degree(major_idx); + } } else { - local_degree = - edge_partition.local_degree(edge_partition.major_offset_from_major_nocheck(major)); + major_idx = edge_partition.major_offset_from_major_nocheck(major); + local_degree = edge_partition.local_degree(major_idx); } + + if (edge_partition_e_mask && (local_degree > edge_t{0})) { + auto local_offset = edge_partition.local_offset(major_idx); + local_degree = static_cast( + count_set_bits((*edge_partition_e_mask).value_first(), local_offset, local_degree)); + } + local_degrees_for_rx_majors[rx_group_firsts[major_comm_rank * minor_comm_size + local_edge_partition_idx] + offset_in_local_edge_partition] = local_degree; @@ -173,7 +185,7 @@ struct update_rx_major_local_degree_t { template struct update_rx_major_local_nbrs_t { int major_comm_size{}; @@ -181,6 +193,8 @@ struct update_rx_major_local_nbrs_t { edge_partition_device_view_t edge_partition{}; edge_partition_e_input_device_view_t edge_partition_e_value_input{}; + thrust::optional> + edge_partition_e_mask{}; size_t reordered_idx_first{}; size_t local_edge_partition_idx{}; @@ -190,12 +204,13 @@ struct update_rx_major_local_nbrs_t { raft::device_span rx_majors{}; raft::device_span local_nbr_offsets_for_rx_majors{}; raft::device_span local_nbrs_for_rx_majors{}; - optional_property_buffer_view_t local_nbrs_properties_for_rx_majors{}; + optional_property_buffer_mutable_view_t local_e_property_values_for_rx_majors{}; __device__ void operator()(size_t idx) { using edge_property_value_t = typename edge_partition_e_input_device_view_t::value_type; - auto it = thrust::upper_bound( + + auto it = thrust::upper_bound( thrust::seq, rx_reordered_group_lasts.begin(), rx_reordered_group_lasts.end(), idx); auto major_comm_rank = static_cast(thrust::distance(rx_reordered_group_lasts.begin(), it)); auto offset_in_local_edge_partition = @@ -204,39 +219,76 @@ struct update_rx_major_local_nbrs_t { auto major = rx_majors[rx_group_firsts[major_comm_rank * minor_comm_size + local_edge_partition_idx] + offset_in_local_edge_partition]; - vertex_t const* indices{nullptr}; - [[maybe_unused]] edge_t edge_offset{0}; + + edge_t edge_offset{0}; edge_t local_degree{0}; if (multi_gpu && (edge_partition.major_hypersparse_first() && (major >= *(edge_partition.major_hypersparse_first())))) { auto major_hypersparse_idx = edge_partition.major_hypersparse_idx_from_major_nocheck(major); if (major_hypersparse_idx) { - thrust::tie(indices, edge_offset, local_degree) = edge_partition.local_edges( + auto major_idx = (*(edge_partition.major_hypersparse_first()) - edge_partition.major_range_first()) + - *major_hypersparse_idx); + *major_hypersparse_idx; + edge_offset = edge_partition.local_offset(major_idx); + local_degree = edge_partition.local_degree(major_idx); } } else { - thrust::tie(indices, edge_offset, local_degree) = - edge_partition.local_edges(edge_partition.major_offset_from_major_nocheck(major)); + auto major_idx = edge_partition.major_offset_from_major_nocheck(major); + edge_offset = edge_partition.local_offset(major_idx); + local_degree = edge_partition.local_degree(major_idx); } - // FIXME: this can lead to thread-divergence with a mix of high-degree and low-degree - // vertices in a single warp (better optimize if this becomes a performance - // bottleneck) - size_t start_offset = + auto indices = edge_partition.indices(); + size_t output_start_offset = local_nbr_offsets_for_rx_majors[rx_group_firsts[major_comm_rank * minor_comm_size + local_edge_partition_idx] + offset_in_local_edge_partition]; - thrust::copy(thrust::seq, - indices, - indices + local_degree, - local_nbrs_for_rx_majors.begin() + start_offset); - if constexpr (!std::is_same_v) { - thrust::copy(thrust::seq, - edge_partition_e_value_input.value_first() + edge_offset, - edge_partition_e_value_input.value_first() + (edge_offset + local_degree), - local_nbrs_properties_for_rx_majors.begin() + start_offset); + // FIXME: this can lead to thread-divergence with a mix of high-degree and low-degree + // vertices in a single warp (better optimize if this becomes a performance + // bottleneck) + + static_assert(!edge_partition_e_input_device_view_t::has_packed_bool_element, "unimplemented."); + if (local_degree > 0) { + if (edge_partition_e_mask) { + auto mask_first = (*edge_partition_e_mask).value_first(); + if constexpr (!std::is_same_v) { + auto input_first = + thrust::make_zip_iterator(indices, edge_partition_e_value_input.value_first()) + + edge_offset; + copy_if_mask_set(input_first, + mask_first, + thrust::make_zip_iterator(local_nbrs_for_rx_majors.begin(), + local_e_property_values_for_rx_majors), + edge_offset, + output_start_offset, + local_degree); + } else { + copy_if_mask_set(indices, + mask_first, + local_nbrs_for_rx_majors.begin(), + edge_offset, + output_start_offset, + local_degree); + } + } else { + if constexpr (!std::is_same_v) { + auto input_first = + thrust::make_zip_iterator(indices, edge_partition_e_value_input.value_first()) + + edge_offset; + thrust::copy(thrust::seq, + input_first, + input_first + local_degree, + thrust::make_zip_iterator(local_nbrs_for_rx_majors.begin(), + local_e_property_values_for_rx_majors) + + output_start_offset); + } else { + thrust::copy(thrust::seq, + indices + edge_offset, + indices + (edge_offset + local_degree), + local_nbrs_for_rx_majors.begin() + output_start_offset); + } + } } } }; @@ -259,36 +311,45 @@ template struct pick_min_degree_t { FirstElementToIdxMap first_element_to_idx_map{}; - raft::device_span first_element_offsets{nullptr}; + raft::device_span first_element_offsets{nullptr}; SecondElementToIdxMap second_element_to_idx_map{}; - raft::device_span second_element_offsets{nullptr}; + raft::device_span second_element_offsets{nullptr}; edge_partition_device_view_t edge_partition{}; + thrust::optional> + edge_partition_e_mask{}; __device__ edge_t operator()(thrust::tuple pair) const { edge_t local_degree0{0}; vertex_t major0 = thrust::get<0>(pair); if constexpr (std::is_same_v) { + vertex_t major_idx{0}; if constexpr (multi_gpu) { if (edge_partition.major_hypersparse_first() && (major0 >= *(edge_partition.major_hypersparse_first()))) { auto major_hypersparse_idx = edge_partition.major_hypersparse_idx_from_major_nocheck(major0); - local_degree0 = - major_hypersparse_idx - ? edge_partition.local_degree((*(edge_partition.major_hypersparse_first()) - - edge_partition.major_range_first()) + - *major_hypersparse_idx) - : edge_t{0}; + if (major_hypersparse_idx) { + major_idx = + (*(edge_partition.major_hypersparse_first()) - edge_partition.major_range_first()) + + *major_hypersparse_idx; + local_degree0 = edge_partition.local_degree(major_idx); + } } else { - local_degree0 = - edge_partition.local_degree(edge_partition.major_offset_from_major_nocheck(major0)); + major_idx = edge_partition.major_offset_from_major_nocheck(major0); + local_degree0 = edge_partition.local_degree(major_idx); } } else { + major_idx = edge_partition.major_offset_from_major_nocheck(major0); + local_degree0 = edge_partition.local_degree(major_idx); + } + + if (edge_partition_e_mask && (local_degree0 > edge_t{0})) { + auto local_offset = edge_partition.local_offset(major_idx); local_degree0 = - edge_partition.local_degree(edge_partition.major_offset_from_major_nocheck(major0)); + count_set_bits((*edge_partition_e_mask).value_first(), local_offset, local_degree0); } } else { auto idx = first_element_to_idx_map.find(major0); @@ -299,24 +360,31 @@ struct pick_min_degree_t { edge_t local_degree1{0}; vertex_t major1 = thrust::get<1>(pair); if constexpr (std::is_same_v) { + vertex_t major_idx{0}; if constexpr (multi_gpu) { if (edge_partition.major_hypersparse_first() && (major1 >= *(edge_partition.major_hypersparse_first()))) { auto major_hypersparse_idx = edge_partition.major_hypersparse_idx_from_major_nocheck(major1); - local_degree1 = - major_hypersparse_idx - ? edge_partition.local_degree((*(edge_partition.major_hypersparse_first()) - - edge_partition.major_range_first()) + - *major_hypersparse_idx) - : edge_t{0}; + if (major_hypersparse_idx) { + major_idx = + (*(edge_partition.major_hypersparse_first()) - edge_partition.major_range_first()) + + *major_hypersparse_idx; + local_degree1 = edge_partition.local_degree(major_idx); + } } else { - local_degree1 = - edge_partition.local_degree(edge_partition.major_offset_from_major_nocheck(major1)); + major_idx = edge_partition.major_offset_from_major_nocheck(major1); + local_degree1 = edge_partition.local_degree(major_idx); } } else { + major_idx = edge_partition.major_offset_from_major_nocheck(major1); + local_degree1 = edge_partition.local_degree(major_idx); + } + + if (edge_partition_e_mask && (local_degree1 > edge_t{0})) { + auto local_offset = edge_partition.local_offset(major_idx); local_degree1 = - edge_partition.local_degree(edge_partition.major_offset_from_major_nocheck(major1)); + count_set_bits((*edge_partition_e_mask).value_first(), local_offset, local_degree1); } } else { auto idx = second_element_to_idx_map.find(major1); @@ -328,6 +396,71 @@ struct pick_min_degree_t { } }; +template +__device__ edge_t set_intersection_by_key_with_mask(InputKeyIterator0 input_key_first0, + InputKeyIterator1 input_key_first1, + InputValueIterator0 input_value_first0, + InputValueIterator1 input_value_first1, + MaskIterator mask_first, + OutputKeyIterator output_key_first, + OutputValueIterator0 output_value_first0, + OutputValueIterator1 output_value_first1, + edge_t input_start_offset0, + edge_t input_size0, + bool apply_mask0, + edge_t input_start_offset1, + edge_t input_size1, + bool apply_mask1, + size_t output_start_offset) +{ + static_assert( + std::is_same_v::value_type, uint32_t>); + static_assert(std::is_same_v == + std::is_same_v); + + check_bit_set_t check_bit_set{mask_first, edge_t{0}}; + + auto idx0 = input_start_offset0; + auto idx1 = input_start_offset1; + auto output_idx = output_start_offset; + while ((idx0 < (input_start_offset0 + input_size0)) && + (idx1 < (input_start_offset1 + input_size1))) { + bool valid0 = apply_mask0 ? check_bit_set(idx0) : true; + bool valid1 = apply_mask1 ? check_bit_set(idx1) : true; + if (!valid0) { ++idx0; } + if (!valid1) { ++idx1; } + + if (valid0 && valid1) { + auto key0 = *(input_key_first0 + idx0); + auto key1 = *(input_key_first1 + idx1); + if (key0 < key1) { + ++idx0; + } else if (key0 > key1) { + ++idx1; + } else { + *(output_key_first + output_idx) = key0; + if constexpr (!std::is_same_v) { + *(output_value_first0 + output_idx) = *(input_value_first0 + idx0); + *(output_value_first1 + output_idx) = *(input_value_first1 + idx1); + } + ++idx0; + ++idx1; + ++output_idx; + } + } + } + + return (output_idx - output_start_offset); +} + template struct copy_intersecting_nbrs_and_update_intersection_size_t { FirstElementToIdxMap first_element_to_idx_map{}; - raft::device_span first_element_offsets{}; + raft::device_span first_element_offsets{}; raft::device_span first_element_indices{nullptr}; - optional_property_buffer_view_t first_element_properties{}; + optional_property_buffer_view_t first_element_edge_property_values{}; SecondElementToIdxMap second_element_to_idx_map{}; - raft::device_span second_element_offsets{}; + raft::device_span second_element_offsets{}; raft::device_span second_element_indices{nullptr}; - optional_property_buffer_view_t second_element_properties{}; + optional_property_buffer_view_t second_element_edge_property_values{}; edge_partition_device_view_t edge_partition{}; edge_partition_e_input_device_view_t edge_partition_e_value_input{}; + thrust::optional> + edge_partition_e_mask{}; VertexPairIterator vertex_pair_first; raft::device_span nbr_intersection_offsets{nullptr}; raft::device_span nbr_intersection_indices{nullptr}; - optional_property_buffer_view_t nbr_intersection_properties0{}; - optional_property_buffer_view_t nbr_intersection_properties1{}; + optional_property_buffer_mutable_view_t nbr_intersection_e_property_values0{}; + optional_property_buffer_mutable_view_t nbr_intersection_e_property_values1{}; vertex_t invalid_id{}; + __device__ edge_t operator()(size_t i) { using edge_property_value_t = typename edge_partition_e_input_device_view_t::value_type; - using optional_const_property_buffer_view_t = - std::conditional_t, - raft::device_span, - std::byte /* dummy */>; auto pair = *(vertex_pair_first + i); + vertex_t const* indices0{nullptr}; - optional_const_property_buffer_view_t properties0{}; + std::conditional_t, + edge_property_value_t const*, + void*> + edge_property_values0{nullptr}; edge_t local_edge_offset0{0}; edge_t local_degree0{0}; if constexpr (std::is_same_v) { + indices0 = edge_partition.indices(); + if constexpr (!std::is_same_v) { + edge_property_values0 = edge_partition_e_value_input.value_first(); + } + vertex_t major = thrust::get<0>(pair); if constexpr (multi_gpu) { if (edge_partition.major_hypersparse_first() && @@ -379,42 +521,47 @@ struct copy_intersecting_nbrs_and_update_intersection_size_t { auto major_hypersparse_idx = edge_partition.major_hypersparse_idx_from_major_nocheck(major); if (major_hypersparse_idx) { - thrust::tie(indices0, local_edge_offset0, local_degree0) = edge_partition.local_edges( + auto major_idx = (*(edge_partition.major_hypersparse_first()) - edge_partition.major_range_first()) + - *major_hypersparse_idx); + *major_hypersparse_idx; + local_edge_offset0 = edge_partition.local_offset(major_idx); + local_degree0 = edge_partition.local_degree(major_idx); } } else { - thrust::tie(indices0, local_edge_offset0, local_degree0) = - edge_partition.local_edges(edge_partition.major_offset_from_major_nocheck(major)); + auto major_idx = edge_partition.major_offset_from_major_nocheck(major); + local_edge_offset0 = edge_partition.local_offset(major_idx); + local_degree0 = edge_partition.local_degree(major_idx); } } else { - thrust::tie(indices0, local_edge_offset0, local_degree0) = - edge_partition.local_edges(edge_partition.major_offset_from_major_nocheck(major)); + auto major_idx = edge_partition.major_offset_from_major_nocheck(major); + local_edge_offset0 = edge_partition.local_offset(major_idx); + local_degree0 = edge_partition.local_degree(major_idx); } - + } else { + indices0 = first_element_indices.begin(); if constexpr (!std::is_same_v) { - properties0 = raft::device_span( - edge_partition_e_value_input.value_first() + local_edge_offset0, local_degree0); + edge_property_values0 = first_element_edge_property_values; } - } else { auto idx = first_element_to_idx_map.find(thrust::get<0>(pair)); local_edge_offset0 = first_element_offsets[idx]; local_degree0 = static_cast(first_element_offsets[idx + 1] - local_edge_offset0); - indices0 = first_element_indices.begin() + local_edge_offset0; - - if constexpr (!std::is_same_v) { - properties0 = raft::device_span( - first_element_properties.begin() + local_edge_offset0, local_degree0); - } } vertex_t const* indices1{nullptr}; - optional_const_property_buffer_view_t properties1{}; + std::conditional_t, + edge_property_value_t const*, + void*> + edge_property_values1{nullptr}; - [[maybe_unused]] edge_t local_edge_offset1{0}; + edge_t local_edge_offset1{0}; edge_t local_degree1{0}; if constexpr (std::is_same_v) { + indices1 = edge_partition.indices(); + if constexpr (!std::is_same_v) { + edge_property_values1 = edge_partition_e_value_input.value_first(); + } + vertex_t major = thrust::get<1>(pair); if constexpr (multi_gpu) { if (edge_partition.major_hypersparse_first() && @@ -422,83 +569,63 @@ struct copy_intersecting_nbrs_and_update_intersection_size_t { auto major_hypersparse_idx = edge_partition.major_hypersparse_idx_from_major_nocheck(major); if (major_hypersparse_idx) { - thrust::tie(indices1, local_edge_offset1, local_degree1) = edge_partition.local_edges( + auto major_idx = (*(edge_partition.major_hypersparse_first()) - edge_partition.major_range_first()) + - *major_hypersparse_idx); + *major_hypersparse_idx; + local_edge_offset1 = edge_partition.local_offset(major_idx); + local_degree1 = edge_partition.local_degree(major_idx); } } else { - thrust::tie(indices1, local_edge_offset1, local_degree1) = - edge_partition.local_edges(edge_partition.major_offset_from_major_nocheck(major)); + auto major_idx = edge_partition.major_offset_from_major_nocheck(major); + local_edge_offset1 = edge_partition.local_offset(major_idx); + local_degree1 = edge_partition.local_degree(major_idx); } } else { - thrust::tie(indices1, local_edge_offset1, local_degree1) = - edge_partition.local_edges(edge_partition.major_offset_from_major_nocheck(major)); + auto major_idx = edge_partition.major_offset_from_major_nocheck(major); + local_edge_offset1 = edge_partition.local_offset(major_idx); + local_degree1 = edge_partition.local_degree(major_idx); } - + } else { + indices1 = second_element_indices.begin(); if constexpr (!std::is_same_v) { - properties1 = raft::device_span( - edge_partition_e_value_input.value_first() + local_edge_offset1, local_degree1); + edge_property_values1 = second_element_edge_property_values; } - } else { auto idx = second_element_to_idx_map.find(thrust::get<1>(pair)); local_edge_offset1 = second_element_offsets[idx]; local_degree1 = static_cast(second_element_offsets[idx + 1] - local_edge_offset1); - indices1 = second_element_indices.begin() + local_edge_offset1; - - if constexpr (!std::is_same_v) { - properties1 = raft::device_span( - second_element_properties.begin() + local_edge_offset1, local_degree1); - } } // FIXME: this can lead to thread-divergence with a mix of high-degree and low-degree // vertices in a single warp (better optimize if this becomes a performance // bottleneck) - auto nbr_intersection_first = nbr_intersection_indices.begin() + nbr_intersection_offsets[i]; - - auto nbr_intersection_last = thrust::set_intersection(thrust::seq, - indices0, - indices0 + local_degree0, - indices1, - indices1 + local_degree1, - nbr_intersection_first); - thrust::fill(thrust::seq, - nbr_intersection_last, - nbr_intersection_indices.begin() + nbr_intersection_offsets[i + 1], - invalid_id); - - auto insection_size = - static_cast(thrust::distance(nbr_intersection_first, nbr_intersection_last)); - if constexpr (!std::is_same_v) { - auto ip0_start = nbr_intersection_properties0.begin() + nbr_intersection_offsets[i]; - - // copy edge properties from first vertex to common neighbors - thrust::transform(thrust::seq, - nbr_intersection_first, - nbr_intersection_last, - ip0_start, - [indices0, local_degree0, properties0] __device__(auto v) { - auto position = - thrust::lower_bound(thrust::seq, indices0, indices0 + local_degree0, v); - return properties0[thrust::distance(indices0, position)]; - }); - - auto ip1_start = nbr_intersection_properties1.begin() + nbr_intersection_offsets[i]; - - // copy edge properties from second vertex to common neighbors - thrust::transform(thrust::seq, - nbr_intersection_first, - nbr_intersection_last, - ip1_start, - [indices1, local_degree1, properties1] __device__(auto v) { - auto position = - thrust::lower_bound(thrust::seq, indices1, indices1 + local_degree1, v); - return properties1[thrust::distance(indices1, position)]; - }); - } - return insection_size; + auto mask_first = edge_partition_e_mask ? (*edge_partition_e_mask).value_first() + : static_cast(nullptr); + auto intersection_size = set_intersection_by_key_with_mask( + indices0, + indices1, + edge_property_values0, + edge_property_values1, + mask_first, + nbr_intersection_indices.begin(), + nbr_intersection_e_property_values0, + nbr_intersection_e_property_values1, + local_edge_offset0, + local_degree0, + (std::is_same_v && edge_partition_e_mask), + local_edge_offset1, + local_degree1, + (std::is_same_v && edge_partition_e_mask), + nbr_intersection_offsets[i]); + + thrust::fill( + thrust::seq, + nbr_intersection_indices.begin() + (nbr_intersection_offsets[i] + intersection_size), + nbr_intersection_indices.begin() + nbr_intersection_offsets[i + 1], + invalid_id); + + return intersection_size; } }; @@ -520,7 +647,8 @@ struct strided_accumulate_t { template + typename optional_property_buffer_view_t, + typename optional_property_buffer_mutable_view_t> struct gatherv_indices_t { size_t output_size{}; int minor_comm_size{}; @@ -530,10 +658,10 @@ struct gatherv_indices_t { raft::device_span combined_nbr_intersection_offsets{}; raft::device_span combined_nbr_intersection_indices{}; - optional_property_buffer_view_t gathered_nbr_intersection_properties0{}; - optional_property_buffer_view_t gathered_nbr_intersection_properties1{}; - optional_property_buffer_view_t combined_nbr_intersection_properties0{}; - optional_property_buffer_view_t combined_nbr_intersection_properties1{}; + optional_property_buffer_view_t gathered_nbr_intersection_e_property_values0{}; + optional_property_buffer_view_t gathered_nbr_intersection_e_property_values1{}; + optional_property_buffer_mutable_view_t combined_nbr_intersection_e_property_values0{}; + optional_property_buffer_mutable_view_t combined_nbr_intersection_e_property_values1{}; __device__ void operator()(size_t i) const { @@ -546,13 +674,13 @@ struct gatherv_indices_t { if constexpr (!std::is_same_v) { auto zipped_gathered_begin = thrust::make_zip_iterator( thrust::make_tuple(gathered_intersection_indices.begin(), - gathered_nbr_intersection_properties0.begin(), - gathered_nbr_intersection_properties1.begin())); + gathered_nbr_intersection_e_property_values0, + gathered_nbr_intersection_e_property_values1)); auto zipped_combined_begin = thrust::make_zip_iterator( thrust::make_tuple(combined_nbr_intersection_indices.begin(), - combined_nbr_intersection_properties0.begin(), - combined_nbr_intersection_properties1.begin())); + combined_nbr_intersection_e_property_values0, + combined_nbr_intersection_e_property_values1)); thrust::copy(thrust::seq, zipped_gathered_begin + gathered_intersection_offsets[output_size * j + i], @@ -694,13 +822,12 @@ nbr_intersection(raft::handle_t const& handle, using optional_property_buffer_view_t = std::conditional_t, - raft::device_span, - std::byte /* dummy */>; - - using optional_nbr_intersected_edge_partitions_t = + edge_property_value_t const*, + void*>; + using optional_property_buffer_mutable_view_t = std::conditional_t, - std::vector>, - std::byte /* dummy */>; + edge_property_value_t*, + void*>; static_assert(std::is_same_v::value_type, thrust::tuple>); @@ -729,19 +856,20 @@ nbr_intersection(raft::handle_t const& handle, "Invalid input arguments: there are invalid input vertex pairs."); } - // 2. Collect neighbor lists for unique second pair elements (for the neighbors within the minor - // range for this GPU); Note that no need to collect for first pair elements as they already - // locally reside. + // 2. Collect neighbor lists (within the minor range for this GPU in multi-GPU) for unique second + // pair elements (all-gathered over minor_comm in multi-GPU); Note that no need to collect for + // first pair elements as they already locally reside. + + auto edge_mask_view = graph_view.edge_mask_view(); std::optional>> major_to_idx_map_ptr{ std::nullopt}; - std::optional> major_nbr_offsets{std::nullopt}; + std::optional> major_nbr_offsets{std::nullopt}; std::optional> major_nbr_indices{std::nullopt}; - [[maybe_unused]] auto major_nbr_properties = + [[maybe_unused]] auto major_e_property_values = cugraph::detail::allocate_optional_dataframe_buffer( 0, handle.get_stream()); - optional_property_buffer_view_t optional_major_nbr_properties{}; if constexpr (GraphViewType::is_multi_gpu) { if (intersect_minor_nbr[1]) { @@ -805,7 +933,7 @@ nbr_intersection(raft::handle_t const& handle, } } - // 2.2 Send majors and group (major_comm_rank, edge_partition_idx) counts + // 2.2 Send majors and group (major_comm_rank, local edge_partition_idx) counts rmm::device_uvector rx_majors(0, handle.get_stream()); std::vector rx_major_counts{}; @@ -859,7 +987,7 @@ nbr_intersection(raft::handle_t const& handle, rmm::device_uvector local_degrees_for_rx_majors(size_t{0}, handle.get_stream()); rmm::device_uvector local_nbrs_for_rx_majors(size_t{0}, handle.get_stream()); - [[maybe_unused]] auto local_nbrs_properties_for_rx_majors = + [[maybe_unused]] auto local_e_property_values_for_rx_majors = cugraph::detail::allocate_optional_dataframe_buffer( 0, handle.get_stream()); @@ -901,6 +1029,13 @@ nbr_intersection(raft::handle_t const& handle, auto edge_partition = edge_partition_device_view_t( graph_view.local_edge_partition_view(i)); + auto edge_partition_e_mask = + edge_mask_view + ? thrust::make_optional< + detail:: + edge_partition_edge_property_device_view_t>( + *edge_mask_view, i) + : thrust::nullopt; auto segment_offsets = graph_view.local_edge_partition_segment_offsets(i); auto reordered_idx_first = (i == size_t{0}) ? size_t{0} : h_rx_reordered_group_lasts[i * major_comm_size - 1]; @@ -913,6 +1048,7 @@ nbr_intersection(raft::handle_t const& handle, major_comm_size, minor_comm_size, edge_partition, + edge_partition_e_mask, reordered_idx_first, i, raft::device_span( @@ -936,22 +1072,28 @@ nbr_intersection(raft::handle_t const& handle, local_nbrs_for_rx_majors.resize( local_nbr_offsets_for_rx_majors.back_element(handle.get_stream()), handle.get_stream()); - optional_property_buffer_view_t optional_local_nbrs_properties{}; + optional_property_buffer_mutable_view_t optional_local_e_property_values{}; if constexpr (!std::is_same_v) { - local_nbrs_properties_for_rx_majors.resize(local_nbrs_for_rx_majors.size(), - handle.get_stream()); - optional_local_nbrs_properties = raft::device_span( - local_nbrs_properties_for_rx_majors.data(), local_nbrs_properties_for_rx_majors.size()); + local_e_property_values_for_rx_majors.resize(local_nbrs_for_rx_majors.size(), + handle.get_stream()); + optional_local_e_property_values = local_e_property_values_for_rx_majors.data(); } for (size_t i = 0; i < graph_view.number_of_local_edge_partitions(); ++i) { auto edge_partition = edge_partition_device_view_t( graph_view.local_edge_partition_view(i)); - auto edge_partition_e_value_input = edge_partition_e_input_device_view_t(edge_value_input, i); + auto edge_partition_e_mask = + edge_mask_view + ? thrust::make_optional< + detail:: + edge_partition_edge_property_device_view_t>( + *edge_mask_view, i) + : thrust::nullopt; + auto segment_offsets = graph_view.local_edge_partition_segment_offsets(i); auto reordered_idx_first = (i == size_t{0}) ? size_t{0} : h_rx_reordered_group_lasts[i * major_comm_size - 1]; @@ -964,12 +1106,13 @@ nbr_intersection(raft::handle_t const& handle, update_rx_major_local_nbrs_t{ major_comm_size, minor_comm_size, edge_partition, edge_partition_e_value_input, + edge_partition_e_mask, reordered_idx_first, i, raft::device_span( @@ -980,7 +1123,7 @@ nbr_intersection(raft::handle_t const& handle, local_nbr_offsets_for_rx_majors.size()), raft::device_span(local_nbrs_for_rx_majors.data(), local_nbrs_for_rx_majors.size()), - optional_local_nbrs_properties}); + optional_local_e_property_values}); } std::vector h_rx_offsets(rx_major_counts.size() + size_t{1}, size_t{0}); @@ -1012,7 +1155,7 @@ nbr_intersection(raft::handle_t const& handle, rmm::device_uvector local_degrees_for_unique_majors(size_t{0}, handle.get_stream()); std::tie(local_degrees_for_unique_majors, std::ignore) = shuffle_values( major_comm, local_degrees_for_rx_majors.begin(), rx_major_counts, handle.get_stream()); - major_nbr_offsets = rmm::device_uvector(local_degrees_for_unique_majors.size() + 1, + major_nbr_offsets = rmm::device_uvector(local_degrees_for_unique_majors.size() + 1, handle.get_stream()); (*major_nbr_offsets).set_element_to_zero_async(size_t{0}, handle.get_stream()); auto degree_first = thrust::make_transform_iterator(local_degrees_for_unique_majors.begin(), @@ -1027,14 +1170,11 @@ nbr_intersection(raft::handle_t const& handle, major_comm, local_nbrs_for_rx_majors.begin(), local_nbr_counts, handle.get_stream()); if constexpr (!std::is_same_v) { - std::tie(major_nbr_properties, std::ignore) = + std::tie(major_e_property_values, std::ignore) = shuffle_values(major_comm, - local_nbrs_properties_for_rx_majors.begin(), + local_e_property_values_for_rx_majors.begin(), local_nbr_counts, handle.get_stream()); - - optional_major_nbr_properties = raft::device_span( - major_nbr_properties.data(), major_nbr_properties.size()); } major_to_idx_map_ptr = std::make_unique>( @@ -1065,11 +1205,11 @@ nbr_intersection(raft::handle_t const& handle, rmm::device_uvector nbr_intersection_offsets(size_t{0}, handle.get_stream()); rmm::device_uvector nbr_intersection_indices(size_t{0}, handle.get_stream()); - [[maybe_unused]] auto nbr_intersection_properties0 = + [[maybe_unused]] auto nbr_intersection_e_property_values0 = cugraph::detail::allocate_optional_dataframe_buffer( 0, handle.get_stream()); - [[maybe_unused]] auto nbr_intersection_properties1 = + [[maybe_unused]] auto nbr_intersection_e_property_values1 = cugraph::detail::allocate_optional_dataframe_buffer( 0, handle.get_stream()); @@ -1116,15 +1256,19 @@ nbr_intersection(raft::handle_t const& handle, edge_partition_nbr_intersection_sizes.reserve(graph_view.number_of_local_edge_partitions()); edge_partition_nbr_intersection_indices.reserve(graph_view.number_of_local_edge_partitions()); - [[maybe_unused]] optional_nbr_intersected_edge_partitions_t - edge_partition_nbr_intersection_property0{}; - [[maybe_unused]] optional_nbr_intersected_edge_partitions_t - edge_partition_nbr_intersection_property1{}; + [[maybe_unused]] std::conditional_t, + std::vector>, + std::byte /* dummy */> + edge_partition_nbr_intersection_e_property_values0{}; + [[maybe_unused]] std::conditional_t, + std::vector>, + std::byte /* dummy */> + edge_partition_nbr_intersection_e_property_values1{}; if constexpr (!std::is_same_v) { - edge_partition_nbr_intersection_property0.reserve( + edge_partition_nbr_intersection_e_property_values0.reserve( graph_view.number_of_local_edge_partitions()); - edge_partition_nbr_intersection_property1.reserve( + edge_partition_nbr_intersection_e_property_values1.reserve( graph_view.number_of_local_edge_partitions()); } @@ -1144,11 +1288,11 @@ nbr_intersection(raft::handle_t const& handle, rmm::device_uvector rx_v_pair_nbr_intersection_indices(size_t{0}, handle.get_stream()); - [[maybe_unused]] auto rx_v_pair_nbr_intersection_properties0 = + [[maybe_unused]] auto rx_v_pair_nbr_intersection_e_property_values0 = cugraph::detail::allocate_optional_dataframe_buffer( 0, handle.get_stream()); - [[maybe_unused]] auto rx_v_pair_nbr_intersection_properties1 = + [[maybe_unused]] auto rx_v_pair_nbr_intersection_e_property_values1 = cugraph::detail::allocate_optional_dataframe_buffer( 0, handle.get_stream()); @@ -1174,9 +1318,15 @@ nbr_intersection(raft::handle_t const& handle, auto edge_partition = edge_partition_device_view_t( graph_view.local_edge_partition_view(i)); - auto edge_partition_e_value_input = edge_partition_e_input_device_view_t(edge_value_input, i); + auto edge_partition_e_mask = + edge_mask_view + ? thrust::make_optional< + detail::edge_partition_edge_property_device_view_t>( + *edge_mask_view, i) + : thrust::nullopt; + auto segment_offsets = graph_view.local_edge_partition_segment_offsets(i); rx_v_pair_nbr_intersection_sizes.resize( @@ -1193,11 +1343,12 @@ nbr_intersection(raft::handle_t const& handle, rx_v_pair_nbr_intersection_sizes.begin(), pick_min_degree_t{ nullptr, - raft::device_span(), + raft::device_span(), second_element_to_idx_map, - raft::device_span((*major_nbr_offsets).data(), + raft::device_span((*major_nbr_offsets).data(), (*major_nbr_offsets).size()), - edge_partition}); + edge_partition, + edge_partition_e_mask}); } else { CUGRAPH_FAIL("unimplemented."); } @@ -1215,25 +1366,30 @@ nbr_intersection(raft::handle_t const& handle, rx_v_pair_nbr_intersection_offsets.back_element(handle.get_stream()), handle.get_stream()); - optional_property_buffer_view_t rx_v_pair_optional_nbr_intersection_properties0{}; - optional_property_buffer_view_t rx_v_pair_optional_nbr_intersection_properties1{}; + optional_property_buffer_mutable_view_t + rx_v_pair_optional_nbr_intersection_e_property_values0{}; + optional_property_buffer_mutable_view_t + rx_v_pair_optional_nbr_intersection_e_property_values1{}; if constexpr (!std::is_same_v) { - rx_v_pair_nbr_intersection_properties0.resize(rx_v_pair_nbr_intersection_indices.size(), - handle.get_stream()); - rx_v_pair_nbr_intersection_properties1.resize(rx_v_pair_nbr_intersection_indices.size(), - handle.get_stream()); + rx_v_pair_nbr_intersection_e_property_values0.resize( + rx_v_pair_nbr_intersection_indices.size(), handle.get_stream()); + rx_v_pair_nbr_intersection_e_property_values1.resize( + rx_v_pair_nbr_intersection_indices.size(), handle.get_stream()); - rx_v_pair_optional_nbr_intersection_properties0 = - raft::device_span(rx_v_pair_nbr_intersection_properties0.data(), - rx_v_pair_nbr_intersection_properties0.size()); + rx_v_pair_optional_nbr_intersection_e_property_values0 = + rx_v_pair_nbr_intersection_e_property_values0.data(); - rx_v_pair_optional_nbr_intersection_properties1 = - raft::device_span(rx_v_pair_nbr_intersection_properties1.data(), - rx_v_pair_nbr_intersection_properties1.size()); + rx_v_pair_optional_nbr_intersection_e_property_values1 = + rx_v_pair_nbr_intersection_e_property_values1.data(); } if (intersect_minor_nbr[0] && intersect_minor_nbr[1]) { + optional_property_buffer_view_t optional_major_e_property_values{}; + if constexpr (!std::is_same_v) { + optional_major_e_property_values = major_e_property_values.data(); + } + auto second_element_to_idx_map = detail::kv_cuco_store_find_device_view_t((*major_to_idx_map_ptr)->view()); thrust::tabulate( @@ -1248,28 +1404,29 @@ nbr_intersection(raft::handle_t const& handle, edge_t, edge_partition_e_input_device_view_t, optional_property_buffer_view_t, + optional_property_buffer_mutable_view_t, true>{nullptr, - raft::device_span(), + raft::device_span(), raft::device_span(), optional_property_buffer_view_t{}, second_element_to_idx_map, - raft::device_span((*major_nbr_offsets).data(), + raft::device_span((*major_nbr_offsets).data(), (*major_nbr_offsets).size()), raft::device_span((*major_nbr_indices).data(), (*major_nbr_indices).size()), - optional_major_nbr_properties, + optional_major_e_property_values, edge_partition, edge_partition_e_value_input, + edge_partition_e_mask, get_dataframe_buffer_begin(vertex_pair_buffer), raft::device_span(rx_v_pair_nbr_intersection_offsets.data(), rx_v_pair_nbr_intersection_offsets.size()), raft::device_span(rx_v_pair_nbr_intersection_indices.data(), rx_v_pair_nbr_intersection_indices.size()), - rx_v_pair_optional_nbr_intersection_properties0, - rx_v_pair_optional_nbr_intersection_properties1, + rx_v_pair_optional_nbr_intersection_e_property_values0, + rx_v_pair_optional_nbr_intersection_e_property_values1, invalid_vertex_id::value}); - } else { CUGRAPH_FAIL("unimplemented."); } @@ -1284,31 +1441,31 @@ nbr_intersection(raft::handle_t const& handle, handle.get_stream()); rx_v_pair_nbr_intersection_indices.shrink_to_fit(handle.get_stream()); } else { - auto common_nbr_and_properties_begin = thrust::make_zip_iterator( + auto common_nbr_and_e_property_values_begin = thrust::make_zip_iterator( thrust::make_tuple(rx_v_pair_nbr_intersection_indices.begin(), - rx_v_pair_nbr_intersection_properties0.begin(), - rx_v_pair_nbr_intersection_properties1.begin())); + rx_v_pair_nbr_intersection_e_property_values0.begin(), + rx_v_pair_nbr_intersection_e_property_values1.begin())); auto last = thrust::remove_if( handle.get_thrust_policy(), - common_nbr_and_properties_begin, - common_nbr_and_properties_begin + rx_v_pair_nbr_intersection_indices.size(), + common_nbr_and_e_property_values_begin, + common_nbr_and_e_property_values_begin + rx_v_pair_nbr_intersection_indices.size(), [] __device__(auto nbr_p0_p1) { return thrust::get<0>(nbr_p0_p1) == invalid_vertex_id::value; }); rx_v_pair_nbr_intersection_indices.resize( - thrust::distance(common_nbr_and_properties_begin, last), handle.get_stream()); + thrust::distance(common_nbr_and_e_property_values_begin, last), handle.get_stream()); rx_v_pair_nbr_intersection_indices.shrink_to_fit(handle.get_stream()); - rx_v_pair_nbr_intersection_properties0.resize(rx_v_pair_nbr_intersection_indices.size(), - handle.get_stream()); - rx_v_pair_nbr_intersection_properties0.shrink_to_fit(handle.get_stream()); + rx_v_pair_nbr_intersection_e_property_values0.resize( + rx_v_pair_nbr_intersection_indices.size(), handle.get_stream()); + rx_v_pair_nbr_intersection_e_property_values0.shrink_to_fit(handle.get_stream()); - rx_v_pair_nbr_intersection_properties1.resize(rx_v_pair_nbr_intersection_indices.size(), - handle.get_stream()); - rx_v_pair_nbr_intersection_properties1.shrink_to_fit(handle.get_stream()); + rx_v_pair_nbr_intersection_e_property_values1.resize( + rx_v_pair_nbr_intersection_indices.size(), handle.get_stream()); + rx_v_pair_nbr_intersection_e_property_values1.shrink_to_fit(handle.get_stream()); } thrust::inclusive_scan(handle.get_thrust_policy(), @@ -1427,11 +1584,11 @@ nbr_intersection(raft::handle_t const& handle, rmm::device_uvector combined_nbr_intersection_indices(size_t{0}, handle.get_stream()); - [[maybe_unused]] auto combined_nbr_intersection_properties0 = + [[maybe_unused]] auto combined_nbr_intersection_e_property_values0 = cugraph::detail::allocate_optional_dataframe_buffer( size_t{0}, handle.get_stream()); - [[maybe_unused]] auto combined_nbr_intersection_properties1 = + [[maybe_unused]] auto combined_nbr_intersection_e_property_values1 = cugraph::detail::allocate_optional_dataframe_buffer( size_t{0}, handle.get_stream()); @@ -1470,47 +1627,47 @@ nbr_intersection(raft::handle_t const& handle, combined_nbr_intersection_indices.resize(gathered_nbr_intersection_indices.size(), handle.get_stream()); - [[maybe_unused]] auto gathered_nbr_intersection_properties0 = + [[maybe_unused]] auto gathered_nbr_intersection_e_property_values0 = cugraph::detail::allocate_optional_dataframe_buffer( rx_displacements.back() + gathered_nbr_intersection_index_rx_counts.back(), handle.get_stream()); - [[maybe_unused]] auto gathered_nbr_intersection_properties1 = + [[maybe_unused]] auto gathered_nbr_intersection_e_property_values1 = cugraph::detail::allocate_optional_dataframe_buffer( rx_displacements.back() + gathered_nbr_intersection_index_rx_counts.back(), handle.get_stream()); if constexpr (!std::is_same_v) { device_multicast_sendrecv(minor_comm, - rx_v_pair_nbr_intersection_properties0.begin(), + rx_v_pair_nbr_intersection_e_property_values0.begin(), rx_v_pair_nbr_intersection_index_tx_counts, tx_displacements, ranks, - gathered_nbr_intersection_properties0.begin(), + gathered_nbr_intersection_e_property_values0.begin(), gathered_nbr_intersection_index_rx_counts, rx_displacements, ranks, handle.get_stream()); - rx_v_pair_nbr_intersection_properties0.resize(size_t{0}, handle.get_stream()); - rx_v_pair_nbr_intersection_properties0.shrink_to_fit(handle.get_stream()); + rx_v_pair_nbr_intersection_e_property_values0.resize(size_t{0}, handle.get_stream()); + rx_v_pair_nbr_intersection_e_property_values0.shrink_to_fit(handle.get_stream()); - combined_nbr_intersection_properties0.resize(gathered_nbr_intersection_properties0.size(), - handle.get_stream()); + combined_nbr_intersection_e_property_values0.resize( + gathered_nbr_intersection_e_property_values0.size(), handle.get_stream()); device_multicast_sendrecv(minor_comm, - rx_v_pair_nbr_intersection_properties1.begin(), + rx_v_pair_nbr_intersection_e_property_values1.begin(), rx_v_pair_nbr_intersection_index_tx_counts, tx_displacements, ranks, - gathered_nbr_intersection_properties1.begin(), + gathered_nbr_intersection_e_property_values1.begin(), gathered_nbr_intersection_index_rx_counts, rx_displacements, ranks, handle.get_stream()); - rx_v_pair_nbr_intersection_properties1.resize(size_t{0}, handle.get_stream()); - rx_v_pair_nbr_intersection_properties1.shrink_to_fit(handle.get_stream()); - combined_nbr_intersection_properties1.resize(gathered_nbr_intersection_properties1.size(), - handle.get_stream()); + rx_v_pair_nbr_intersection_e_property_values1.resize(size_t{0}, handle.get_stream()); + rx_v_pair_nbr_intersection_e_property_values1.shrink_to_fit(handle.get_stream()); + combined_nbr_intersection_e_property_values1.resize( + gathered_nbr_intersection_e_property_values1.size(), handle.get_stream()); } if constexpr (!std::is_same_v) { @@ -1518,7 +1675,10 @@ nbr_intersection(raft::handle_t const& handle, handle.get_thrust_policy(), thrust::make_counting_iterator(size_t{0}), thrust::make_counting_iterator(rx_v_pair_counts[minor_comm_rank]), - gatherv_indices_t{ + gatherv_indices_t{ rx_v_pair_counts[minor_comm_rank], minor_comm_size, raft::device_span(gathered_nbr_intersection_offsets.data(), @@ -1529,25 +1689,19 @@ nbr_intersection(raft::handle_t const& handle, combined_nbr_intersection_offsets.size()), raft::device_span(combined_nbr_intersection_indices.data(), combined_nbr_intersection_indices.size()), - raft::device_span( - gathered_nbr_intersection_properties0.data(), - gathered_nbr_intersection_properties0.size()), - raft::device_span( - gathered_nbr_intersection_properties1.data(), - gathered_nbr_intersection_properties1.size()), - raft::device_span( - combined_nbr_intersection_properties0.data(), - combined_nbr_intersection_properties0.size()), - raft::device_span( - combined_nbr_intersection_properties1.data(), - combined_nbr_intersection_properties1.size())}); - + gathered_nbr_intersection_e_property_values0.data(), + gathered_nbr_intersection_e_property_values1.data(), + combined_nbr_intersection_e_property_values0.data(), + combined_nbr_intersection_e_property_values1.data()}); } else { thrust::for_each( handle.get_thrust_policy(), thrust::make_counting_iterator(size_t{0}), thrust::make_counting_iterator(rx_v_pair_counts[minor_comm_rank]), - gatherv_indices_t{ + gatherv_indices_t{ rx_v_pair_counts[minor_comm_rank], minor_comm_size, raft::device_span(gathered_nbr_intersection_offsets.data(), @@ -1567,10 +1721,10 @@ nbr_intersection(raft::handle_t const& handle, edge_partition_nbr_intersection_indices.push_back( std::move(combined_nbr_intersection_indices)); if constexpr (!std::is_same_v) { - edge_partition_nbr_intersection_property0.push_back( - std::move(combined_nbr_intersection_properties0)); - edge_partition_nbr_intersection_property1.push_back( - std::move(combined_nbr_intersection_properties1)); + edge_partition_nbr_intersection_e_property_values0.push_back( + std::move(combined_nbr_intersection_e_property_values0)); + edge_partition_nbr_intersection_e_property_values1.push_back( + std::move(combined_nbr_intersection_e_property_values1)); } } @@ -1581,8 +1735,10 @@ nbr_intersection(raft::handle_t const& handle, } nbr_intersection_indices.resize(num_nbr_intersection_indices, handle.get_stream()); if constexpr (!std::is_same_v) { - nbr_intersection_properties0.resize(nbr_intersection_indices.size(), handle.get_stream()); - nbr_intersection_properties1.resize(nbr_intersection_indices.size(), handle.get_stream()); + nbr_intersection_e_property_values0.resize(nbr_intersection_indices.size(), + handle.get_stream()); + nbr_intersection_e_property_values1.resize(nbr_intersection_indices.size(), + handle.get_stream()); } size_t size_offset{0}; size_t index_offset{0}; @@ -1599,14 +1755,14 @@ nbr_intersection(raft::handle_t const& handle, if constexpr (!std::is_same_v) { thrust::copy(handle.get_thrust_policy(), - edge_partition_nbr_intersection_property0[i].begin(), - edge_partition_nbr_intersection_property0[i].end(), - nbr_intersection_properties0.begin() + index_offset); + edge_partition_nbr_intersection_e_property_values0[i].begin(), + edge_partition_nbr_intersection_e_property_values0[i].end(), + nbr_intersection_e_property_values0.begin() + index_offset); thrust::copy(handle.get_thrust_policy(), - edge_partition_nbr_intersection_property1[i].begin(), - edge_partition_nbr_intersection_property1[i].end(), - nbr_intersection_properties1.begin() + index_offset); + edge_partition_nbr_intersection_e_property_values1[i].begin(), + edge_partition_nbr_intersection_e_property_values1[i].end(), + nbr_intersection_e_property_values1.begin() + index_offset); } index_offset += edge_partition_nbr_intersection_indices[i].size(); @@ -1619,13 +1775,18 @@ nbr_intersection(raft::handle_t const& handle, size_first, size_first + nbr_intersection_sizes.size(), nbr_intersection_offsets.begin() + 1); - } else { auto edge_partition = edge_partition_device_view_t( graph_view.local_edge_partition_view(size_t{0})); - auto edge_partition_e_value_input = edge_partition_e_input_device_view_t(edge_value_input, 0); + auto edge_partition_e_mask = + edge_mask_view + ? thrust::make_optional< + detail::edge_partition_edge_property_device_view_t>( + *edge_mask_view, 0) + : thrust::nullopt; + rmm::device_uvector nbr_intersection_sizes( input_size, handle.get_stream()); // initially store minimum degrees (upper bound for intersection sizes) @@ -1636,10 +1797,11 @@ nbr_intersection(raft::handle_t const& handle, vertex_pair_first + input_size, nbr_intersection_sizes.begin(), pick_min_degree_t{nullptr, - raft::device_span(), + raft::device_span(), nullptr, - raft::device_span(), - edge_partition}); + raft::device_span(), + edge_partition, + edge_partition_e_mask}); } else { CUGRAPH_FAIL("unimplemented."); } @@ -1656,51 +1818,51 @@ nbr_intersection(raft::handle_t const& handle, nbr_intersection_indices.resize(nbr_intersection_offsets.back_element(handle.get_stream()), handle.get_stream()); - optional_property_buffer_view_t optional_nbr_intersection_properties0{}; - optional_property_buffer_view_t optional_nbr_intersection_properties1{}; + optional_property_buffer_mutable_view_t optional_nbr_intersection_e_property_values0{}; + optional_property_buffer_mutable_view_t optional_nbr_intersection_e_property_values1{}; if constexpr (!std::is_same_v) { - nbr_intersection_properties0.resize(nbr_intersection_indices.size(), handle.get_stream()); - nbr_intersection_properties1.resize(nbr_intersection_indices.size(), handle.get_stream()); - - optional_nbr_intersection_properties0 = raft::device_span( - nbr_intersection_properties0.data(), nbr_intersection_properties0.size()); + nbr_intersection_e_property_values0.resize(nbr_intersection_indices.size(), + handle.get_stream()); + nbr_intersection_e_property_values1.resize(nbr_intersection_indices.size(), + handle.get_stream()); - optional_nbr_intersection_properties1 = raft::device_span( - nbr_intersection_properties1.data(), nbr_intersection_properties1.size()); + optional_nbr_intersection_e_property_values0 = nbr_intersection_e_property_values0.data(); + optional_nbr_intersection_e_property_values1 = nbr_intersection_e_property_values1.data(); } if (intersect_minor_nbr[0] && intersect_minor_nbr[1]) { - thrust::tabulate( - handle.get_thrust_policy(), - nbr_intersection_sizes.begin(), - nbr_intersection_sizes.end(), - copy_intersecting_nbrs_and_update_intersection_size_t{ - nullptr, - raft::device_span(), - raft::device_span(), - optional_property_buffer_view_t{}, - nullptr, - raft::device_span(), - raft::device_span(), - optional_property_buffer_view_t{}, - edge_partition, - edge_partition_e_value_input, - vertex_pair_first, - raft::device_span(nbr_intersection_offsets.data(), - nbr_intersection_offsets.size()), - raft::device_span(nbr_intersection_indices.data(), - nbr_intersection_indices.size()), - optional_nbr_intersection_properties0, - optional_nbr_intersection_properties1, - invalid_vertex_id::value}); + thrust::tabulate(handle.get_thrust_policy(), + nbr_intersection_sizes.begin(), + nbr_intersection_sizes.end(), + copy_intersecting_nbrs_and_update_intersection_size_t< + void*, + void*, + decltype(vertex_pair_first), + vertex_t, + edge_t, + edge_partition_e_input_device_view_t, + optional_property_buffer_view_t, + optional_property_buffer_mutable_view_t, + false>{nullptr, + raft::device_span(), + raft::device_span(), + optional_property_buffer_view_t{}, + nullptr, + raft::device_span(), + raft::device_span(), + optional_property_buffer_view_t{}, + edge_partition, + edge_partition_e_value_input, + edge_partition_e_mask, + vertex_pair_first, + raft::device_span(nbr_intersection_offsets.data(), + nbr_intersection_offsets.size()), + raft::device_span(nbr_intersection_indices.data(), + nbr_intersection_indices.size()), + optional_nbr_intersection_e_property_values0, + optional_nbr_intersection_e_property_values1, + invalid_vertex_id::value}); } else { CUGRAPH_FAIL("unimplemented."); } @@ -1711,14 +1873,14 @@ nbr_intersection(raft::handle_t const& handle, thrust::count_if(handle.get_thrust_policy(), nbr_intersection_indices.begin(), nbr_intersection_indices.end(), - detail::not_equal_t{invalid_vertex_id::value}), + detail::is_not_equal_t{invalid_vertex_id::value}), handle.get_stream()); - [[maybe_unused]] auto tmp_properties0 = + [[maybe_unused]] auto tmp_property_values0 = cugraph::detail::allocate_optional_dataframe_buffer( tmp_indices.size(), handle.get_stream()); - [[maybe_unused]] auto tmp_properties1 = + [[maybe_unused]] auto tmp_property_values1 = cugraph::detail::allocate_optional_dataframe_buffer( tmp_indices.size(), handle.get_stream()); @@ -1737,39 +1899,38 @@ nbr_intersection(raft::handle_t const& handle, nbr_intersection_indices.begin() + num_scanned, nbr_intersection_indices.begin() + num_scanned + this_scan_size, tmp_indices.begin() + num_copied, - detail::not_equal_t{invalid_vertex_id::value}))); + detail::is_not_equal_t{invalid_vertex_id::value}))); } else { - auto zipped_itr_to_indices_and_properties_begin = - thrust::make_zip_iterator(thrust::make_tuple(nbr_intersection_indices.begin(), - nbr_intersection_properties0.begin(), - nbr_intersection_properties1.begin())); + auto zipped_itr_to_indices_and_e_property_values_begin = thrust::make_zip_iterator( + thrust::make_tuple(nbr_intersection_indices.begin(), + nbr_intersection_e_property_values0.begin(), + nbr_intersection_e_property_values1.begin())); auto zipped_itr_to_tmps_begin = thrust::make_zip_iterator(thrust::make_tuple( - tmp_indices.begin(), tmp_properties0.begin(), tmp_properties1.begin())); + tmp_indices.begin(), tmp_property_values0.begin(), tmp_property_values1.begin())); num_copied += static_cast(thrust::distance( zipped_itr_to_tmps_begin + num_copied, - thrust::copy_if(handle.get_thrust_policy(), - zipped_itr_to_indices_and_properties_begin + num_scanned, - zipped_itr_to_indices_and_properties_begin + num_scanned + this_scan_size, - zipped_itr_to_tmps_begin + num_copied, - [] __device__(auto nbr_p0_p1) { - auto nbr = thrust::get<0>(nbr_p0_p1); - auto p0 = thrust::get<1>(nbr_p0_p1); - auto p1 = thrust::get<2>(nbr_p0_p1); - return thrust::get<0>(nbr_p0_p1) != invalid_vertex_id::value; - }))); + thrust::copy_if( + handle.get_thrust_policy(), + zipped_itr_to_indices_and_e_property_values_begin + num_scanned, + zipped_itr_to_indices_and_e_property_values_begin + num_scanned + this_scan_size, + zipped_itr_to_tmps_begin + num_copied, + [] __device__(auto nbr_p0_p1) { + auto nbr = thrust::get<0>(nbr_p0_p1); + auto p0 = thrust::get<1>(nbr_p0_p1); + auto p1 = thrust::get<2>(nbr_p0_p1); + return thrust::get<0>(nbr_p0_p1) != invalid_vertex_id::value; + }))); } num_scanned += this_scan_size; } nbr_intersection_indices = std::move(tmp_indices); if constexpr (!std::is_same_v) { - nbr_intersection_properties0 = std::move(tmp_properties0); - nbr_intersection_properties1 = std::move(tmp_properties1); + nbr_intersection_e_property_values0 = std::move(tmp_property_values0); + nbr_intersection_e_property_values1 = std::move(tmp_property_values1); } - #else - if constexpr (std::is_same_v) { nbr_intersection_indices.resize( thrust::distance(nbr_intersection_indices.begin(), @@ -1780,10 +1941,10 @@ nbr_intersection(raft::handle_t const& handle, handle.get_stream()); } else { nbr_intersection_indices.resize( - thrust::distance(zipped_itr_to_indices_and_properties_begin, + thrust::distance(zipped_itr_to_indices_and_e_property_values_begin, thrust::remove_if(handle.get_thrust_policy(), - zipped_itr_to_indices_and_properties_begin, - zipped_itr_to_indices_and_properties_begin + + zipped_itr_to_indices_and_e_property_values_begin, + zipped_itr_to_indices_and_e_property_values_begin + nbr_intersection_indices.size(), [] __device__(auto nbr_p0_p1) { return thrust::get<0>(nbr_p0_p1) == @@ -1791,8 +1952,10 @@ nbr_intersection(raft::handle_t const& handle, })), handle.get_stream()); - nbr_intersection_properties0.resize(nbr_intersection_indices.size(), handle.get_stream()); - nbr_intersection_properties1.resize(nbr_intersection_indices.size(), handle.get_stream()); + nbr_intersection_e_property_values0.resize(nbr_intersection_indices.size(), + handle.get_stream()); + nbr_intersection_e_property_values1.resize(nbr_intersection_indices.size(), + handle.get_stream()); } #endif @@ -1811,8 +1974,8 @@ nbr_intersection(raft::handle_t const& handle, } else { return std::make_tuple(std::move(nbr_intersection_offsets), std::move(nbr_intersection_indices), - std::move(nbr_intersection_properties0), - std::move(nbr_intersection_properties1)); + std::move(nbr_intersection_e_property_values0), + std::move(nbr_intersection_e_property_values1)); } } diff --git a/cpp/src/prims/detail/optional_dataframe_buffer.hpp b/cpp/src/prims/detail/optional_dataframe_buffer.hpp index dd40e6932e4..62b2245a651 100644 --- a/cpp/src/prims/detail/optional_dataframe_buffer.hpp +++ b/cpp/src/prims/detail/optional_dataframe_buffer.hpp @@ -97,6 +97,7 @@ void shrink_to_fit_optional_dataframe_buffer( { return shrink_to_fit_dataframe_buffer(optional_dataframe_buffer, stream_view); } + } // namespace detail } // namespace cugraph diff --git a/cpp/src/prims/kv_store.cuh b/cpp/src/prims/kv_store.cuh index c46e83aa5da..f17441ad6ab 100644 --- a/cpp/src/prims/kv_store.cuh +++ b/cpp/src/prims/kv_store.cuh @@ -604,7 +604,7 @@ class kv_cuco_store_t { store_value_offsets.begin() /* map */, store_value_offsets.begin() /* stencil */, get_dataframe_buffer_begin(store_values_), - not_equal_t{std::numeric_limits::max()}); + is_not_equal_t{std::numeric_limits::max()}); } } @@ -649,7 +649,7 @@ class kv_cuco_store_t { store_value_offsets.begin() /* map */, store_value_offsets.begin() /* stencil */, get_dataframe_buffer_begin(store_values_), - not_equal_t{std::numeric_limits::max()}); + is_not_equal_t{std::numeric_limits::max()}); } } @@ -695,7 +695,7 @@ class kv_cuco_store_t { store_value_offsets.begin() /* map */, store_value_offsets.begin() /* stencil */, get_dataframe_buffer_begin(store_values_), - not_equal_t{std::numeric_limits::max()}); + is_not_equal_t{std::numeric_limits::max()}); // now perform assigns (for k,v pairs that failed to insert) diff --git a/cpp/src/prims/per_v_pair_transform_dst_nbr_intersection.cuh b/cpp/src/prims/per_v_pair_transform_dst_nbr_intersection.cuh index 640c3c04bfd..201c08325d7 100644 --- a/cpp/src/prims/per_v_pair_transform_dst_nbr_intersection.cuh +++ b/cpp/src/prims/per_v_pair_transform_dst_nbr_intersection.cuh @@ -112,8 +112,8 @@ struct call_intersection_op_t { IntersectionOp intersection_op{}; size_t const* nbr_offsets{nullptr}; typename GraphViewType::vertex_type const* nbr_indices{nullptr}; - EdgeValueInputIterator nbr_intersection_properties0{nullptr}; - EdgeValueInputIterator nbr_intersection_properties1{nullptr}; + EdgeValueInputIterator nbr_intersection_property_values0{nullptr}; + EdgeValueInputIterator nbr_intersection_property_values1{nullptr}; VertexPairIndexIterator major_minor_pair_index_first{}; VertexPairIterator major_minor_pair_first{}; VertexPairValueOutputIterator major_minor_pair_value_output_first{}; @@ -136,20 +136,20 @@ struct call_intersection_op_t { std::conditional_t, raft::device_span, std::byte /* dummy */> - properties0{}; + property_values0{}; std::conditional_t, raft::device_span, std::byte /* dummy */> - properties1{}; + property_values1{}; if constexpr (!std::is_same_v) { - properties0 = raft::device_span( - nbr_intersection_properties0 + nbr_offsets[i], - nbr_intersection_properties0 + +nbr_offsets[i + 1]); - properties1 = raft::device_span( - nbr_intersection_properties1 + nbr_offsets[i], - nbr_intersection_properties1 + +nbr_offsets[i + 1]); + property_values0 = raft::device_span( + nbr_intersection_property_values0 + nbr_offsets[i], + nbr_intersection_property_values0 + +nbr_offsets[i + 1]); + property_values1 = raft::device_span( + nbr_intersection_property_values1 + nbr_offsets[i], + nbr_intersection_property_values1 + +nbr_offsets[i + 1]); } property_t src_prop{}; @@ -174,8 +174,8 @@ struct call_intersection_op_t { dst_prop = *(vertex_property_first + dst_offset); } - *(major_minor_pair_value_output_first + index) = - intersection_op(src, dst, src_prop, dst_prop, intersection, properties0, properties1); + *(major_minor_pair_value_output_first + index) = intersection_op( + src, dst, src_prop, dst_prop, intersection, property_values0, property_values1); } }; @@ -240,8 +240,6 @@ void per_v_pair_transform_dst_nbr_intersection( using edge_property_value_t = typename EdgeValueInputIterator::value_type; using result_t = typename thrust::iterator_traits::value_type; - CUGRAPH_EXPECTS(!graph_view.has_edge_mask(), "unimplemented."); - if (do_expensive_check) { auto num_invalids = detail::count_invalid_vertex_pairs(handle, graph_view, vertex_pair_first, vertex_pair_last); @@ -384,16 +382,16 @@ void per_v_pair_transform_dst_nbr_intersection( rmm::device_uvector intersection_offsets(size_t{0}, handle.get_stream()); rmm::device_uvector intersection_indices(size_t{0}, handle.get_stream()); - [[maybe_unused]] rmm::device_uvector r_nbr_intersection_properties0( - size_t{0}, handle.get_stream()); - [[maybe_unused]] rmm::device_uvector r_nbr_intersection_properties1( - size_t{0}, handle.get_stream()); + [[maybe_unused]] rmm::device_uvector + r_nbr_intersection_property_values0(size_t{0}, handle.get_stream()); + [[maybe_unused]] rmm::device_uvector + r_nbr_intersection_property_values1(size_t{0}, handle.get_stream()); if constexpr (!std::is_same_v) { std::tie(intersection_offsets, intersection_indices, - r_nbr_intersection_properties0, - r_nbr_intersection_properties1) = + r_nbr_intersection_property_values0, + r_nbr_intersection_property_values1) = detail::nbr_intersection(handle, graph_view, edge_value_input, @@ -422,7 +420,7 @@ void per_v_pair_transform_dst_nbr_intersection( detail::call_intersection_op_t< GraphViewType, decltype(vertex_value_input_for_unique_vertices_first), - typename decltype(r_nbr_intersection_properties0)::const_pointer, + typename decltype(r_nbr_intersection_property_values0)::const_pointer, IntersectionOp, decltype(chunk_vertex_pair_index_first), VertexPairIterator, @@ -433,8 +431,8 @@ void per_v_pair_transform_dst_nbr_intersection( intersection_op, intersection_offsets.data(), intersection_indices.data(), - r_nbr_intersection_properties0.data(), - r_nbr_intersection_properties1.data(), + r_nbr_intersection_property_values0.data(), + r_nbr_intersection_property_values1.data(), chunk_vertex_pair_index_first, vertex_pair_first, vertex_pair_value_output_first}); @@ -445,7 +443,7 @@ void per_v_pair_transform_dst_nbr_intersection( detail::call_intersection_op_t< GraphViewType, VertexValueInputIterator, - typename decltype(r_nbr_intersection_properties0)::const_pointer, + typename decltype(r_nbr_intersection_property_values0)::const_pointer, IntersectionOp, decltype(chunk_vertex_pair_index_first), VertexPairIterator, @@ -456,8 +454,8 @@ void per_v_pair_transform_dst_nbr_intersection( intersection_op, intersection_offsets.data(), intersection_indices.data(), - r_nbr_intersection_properties0.data(), - r_nbr_intersection_properties1.data(), + r_nbr_intersection_property_values0.data(), + r_nbr_intersection_property_values1.data(), chunk_vertex_pair_index_first, vertex_pair_first, vertex_pair_value_output_first}); diff --git a/cpp/src/prims/per_v_random_select_transform_outgoing_e.cuh b/cpp/src/prims/per_v_random_select_transform_outgoing_e.cuh index e6db21f1c7c..5fee97790f1 100644 --- a/cpp/src/prims/per_v_random_select_transform_outgoing_e.cuh +++ b/cpp/src/prims/per_v_random_select_transform_outgoing_e.cuh @@ -941,7 +941,7 @@ per_v_random_select_transform_e(raft::handle_t const& handle, minor_comm_ranks.begin(), thrust::make_zip_iterator( thrust::make_tuple(tmp_sample_local_nbr_indices.begin(), tmp_sample_key_indices.begin())), - not_equal_t{-1}); + is_not_equal_t{-1}); sample_local_nbr_indices = std::move(tmp_sample_local_nbr_indices); sample_key_indices = std::move(tmp_sample_key_indices); diff --git a/cpp/src/prims/per_v_transform_reduce_dst_key_aggregated_outgoing_e.cuh b/cpp/src/prims/per_v_transform_reduce_dst_key_aggregated_outgoing_e.cuh index 2e19adc34c4..d4f8606257e 100644 --- a/cpp/src/prims/per_v_transform_reduce_dst_key_aggregated_outgoing_e.cuh +++ b/cpp/src/prims/per_v_transform_reduce_dst_key_aggregated_outgoing_e.cuh @@ -336,8 +336,14 @@ void per_v_transform_reduce_dst_key_aggregated_outgoing_e( if (edge_partition.number_of_edges() > 0) { auto segment_offsets = graph_view.local_edge_partition_segment_offsets(i); - detail::decompress_edge_partition_to_fill_edgelist_majors( - handle, edge_partition, tmp_majors.data(), segment_offsets); + detail::decompress_edge_partition_to_fill_edgelist_majors( + handle, + edge_partition, + std::nullopt, + raft::device_span(tmp_majors.data(), tmp_majors.size()), + segment_offsets); auto minor_key_first = thrust::make_transform_iterator( edge_partition.indices(), diff --git a/cpp/src/prims/transform_reduce_dst_nbr_intersection_of_e_endpoints_by_v.cuh b/cpp/src/prims/transform_reduce_dst_nbr_intersection_of_e_endpoints_by_v.cuh index f773a102959..bcf7606c423 100644 --- a/cpp/src/prims/transform_reduce_dst_nbr_intersection_of_e_endpoints_by_v.cuh +++ b/cpp/src/prims/transform_reduce_dst_nbr_intersection_of_e_endpoints_by_v.cuh @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -269,10 +270,17 @@ void transform_reduce_dst_nbr_intersection_of_e_endpoints_by_v( vertex_value_output_first + graph_view.local_vertex_partition_range_size(), init); + auto edge_mask_view = graph_view.edge_mask_view(); for (size_t i = 0; i < graph_view.number_of_local_edge_partitions(); ++i) { auto edge_partition = edge_partition_device_view_t( graph_view.local_edge_partition_view(i)); + auto edge_partition_e_mask = + edge_mask_view + ? std::make_optional< + detail::edge_partition_edge_property_device_view_t>( + *edge_mask_view, i) + : std::nullopt; edge_partition_src_input_device_view_t edge_partition_src_value_input{}; edge_partition_dst_input_device_view_t edge_partition_dst_value_input{}; @@ -286,22 +294,29 @@ void transform_reduce_dst_nbr_intersection_of_e_endpoints_by_v( edge_partition_dst_value_input = edge_partition_dst_input_device_view_t(edge_dst_value_input); } - rmm::device_uvector majors(edge_partition.number_of_edges(), handle.get_stream()); + rmm::device_uvector majors( + edge_partition_e_mask + ? detail::count_set_bits( + handle, (*edge_partition_e_mask).value_first(), edge_partition.number_of_edges()) + : static_cast(edge_partition.number_of_edges()), + handle.get_stream()); rmm::device_uvector minors(majors.size(), handle.get_stream()); auto segment_offsets = graph_view.local_edge_partition_segment_offsets(i); detail::decompress_edge_partition_to_edgelist(handle, - edge_partition, - std::nullopt, - std::nullopt, - majors.data(), - minors.data(), - std::nullopt, - std::nullopt, - segment_offsets); + GraphViewType::is_multi_gpu>( + handle, + edge_partition, + std::nullopt, + std::nullopt, + edge_partition_e_mask, + raft::device_span(majors.data(), majors.size()), + raft::device_span(minors.data(), minors.size()), + std::nullopt, + std::nullopt, + segment_offsets); auto vertex_pair_first = thrust::make_zip_iterator(thrust::make_tuple(majors.begin(), minors.begin())); diff --git a/cpp/src/structure/coarsen_graph_impl.cuh b/cpp/src/structure/coarsen_graph_impl.cuh index b8dc28d563e..0704bc6f23b 100644 --- a/cpp/src/structure/coarsen_graph_impl.cuh +++ b/cpp/src/structure/coarsen_graph_impl.cuh @@ -164,16 +164,18 @@ decompress_edge_partition_to_relabeled_and_grouped_and_coarsened_edgelist( ? std::make_optional>( edgelist_majors.size(), handle.get_stream()) : std::nullopt; - detail::decompress_edge_partition_to_edgelist( + detail::decompress_edge_partition_to_edgelist( handle, edge_partition, edge_partition_weight_view, - std::optional>{ - std::nullopt}, - edgelist_majors.data(), - edgelist_minors.data(), - edgelist_weights ? std::optional{(*edgelist_weights).data()} : std::nullopt, - std::optional{std::nullopt}, + std::nullopt, + std::nullopt, + raft::device_span(edgelist_majors.data(), edgelist_majors.size()), + raft::device_span(edgelist_minors.data(), edgelist_minors.size()), + edgelist_weights ? std::make_optional>((*edgelist_weights).data(), + (*edgelist_weights).size()) + : std::nullopt, + std::nullopt, segment_offsets); auto pair_first = diff --git a/cpp/src/structure/decompress_to_edgelist_impl.cuh b/cpp/src/structure/decompress_to_edgelist_impl.cuh index d653307c620..e9e84f5bf15 100644 --- a/cpp/src/structure/decompress_to_edgelist_impl.cuh +++ b/cpp/src/structure/decompress_to_edgelist_impl.cuh @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -80,8 +81,11 @@ decompress_to_edgelist_impl( std::vector edgelist_edge_counts(graph_view.number_of_local_edge_partitions(), size_t{0}); for (size_t i = 0; i < edgelist_edge_counts.size(); ++i) { - edgelist_edge_counts[i] = - static_cast(graph_view.number_of_local_edge_partition_edges(i)); + edgelist_edge_counts[i] = graph_view.local_edge_partition_view(i).number_of_edges(); + if (graph_view.has_edge_mask()) { + edgelist_edge_counts[i] = detail::count_set_bits( + handle, (*(graph_view.edge_mask_view())).value_firsts()[i], edgelist_edge_counts[i]); + } } auto number_of_local_edges = std::reduce(edgelist_edge_counts.begin(), edgelist_edge_counts.end()); @@ -97,7 +101,7 @@ decompress_to_edgelist_impl( size_t cur_size{0}; for (size_t i = 0; i < edgelist_edge_counts.size(); ++i) { - detail::decompress_edge_partition_to_edgelist( + detail::decompress_edge_partition_to_edgelist( handle, edge_partition_device_view_t( graph_view.local_edge_partition_view(i)), @@ -110,11 +114,19 @@ decompress_to_edgelist_impl( detail::edge_partition_edge_property_device_view_t>( (*edge_id_view), i) : std::nullopt, - edgelist_majors.data() + cur_size, - edgelist_minors.data() + cur_size, - edgelist_weights ? std::optional{(*edgelist_weights).data() + cur_size} + graph_view.has_edge_mask() + ? std::make_optional< + detail::edge_partition_edge_property_device_view_t>( + *(graph_view.edge_mask_view()), i) + : std::nullopt, + raft::device_span(edgelist_majors.data() + cur_size, edgelist_edge_counts[i]), + raft::device_span(edgelist_minors.data() + cur_size, edgelist_edge_counts[i]), + edgelist_weights ? std::make_optional>( + (*edgelist_weights).data() + cur_size, edgelist_edge_counts[i]) : std::nullopt, - edgelist_ids ? std::optional{(*edgelist_ids).data() + cur_size} : std::nullopt, + edgelist_ids ? std::make_optional>( + (*edgelist_ids).data() + cur_size, edgelist_edge_counts[i]) + : std::nullopt, graph_view.local_edge_partition_segment_offsets(i)); cur_size += edgelist_edge_counts[i]; } @@ -231,8 +243,13 @@ decompress_to_edgelist_impl( if (do_expensive_check) { /* currently, nothing to do */ } - rmm::device_uvector edgelist_majors(graph_view.number_of_local_edge_partition_edges(), - handle.get_stream()); + auto num_edges = graph_view.local_edge_partition_view().number_of_edges(); + if (graph_view.has_edge_mask()) { + num_edges = + detail::count_set_bits(handle, (*(graph_view.edge_mask_view())).value_firsts()[0], num_edges); + } + + rmm::device_uvector edgelist_majors(num_edges, handle.get_stream()); rmm::device_uvector edgelist_minors(edgelist_majors.size(), handle.get_stream()); auto edgelist_weights = edge_weight_view ? std::make_optional>( edgelist_majors.size(), handle.get_stream()) @@ -240,7 +257,7 @@ decompress_to_edgelist_impl( auto edgelist_ids = edge_id_view ? std::make_optional>( edgelist_majors.size(), handle.get_stream()) : std::nullopt; - detail::decompress_edge_partition_to_edgelist( + detail::decompress_edge_partition_to_edgelist( handle, edge_partition_device_view_t( graph_view.local_edge_partition_view()), @@ -253,10 +270,19 @@ decompress_to_edgelist_impl( detail::edge_partition_edge_property_device_view_t>( (*edge_id_view), 0) : std::nullopt, - edgelist_majors.data(), - edgelist_minors.data(), - edgelist_weights ? std::optional{(*edgelist_weights).data()} : std::nullopt, - edgelist_ids ? std::optional{(*edgelist_ids).data()} : std::nullopt, + graph_view.has_edge_mask() + ? std::make_optional< + detail::edge_partition_edge_property_device_view_t>( + *(graph_view.edge_mask_view()), 0) + : std::nullopt, + raft::device_span(edgelist_majors.data(), edgelist_majors.size()), + raft::device_span(edgelist_minors.data(), edgelist_minors.size()), + edgelist_weights ? std::make_optional>((*edgelist_weights).data(), + (*edgelist_weights).size()) + : std::nullopt, + edgelist_ids ? std::make_optional>((*edgelist_ids).data(), + (*edgelist_ids).size()) + : std::nullopt, graph_view.local_edge_partition_segment_offsets()); if (renumber_map) { @@ -294,8 +320,6 @@ decompress_to_edgelist( std::optional> renumber_map, bool do_expensive_check) { - CUGRAPH_EXPECTS(!graph_view.has_edge_mask(), "unimplemented."); - return decompress_to_edgelist_impl( handle, graph_view, edge_weight_view, edge_id_view, renumber_map, do_expensive_check); } diff --git a/cpp/src/structure/graph_impl.cuh b/cpp/src/structure/graph_impl.cuh index 97975897e08..75862266789 100644 --- a/cpp/src/structure/graph_impl.cuh +++ b/cpp/src/structure/graph_impl.cuh @@ -213,16 +213,17 @@ update_local_sorted_unique_edge_majors_minors( thrust::make_counting_iterator(minor_range_first + num_scanned), thrust::make_counting_iterator(minor_range_first + num_scanned + this_scan_size), unique_edge_minors.begin() + num_copied, - cugraph::detail::check_bit_set_t{minor_bitmaps.data(), minor_range_first}))); + cugraph::detail::check_bit_set_t{minor_bitmaps.data(), + minor_range_first}))); num_scanned += this_scan_size; } #else - thrust::copy_if( - handle.get_thrust_policy(), - thrust::make_counting_iterator(minor_range_first), - thrust::make_counting_iterator(minor_range_last), - unique_edge_minors.begin(), - cugraph::detail::check_bit_set_t{minor_bitmaps.data(), minor_range_first}); + thrust::copy_if(handle.get_thrust_policy(), + thrust::make_counting_iterator(minor_range_first), + thrust::make_counting_iterator(minor_range_last), + unique_edge_minors.begin(), + cugraph::detail::check_bit_set_t{ + minor_bitmaps.data(), minor_range_first}); #endif auto num_chunks = diff --git a/cpp/src/structure/graph_view_impl.cuh b/cpp/src/structure/graph_view_impl.cuh index 7626784c13c..64a8a3212b3 100644 --- a/cpp/src/structure/graph_view_impl.cuh +++ b/cpp/src/structure/graph_view_impl.cuh @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -106,6 +107,7 @@ rmm::device_uvector compute_major_degrees( std::vector const& edge_partition_offsets, std::optional> const& edge_partition_dcs_nzd_vertices, std::optional> const& edge_partition_dcs_nzd_vertex_counts, + std::optional> const& edge_partition_masks, partition_t const& partition, std::vector const& edge_partition_segment_offsets) { @@ -142,7 +144,9 @@ rmm::device_uvector compute_major_degrees( vertex_t major_range_last{}; std::tie(major_range_first, major_range_last) = partition.vertex_partition_range(major_range_vertex_partition_id); - auto p_offsets = edge_partition_offsets[i]; + auto offsets = edge_partition_offsets[i]; + auto masks = + edge_partition_masks ? thrust::make_optional((*edge_partition_masks)[i]) : thrust::nullopt; auto segment_offset_size_per_partition = edge_partition_segment_offsets.size() / static_cast(minor_comm_size); auto major_hypersparse_first = @@ -155,9 +159,16 @@ rmm::device_uvector compute_major_degrees( thrust::make_counting_iterator(vertex_t{0}), thrust::make_counting_iterator(major_hypersparse_first - major_range_first), local_degrees.begin(), - [p_offsets] __device__(auto i) { return p_offsets[i + 1] - p_offsets[i]; }); + [offsets, masks] __device__(auto i) { + auto local_degree = offsets[i + 1] - offsets[i]; + if (masks) { + local_degree = static_cast( + detail::count_set_bits(*masks, offsets[i], local_degree)); + } + return local_degree; + }); if (use_dcs) { - auto p_dcs_nzd_vertices = (*edge_partition_dcs_nzd_vertices)[i]; + auto dcs_nzd_vertices = (*edge_partition_dcs_nzd_vertices)[i]; auto dcs_nzd_vertex_count = (*edge_partition_dcs_nzd_vertex_counts)[i]; thrust::fill(execution_policy, local_degrees.begin() + (major_hypersparse_first - major_range_first), @@ -166,15 +177,20 @@ rmm::device_uvector compute_major_degrees( thrust::for_each(execution_policy, thrust::make_counting_iterator(vertex_t{0}), thrust::make_counting_iterator(dcs_nzd_vertex_count), - [p_offsets, - p_dcs_nzd_vertices, + [offsets, + dcs_nzd_vertices, + masks, major_range_first, major_hypersparse_first, local_degrees = local_degrees.data()] __device__(auto i) { - auto d = p_offsets[(major_hypersparse_first - major_range_first) + i + 1] - - p_offsets[(major_hypersparse_first - major_range_first) + i]; - auto v = p_dcs_nzd_vertices[i]; - local_degrees[v - major_range_first] = d; + auto major_idx = (major_hypersparse_first - major_range_first) + i; + auto local_degree = offsets[major_idx + 1] - offsets[major_idx]; + if (masks) { + local_degree = static_cast( + detail::count_set_bits(*masks, offsets[major_idx], local_degree)); + } + auto v = dcs_nzd_vertices[i]; + local_degrees[v - major_range_first] = local_degree; }); } minor_comm.reduce(local_degrees.data(), @@ -193,12 +209,21 @@ rmm::device_uvector compute_major_degrees( template rmm::device_uvector compute_major_degrees(raft::handle_t const& handle, edge_t const* offsets, + std::optional masks, vertex_t number_of_vertices) { rmm::device_uvector degrees(number_of_vertices, handle.get_stream()); thrust::tabulate( - handle.get_thrust_policy(), degrees.begin(), degrees.end(), [offsets] __device__(auto i) { - return offsets[i + 1] - offsets[i]; + handle.get_thrust_policy(), + degrees.begin(), + degrees.end(), + [offsets, masks = masks ? thrust::make_optional(*masks) : thrust::nullopt] __device__(auto i) { + auto local_degree = offsets[i + 1] - offsets[i]; + if (masks) { + local_degree = + static_cast(detail::count_set_bits(*masks, offsets[i], local_degree)); + } + return local_degree; }); return degrees; } @@ -512,16 +537,18 @@ rmm::device_uvector graph_view_t>:: compute_in_degrees(raft::handle_t const& handle) const { - CUGRAPH_EXPECTS(!has_edge_mask(), "unimplemented."); - if (store_transposed) { return compute_major_degrees(handle, this->edge_partition_offsets_, this->edge_partition_dcs_nzd_vertices_, this->edge_partition_dcs_nzd_vertex_counts_, + this->has_edge_mask() + ? std::make_optional((*(this->edge_mask_view())).value_firsts()) + : std::nullopt, this->partition_, this->edge_partition_segment_offsets_); } else { + CUGRAPH_EXPECTS(!has_edge_mask(), "unimplemented."); return compute_minor_degrees(handle, *this); } } @@ -531,11 +558,15 @@ rmm::device_uvector graph_view_t>:: compute_in_degrees(raft::handle_t const& handle) const { - CUGRAPH_EXPECTS(!has_edge_mask(), "unimplemented."); - if (store_transposed) { - return compute_major_degrees(handle, this->offsets_, this->local_vertex_partition_range_size()); + return compute_major_degrees( + handle, + this->offsets_, + this->has_edge_mask() ? std::make_optional((*(this->edge_mask_view())).value_firsts()[0]) + : std::nullopt, + this->local_vertex_partition_range_size()); } else { + CUGRAPH_EXPECTS(!has_edge_mask(), "unimplemented."); return compute_minor_degrees(handle, *this); } } @@ -545,15 +576,17 @@ rmm::device_uvector graph_view_t>:: compute_out_degrees(raft::handle_t const& handle) const { - CUGRAPH_EXPECTS(!has_edge_mask(), "unimplemented."); - if (store_transposed) { + CUGRAPH_EXPECTS(!has_edge_mask(), "unimplemented."); return compute_minor_degrees(handle, *this); } else { return compute_major_degrees(handle, this->edge_partition_offsets_, this->edge_partition_dcs_nzd_vertices_, this->edge_partition_dcs_nzd_vertex_counts_, + this->has_edge_mask() + ? std::make_optional((*(this->edge_mask_view())).value_firsts()) + : std::nullopt, this->partition_, this->edge_partition_segment_offsets_); } @@ -564,12 +597,16 @@ rmm::device_uvector graph_view_t>:: compute_out_degrees(raft::handle_t const& handle) const { - CUGRAPH_EXPECTS(!has_edge_mask(), "unimplemented."); - if (store_transposed) { + CUGRAPH_EXPECTS(!has_edge_mask(), "unimplemented."); return compute_minor_degrees(handle, *this); } else { - return compute_major_degrees(handle, this->offsets_, this->local_vertex_partition_range_size()); + return compute_major_degrees( + handle, + this->offsets_, + this->has_edge_mask() ? std::make_optional((*(this->edge_mask_view())).value_firsts()[0]) + : std::nullopt, + this->local_vertex_partition_range_size()); } } diff --git a/cpp/tests/CMakeLists.txt b/cpp/tests/CMakeLists.txt index 2a4bb8ab2a5..6775ed2eb16 100644 --- a/cpp/tests/CMakeLists.txt +++ b/cpp/tests/CMakeLists.txt @@ -288,10 +288,6 @@ ConfigureTest(GENERATE_RMAT_TEST generators/generate_rmat_test.cpp) ConfigureTest(GENERATE_BIPARTITE_RMAT_TEST generators/generate_bipartite_rmat_test.cpp GPUS 1 PERCENT 100) -################################################################################################### -# - Graph mask tests ------------------------------------------------------------------------------ -ConfigureTest(GRAPH_MASK_TEST structure/graph_mask_test.cpp) - ################################################################################################### # - Symmetrize tests ------------------------------------------------------------------------------ ConfigureTest(SYMMETRIZE_TEST structure/symmetrize_test.cpp) diff --git a/cpp/tests/prims/mg_per_v_pair_transform_dst_nbr_intersection.cu b/cpp/tests/prims/mg_per_v_pair_transform_dst_nbr_intersection.cu index a7cd8a989b0..a3edb1f6372 100644 --- a/cpp/tests/prims/mg_per_v_pair_transform_dst_nbr_intersection.cu +++ b/cpp/tests/prims/mg_per_v_pair_transform_dst_nbr_intersection.cu @@ -21,9 +21,13 @@ #include #include +#include +#include #include +#include #include +#include #include #include #include @@ -61,6 +65,7 @@ struct intersection_op_t { struct Prims_Usecase { size_t num_vertex_pairs{0}; + bool edge_masking{false}; bool check_correctness{true}; }; @@ -78,7 +83,7 @@ class Tests_MGPerVPairTransformDstNbrIntersection virtual void TearDown() {} // Verify the results of per_v_pair_transform_dst_nbr_intersection primitive - template + template void run_current_test(Prims_Usecase const& prims_usecase, input_usecase_t const& input_usecase) { HighResTimer hr_timer{}; @@ -109,6 +114,34 @@ class Tests_MGPerVPairTransformDstNbrIntersection auto mg_graph_view = mg_graph.view(); + std::optional> edge_mask{std::nullopt}; + if (prims_usecase.edge_masking) { + cugraph::edge_src_property_t edge_src_renumber_map( + *handle_, mg_graph_view); + cugraph::edge_dst_property_t edge_dst_renumber_map( + *handle_, mg_graph_view); + cugraph::update_edge_src_property( + *handle_, mg_graph_view, (*mg_renumber_map).begin(), edge_src_renumber_map); + cugraph::update_edge_dst_property( + *handle_, mg_graph_view, (*mg_renumber_map).begin(), edge_dst_renumber_map); + + edge_mask = cugraph::edge_property_t(*handle_, mg_graph_view); + + cugraph::transform_e( + *handle_, + mg_graph_view, + edge_src_renumber_map.view(), + edge_dst_renumber_map.view(), + cugraph::edge_dummy_property_t{}.view(), + [] __device__(auto src, auto dst, auto src_property, auto dst_property, thrust::nullopt_t) { + return ((src_property % 2 == 0) && (dst_property % 2 == 0)) + ? false + : true; // mask out the edges with even unrenumbered src & dst vertex IDs + }, + (*edge_mask).mutable_view()); + mg_graph_view.attach_edge_mask((*edge_mask).view()); + } + // 2. run MG per_v_pair_transform_dst_nbr_intersection primitive ASSERT_TRUE( @@ -224,6 +257,42 @@ class Tests_MGPerVPairTransformDstNbrIntersection if (handle_->get_comms().get_rank() == 0) { auto sg_graph_view = sg_graph.view(); + if (prims_usecase.edge_masking) { + rmm::device_uvector srcs(0, handle_->get_stream()); + rmm::device_uvector dsts(0, handle_->get_stream()); + std::tie(srcs, dsts, std::ignore, std::ignore) = + cugraph::decompress_to_edgelist( + *handle_, sg_graph_view, std::nullopt, std::nullopt, std::nullopt); + auto edge_first = thrust::make_zip_iterator(srcs.begin(), dsts.begin()); + srcs.resize(thrust::distance(edge_first, + thrust::remove_if(handle_->get_thrust_policy(), + edge_first, + edge_first + srcs.size(), + [] __device__(auto pair) { + return (thrust::get<0>(pair) % 2 == 0) && + (thrust::get<1>(pair) % 2 == 0); + })), + handle_->get_stream()); + dsts.resize(srcs.size(), handle_->get_stream()); + rmm::device_uvector vertices(sg_graph_view.number_of_vertices(), + handle_->get_stream()); + thrust::sequence( + handle_->get_thrust_policy(), vertices.begin(), vertices.end(), vertex_t{0}); + std::tie(sg_graph, std::ignore, std::ignore, std::ignore, std::ignore) = cugraph:: + create_graph_from_edgelist( + *handle_, + std::move(vertices), + std::move(srcs), + std::move(dsts), + std::nullopt, + std::nullopt, + std::nullopt, + cugraph::graph_properties_t{sg_graph_view.is_symmetric(), + sg_graph_view.is_multigraph()}, + false); + sg_graph_view = sg_graph.view(); + } + auto sg_result_buffer = cugraph::allocate_dataframe_buffer>( cugraph::size_dataframe_buffer(mg_aggregate_vertex_pair_buffer), handle_->get_stream()); auto sg_out_degrees = sg_graph_view.compute_out_degrees(*handle_); @@ -262,47 +331,16 @@ using Tests_MGPerVPairTransformDstNbrIntersection_File = using Tests_MGPerVPairTransformDstNbrIntersection_Rmat = Tests_MGPerVPairTransformDstNbrIntersection; -TEST_P(Tests_MGPerVPairTransformDstNbrIntersection_File, CheckInt32Int32FloatTupleIntFloat) -{ - auto param = GetParam(); - run_current_test>(std::get<0>(param), - std::get<1>(param)); -} - -TEST_P(Tests_MGPerVPairTransformDstNbrIntersection_Rmat, CheckInt32Int32FloatTupleIntFloat) -{ - auto param = GetParam(); - run_current_test>( - std::get<0>(param), - cugraph::test::override_Rmat_Usecase_with_cmd_line_arguments(std::get<1>(param))); -} - -TEST_P(Tests_MGPerVPairTransformDstNbrIntersection_Rmat, CheckInt32Int64FloatTupleIntFloat) -{ - auto param = GetParam(); - run_current_test>( - std::get<0>(param), - cugraph::test::override_Rmat_Usecase_with_cmd_line_arguments(std::get<1>(param))); -} - -TEST_P(Tests_MGPerVPairTransformDstNbrIntersection_Rmat, CheckInt64Int64FloatTupleIntFloat) -{ - auto param = GetParam(); - run_current_test>( - std::get<0>(param), - cugraph::test::override_Rmat_Usecase_with_cmd_line_arguments(std::get<1>(param))); -} - TEST_P(Tests_MGPerVPairTransformDstNbrIntersection_File, CheckInt32Int32Float) { auto param = GetParam(); - run_current_test(std::get<0>(param), std::get<1>(param)); + run_current_test(std::get<0>(param), std::get<1>(param)); } TEST_P(Tests_MGPerVPairTransformDstNbrIntersection_Rmat, CheckInt32Int32Float) { auto param = GetParam(); - run_current_test( + run_current_test( std::get<0>(param), cugraph::test::override_Rmat_Usecase_with_cmd_line_arguments(std::get<1>(param))); } @@ -310,7 +348,7 @@ TEST_P(Tests_MGPerVPairTransformDstNbrIntersection_Rmat, CheckInt32Int32Float) TEST_P(Tests_MGPerVPairTransformDstNbrIntersection_Rmat, CheckInt32Int64Float) { auto param = GetParam(); - run_current_test( + run_current_test( std::get<0>(param), cugraph::test::override_Rmat_Usecase_with_cmd_line_arguments(std::get<1>(param))); } @@ -318,7 +356,7 @@ TEST_P(Tests_MGPerVPairTransformDstNbrIntersection_Rmat, CheckInt32Int64Float) TEST_P(Tests_MGPerVPairTransformDstNbrIntersection_Rmat, CheckInt64Int64Float) { auto param = GetParam(); - run_current_test( + run_current_test( std::get<0>(param), cugraph::test::override_Rmat_Usecase_with_cmd_line_arguments(std::get<1>(param))); } @@ -327,15 +365,18 @@ INSTANTIATE_TEST_SUITE_P( file_test, Tests_MGPerVPairTransformDstNbrIntersection_File, ::testing::Combine( - ::testing::Values(Prims_Usecase{size_t{1024}, true}), + ::testing::Values(Prims_Usecase{size_t{1024}, false, true}, + Prims_Usecase{size_t{1024}, true, true}), ::testing::Values(cugraph::test::File_Usecase("test/datasets/karate.mtx"), cugraph::test::File_Usecase("test/datasets/netscience.mtx")))); -INSTANTIATE_TEST_SUITE_P(rmat_small_test, - Tests_MGPerVPairTransformDstNbrIntersection_Rmat, - ::testing::Combine(::testing::Values(Prims_Usecase{size_t{1024}, true}), - ::testing::Values(cugraph::test::Rmat_Usecase( - 10, 16, 0.57, 0.19, 0.19, 0, false, false)))); +INSTANTIATE_TEST_SUITE_P( + rmat_small_test, + Tests_MGPerVPairTransformDstNbrIntersection_Rmat, + ::testing::Combine( + ::testing::Values(Prims_Usecase{size_t{1024}, false, true}, + Prims_Usecase{size_t{1024}, true, true}), + ::testing::Values(cugraph::test::Rmat_Usecase(10, 16, 0.57, 0.19, 0.19, 0, false, false)))); INSTANTIATE_TEST_SUITE_P( rmat_benchmark_test, /* note that scale & edge factor can be overridden in benchmarking (with @@ -345,7 +386,8 @@ INSTANTIATE_TEST_SUITE_P( factor (to avoid running same benchmarks more than once) */ Tests_MGPerVPairTransformDstNbrIntersection_Rmat, ::testing::Combine( - ::testing::Values(Prims_Usecase{size_t{1024 * 1024}, false}), + ::testing::Values(Prims_Usecase{size_t{1024 * 1024}, false, false}, + Prims_Usecase{size_t{1024 * 1024}, true, false}), ::testing::Values(cugraph::test::Rmat_Usecase(20, 32, 0.57, 0.19, 0.19, 0, false, false)))); CUGRAPH_MG_TEST_PROGRAM_MAIN() diff --git a/cpp/tests/prims/mg_per_v_pair_transform_dst_nbr_weighted_intersection.cu b/cpp/tests/prims/mg_per_v_pair_transform_dst_nbr_weighted_intersection.cu index 3b6a6b9c4c5..4d05b0c9e65 100644 --- a/cpp/tests/prims/mg_per_v_pair_transform_dst_nbr_weighted_intersection.cu +++ b/cpp/tests/prims/mg_per_v_pair_transform_dst_nbr_weighted_intersection.cu @@ -50,14 +50,14 @@ template struct intersection_op_t { - __device__ thrust::tuple operator()( + __device__ thrust::tuple operator()( vertex_t a, vertex_t b, weight_t weight_a /* weighted out degree */, weight_t weight_b /* weighted out degree */, raft::device_span intersection, - raft::device_span intersected_properties_a, - raft::device_span intersected_properties_b) const + raft::device_span intersected_property_values_a, + raft::device_span intersected_property_values_b) const { weight_t min_weight_a_intersect_b = weight_t{0}; weight_t max_weight_a_intersect_b = weight_t{0}; @@ -65,10 +65,12 @@ struct intersection_op_t { weight_t sum_of_intersected_b = weight_t{0}; for (size_t k = 0; k < intersection.size(); k++) { - min_weight_a_intersect_b += min(intersected_properties_a[k], intersected_properties_b[k]); - max_weight_a_intersect_b += max(intersected_properties_a[k], intersected_properties_b[k]); - sum_of_intersected_a += intersected_properties_a[k]; - sum_of_intersected_b += intersected_properties_b[k]; + min_weight_a_intersect_b += + min(intersected_property_values_a[k], intersected_property_values_b[k]); + max_weight_a_intersect_b += + max(intersected_property_values_a[k], intersected_property_values_b[k]); + sum_of_intersected_a += intersected_property_values_a[k]; + sum_of_intersected_b += intersected_property_values_b[k]; } weight_t sum_of_uniq_a = weight_a - sum_of_intersected_a; @@ -99,7 +101,7 @@ class Tests_MGPerVPairTransformDstNbrIntersection virtual void TearDown() {} // Verify the results of per_v_pair_transform_dst_nbr_intersection primitive - template + template void run_current_test(Prims_Usecase const& prims_usecase, input_usecase_t const& input_usecase) { HighResTimer hr_timer{}; @@ -189,7 +191,7 @@ class Tests_MGPerVPairTransformDstNbrIntersection auto mg_result_buffer = cugraph::allocate_dataframe_buffer>( cugraph::size_dataframe_buffer(mg_vertex_pair_buffer), handle_->get_stream()); - auto mg_out_degrees = mg_graph_view.compute_out_degrees(*handle_); + auto mg_out_weight_sums = compute_out_weight_sums(*handle_, mg_graph_view, mg_edge_weight_view); if (cugraph::test::g_perf) { RAFT_CUDA_TRY(cudaDeviceSynchronize()); // for consistent performance measurement @@ -197,9 +199,6 @@ class Tests_MGPerVPairTransformDstNbrIntersection hr_timer.start("MG per_v_pair_transform_dst_nbr_intersection"); } - rmm::device_uvector mg_out_weight_sums = - compute_out_weight_sums(*handle_, mg_graph_view, mg_edge_weight_view); - cugraph::per_v_pair_transform_dst_nbr_intersection( *handle_, mg_graph_view, @@ -272,7 +271,6 @@ class Tests_MGPerVPairTransformDstNbrIntersection if (handle_->get_comms().get_rank() == 0) { auto sg_graph_view = sg_graph.view(); - auto sg_result_buffer = cugraph::allocate_dataframe_buffer>( cugraph::size_dataframe_buffer(mg_aggregate_vertex_pair_buffer), handle_->get_stream()); @@ -290,11 +288,21 @@ class Tests_MGPerVPairTransformDstNbrIntersection */), sg_out_weight_sums.begin(), intersection_op_t{}, cugraph::get_dataframe_buffer_begin(sg_result_buffer)); + auto threshold_ratio = weight_t{1e-4}; + auto threshold_magnitude = std::numeric_limits::min(); + auto nearly_equal = [threshold_ratio, threshold_magnitude] __device__(auto lhs, auto rhs) { + return (fabs(thrust::get<0>(lhs) - thrust::get<0>(rhs)) < + max(max(thrust::get<0>(lhs), thrust::get<0>(rhs)) * threshold_ratio, + threshold_magnitude)) && + (fabs(thrust::get<1>(lhs) - thrust::get<1>(rhs)) < + max(max(thrust::get<1>(lhs), thrust::get<1>(rhs)) * threshold_ratio, + threshold_magnitude)); + }; bool valid = thrust::equal(handle_->get_thrust_policy(), cugraph::get_dataframe_buffer_begin(mg_aggregate_result_buffer), cugraph::get_dataframe_buffer_end(mg_aggregate_result_buffer), - cugraph::get_dataframe_buffer_begin(sg_result_buffer)); - + cugraph::get_dataframe_buffer_begin(sg_result_buffer), + nearly_equal); ASSERT_TRUE(valid); } } @@ -313,47 +321,16 @@ using Tests_MGPerVPairTransformDstNbrIntersection_File = using Tests_MGPerVPairTransformDstNbrIntersection_Rmat = Tests_MGPerVPairTransformDstNbrIntersection; -TEST_P(Tests_MGPerVPairTransformDstNbrIntersection_File, CheckInt32Int32FloatTupleIntFloat) -{ - auto param = GetParam(); - run_current_test>(std::get<0>(param), - std::get<1>(param)); -} - -TEST_P(Tests_MGPerVPairTransformDstNbrIntersection_Rmat, CheckInt32Int32FloatTupleIntFloat) -{ - auto param = GetParam(); - run_current_test>( - std::get<0>(param), - cugraph::test::override_Rmat_Usecase_with_cmd_line_arguments(std::get<1>(param))); -} - -TEST_P(Tests_MGPerVPairTransformDstNbrIntersection_Rmat, CheckInt32Int64FloatTupleIntFloat) -{ - auto param = GetParam(); - run_current_test>( - std::get<0>(param), - cugraph::test::override_Rmat_Usecase_with_cmd_line_arguments(std::get<1>(param))); -} - -TEST_P(Tests_MGPerVPairTransformDstNbrIntersection_Rmat, CheckInt64Int64FloatTupleIntFloat) -{ - auto param = GetParam(); - run_current_test>( - std::get<0>(param), - cugraph::test::override_Rmat_Usecase_with_cmd_line_arguments(std::get<1>(param))); -} - TEST_P(Tests_MGPerVPairTransformDstNbrIntersection_File, CheckInt32Int32Float) { auto param = GetParam(); - run_current_test(std::get<0>(param), std::get<1>(param)); + run_current_test(std::get<0>(param), std::get<1>(param)); } TEST_P(Tests_MGPerVPairTransformDstNbrIntersection_Rmat, CheckInt32Int32Float) { auto param = GetParam(); - run_current_test( + run_current_test( std::get<0>(param), cugraph::test::override_Rmat_Usecase_with_cmd_line_arguments(std::get<1>(param))); } @@ -361,7 +338,7 @@ TEST_P(Tests_MGPerVPairTransformDstNbrIntersection_Rmat, CheckInt32Int32Float) TEST_P(Tests_MGPerVPairTransformDstNbrIntersection_Rmat, CheckInt32Int64Float) { auto param = GetParam(); - run_current_test( + run_current_test( std::get<0>(param), cugraph::test::override_Rmat_Usecase_with_cmd_line_arguments(std::get<1>(param))); } @@ -369,7 +346,7 @@ TEST_P(Tests_MGPerVPairTransformDstNbrIntersection_Rmat, CheckInt32Int64Float) TEST_P(Tests_MGPerVPairTransformDstNbrIntersection_Rmat, CheckInt64Int64Float) { auto param = GetParam(); - run_current_test( + run_current_test( std::get<0>(param), cugraph::test::override_Rmat_Usecase_with_cmd_line_arguments(std::get<1>(param))); } diff --git a/cpp/tests/structure/graph_mask_test.cpp b/cpp/tests/structure/graph_mask_test.cpp deleted file mode 100644 index fc704d683a7..00000000000 --- a/cpp/tests/structure/graph_mask_test.cpp +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (c) 2022, NVIDIA CORPORATION. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governin_from_mtxg permissions and - * limitations under the License. - */ - -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include - -TEST(Test_GraphMask, BasicGraphMaskTestInt64) -{ - raft::handle_t handle; - - int number_of_vertices = 500; - int number_of_edges = 1000; - - cugraph::graph_mask_t mask( - handle, number_of_vertices, number_of_edges); - - auto mask_view = mask.view(); - - ASSERT_EQ(false, mask.has_vertex_mask()); - ASSERT_EQ(false, mask.has_edge_mask()); - ASSERT_EQ(false, mask_view.has_vertex_mask()); - ASSERT_EQ(false, mask_view.has_edge_mask()); - - mask.initialize_vertex_mask(); - mask.initialize_edge_mask(); - - auto mask_view2 = mask.view(); - - ASSERT_EQ(true, mask.has_vertex_mask()); - ASSERT_EQ(true, mask.has_edge_mask()); - ASSERT_EQ(true, mask_view2.has_vertex_mask()); - ASSERT_EQ(true, mask_view2.has_edge_mask()); -} \ No newline at end of file From e0c3a9391851528407bc085eeab202e6c260c9f4 Mon Sep 17 00:00:00 2001 From: Andrei Ivanov <32910461+drivanov@users.noreply.github.com> Date: Mon, 30 Oct 2023 06:10:52 -0700 Subject: [PATCH 062/111] Fix an issue occurring in the cuGraph-DGL example for "mixed" mode. (#3927) Fixing the following bug: ``` Training in mixed mode. Loading data Traceback (most recent call last): File "/opt/rapids/cugraph/python/cugraph-dgl/examples/graphsage/node-classification.py", line 249, in g.get_node_storage(key="feat", ntype="_N") File "/usr/local/lib/python3.10/dist-packages/dgl/frame.py", line 530, in fetch return super().fetch(indices, device, pin_memory=pin_memory, **kwargs) File "/usr/local/lib/python3.10/dist-packages/dgl/storages/pytorch_tensor.py", line 40, in fetch raise ValueError( ValueError: Got indices on device cuda:0 whereas the feature tensor is on cpu. Please either (1) move the graph to GPU with to() method, or (2) pin the graph with pin_memory_() method. ``` which appears in `mixed` mode. NOTE: the option `(1) move the graph to GPU with to()` is not available because in mixed mode the graph must be on the CPU. Authors: - Andrei Ivanov (https://github.com/drivanov) - Brad Rees (https://github.com/BradReesWork) Approvers: - Alex Barghi (https://github.com/alexbarghi-nv) - Vibhu Jawa (https://github.com/VibhuJawa) URL: https://github.com/rapidsai/cugraph/pull/3927 --- python/cugraph-dgl/examples/graphsage/node-classification.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/python/cugraph-dgl/examples/graphsage/node-classification.py b/python/cugraph-dgl/examples/graphsage/node-classification.py index 320890b0312..539fd86d136 100644 --- a/python/cugraph-dgl/examples/graphsage/node-classification.py +++ b/python/cugraph-dgl/examples/graphsage/node-classification.py @@ -243,7 +243,9 @@ def train(args, device, g, dataset, model): else: g = g.to("cuda" if args.mode == "gpu_dgl" else "cpu") - device = torch.device("cpu" if args.mode == "cpu" else "cuda") + device = torch.device( + "cpu" if args.mode == "cpu" or args.mode == "mixed" else "cuda" + ) # create GraphSAGE model feat_shape = ( From 975502209991c1abe7b1878585fec9e1978f5b07 Mon Sep 17 00:00:00 2001 From: Erik Welch Date: Mon, 30 Oct 2023 14:01:25 -0500 Subject: [PATCH 063/111] nx-cugraph: handle seed argument in edge_betweenness_centrality (#3943) CC @rlratzel who brought this to my attention and can test this fix Authors: - Erik Welch (https://github.com/eriknw) - Brad Rees (https://github.com/BradReesWork) Approvers: - Rick Ratzel (https://github.com/rlratzel) URL: https://github.com/rapidsai/cugraph/pull/3943 --- .../nx-cugraph/nx_cugraph/algorithms/centrality/betweenness.py | 1 + 1 file changed, 1 insertion(+) diff --git a/python/nx-cugraph/nx_cugraph/algorithms/centrality/betweenness.py b/python/nx-cugraph/nx_cugraph/algorithms/centrality/betweenness.py index 104ac87414c..210e1f0a2b2 100644 --- a/python/nx-cugraph/nx_cugraph/algorithms/centrality/betweenness.py +++ b/python/nx-cugraph/nx_cugraph/algorithms/centrality/betweenness.py @@ -53,6 +53,7 @@ def edge_betweenness_centrality(G, k=None, normalized=True, weight=None, seed=No raise NotImplementedError( "Weighted implementation of betweenness centrality not currently supported" ) + seed = _seed_to_int(seed) G = _to_graph(G, weight) src_ids, dst_ids, values, _edge_ids = plc.edge_betweenness_centrality( resource_handle=plc.ResourceHandle(), From 35875a84b95e9d8ba9c691f33521c45845d3e084 Mon Sep 17 00:00:00 2001 From: Chuck Hastings <45364586+ChuckHastings@users.noreply.github.com> Date: Tue, 31 Oct 2023 09:01:07 -0400 Subject: [PATCH 064/111] Errors compiling for DLFW on CUDA 12.3 (#3952) DLFW compilation found a few issues building cugraph 23.10. This PR addresses these issues. Two main issues: * We were brace initializing `raft::device_span` objects with only `nullptr`. The device span already defaults to `nullptr`, and the class hierarchy doesn't support just passing `nullptr`. * We were brace initializing the `raft::random::DeviceState`, but there's no default constructor. Changed it to not brace initialize and require a reference to make it clear that you must initialize the DeviceState. Authors: - Chuck Hastings (https://github.com/ChuckHastings) - Ralph Liu (https://github.com/nv-rliu) Approvers: - Seunghwa Kang (https://github.com/seunghwak) - Robert Maynard (https://github.com/robertmaynard) URL: https://github.com/rapidsai/cugraph/pull/3952 --- cpp/src/community/detail/refine_impl.cuh | 2 +- cpp/src/prims/detail/nbr_intersection.cuh | 25 +++++++++++------------ 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/cpp/src/community/detail/refine_impl.cuh b/cpp/src/community/detail/refine_impl.cuh index e811aafc776..6b6470991bb 100644 --- a/cpp/src/community/detail/refine_impl.cuh +++ b/cpp/src/community/detail/refine_impl.cuh @@ -64,7 +64,7 @@ struct leiden_key_aggregated_edge_op_t { weight_t total_edge_weight{}; weight_t resolution{}; // resolution parameter weight_t theta{}; // scaling factor - raft::random::DeviceState device_state{}; + raft::random::DeviceState& device_state; __device__ auto operator()( vertex_t src, vertex_t neighboring_leiden_cluster, diff --git a/cpp/src/prims/detail/nbr_intersection.cuh b/cpp/src/prims/detail/nbr_intersection.cuh index 32247ca3466..cefc1836fa6 100644 --- a/cpp/src/prims/detail/nbr_intersection.cuh +++ b/cpp/src/prims/detail/nbr_intersection.cuh @@ -138,7 +138,7 @@ struct update_rx_major_local_degree_t { size_t local_edge_partition_idx{}; raft::device_span rx_reordered_group_lasts{}; - raft::device_span rx_group_firsts{nullptr}; + raft::device_span rx_group_firsts{}; raft::device_span rx_majors{}; raft::device_span local_degrees_for_rx_majors{}; @@ -200,7 +200,7 @@ struct update_rx_major_local_nbrs_t { size_t local_edge_partition_idx{}; raft::device_span rx_reordered_group_lasts{}; - raft::device_span rx_group_firsts{nullptr}; + raft::device_span rx_group_firsts{}; raft::device_span rx_majors{}; raft::device_span local_nbr_offsets_for_rx_majors{}; raft::device_span local_nbrs_for_rx_majors{}; @@ -311,10 +311,10 @@ template struct pick_min_degree_t { FirstElementToIdxMap first_element_to_idx_map{}; - raft::device_span first_element_offsets{nullptr}; + raft::device_span first_element_offsets{}; SecondElementToIdxMap second_element_to_idx_map{}; - raft::device_span second_element_offsets{nullptr}; + raft::device_span second_element_offsets{}; edge_partition_device_view_t edge_partition{}; thrust::optional> @@ -473,12 +473,12 @@ template first_element_offsets{}; - raft::device_span first_element_indices{nullptr}; + raft::device_span first_element_indices{}; optional_property_buffer_view_t first_element_edge_property_values{}; SecondElementToIdxMap second_element_to_idx_map{}; raft::device_span second_element_offsets{}; - raft::device_span second_element_indices{nullptr}; + raft::device_span second_element_indices{}; optional_property_buffer_view_t second_element_edge_property_values{}; edge_partition_device_view_t edge_partition{}; @@ -487,8 +487,8 @@ struct copy_intersecting_nbrs_and_update_intersection_size_t { edge_partition_e_mask{}; VertexPairIterator vertex_pair_first; - raft::device_span nbr_intersection_offsets{nullptr}; - raft::device_span nbr_intersection_indices{nullptr}; + raft::device_span nbr_intersection_offsets{}; + raft::device_span nbr_intersection_indices{}; optional_property_buffer_mutable_view_t nbr_intersection_e_property_values0{}; optional_property_buffer_mutable_view_t nbr_intersection_e_property_values1{}; @@ -499,12 +499,11 @@ struct copy_intersecting_nbrs_and_update_intersection_size_t { using edge_property_value_t = typename edge_partition_e_input_device_view_t::value_type; auto pair = *(vertex_pair_first + i); - - vertex_t const* indices0{nullptr}; + vertex_t const* indices0{}; std::conditional_t, edge_property_value_t const*, void*> - edge_property_values0{nullptr}; + edge_property_values0{}; edge_t local_edge_offset0{0}; edge_t local_degree0{0}; @@ -548,11 +547,11 @@ struct copy_intersecting_nbrs_and_update_intersection_size_t { local_degree0 = static_cast(first_element_offsets[idx + 1] - local_edge_offset0); } - vertex_t const* indices1{nullptr}; + vertex_t const* indices1{}; std::conditional_t, edge_property_value_t const*, void*> - edge_property_values1{nullptr}; + edge_property_values1{}; edge_t local_edge_offset1{0}; edge_t local_degree1{0}; From d6c7fa131ea5bb901ca4fde3094a29b4681b77bd Mon Sep 17 00:00:00 2001 From: Erik Welch Date: Tue, 31 Oct 2023 14:10:20 -0500 Subject: [PATCH 065/111] Add many graph generators to nx-cugraph (#3954) Also, better handle dtypes for edge values passed to pylibcugraph, which only takes float32 and float64 atm. I also defined `index_dtype` (currently int32) to globally control the dtype of indices. Authors: - Erik Welch (https://github.com/eriknw) - Ralph Liu (https://github.com/nv-rliu) Approvers: - Rick Ratzel (https://github.com/rlratzel) URL: https://github.com/rapidsai/cugraph/pull/3954 --- python/nx-cugraph/_nx_cugraph/__init__.py | 45 ++ python/nx-cugraph/lint.yaml | 8 +- python/nx-cugraph/nx_cugraph/__init__.py | 8 +- .../nx_cugraph/algorithms/__init__.py | 3 +- .../algorithms/bipartite/__init__.py | 13 + .../algorithms/bipartite/generators.py | 62 ++ .../nx-cugraph/nx_cugraph/algorithms/core.py | 11 +- python/nx-cugraph/nx_cugraph/classes/graph.py | 188 +++++- .../nx_cugraph/classes/multidigraph.py | 5 + .../nx_cugraph/classes/multigraph.py | 74 ++- python/nx-cugraph/nx_cugraph/convert.py | 78 ++- .../nx-cugraph/nx_cugraph/convert_matrix.py | 146 ++++ .../nx_cugraph/generators/__init__.py | 16 + .../nx_cugraph/generators/_utils.py | 136 ++++ .../nx_cugraph/generators/classic.py | 423 ++++++++++++ .../nx_cugraph/generators/community.py | 45 ++ .../nx-cugraph/nx_cugraph/generators/small.py | 622 ++++++++++++++++++ .../nx_cugraph/generators/social.py | 294 +++++++++ python/nx-cugraph/nx_cugraph/interface.py | 35 + .../nx_cugraph/tests/test_generators.py | 277 ++++++++ .../nx-cugraph/nx_cugraph/tests/test_utils.py | 87 +++ python/nx-cugraph/nx_cugraph/typing.py | 8 + python/nx-cugraph/nx_cugraph/utils/misc.py | 72 +- python/nx-cugraph/pyproject.toml | 3 +- 24 files changed, 2573 insertions(+), 86 deletions(-) create mode 100644 python/nx-cugraph/nx_cugraph/algorithms/bipartite/__init__.py create mode 100644 python/nx-cugraph/nx_cugraph/algorithms/bipartite/generators.py create mode 100644 python/nx-cugraph/nx_cugraph/convert_matrix.py create mode 100644 python/nx-cugraph/nx_cugraph/generators/__init__.py create mode 100644 python/nx-cugraph/nx_cugraph/generators/_utils.py create mode 100644 python/nx-cugraph/nx_cugraph/generators/classic.py create mode 100644 python/nx-cugraph/nx_cugraph/generators/community.py create mode 100644 python/nx-cugraph/nx_cugraph/generators/small.py create mode 100644 python/nx-cugraph/nx_cugraph/generators/social.py create mode 100644 python/nx-cugraph/nx_cugraph/tests/test_generators.py create mode 100644 python/nx-cugraph/nx_cugraph/tests/test_utils.py diff --git a/python/nx-cugraph/_nx_cugraph/__init__.py b/python/nx-cugraph/_nx_cugraph/__init__.py index 965b5b232ab..af1df04644c 100644 --- a/python/nx-cugraph/_nx_cugraph/__init__.py +++ b/python/nx-cugraph/_nx_cugraph/__init__.py @@ -29,23 +29,68 @@ # "description": "TODO", "functions": { # BEGIN: functions + "barbell_graph", "betweenness_centrality", + "bull_graph", + "caveman_graph", + "chvatal_graph", + "circular_ladder_graph", + "complete_bipartite_graph", + "complete_graph", + "complete_multipartite_graph", + "cubical_graph", + "cycle_graph", + "davis_southern_women_graph", "degree_centrality", + "desargues_graph", + "diamond_graph", + "dodecahedral_graph", "edge_betweenness_centrality", + "empty_graph", + "florentine_families_graph", + "from_pandas_edgelist", + "from_scipy_sparse_array", + "frucht_graph", + "heawood_graph", + "house_graph", + "house_x_graph", + "icosahedral_graph", "in_degree_centrality", "is_isolate", "isolates", "k_truss", + "karate_club_graph", + "krackhardt_kite_graph", + "ladder_graph", + "les_miserables_graph", + "lollipop_graph", "louvain_communities", + "moebius_kantor_graph", + "null_graph", "number_of_isolates", "number_of_selfloops", + "octahedral_graph", "out_degree_centrality", + "pappus_graph", + "path_graph", + "petersen_graph", + "sedgewick_maze_graph", + "star_graph", + "tadpole_graph", + "tetrahedral_graph", + "trivial_graph", + "truncated_cube_graph", + "truncated_tetrahedron_graph", + "turan_graph", + "tutte_graph", + "wheel_graph", # END: functions }, "extra_docstrings": { # BEGIN: extra_docstrings "betweenness_centrality": "`weight` parameter is not yet supported.", "edge_betweenness_centrality": "`weight` parameter is not yet supported.", + "from_pandas_edgelist": "cudf.DataFrame inputs also supported.", "louvain_communities": "`seed` parameter is currently ignored.", # END: extra_docstrings }, diff --git a/python/nx-cugraph/lint.yaml b/python/nx-cugraph/lint.yaml index fef2cebc7f5..01a806e6162 100644 --- a/python/nx-cugraph/lint.yaml +++ b/python/nx-cugraph/lint.yaml @@ -45,12 +45,12 @@ repos: - id: pyupgrade args: [--py39-plus] - repo: https://github.com/psf/black - rev: 23.10.0 + rev: 23.10.1 hooks: - id: black # - id: black-jupyter - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.1.1 + rev: v0.1.3 hooks: - id: ruff args: [--fix-only, --show-fixes] @@ -58,7 +58,7 @@ repos: rev: 6.1.0 hooks: - id: flake8 - args: ['--per-file-ignores=_nx_cugraph/__init__.py:E501'] # Why is this necessary? + args: ['--per-file-ignores=_nx_cugraph/__init__.py:E501', '--extend-ignore=SIM105'] # Why is this necessary? additional_dependencies: &flake8_dependencies # These versions need updated manually - flake8==6.1.0 @@ -77,7 +77,7 @@ repos: additional_dependencies: [tomli] files: ^(nx_cugraph|docs)/ - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.1.1 + rev: v0.1.3 hooks: - id: ruff - repo: https://github.com/pre-commit/pre-commit-hooks diff --git a/python/nx-cugraph/nx_cugraph/__init__.py b/python/nx-cugraph/nx_cugraph/__init__.py index 1eaf3ec6bbb..25d44212264 100644 --- a/python/nx-cugraph/nx_cugraph/__init__.py +++ b/python/nx-cugraph/nx_cugraph/__init__.py @@ -20,11 +20,11 @@ from . import convert from .convert import * -# from . import convert_matrix -# from .convert_matrix import * +from . import convert_matrix +from .convert_matrix import * -# from . import generators -# from .generators import * +from . import generators +from .generators import * from . import algorithms from .algorithms import * diff --git a/python/nx-cugraph/nx_cugraph/algorithms/__init__.py b/python/nx-cugraph/nx_cugraph/algorithms/__init__.py index 22600bfdc2d..69feb8f6437 100644 --- a/python/nx-cugraph/nx_cugraph/algorithms/__init__.py +++ b/python/nx-cugraph/nx_cugraph/algorithms/__init__.py @@ -10,7 +10,8 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -from . import centrality, community +from . import bipartite, centrality, community +from .bipartite import complete_bipartite_graph from .centrality import * from .core import * from .isolate import * diff --git a/python/nx-cugraph/nx_cugraph/algorithms/bipartite/__init__.py b/python/nx-cugraph/nx_cugraph/algorithms/bipartite/__init__.py new file mode 100644 index 00000000000..062be973d55 --- /dev/null +++ b/python/nx-cugraph/nx_cugraph/algorithms/bipartite/__init__.py @@ -0,0 +1,13 @@ +# Copyright (c) 2023, NVIDIA CORPORATION. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +from .generators import * diff --git a/python/nx-cugraph/nx_cugraph/algorithms/bipartite/generators.py b/python/nx-cugraph/nx_cugraph/algorithms/bipartite/generators.py new file mode 100644 index 00000000000..1d3e762b4fd --- /dev/null +++ b/python/nx-cugraph/nx_cugraph/algorithms/bipartite/generators.py @@ -0,0 +1,62 @@ +# Copyright (c) 2023, NVIDIA CORPORATION. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +from numbers import Integral + +import cupy as cp +import networkx as nx +import numpy as np + +from nx_cugraph.generators._utils import _create_using_class, _number_and_nodes +from nx_cugraph.utils import index_dtype, networkx_algorithm, nodes_or_number + +__all__ = [ + "complete_bipartite_graph", +] + + +@nodes_or_number([0, 1]) +@networkx_algorithm +def complete_bipartite_graph(n1, n2, create_using=None): + graph_class, inplace = _create_using_class(create_using) + if graph_class.is_directed(): + raise nx.NetworkXError("Directed Graph not supported") + orig_n1, unused_nodes1 = n1 + orig_n2, unused_nodes2 = n2 + n1, nodes1 = _number_and_nodes(n1) + n2, nodes2 = _number_and_nodes(n2) + all_indices = cp.indices((n1, n2), dtype=index_dtype) + indices0 = all_indices[0].ravel() + indices1 = all_indices[1].ravel() + n1 + del all_indices + src_indices = cp.hstack((indices0, indices1)) + dst_indices = cp.hstack((indices1, indices0)) + bipartite = cp.zeros(n1 + n2, np.int8) + bipartite[n1:] = 1 + if isinstance(orig_n1, Integral) and isinstance(orig_n2, Integral): + nodes = None + else: + nodes = list(range(n1)) if nodes1 is None else nodes1 + nodes.extend(range(n2) if nodes2 is None else nodes2) + if len(set(nodes)) != len(nodes): + raise nx.NetworkXError("Inputs n1 and n2 must contain distinct nodes") + G = graph_class.from_coo( + n1 + n2, + src_indices, + dst_indices, + node_values={"bipartite": bipartite}, + id_to_key=nodes, + name=f"complete_bipartite_graph({orig_n1}, {orig_n2})", + ) + if inplace: + return create_using._become(G) + return G diff --git a/python/nx-cugraph/nx_cugraph/algorithms/core.py b/python/nx-cugraph/nx_cugraph/algorithms/core.py index 0a64dd71c69..33e79793553 100644 --- a/python/nx-cugraph/nx_cugraph/algorithms/core.py +++ b/python/nx-cugraph/nx_cugraph/algorithms/core.py @@ -12,11 +12,10 @@ # limitations under the License. import cupy as cp import networkx as nx -import numpy as np import pylibcugraph as plc import nx_cugraph as nxcg -from nx_cugraph.utils import networkx_algorithm, not_implemented_for +from nx_cugraph.utils import _get_int_dtype, networkx_algorithm, not_implemented_for __all__ = ["k_truss"] @@ -51,8 +50,8 @@ def k_truss(G, k): edge_values = {key: val.copy() for key, val in G.edge_values.items()} edge_masks = {key: val.copy() for key, val in G.edge_masks.items()} else: - # int dtype for edge_indices would be preferred - edge_indices = cp.arange(G.src_indices.size, dtype=np.float64) + edge_dtype = _get_int_dtype(G.src_indices.size - 1) + edge_indices = cp.arange(G.src_indices.size, dtype=edge_dtype) src_indices, dst_indices, edge_indices, _ = plc.k_truss_subgraph( resource_handle=plc.ResourceHandle(), graph=G._get_plc_graph(edge_array=edge_indices), @@ -62,7 +61,9 @@ def k_truss(G, k): # Renumber step 0: node indices node_indices = cp.unique(cp.concatenate([src_indices, dst_indices])) # Renumber step 1: edge values - edge_indices = edge_indices.astype(np.int64) + if edge_indices.dtype != edge_dtype: + # The returned edge_indices may have different dtype (and float) + edge_indices = edge_indices.astype(edge_dtype) edge_values = {key: val[edge_indices] for key, val in G.edge_values.items()} edge_masks = {key: val[edge_indices] for key, val in G.edge_masks.items()} # Renumber step 2: edge indices diff --git a/python/nx-cugraph/nx_cugraph/classes/graph.py b/python/nx-cugraph/nx_cugraph/classes/graph.py index 166b6b9dc6b..2048c4c3d72 100644 --- a/python/nx-cugraph/nx_cugraph/classes/graph.py +++ b/python/nx-cugraph/nx_cugraph/classes/graph.py @@ -14,7 +14,7 @@ import operator as op from copy import deepcopy -from typing import TYPE_CHECKING, ClassVar +from typing import TYPE_CHECKING import cupy as cp import networkx as nx @@ -23,8 +23,11 @@ import nx_cugraph as nxcg +from ..utils import index_dtype + if TYPE_CHECKING: # pragma: no cover from collections.abc import Iterable, Iterator + from typing import ClassVar from nx_cugraph.typing import ( AttrKey, @@ -34,6 +37,7 @@ IndexValue, NodeKey, NodeValue, + any_ndarray, ) __all__ = ["Graph"] @@ -51,17 +55,38 @@ class Graph: graph_attr_dict_factory: ClassVar[type] = dict # Not networkx properties - # We store edge data in COO format with {row,col}_indices and edge_values. + # We store edge data in COO format with {src,dst}_indices and edge_values. src_indices: cp.ndarray[IndexValue] dst_indices: cp.ndarray[IndexValue] edge_values: dict[AttrKey, cp.ndarray[EdgeValue]] edge_masks: dict[AttrKey, cp.ndarray[bool]] - node_values: dict[AttrKey, cp.ndarray[NodeValue]] - node_masks: dict[AttrKey, cp.ndarray[bool]] + node_values: dict[AttrKey, any_ndarray[NodeValue]] + node_masks: dict[AttrKey, any_ndarray[bool]] key_to_id: dict[NodeKey, IndexValue] | None _id_to_key: list[NodeKey] | None _N: int + # Used by graph._get_plc_graph + _plc_type_map: ClassVar[dict[np.dtype, np.dtype]] = { + # signed int + np.dtype(np.int8): np.dtype(np.float32), + np.dtype(np.int16): np.dtype(np.float32), + np.dtype(np.int32): np.dtype(np.float64), + np.dtype(np.int64): np.dtype(np.float64), # raise if abs(x) > 2**53 + # unsigned int + np.dtype(np.uint8): np.dtype(np.float32), + np.dtype(np.uint16): np.dtype(np.float32), + np.dtype(np.uint32): np.dtype(np.float64), + np.dtype(np.uint64): np.dtype(np.float64), # raise if x > 2**53 + # other + np.dtype(np.bool_): np.dtype(np.float16), + np.dtype(np.float16): np.dtype(np.float32), + } + _plc_allowed_edge_types: ClassVar[set[np.dtype]] = { + np.dtype(np.float32), + np.dtype(np.float64), + } + #################### # Creation methods # #################### @@ -74,8 +99,8 @@ def from_coo( dst_indices: cp.ndarray[IndexValue], edge_values: dict[AttrKey, cp.ndarray[EdgeValue]] | None = None, edge_masks: dict[AttrKey, cp.ndarray[bool]] | None = None, - node_values: dict[AttrKey, cp.ndarray[NodeValue]] | None = None, - node_masks: dict[AttrKey, cp.ndarray[bool]] | None = None, + node_values: dict[AttrKey, any_ndarray[NodeValue]] | None = None, + node_masks: dict[AttrKey, any_ndarray[bool]] | None = None, *, key_to_id: dict[NodeKey, IndexValue] | None = None, id_to_key: list[NodeKey] | None = None, @@ -111,6 +136,27 @@ def from_coo( raise ValueError if new_graph._id_to_key is not None and len(new_graph._id_to_key) != N: raise ValueError + if new_graph._id_to_key is not None and new_graph.key_to_id is None: + try: + new_graph.key_to_id = dict(zip(new_graph._id_to_key, range(N))) + except TypeError as exc: + raise ValueError("Bad type of a node value") from exc + if new_graph.src_indices.dtype != index_dtype: + src_indices = new_graph.src_indices.astype(index_dtype) + if not (new_graph.src_indices == src_indices).all(): + raise ValueError( + f"Unable to convert src_indices to {src_indices.dtype.name} " + f"(got {new_graph.src_indices.dtype.name})." + ) + new_graph.src_indices = src_indices + if new_graph.dst_indices.dtype != index_dtype: + dst_indices = new_graph.dst_indices.astype(index_dtype) + if not (new_graph.dst_indices == dst_indices).all(): + raise ValueError( + f"Unable to convert dst_indices to {dst_indices.dtype.name} " + f"(got {new_graph.dst_indices.dtype.name})." + ) + new_graph.dst_indices = dst_indices return new_graph @classmethod @@ -120,8 +166,8 @@ def from_csr( dst_indices: cp.ndarray[IndexValue], edge_values: dict[AttrKey, cp.ndarray[EdgeValue]] | None = None, edge_masks: dict[AttrKey, cp.ndarray[bool]] | None = None, - node_values: dict[AttrKey, cp.ndarray[NodeValue]] | None = None, - node_masks: dict[AttrKey, cp.ndarray[bool]] | None = None, + node_values: dict[AttrKey, any_ndarray[NodeValue]] | None = None, + node_masks: dict[AttrKey, any_ndarray[bool]] | None = None, *, key_to_id: dict[NodeKey, IndexValue] | None = None, id_to_key: list[NodeKey] | None = None, @@ -130,7 +176,7 @@ def from_csr( N = indptr.size - 1 src_indices = cp.array( # cp.repeat is slow to use here, so use numpy instead - np.repeat(np.arange(N, dtype=np.int32), cp.diff(indptr).get()) + np.repeat(np.arange(N, dtype=index_dtype), cp.diff(indptr).get()) ) return cls.from_coo( N, @@ -152,8 +198,8 @@ def from_csc( src_indices: cp.ndarray[IndexValue], edge_values: dict[AttrKey, cp.ndarray[EdgeValue]] | None = None, edge_masks: dict[AttrKey, cp.ndarray[bool]] | None = None, - node_values: dict[AttrKey, cp.ndarray[NodeValue]] | None = None, - node_masks: dict[AttrKey, cp.ndarray[bool]] | None = None, + node_values: dict[AttrKey, any_ndarray[NodeValue]] | None = None, + node_masks: dict[AttrKey, any_ndarray[bool]] | None = None, *, key_to_id: dict[NodeKey, IndexValue] | None = None, id_to_key: list[NodeKey] | None = None, @@ -162,7 +208,7 @@ def from_csc( N = indptr.size - 1 dst_indices = cp.array( # cp.repeat is slow to use here, so use numpy instead - np.repeat(np.arange(N, dtype=np.int32), cp.diff(indptr).get()) + np.repeat(np.arange(N, dtype=index_dtype), cp.diff(indptr).get()) ) return cls.from_coo( N, @@ -181,13 +227,13 @@ def from_csc( def from_dcsr( cls, N: int, - compressed_rows: cp.ndarray[IndexValue], + compressed_srcs: cp.ndarray[IndexValue], indptr: cp.ndarray[IndexValue], dst_indices: cp.ndarray[IndexValue], edge_values: dict[AttrKey, cp.ndarray[EdgeValue]] | None = None, edge_masks: dict[AttrKey, cp.ndarray[bool]] | None = None, - node_values: dict[AttrKey, cp.ndarray[NodeValue]] | None = None, - node_masks: dict[AttrKey, cp.ndarray[bool]] | None = None, + node_values: dict[AttrKey, any_ndarray[NodeValue]] | None = None, + node_masks: dict[AttrKey, any_ndarray[bool]] | None = None, *, key_to_id: dict[NodeKey, IndexValue] | None = None, id_to_key: list[NodeKey] | None = None, @@ -195,7 +241,7 @@ def from_dcsr( ) -> Graph: src_indices = cp.array( # cp.repeat is slow to use here, so use numpy instead - np.repeat(compressed_rows.get(), cp.diff(indptr).get()) + np.repeat(compressed_srcs.get(), cp.diff(indptr).get()) ) return cls.from_coo( N, @@ -214,13 +260,13 @@ def from_dcsr( def from_dcsc( cls, N: int, - compressed_cols: cp.ndarray[IndexValue], + compressed_dsts: cp.ndarray[IndexValue], indptr: cp.ndarray[IndexValue], src_indices: cp.ndarray[IndexValue], edge_values: dict[AttrKey, cp.ndarray[EdgeValue]] | None = None, edge_masks: dict[AttrKey, cp.ndarray[bool]] | None = None, - node_values: dict[AttrKey, cp.ndarray[NodeValue]] | None = None, - node_masks: dict[AttrKey, cp.ndarray[bool]] | None = None, + node_values: dict[AttrKey, any_ndarray[NodeValue]] | None = None, + node_masks: dict[AttrKey, any_ndarray[bool]] | None = None, *, key_to_id: dict[NodeKey, IndexValue] | None = None, id_to_key: list[NodeKey] | None = None, @@ -228,7 +274,7 @@ def from_dcsc( ) -> Graph: dst_indices = cp.array( # cp.repeat is slow to use here, so use numpy instead - np.repeat(compressed_cols.get(), cp.diff(indptr).get()) + np.repeat(compressed_dsts.get(), cp.diff(indptr).get()) ) return cls.from_coo( N, @@ -245,7 +291,9 @@ def from_dcsc( def __new__(cls, incoming_graph_data=None, **attr) -> Graph: if incoming_graph_data is None: - new_graph = cls.from_coo(0, cp.empty(0, np.int32), cp.empty(0, np.int32)) + new_graph = cls.from_coo( + 0, cp.empty(0, index_dtype), cp.empty(0, index_dtype) + ) elif incoming_graph_data.__class__ is cls: new_graph = incoming_graph_data.copy() elif incoming_graph_data.__class__ is cls.to_networkx_class(): @@ -336,6 +384,17 @@ def __len__(self) -> int: # NetworkX graph methods # ########################## + @networkx_api + def add_nodes_from(self, nodes_for_adding: Iterable[NodeKey], **attr) -> None: + if self._N != 0: + raise NotImplementedError( + "add_nodes_from is not implemented for graph that already has nodes." + ) + G = self.to_networkx_class()() + G.add_nodes_from(nodes_for_adding, **attr) + G = nxcg.from_networkx(G, preserve_node_attrs=True) + self._become(G) + @networkx_api def clear(self) -> None: self.edge_values.clear() @@ -522,11 +581,38 @@ def _get_plc_graph( # Mask is all True; don't need anymore del self.edge_masks[edge_attr] edge_array = self.edge_values[edge_attr] + if edge_array is not None: + if edge_dtype is not None: + edge_dtype = np.dtype(edge_dtype) + if edge_array.dtype != edge_dtype: + edge_array = edge_array.astype(edge_dtype) + # PLC doesn't handle int edge weights right now, so cast int to float + if edge_array.dtype in self._plc_type_map: + if edge_array.dtype == np.int64: + if (val := edge_array.max().tolist()) > 2**53: + raise ValueError( + f"Integer value of value is too large (> 2**53): {val}; " + "pylibcugraph only supports float16 and float32 dtypes." + ) + if (val := edge_array.min().tolist()) < -(2**53): + raise ValueError( + f"Integer value of value is small large (< -2**53): {val}; " + "pylibcugraph only supports float16 and float32 dtypes." + ) + elif ( + edge_array.dtype == np.uint64 + and edge_array.max().tolist() > 2**53 + ): + raise ValueError( + f"Integer value of value is too large (> 2**53): {val}; " + "pylibcugraph only supports float16 and float32 dtypes." + ) + # Consider warning here if we add algorithms that may + # introduce roundoff errors when using floats as ints. + edge_array = edge_array.astype(self._plc_type_map[edge_array.dtype]) + elif edge_array.dtype not in self._plc_allowed_edge_types: + raise TypeError(edge_array.dtype) # Should we cache PLC graph? - if edge_dtype is not None: - edge_dtype = np.dtype(edge_dtype) - if edge_array.dtype != edge_dtype: - edge_array = edge_array.astype(edge_dtype) return plc.SGGraph( resource_handle=plc.ResourceHandle(), graph_properties=plc.GraphProperties( @@ -541,12 +627,60 @@ def _get_plc_graph( do_expensive_check=False, ) + def _sort_edge_indices(self, primary="src"): + # DRY warning: see also MultiGraph._sort_edge_indices + if primary == "src": + stacked = cp.vstack((self.dst_indices, self.src_indices)) + elif primary == "dst": + stacked = cp.vstack((self.src_indices, self.dst_indices)) + else: + raise ValueError( + f'Bad `primary` argument; expected "src" or "dst", got {primary!r}' + ) + indices = cp.lexsort(stacked) + if (cp.diff(indices) > 0).all(): + # Already sorted + return + self.src_indices = self.src_indices[indices] + self.dst_indices = self.dst_indices[indices] + self.edge_values.update( + {key: val[indices] for key, val in self.edge_values.items()} + ) + self.edge_masks.update( + {key: val[indices] for key, val in self.edge_masks.items()} + ) + + def _become(self, other: Graph): + if self.__class__ is not other.__class__: + raise TypeError( + "Attempting to update graph inplace with graph of different type!" + ) + self.clear() + edge_values = self.edge_values + edge_masks = self.edge_masks + node_values = self.node_values + node_masks = self.node_masks + graph = self.graph + edge_values.update(other.edge_values) + edge_masks.update(other.edge_masks) + node_values.update(other.node_values) + node_masks.update(other.node_masks) + graph.update(other.graph) + self.__dict__.update(other.__dict__) + self.edge_values = edge_values + self.edge_masks = edge_masks + self.node_values = node_values + self.node_masks = node_masks + self.graph = graph + return self + def _degrees_array(self): degrees = cp.bincount(self.src_indices, minlength=self._N) if self.is_directed(): degrees += cp.bincount(self.dst_indices, minlength=self._N) return degrees + # Data conversions def _nodeiter_to_iter(self, node_ids: Iterable[IndexValue]) -> Iterable[NodeKey]: """Convert an iterable of node IDs to an iterable of node keys.""" if (id_to_key := self.id_to_key) is not None: @@ -567,7 +701,7 @@ def _nodearray_to_dict( return dict(it) def _nodearrays_to_dict( - self, node_ids: cp.ndarray[IndexValue], values: cp.ndarray[NodeValue] + self, node_ids: cp.ndarray[IndexValue], values: any_ndarray[NodeValue] ) -> dict[NodeKey, NodeValue]: it = zip(node_ids.tolist(), values.tolist()) if (id_to_key := self.id_to_key) is not None: @@ -597,7 +731,7 @@ def _dict_to_nodearrays( indices_iter = d else: indices_iter = map(self.key_to_id.__getitem__, d) - node_ids = cp.fromiter(indices_iter, np.int32) + node_ids = cp.fromiter(indices_iter, index_dtype) if dtype is None: values = cp.array(list(d.values())) else: diff --git a/python/nx-cugraph/nx_cugraph/classes/multidigraph.py b/python/nx-cugraph/nx_cugraph/classes/multidigraph.py index 5629e2c9c06..2c7bfc00752 100644 --- a/python/nx-cugraph/nx_cugraph/classes/multidigraph.py +++ b/python/nx-cugraph/nx_cugraph/classes/multidigraph.py @@ -25,6 +25,11 @@ class MultiDiGraph(MultiGraph, DiGraph): + @classmethod + @networkx_api + def is_directed(cls) -> bool: + return True + @classmethod def to_networkx_class(cls) -> type[nx.MultiDiGraph]: return nx.MultiDiGraph diff --git a/python/nx-cugraph/nx_cugraph/classes/multigraph.py b/python/nx-cugraph/nx_cugraph/classes/multigraph.py index 3d90861a328..23466dc7dd4 100644 --- a/python/nx-cugraph/nx_cugraph/classes/multigraph.py +++ b/python/nx-cugraph/nx_cugraph/classes/multigraph.py @@ -21,6 +21,7 @@ import nx_cugraph as nxcg +from ..utils import index_dtype from .graph import Graph if TYPE_CHECKING: @@ -31,6 +32,7 @@ IndexValue, NodeKey, NodeValue, + any_ndarray, ) __all__ = ["MultiGraph"] @@ -43,11 +45,11 @@ class MultiGraph(Graph): # Not networkx properties - # In a MultiGraph, each edge has a unique `(row, col, key)` key. + # In a MultiGraph, each edge has a unique `(src, dst, key)` key. # By default, `key` is 0 if possible, else 1, else 2, etc. # This key can be any hashable Python object in NetworkX. # We don't use a dict for our data structure here, because - # that would require a `(row, col, key)` key. + # that would require a `(src, dst, key)` key. # Instead, we keep `edge_keys` and/or `edge_indices`. # `edge_keys` is the list of Python objects for each edge. # `edge_indices` is for the common case of default multiedge keys, @@ -72,8 +74,8 @@ def from_coo( edge_indices: cp.ndarray[IndexValue] | None = None, edge_values: dict[AttrKey, cp.ndarray[EdgeValue]] | None = None, edge_masks: dict[AttrKey, cp.ndarray[bool]] | None = None, - node_values: dict[AttrKey, cp.ndarray[NodeValue]] | None = None, - node_masks: dict[AttrKey, cp.ndarray[bool]] | None = None, + node_values: dict[AttrKey, any_ndarray[NodeValue]] | None = None, + node_masks: dict[AttrKey, any_ndarray[bool]] | None = None, *, key_to_id: dict[NodeKey, IndexValue] | None = None, id_to_key: list[NodeKey] | None = None, @@ -110,8 +112,8 @@ def from_csr( edge_indices: cp.ndarray[IndexValue] | None = None, edge_values: dict[AttrKey, cp.ndarray[EdgeValue]] | None = None, edge_masks: dict[AttrKey, cp.ndarray[bool]] | None = None, - node_values: dict[AttrKey, cp.ndarray[NodeValue]] | None = None, - node_masks: dict[AttrKey, cp.ndarray[bool]] | None = None, + node_values: dict[AttrKey, any_ndarray[NodeValue]] | None = None, + node_masks: dict[AttrKey, any_ndarray[bool]] | None = None, *, key_to_id: dict[NodeKey, IndexValue] | None = None, id_to_key: list[NodeKey] | None = None, @@ -121,7 +123,7 @@ def from_csr( N = indptr.size - 1 src_indices = cp.array( # cp.repeat is slow to use here, so use numpy instead - np.repeat(np.arange(N, dtype=np.int32), cp.diff(indptr).get()) + np.repeat(np.arange(N, dtype=index_dtype), cp.diff(indptr).get()) ) return cls.from_coo( N, @@ -146,8 +148,8 @@ def from_csc( edge_indices: cp.ndarray[IndexValue] | None = None, edge_values: dict[AttrKey, cp.ndarray[EdgeValue]] | None = None, edge_masks: dict[AttrKey, cp.ndarray[bool]] | None = None, - node_values: dict[AttrKey, cp.ndarray[NodeValue]] | None = None, - node_masks: dict[AttrKey, cp.ndarray[bool]] | None = None, + node_values: dict[AttrKey, any_ndarray[NodeValue]] | None = None, + node_masks: dict[AttrKey, any_ndarray[bool]] | None = None, *, key_to_id: dict[NodeKey, IndexValue] | None = None, id_to_key: list[NodeKey] | None = None, @@ -157,7 +159,7 @@ def from_csc( N = indptr.size - 1 dst_indices = cp.array( # cp.repeat is slow to use here, so use numpy instead - np.repeat(np.arange(N, dtype=np.int32), cp.diff(indptr).get()) + np.repeat(np.arange(N, dtype=index_dtype), cp.diff(indptr).get()) ) return cls.from_coo( N, @@ -178,14 +180,14 @@ def from_csc( def from_dcsr( cls, N: int, - compressed_rows: cp.ndarray[IndexValue], + compressed_srcs: cp.ndarray[IndexValue], indptr: cp.ndarray[IndexValue], dst_indices: cp.ndarray[IndexValue], edge_indices: cp.ndarray[IndexValue] | None = None, edge_values: dict[AttrKey, cp.ndarray[EdgeValue]] | None = None, edge_masks: dict[AttrKey, cp.ndarray[bool]] | None = None, - node_values: dict[AttrKey, cp.ndarray[NodeValue]] | None = None, - node_masks: dict[AttrKey, cp.ndarray[bool]] | None = None, + node_values: dict[AttrKey, any_ndarray[NodeValue]] | None = None, + node_masks: dict[AttrKey, any_ndarray[bool]] | None = None, *, key_to_id: dict[NodeKey, IndexValue] | None = None, id_to_key: list[NodeKey] | None = None, @@ -194,7 +196,7 @@ def from_dcsr( ) -> MultiGraph: src_indices = cp.array( # cp.repeat is slow to use here, so use numpy instead - np.repeat(compressed_rows.get(), cp.diff(indptr).get()) + np.repeat(compressed_srcs.get(), cp.diff(indptr).get()) ) return cls.from_coo( N, @@ -215,14 +217,14 @@ def from_dcsr( def from_dcsc( cls, N: int, - compressed_cols: cp.ndarray[IndexValue], + compressed_dsts: cp.ndarray[IndexValue], indptr: cp.ndarray[IndexValue], src_indices: cp.ndarray[IndexValue], edge_indices: cp.ndarray[IndexValue] | None = None, edge_values: dict[AttrKey, cp.ndarray[EdgeValue]] | None = None, edge_masks: dict[AttrKey, cp.ndarray[bool]] | None = None, - node_values: dict[AttrKey, cp.ndarray[NodeValue]] | None = None, - node_masks: dict[AttrKey, cp.ndarray[bool]] | None = None, + node_values: dict[AttrKey, any_ndarray[NodeValue]] | None = None, + node_masks: dict[AttrKey, any_ndarray[bool]] | None = None, *, key_to_id: dict[NodeKey, IndexValue] | None = None, id_to_key: list[NodeKey] | None = None, @@ -231,7 +233,7 @@ def from_dcsc( ) -> Graph: dst_indices = cp.array( # cp.repeat is slow to use here, so use numpy instead - np.repeat(compressed_cols.get(), cp.diff(indptr).get()) + np.repeat(compressed_dsts.get(), cp.diff(indptr).get()) ) return cls.from_coo( N, @@ -449,3 +451,39 @@ def _copy(self, as_view: bool, cls: type[Graph], reverse: bool = False): else: rv.graph.update(deepcopy(self.graph)) return rv + + def _sort_edge_indices(self, primary="src"): + # DRY warning: see also Graph._sort_edge_indices + if self.edge_indices is None and self.edge_keys is None: + return super()._sort_edge_indices(primary=primary) + if primary == "src": + if self.edge_indices is None: + stacked = (self.dst_indices, self.src_indices) + else: + stacked = (self.edge_indices, self.dst_indices, self.src_indices) + elif primary == "dst": + if self.edge_indices is None: + stacked = (self.src_indices, self.dst_indices) + else: + stacked = (self.edge_indices, self.dst_indices, self.src_indices) + else: + raise ValueError( + f'Bad `primary` argument; expected "src" or "dst", got {primary!r}' + ) + indices = cp.lexsort(cp.vstack(stacked)) + if (cp.diff(indices) > 0).all(): + # Already sorted + return + self.src_indices = self.src_indices[indices] + self.dst_indices = self.dst_indices[indices] + self.edge_values.update( + {key: val[indices] for key, val in self.edge_values.items()} + ) + self.edge_masks.update( + {key: val[indices] for key, val in self.edge_masks.items()} + ) + if self.edge_indices is not None: + self.edge_indices = self.edge_indices[indices] + if self.edge_keys is not None: + edge_keys = self.edge_keys + self.edge_keys = [edge_keys[i] for i in indices.tolist()] diff --git a/python/nx-cugraph/nx_cugraph/convert.py b/python/nx-cugraph/nx_cugraph/convert.py index d117c8e5c03..3c0814370d3 100644 --- a/python/nx-cugraph/nx_cugraph/convert.py +++ b/python/nx-cugraph/nx_cugraph/convert.py @@ -24,8 +24,10 @@ import nx_cugraph as nxcg +from .utils import index_dtype + if TYPE_CHECKING: # pragma: no cover - from nx_cugraph.typing import AttrKey, Dtype, EdgeValue, NodeValue + from nx_cugraph.typing import AttrKey, Dtype, EdgeValue, NodeValue, any_ndarray __all__ = [ "from_networkx", @@ -256,7 +258,7 @@ def from_networkx( node_attrs[attr] = REQUIRED key_to_id = dict(zip(adj, range(N))) - col_iter = concat(adj.values()) + dst_iter = concat(adj.values()) try: no_renumber = all(k == v for k, v in key_to_id.items()) except Exception: @@ -264,11 +266,11 @@ def from_networkx( if no_renumber: key_to_id = None else: - col_iter = map(key_to_id.__getitem__, col_iter) + dst_iter = map(key_to_id.__getitem__, dst_iter) if graph.is_multigraph(): - dst_indices = np.fromiter(col_iter, np.int32) + dst_indices = np.fromiter(dst_iter, index_dtype) num_multiedges = np.fromiter( - map(len, concat(map(dict.values, adj.values()))), np.int32 + map(len, concat(map(dict.values, adj.values()))), index_dtype ) # cp.repeat is slow to use here, so use numpy instead dst_indices = cp.array(np.repeat(dst_indices, num_multiedges)) @@ -276,12 +278,12 @@ def from_networkx( edge_keys = list(concat(concat(map(dict.values, adj.values())))) edge_indices = cp.fromiter( concat(map(range, map(len, concat(map(dict.values, adj.values()))))), - np.int32, + index_dtype, ) if edge_keys == edge_indices.tolist(): edge_keys = None # Prefer edge_indices else: - dst_indices = cp.fromiter(col_iter, np.int32) + dst_indices = cp.fromiter(dst_iter, index_dtype) edge_values = {} edge_masks = {} @@ -354,7 +356,8 @@ def from_networkx( # cp.repeat is slow to use here, so use numpy instead src_indices = np.repeat( - np.arange(N, dtype=np.int32), np.fromiter(map(len, adj.values()), np.int32) + np.arange(N, dtype=index_dtype), + np.fromiter(map(len, adj.values()), index_dtype), ) if graph.is_multigraph(): src_indices = np.repeat(src_indices, num_multiedges) @@ -383,8 +386,18 @@ def from_networkx( or present for node_id in adj ) - node_masks[node_attr] = cp.fromiter(iter_mask, bool) - node_values[node_attr] = cp.array(vals, dtype) + # Node values may be numpy or cupy arrays (useful for str, object, etc). + # Someday we'll let the user choose np or cp, and support edge values. + node_mask = np.fromiter(iter_mask, bool) + node_value = np.array(vals, dtype) + try: + node_value = cp.array(node_value) + except ValueError: + pass + else: + node_mask = cp.array(node_mask) + node_values[node_attr] = node_value + node_masks[node_attr] = node_mask # if vals.ndim > 1: ... else: if node_default is REQUIRED: @@ -393,10 +406,17 @@ def from_networkx( iter_values = ( nodes[node_id].get(node_attr, node_default) for node_id in adj ) + # Node values may be numpy or cupy arrays (useful for str, object, etc). + # Someday we'll let the user choose np or cp, and support edge values. if dtype is None: - node_values[node_attr] = cp.array(list(iter_values)) + node_value = np.array(list(iter_values)) else: - node_values[node_attr] = cp.fromiter(iter_values, dtype) + node_value = np.fromiter(iter_values, dtype) + try: + node_value = cp.array(node_value) + except ValueError: + pass + node_values[node_attr] = node_value # if vals.ndim > 1: ... if graph.is_multigraph(): if graph.is_directed() or as_directed: @@ -436,8 +456,8 @@ def from_networkx( def _iter_attr_dicts( - values: dict[AttrKey, cp.ndarray[EdgeValue | NodeValue]], - masks: dict[AttrKey, cp.ndarray[bool]], + values: dict[AttrKey, any_ndarray[EdgeValue | NodeValue]], + masks: dict[AttrKey, any_ndarray[bool]], ): full_attrs = list(values.keys() - masks.keys()) if full_attrs: @@ -463,7 +483,7 @@ def _iter_attr_dicts( return full_dicts -def to_networkx(G: nxcg.Graph) -> nx.Graph: +def to_networkx(G: nxcg.Graph, *, sort_edges: bool = False) -> nx.Graph: """Convert a nx_cugraph graph to networkx graph. All edge and node attributes and ``G.graph`` properties are converted. @@ -471,6 +491,11 @@ def to_networkx(G: nxcg.Graph) -> nx.Graph: Parameters ---------- G : nx_cugraph.Graph + sort_edges : bool, default False + Whether to sort the edge data of the input graph by (src, dst) indices + before converting. This can be useful to convert to networkx graphs + that iterate over edges consistently since edges are stored in dicts + in the order they were added. Returns ------- @@ -482,6 +507,8 @@ def to_networkx(G: nxcg.Graph) -> nx.Graph: """ rv = G.to_networkx_class()() id_to_key = G.id_to_key + if sort_edges: + G._sort_edge_indices() node_values = G.node_values node_masks = G.node_masks @@ -500,19 +527,20 @@ def to_networkx(G: nxcg.Graph) -> nx.Graph: dst_indices = G.dst_indices edge_values = G.edge_values edge_masks = G.edge_masks - if edge_values and not G.is_directed(): + if not G.is_directed(): # Only add upper triangle of the adjacency matrix so we don't double-add edges mask = src_indices <= dst_indices src_indices = src_indices[mask] dst_indices = dst_indices[mask] - edge_values = {k: v[mask] for k, v in edge_values.items()} + if edge_values: + edge_values = {k: v[mask] for k, v in edge_values.items()} if edge_masks: edge_masks = {k: v[mask] for k, v in edge_masks.items()} - src_indices = row_iter = src_indices.tolist() - dst_indices = col_iter = dst_indices.tolist() + src_indices = src_iter = src_indices.tolist() + dst_indices = dst_iter = dst_indices.tolist() if id_to_key is not None: - row_iter = map(id_to_key.__getitem__, src_indices) - col_iter = map(id_to_key.__getitem__, dst_indices) + src_iter = map(id_to_key.__getitem__, src_indices) + dst_iter = map(id_to_key.__getitem__, dst_indices) if G.is_multigraph() and (G.edge_keys is not None or G.edge_indices is not None): if G.edge_keys is not None: edge_keys = G.edge_keys @@ -520,14 +548,14 @@ def to_networkx(G: nxcg.Graph) -> nx.Graph: edge_keys = G.edge_indices.tolist() if edge_values: full_edge_dicts = _iter_attr_dicts(edge_values, edge_masks) - rv.add_edges_from(zip(row_iter, col_iter, edge_keys, full_edge_dicts)) + rv.add_edges_from(zip(src_iter, dst_iter, edge_keys, full_edge_dicts)) else: - rv.add_edges_from(zip(row_iter, col_iter, edge_keys)) + rv.add_edges_from(zip(src_iter, dst_iter, edge_keys)) elif edge_values: full_edge_dicts = _iter_attr_dicts(edge_values, edge_masks) - rv.add_edges_from(zip(row_iter, col_iter, full_edge_dicts)) + rv.add_edges_from(zip(src_iter, dst_iter, full_edge_dicts)) else: - rv.add_edges_from(zip(row_iter, col_iter)) + rv.add_edges_from(zip(src_iter, dst_iter)) rv.graph.update(G.graph) return rv diff --git a/python/nx-cugraph/nx_cugraph/convert_matrix.py b/python/nx-cugraph/nx_cugraph/convert_matrix.py new file mode 100644 index 00000000000..6c8b8fb4a1d --- /dev/null +++ b/python/nx-cugraph/nx_cugraph/convert_matrix.py @@ -0,0 +1,146 @@ +# Copyright (c) 2023, NVIDIA CORPORATION. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import cupy as cp +import networkx as nx +import numpy as np + +from .generators._utils import _create_using_class +from .utils import index_dtype, networkx_algorithm + +__all__ = [ + "from_pandas_edgelist", + "from_scipy_sparse_array", +] + + +@networkx_algorithm +def from_pandas_edgelist( + df, + source="source", + target="target", + edge_attr=None, + create_using=None, + edge_key=None, +): + """cudf.DataFrame inputs also supported.""" + graph_class, inplace = _create_using_class(create_using) + src_array = df[source].to_numpy() + dst_array = df[target].to_numpy() + # Renumber step 0: node keys + nodes = np.unique(np.concatenate([src_array, dst_array])) + N = nodes.size + kwargs = {} + if N > 0 and ( + nodes[0] != 0 + or nodes[N - 1] != N - 1 + or ( + nodes.dtype.kind not in {"i", "u"} + and not (nodes == np.arange(N, dtype=np.int64)).all() + ) + ): + # We need to renumber indices--np.searchsorted to the rescue! + kwargs["id_to_key"] = nodes.tolist() + src_indices = cp.array(np.searchsorted(nodes, src_array), index_dtype) + dst_indices = cp.array(np.searchsorted(nodes, dst_array), index_dtype) + else: + src_indices = cp.array(src_array) + dst_indices = cp.array(dst_array) + + if not graph_class.is_directed(): + # Symmetrize the edges + mask = src_indices != dst_indices + if mask.all(): + mask = None + src_indices, dst_indices = ( + cp.hstack( + (src_indices, dst_indices[mask] if mask is not None else dst_indices) + ), + cp.hstack( + (dst_indices, src_indices[mask] if mask is not None else src_indices) + ), + ) + + if edge_attr is not None: + # Additional columns requested for edge data + if edge_attr is True: + attr_col_headings = df.columns.difference({source, target}).to_list() + elif isinstance(edge_attr, (list, tuple)): + attr_col_headings = edge_attr + else: + attr_col_headings = [edge_attr] + if len(attr_col_headings) == 0: + raise nx.NetworkXError( + "Invalid edge_attr argument: No columns found with name: " + f"{attr_col_headings}" + ) + try: + edge_values = { + key: cp.array(val.to_numpy()) + for key, val in df[attr_col_headings].items() + } + except (KeyError, TypeError) as exc: + raise nx.NetworkXError(f"Invalid edge_attr argument: {edge_attr}") from exc + + if not graph_class.is_directed(): + # Symmetrize the edges + edge_values = { + key: cp.hstack((val, val[mask] if mask is not None else val)) + for key, val in edge_values.items() + } + kwargs["edge_values"] = edge_values + + if graph_class.is_multigraph() and edge_key is not None: + try: + edge_keys = df[edge_key].to_list() + except (KeyError, TypeError) as exc: + raise nx.NetworkXError( + f"Invalid edge_key argument: {edge_key}" + ) from exc + if not graph_class.is_directed(): + # Symmetrize the edges + edge_keys = cp.hstack( + (edge_keys, edge_keys[mask] if mask is not None else edge_keys) + ) + kwargs["edge_keys"] = edge_keys + + G = graph_class.from_coo(N, src_indices, dst_indices, **kwargs) + if inplace: + return create_using._become(G) + return G + + +@networkx_algorithm +def from_scipy_sparse_array( + A, parallel_edges=False, create_using=None, edge_attribute="weight" +): + graph_class, inplace = _create_using_class(create_using) + m, n = A.shape + if m != n: + raise nx.NetworkXError(f"Adjacency matrix not square: nx,ny={A.shape}") + if A.format != "coo": + A = A.tocoo() + if A.dtype.kind in {"i", "u"} and graph_class.is_multigraph() and parallel_edges: + src_indices = cp.array(np.repeat(A.row, A.data), index_dtype) + dst_indices = cp.array(np.repeat(A.col, A.data), index_dtype) + weight = cp.empty(src_indices.size, A.data.dtype) + weight[:] = 1 + else: + src_indices = cp.array(A.row, index_dtype) + dst_indices = cp.array(A.col, index_dtype) + weight = cp.array(A.data) + G = graph_class.from_coo( + n, src_indices, dst_indices, edge_values={"weight": weight} + ) + if inplace: + return create_using._become(G) + return G diff --git a/python/nx-cugraph/nx_cugraph/generators/__init__.py b/python/nx-cugraph/nx_cugraph/generators/__init__.py new file mode 100644 index 00000000000..c1834a4dec7 --- /dev/null +++ b/python/nx-cugraph/nx_cugraph/generators/__init__.py @@ -0,0 +1,16 @@ +# Copyright (c) 2023, NVIDIA CORPORATION. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +from .classic import * +from .community import * +from .small import * +from .social import * diff --git a/python/nx-cugraph/nx_cugraph/generators/_utils.py b/python/nx-cugraph/nx_cugraph/generators/_utils.py new file mode 100644 index 00000000000..e38ace5b28d --- /dev/null +++ b/python/nx-cugraph/nx_cugraph/generators/_utils.py @@ -0,0 +1,136 @@ +# Copyright (c) 2023, NVIDIA CORPORATION. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import operator as op + +import cupy as cp +import networkx as nx + +import nx_cugraph as nxcg + +from ..utils import index_dtype + +# 3.2.1 fixed some issues in generators that occur in 3.2 and earlier +_IS_NX32_OR_LESS = (nxver := nx.__version__)[:3] <= "3.2" and ( + len(nxver) <= 3 or nxver[3] != "." and not nxver[3].isdigit() +) + + +def _ensure_int(n): + """Ensure n is integral.""" + return op.index(n) + + +def _ensure_nonnegative_int(n): + """Ensure n is a nonnegative integer.""" + n = op.index(n) + if n < 0: + raise nx.NetworkXError(f"Negative number of nodes not valid: {n}") + return n + + +def _complete_graph_indices(n): + all_indices = cp.indices((n, n), dtype=index_dtype) + src_indices = all_indices[0].ravel() + dst_indices = all_indices[1].ravel() + del all_indices + mask = src_indices != dst_indices + return (src_indices[mask], dst_indices[mask]) + + +def _common_small_graph(n, nodes, create_using, *, allow_directed=True): + """Create a "common graph" for small n. + + n == 0: empty graph + n == 1: empty graph + n == 2: complete graph + n > 2: undefined + """ + graph_class, inplace = _create_using_class(create_using) + if not allow_directed and graph_class.is_directed(): + raise nx.NetworkXError("Directed Graph not supported") + if n < 2: + G = graph_class.from_coo( + n, cp.empty(0, index_dtype), cp.empty(0, index_dtype), id_to_key=nodes + ) + else: + G = graph_class.from_coo( + n, + cp.arange(2, dtype=index_dtype), + cp.array([1, 0], index_dtype), + id_to_key=nodes, + ) + if inplace: + return create_using._become(G) + return G + + +def _create_using_class(create_using, *, default=nxcg.Graph): + """Handle ``create_using`` argument and return a Graph type from nx_cugraph.""" + inplace = False + if create_using is None: + G = default() + elif isinstance(create_using, type): + G = create_using() + elif not hasattr(create_using, "is_directed") or not hasattr( + create_using, "is_multigraph" + ): + raise TypeError("create_using is not a valid graph type or instance") + elif not isinstance(create_using, nxcg.Graph): + raise NotImplementedError( + f"create_using with object of type {type(create_using)} is not supported " + "by the cugraph backend; only nx_cugraph.Graph objects are allowed." + ) + else: + inplace = True + G = create_using + G.clear() + if not isinstance(G, nxcg.Graph): + if G.is_multigraph(): + if G.is_directed(): + graph_class = nxcg.MultiDiGraph + else: + graph_class = nxcg.MultiGraph + elif G.is_directed(): + graph_class = nxcg.DiGraph + else: + graph_class = nxcg.Graph + if G.__class__ not in {nx.Graph, nx.DiGraph, nx.MultiGraph, nx.MultiDiGraph}: + raise NotImplementedError( + f"create_using with type {type(G)} is not supported by the cugraph " + "backend; only standard networkx or nx_cugraph Graph objects are " + "allowed (but not customized subclasses derived from them)." + ) + else: + graph_class = G.__class__ + return graph_class, inplace + + +def _number_and_nodes(n_and_nodes): + n, nodes = n_and_nodes + try: + n = op.index(n) + except TypeError: + n = len(nodes) + if n < 0: + raise nx.NetworkXError(f"Negative number of nodes not valid: {n}") + if not isinstance(nodes, list): + nodes = list(nodes) + if not nodes: + return (n, None) + if nodes[0] == 0 and nodes[n - 1] == n - 1: + try: + if nodes == list(range(n)): + return (n, None) + except Exception: + pass + return (n, nodes) diff --git a/python/nx-cugraph/nx_cugraph/generators/classic.py b/python/nx-cugraph/nx_cugraph/generators/classic.py new file mode 100644 index 00000000000..b196c232320 --- /dev/null +++ b/python/nx-cugraph/nx_cugraph/generators/classic.py @@ -0,0 +1,423 @@ +# Copyright (c) 2023, NVIDIA CORPORATION. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import itertools +from numbers import Integral + +import cupy as cp +import networkx as nx +import numpy as np + +import nx_cugraph as nxcg + +from ..utils import _get_int_dtype, index_dtype, networkx_algorithm, nodes_or_number +from ._utils import ( + _IS_NX32_OR_LESS, + _common_small_graph, + _complete_graph_indices, + _create_using_class, + _ensure_int, + _ensure_nonnegative_int, + _number_and_nodes, +) + +__all__ = [ + "barbell_graph", + "circular_ladder_graph", + "complete_graph", + "complete_multipartite_graph", + "cycle_graph", + "empty_graph", + "ladder_graph", + "lollipop_graph", + "null_graph", + "path_graph", + "star_graph", + "tadpole_graph", + "trivial_graph", + "turan_graph", + "wheel_graph", +] + +concat = itertools.chain.from_iterable + + +@networkx_algorithm +def barbell_graph(m1, m2, create_using=None): + # Like two complete graphs and a path_graph + m1 = _ensure_nonnegative_int(m1) + if m1 < 2: + raise nx.NetworkXError("Invalid graph description, m1 should be >=2") + m2 = _ensure_nonnegative_int(m2) + graph_class, inplace = _create_using_class(create_using) + if graph_class.is_directed(): + raise nx.NetworkXError("Directed Graph not supported") + src_bell1, dst_bell1 = _complete_graph_indices(m1) + src_bell2 = src_bell1 + (m1 + m2) + dst_bell2 = dst_bell1 + (m1 + m2) + if m2 == 0: + src_bar = cp.array([m1 - 1, m1], index_dtype) + dst_bar = cp.array([m1, m1 - 1], index_dtype) + else: + src_bar = cp.arange(2 * m1 - 1, 2 * m1 + 2 * m2 + 1, dtype=index_dtype) // 2 + dst_bar = ( + cp.arange(m1 - 1, m1 + m2 + 1, dtype=index_dtype)[:, None] + + cp.array([-1, 1], index_dtype) + ).ravel()[1:-1] + src_indices = cp.hstack((src_bell1, src_bar, src_bell2)) + dst_indices = cp.hstack((dst_bell1, dst_bar, dst_bell2)) + G = graph_class.from_coo(2 * m1 + m2, src_indices, dst_indices) + if inplace: + return create_using._become(G) + return G + + +@networkx_algorithm +def circular_ladder_graph(n, create_using=None): + return _ladder_graph(n, create_using, is_circular=True) + + +@nodes_or_number(0) +@networkx_algorithm +def complete_graph(n, create_using=None): + n, nodes = _number_and_nodes(n) + if n < 3: + return _common_small_graph(n, nodes, create_using) + graph_class, inplace = _create_using_class(create_using) + src_indices, dst_indices = _complete_graph_indices(n) + G = graph_class.from_coo(n, src_indices, dst_indices, id_to_key=nodes) + if inplace: + return create_using._become(G) + return G + + +@networkx_algorithm +def complete_multipartite_graph(*subset_sizes): + if not subset_sizes: + return nxcg.Graph() + try: + subset_sizes = [_ensure_int(size) for size in subset_sizes] + except TypeError: + subsets = [list(subset) for subset in subset_sizes] + subset_sizes = [len(subset) for subset in subsets] + nodes = list(concat(subsets)) + else: + subsets = nodes = None + try: + subset_sizes = [_ensure_nonnegative_int(size) for size in subset_sizes] + except nx.NetworkXError: + if _IS_NX32_OR_LESS: + raise NotImplementedError("Negative number of nodes is not supported") + raise + L1 = [] + L2 = [] + total = 0 + for size in subset_sizes: + all_indices = cp.indices((total, size), dtype=index_dtype) + L1.append(all_indices[0].ravel()) + L2.append(all_indices[1].ravel() + total) + total += size + src_indices = cp.hstack(L1 + L2) + dst_indices = cp.hstack(L2 + L1) + subsets_array = cp.array( + np.repeat( + np.arange(len(subset_sizes), dtype=_get_int_dtype(len(subset_sizes) - 1)), + subset_sizes, + ) + ) + return nxcg.Graph.from_coo( + subsets_array.size, + src_indices, + dst_indices, + node_values={"subset": subsets_array}, + id_to_key=nodes, + ) + + +@nodes_or_number(0) +@networkx_algorithm +def cycle_graph(n, create_using=None): + n, nodes = _number_and_nodes(n) + graph_class, inplace = _create_using_class(create_using) + if n == 1: + src_indices = cp.zeros(1, index_dtype) + dst_indices = cp.zeros(1, index_dtype) + elif n == 2 and graph_class.is_multigraph() and not graph_class.is_directed(): + # This is kind of a peculiar edge case + src_indices = cp.array([0, 0, 1, 1], index_dtype) + dst_indices = cp.array([1, 1, 0, 0], index_dtype) + elif n < 3: + return _common_small_graph(n, nodes, create_using) + elif graph_class.is_directed(): + src_indices = cp.arange(n, dtype=index_dtype) + dst_indices = cp.arange(1, n + 1, dtype=index_dtype) + dst_indices[-1] = 0 + else: + src_indices = cp.arange(2 * n, dtype=index_dtype) // 2 + dst_indices = ( + cp.arange(n, dtype=index_dtype)[:, None] + cp.array([-1, 1], index_dtype) + ).ravel() + dst_indices[0] = n - 1 + dst_indices[-1] = 0 + G = graph_class.from_coo(n, src_indices, dst_indices, id_to_key=nodes) + if inplace: + return create_using._become(G) + return G + + +@nodes_or_number(0) +@networkx_algorithm +def empty_graph(n=0, create_using=None, default=nx.Graph): + n, nodes = _number_and_nodes(n) + graph_class, inplace = _create_using_class(create_using, default=default) + G = graph_class.from_coo( + n, cp.empty(0, index_dtype), cp.empty(0, index_dtype), id_to_key=nodes + ) + if inplace: + return create_using._become(G) + return G + + +def _ladder_graph(n, create_using, *, is_circular=False): + # Like path path_graph with extra arange, and middle link missing + n = _ensure_nonnegative_int(n) + if n < 2: + if not is_circular: + return _common_small_graph(2 * n, None, create_using, allow_directed=False) + graph_class, inplace = _create_using_class(create_using) + if graph_class.is_directed(): + raise nx.NetworkXError("Directed Graph not supported") + if n == 1: + src_indices = cp.array([0, 1, 0, 1], index_dtype) + dst_indices = cp.array([0, 0, 1, 1], index_dtype) + nodes = None + elif graph_class.is_multigraph(): + src_indices = cp.array([0, 0, 1, 1], index_dtype) + dst_indices = cp.array([1, 1, 0, 0], index_dtype) + nodes = [0, -1] + else: + src_indices = cp.array([0, 1], index_dtype) + dst_indices = cp.array([1, 0], index_dtype) + nodes = [0, -1] + G = graph_class.from_coo(2, src_indices, dst_indices, id_to_key=nodes) + if inplace: + return create_using._become(G) + return G + graph_class, inplace = _create_using_class(create_using) + if graph_class.is_directed(): + raise nx.NetworkXError("Directed Graph not supported") + path_src = cp.arange(1, 2 * n - 1, dtype=index_dtype) // 2 + path_dst = ( + cp.arange(n, dtype=index_dtype)[:, None] + cp.array([-1, 1], index_dtype) + ).ravel()[1:-1] + srcs = [path_src, path_src + n, cp.arange(2 * n, dtype=index_dtype)] + dsts = [ + path_dst, + path_dst + n, + cp.arange(n, 2 * n, dtype=index_dtype), + cp.arange(0, n, dtype=index_dtype), + ] + if is_circular and (n > 2 or graph_class.is_multigraph()): + srcs.append(cp.array([0, n - 1, n, 2 * n - 1], index_dtype)) + dsts.append(cp.array([n - 1, 0, 2 * n - 1, n], index_dtype)) + src_indices = cp.hstack(srcs) + dst_indices = cp.hstack(dsts) + G = graph_class.from_coo(2 * n, src_indices, dst_indices) + if inplace: + return create_using._become(G) + return G + + +@networkx_algorithm +def ladder_graph(n, create_using=None): + return _ladder_graph(n, create_using) + + +@nodes_or_number([0, 1]) +@networkx_algorithm +def lollipop_graph(m, n, create_using=None): + # Like complete_graph then path_graph + orig_m, unused_nodes_m = m + orig_n, unused_nodes_n = n + m, m_nodes = _number_and_nodes(m) + if m < 2: + raise nx.NetworkXError( + "Invalid description: m should indicate at least 2 nodes" + ) + n, n_nodes = _number_and_nodes(n) + graph_class, inplace = _create_using_class(create_using) + if graph_class.is_directed(): + raise nx.NetworkXError("Directed Graph not supported") + msrc_indices, mdst_indices = _complete_graph_indices(m) + nsrc_indices = cp.arange(2 * m - 1, 2 * m + 2 * n - 1, dtype=index_dtype) // 2 + ndst_indices = ( + cp.arange(m - 1, m + n, dtype=index_dtype)[:, None] + + cp.array([-1, 1], index_dtype) + ).ravel()[1:-1] + src_indices = cp.hstack((msrc_indices, nsrc_indices)) + dst_indices = cp.hstack((mdst_indices, ndst_indices)) + if isinstance(orig_m, Integral) and isinstance(orig_n, Integral): + nodes = None + else: + nodes = list(range(m)) if m_nodes is None else m_nodes + nodes.extend(range(n) if n_nodes is None else n_nodes) + if len(set(nodes)) != len(nodes): + raise nx.NetworkXError("Nodes must be distinct in containers m and n") + G = graph_class.from_coo(m + n, src_indices, dst_indices, id_to_key=nodes) + if inplace: + return create_using._become(G) + return G + + +@networkx_algorithm +def null_graph(create_using=None): + return _common_small_graph(0, None, create_using) + + +@nodes_or_number(0) +@networkx_algorithm +def path_graph(n, create_using=None): + n, nodes = _number_and_nodes(n) + graph_class, inplace = _create_using_class(create_using) + if graph_class.is_directed(): + src_indices = cp.arange(n - 1, dtype=index_dtype) + dst_indices = cp.arange(1, n, dtype=index_dtype) + elif n < 3: + return _common_small_graph(n, nodes, create_using) + else: + src_indices = cp.arange(1, 2 * n - 1, dtype=index_dtype) // 2 + dst_indices = ( + cp.arange(n, dtype=index_dtype)[:, None] + cp.array([-1, 1], index_dtype) + ).ravel()[1:-1] + G = graph_class.from_coo(n, src_indices, dst_indices, id_to_key=nodes) + if inplace: + return create_using._become(G) + return G + + +@nodes_or_number(0) +@networkx_algorithm +def star_graph(n, create_using=None): + orig_n, orig_nodes = n + n, nodes = _number_and_nodes(n) + # star_graph behaves differently whether the input was an int or iterable + if isinstance(orig_n, Integral): + if nodes is not None: + nodes.append(n) + n += 1 + if n < 3: + return _common_small_graph(n, nodes, create_using, allow_directed=False) + graph_class, inplace = _create_using_class(create_using) + if graph_class.is_directed(): + raise nx.NetworkXError("Directed Graph not supported") + flat = cp.zeros(n - 1, index_dtype) + ramp = cp.arange(1, n, dtype=index_dtype) + src_indices = cp.hstack((flat, ramp)) + dst_indices = cp.hstack((ramp, flat)) + G = graph_class.from_coo(n, src_indices, dst_indices, id_to_key=nodes) + if inplace: + return create_using._become(G) + return G + + +@nodes_or_number([0, 1]) +@networkx_algorithm +def tadpole_graph(m, n, create_using=None): + orig_m, unused_nodes_m = m + orig_n, unused_nodes_n = n + m, m_nodes = _number_and_nodes(m) + if m < 2: + raise nx.NetworkXError( + "Invalid description: m should indicate at least 2 nodes" + ) + n, n_nodes = _number_and_nodes(n) + graph_class, inplace = _create_using_class(create_using) + if graph_class.is_directed(): + raise nx.NetworkXError("Directed Graph not supported") + if isinstance(orig_m, Integral) and isinstance(orig_n, Integral): + nodes = None + else: + nodes = list(range(m)) if m_nodes is None else m_nodes + nodes.extend(range(n) if n_nodes is None else n_nodes) + if m == 2 and not graph_class.is_multigraph(): + src_indices = cp.arange(1, 2 * (m + n) - 1, dtype=index_dtype) // 2 + dst_indices = ( + cp.arange((m + n), dtype=index_dtype)[:, None] + + cp.array([-1, 1], index_dtype) + ).ravel()[1:-1] + else: + src_indices = cp.arange(2 * (m + n), dtype=index_dtype) // 2 + dst_indices = ( + cp.arange((m + n), dtype=index_dtype)[:, None] + + cp.array([-1, 1], index_dtype) + ).ravel() + dst_indices[0] = m - 1 + dst_indices[-1] = 0 + G = graph_class.from_coo(m + n, src_indices, dst_indices, id_to_key=nodes) + if inplace: + return create_using._become(G) + return G + + +@networkx_algorithm +def trivial_graph(create_using=None): + return _common_small_graph(1, None, create_using) + + +@networkx_algorithm +def turan_graph(n, r): + if not 1 <= r <= n: + raise nx.NetworkXError("Must satisfy 1 <= r <= n") + n_div_r, n_mod_r = divmod(n, r) + partitions = [n_div_r] * (r - n_mod_r) + [n_div_r + 1] * n_mod_r + return complete_multipartite_graph(*partitions) + + +@nodes_or_number(0) +@networkx_algorithm +def wheel_graph(n, create_using=None): + n, nodes = _number_and_nodes(n) + graph_class, inplace = _create_using_class(create_using) + if graph_class.is_directed(): + raise nx.NetworkXError("Directed Graph not supported") + if n < 2: + G = graph_class.from_coo( + n, cp.empty(0, index_dtype), cp.empty(0, index_dtype), id_to_key=nodes + ) + else: + # Like star_graph + flat = cp.zeros(n - 1, index_dtype) + ramp = cp.arange(1, n, dtype=index_dtype) + # Like cycle_graph + if n < 3: + src_indices = cp.empty(0, index_dtype) + dst_indices = cp.empty(0, index_dtype) + elif n > 3: + src_indices = cp.arange(2, 2 * n, dtype=index_dtype) // 2 + dst_indices = ( + cp.arange(1, n, dtype=index_dtype)[:, None] + + cp.array([-1, 1], index_dtype) + ).ravel() + dst_indices[-1] = 1 + dst_indices[0] = n - 1 + elif graph_class.is_multigraph(): + src_indices = cp.array([1, 1, 2, 2], index_dtype) + dst_indices = cp.array([2, 2, 1, 1], index_dtype) + else: + src_indices = cp.array([1, 2], index_dtype) + dst_indices = cp.array([2, 1], index_dtype) + src_indices = cp.hstack((flat, ramp, src_indices)) + dst_indices = cp.hstack((ramp, flat, dst_indices)) + G = graph_class.from_coo(n, src_indices, dst_indices, id_to_key=nodes) + if inplace: + return create_using._become(G) + return G diff --git a/python/nx-cugraph/nx_cugraph/generators/community.py b/python/nx-cugraph/nx_cugraph/generators/community.py new file mode 100644 index 00000000000..e5cb03e8cc0 --- /dev/null +++ b/python/nx-cugraph/nx_cugraph/generators/community.py @@ -0,0 +1,45 @@ +# Copyright (c) 2023, NVIDIA CORPORATION. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import cupy as cp + +import nx_cugraph as nxcg + +from ..utils import networkx_algorithm +from ._utils import ( + _common_small_graph, + _complete_graph_indices, + _ensure_int, + _ensure_nonnegative_int, +) + +__all__ = [ + "caveman_graph", +] + + +@networkx_algorithm +def caveman_graph(l, k): # noqa: E741 + l = _ensure_int(l) # noqa: E741 + k = _ensure_int(k) + N = _ensure_nonnegative_int(k * l) + if l == 0 or k < 1: + return _common_small_graph(N, None, None) + k = _ensure_nonnegative_int(k) + src_clique, dst_clique = _complete_graph_indices(k) + src_cliques = [src_clique] + dst_cliques = [dst_clique] + src_cliques.extend(src_clique + i * k for i in range(1, l)) + dst_cliques.extend(dst_clique + i * k for i in range(1, l)) + src_indices = cp.hstack(src_cliques) + dst_indices = cp.hstack(dst_cliques) + return nxcg.Graph.from_coo(l * k, src_indices, dst_indices) diff --git a/python/nx-cugraph/nx_cugraph/generators/small.py b/python/nx-cugraph/nx_cugraph/generators/small.py new file mode 100644 index 00000000000..b9a189c31d5 --- /dev/null +++ b/python/nx-cugraph/nx_cugraph/generators/small.py @@ -0,0 +1,622 @@ +# Copyright (c) 2023, NVIDIA CORPORATION. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import cupy as cp +import networkx as nx + +import nx_cugraph as nxcg + +from ..utils import index_dtype, networkx_algorithm +from ._utils import _IS_NX32_OR_LESS, _create_using_class + +__all__ = [ + "bull_graph", + "chvatal_graph", + "cubical_graph", + "desargues_graph", + "diamond_graph", + "dodecahedral_graph", + "frucht_graph", + "heawood_graph", + "house_graph", + "house_x_graph", + "icosahedral_graph", + "krackhardt_kite_graph", + "moebius_kantor_graph", + "octahedral_graph", + "pappus_graph", + "petersen_graph", + "sedgewick_maze_graph", + "tetrahedral_graph", + "truncated_cube_graph", + "truncated_tetrahedron_graph", + "tutte_graph", +] + + +@networkx_algorithm +def bull_graph(create_using=None): + graph_class, inplace = _create_using_class(create_using) + if graph_class.is_directed(): + raise nx.NetworkXError("Directed Graph not supported") + src_indices = cp.array([0, 0, 1, 1, 1, 2, 2, 2, 3, 4], index_dtype) + dst_indices = cp.array([1, 2, 0, 2, 3, 0, 1, 4, 1, 2], index_dtype) + G = graph_class.from_coo(5, src_indices, dst_indices, name="Bull Graph") + if inplace: + return create_using._become(G) + return G + + +@networkx_algorithm +def chvatal_graph(create_using=None): + graph_class, inplace = _create_using_class(create_using) + if graph_class.is_directed(): + raise nx.NetworkXError("Directed Graph not supported") + # fmt: off + src_indices = cp.array( + [ + 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, + 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 10, 10, 10, 10, 11, 11, + 11, 11, + ], + index_dtype, + ) + dst_indices = cp.array( + [ + 1, 4, 6, 9, 0, 2, 5, 7, 1, 3, 6, 8, 2, 4, 7, 9, 0, 3, 5, 8, 1, 4, 10, 11, + 0, 2, 10, 11, 1, 3, 8, 11, 2, 4, 7, 10, 0, 3, 10, 11, 5, 6, 8, 9, 5, 6, + 7, 9, + ], + index_dtype, + ) + # fmt: on + G = graph_class.from_coo(12, src_indices, dst_indices, name="Chvatal Graph") + if inplace: + return create_using._become(G) + return G + + +@networkx_algorithm +def cubical_graph(create_using=None): + graph_class, inplace = _create_using_class(create_using) + if graph_class.is_directed(): + raise nx.NetworkXError("Directed Graph not supported") + src_indices = cp.array( + [0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6, 7, 7, 7], + index_dtype, + ) + dst_indices = cp.array( + [1, 3, 4, 0, 2, 7, 1, 3, 6, 0, 2, 5, 0, 5, 7, 3, 4, 6, 2, 5, 7, 1, 4, 6], + index_dtype, + ) + name = ("Platonic Cubical Graph",) if _IS_NX32_OR_LESS else "Platonic Cubical Graph" + G = graph_class.from_coo(8, src_indices, dst_indices, name=name) + if inplace: + return create_using._become(G) + return G + + +@networkx_algorithm +def desargues_graph(create_using=None): + # This can also be defined w.r.t. LCF_graph + graph_class, inplace = _create_using_class(create_using) + if graph_class.is_directed(): + raise nx.NetworkXError("Directed Graph not supported") + # fmt: off + src_indices = cp.array( + [ + 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6, 7, 7, 7, + 8, 8, 8, 9, 9, 9, 10, 10, 10, 11, 11, 11, 12, 12, 12, 13, 13, 13, 14, 14, + 14, 15, 15, 15, 16, 16, 16, 17, 17, 17, 18, 18, 18, 19, 19, 19, + ], + index_dtype, + ) + dst_indices = cp.array( + [ + 1, 5, 19, 0, 2, 16, 1, 3, 11, 2, 4, 14, 3, 5, 9, 0, 4, 6, 5, 7, 15, 6, 8, + 18, 7, 9, 13, 4, 8, 10, 9, 11, 19, 2, 10, 12, 11, 13, 17, 8, 12, 14, 3, + 13, 15, 6, 14, 16, 1, 15, 17, 12, 16, 18, 7, 17, 19, 0, 10, 18, + ], + index_dtype, + ) + # fmt: on + if graph_class.is_multigraph(): + src_indices_extra = cp.array( + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19], + index_dtype, + ) + dst_indices_extra = cp.array( + [5, 16, 11, 14, 9, 0, 15, 18, 13, 4, 19, 2, 17, 8, 3, 6, 1, 12, 7, 10], + index_dtype, + ) + src_indices = cp.hstack((src_indices, src_indices_extra)) + dst_indices = cp.hstack((dst_indices, dst_indices_extra)) + G = graph_class.from_coo(20, src_indices, dst_indices, name="Desargues Graph") + if inplace: + return create_using._become(G) + return G + + +@networkx_algorithm +def diamond_graph(create_using=None): + graph_class, inplace = _create_using_class(create_using) + if graph_class.is_directed(): + raise nx.NetworkXError("Directed Graph not supported") + src_indices = cp.array([0, 0, 1, 1, 1, 2, 2, 2, 3, 3], index_dtype) + dst_indices = cp.array([1, 2, 0, 2, 3, 0, 1, 3, 1, 2], index_dtype) + G = graph_class.from_coo(4, src_indices, dst_indices, name="Diamond Graph") + if inplace: + return create_using._become(G) + return G + + +@networkx_algorithm +def dodecahedral_graph(create_using=None): + # This can also be defined w.r.t. LCF_graph + graph_class, inplace = _create_using_class(create_using) + if graph_class.is_directed(): + raise nx.NetworkXError("Directed Graph not supported") + # fmt: off + src_indices = cp.array( + [ + 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6, 7, 7, 7, + 8, 8, 8, 9, 9, 9, 10, 10, 10, 11, 11, 11, 12, 12, 12, 13, 13, 13, 14, 14, + 14, 15, 15, 15, 16, 16, 16, 17, 17, 17, 18, 18, 18, 19, 19, 19, + ], + index_dtype, + ) + dst_indices = cp.array( + [ + 1, 10, 19, 0, 2, 8, 1, 3, 6, 2, 4, 19, 3, 5, 17, 4, 6, 15, 2, 5, 7, 6, 8, + 14, 1, 7, 9, 8, 10, 13, 0, 9, 11, 10, 12, 18, 11, 13, 16, 9, 12, 14, 7, + 13, 15, 5, 14, 16, 12, 15, 17, 4, 16, 18, 11, 17, 19, 0, 3, 18, + ], + index_dtype, + ) + # fmt: on + if graph_class.is_multigraph(): + src_indices_extra = cp.array( + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19], + index_dtype, + ) + dst_indices_extra = cp.array( + [10, 8, 6, 19, 17, 15, 2, 14, 1, 13, 0, 18, 16, 9, 7, 5, 12, 4, 11, 3], + index_dtype, + ) + src_indices = cp.hstack((src_indices, src_indices_extra)) + dst_indices = cp.hstack((dst_indices, dst_indices_extra)) + G = graph_class.from_coo(20, src_indices, dst_indices, name="Dodecahedral Graph") + if inplace: + return create_using._become(G) + return G + + +@networkx_algorithm +def frucht_graph(create_using=None): + graph_class, inplace = _create_using_class(create_using) + if graph_class.is_directed(): + src_indices = cp.array( + [0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 8, 8, 10], + index_dtype, + ) + dst_indices = cp.array( + [1, 7, 2, 7, 3, 8, 4, 9, 5, 9, 6, 10, 0, 10, 11, 9, 11, 11], + index_dtype, + ) + else: + # fmt: off + src_indices = cp.array( + [ + 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6, 7, 7, + 7, 8, 8, 8, 9, 9, 9, 10, 10, 10, 11, 11, 11, + ], + index_dtype, + ) + dst_indices = cp.array( + [ + 1, 6, 7, 0, 2, 7, 1, 3, 8, 2, 4, 9, 3, 5, 9, 4, 6, 10, 0, 5, 10, 0, + 1, 11, 2, 9, 11, 3, 4, 8, 5, 6, 11, 7, 8, 10, + ], + index_dtype, + ) + # fmt: on + G = graph_class.from_coo(12, src_indices, dst_indices, name="Frucht Graph") + if inplace: + return create_using._become(G) + return G + + +@networkx_algorithm +def heawood_graph(create_using=None): + # This can also be defined w.r.t. LCF_graph + graph_class, inplace = _create_using_class(create_using) + if graph_class.is_directed(): + raise nx.NetworkXError("Directed Graph not supported") + # fmt: off + src_indices = cp.array( + [ + 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6, 7, 7, 7, + 8, 8, 8, 9, 9, 9, 10, 10, 10, 11, 11, 11, 12, 12, 12, 13, 13, 13, + ], + index_dtype, + ) + dst_indices = cp.array( + [ + 1, 5, 13, 0, 2, 10, 1, 3, 7, 2, 4, 12, 3, 5, 9, 0, 4, 6, 5, 7, 11, 2, 6, + 8, 7, 9, 13, 4, 8, 10, 1, 9, 11, 6, 10, 12, 3, 11, 13, 0, 8, 12, + ], + index_dtype, + ) + # fmt: on + if graph_class.is_multigraph(): + src_indices_extra = cp.array( + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13], + index_dtype, + ) + dst_indices_extra = cp.array( + [5, 10, 7, 12, 9, 0, 11, 2, 13, 4, 1, 6, 3, 8], + index_dtype, + ) + src_indices = cp.hstack((src_indices, src_indices_extra)) + dst_indices = cp.hstack((dst_indices, dst_indices_extra)) + G = graph_class.from_coo(14, src_indices, dst_indices, name="Heawood Graph") + if inplace: + return create_using._become(G) + return G + + +@networkx_algorithm +def house_graph(create_using=None): + graph_class, inplace = _create_using_class(create_using) + if graph_class.is_directed(): + raise nx.NetworkXError("Directed Graph not supported") + src_indices = cp.array([0, 0, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4], index_dtype) + dst_indices = cp.array([1, 2, 0, 3, 0, 3, 4, 1, 2, 4, 2, 3], index_dtype) + G = graph_class.from_coo(5, src_indices, dst_indices, name="House Graph") + if inplace: + return create_using._become(G) + return G + + +@networkx_algorithm +def house_x_graph(create_using=None): + graph_class, inplace = _create_using_class(create_using) + if graph_class.is_directed(): + raise nx.NetworkXError("Directed Graph not supported") + src_indices = cp.array( + [0, 0, 0, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4], index_dtype + ) + dst_indices = cp.array( + [1, 2, 3, 0, 2, 3, 0, 1, 3, 4, 0, 1, 2, 4, 2, 3], index_dtype + ) + G = graph_class.from_coo( + 5, src_indices, dst_indices, name="House-with-X-inside Graph" + ) + if inplace: + return create_using._become(G) + return G + + +@networkx_algorithm +def icosahedral_graph(create_using=None): + graph_class, inplace = _create_using_class(create_using) + if graph_class.is_directed(): + raise nx.NetworkXError("Directed Graph not supported") + # fmt: off + src_indices = cp.array( + [ + 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4, + 4, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 9, 9, 9, + 9, 9, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, + ], + index_dtype, + ) + dst_indices = cp.array( + [ + 1, 5, 7, 8, 11, 0, 2, 5, 6, 8, 1, 3, 6, 8, 9, 2, 4, 6, 9, 10, 3, 5, 6, + 10, 11, 0, 1, 4, 6, 11, 1, 2, 3, 4, 5, 0, 8, 9, 10, 11, 0, 1, 2, 7, 9, 2, + 3, 7, 8, 10, 3, 4, 7, 9, 11, 0, 4, 5, 7, 10, + ], + index_dtype, + ) + # fmt: on + G = graph_class.from_coo( + 12, src_indices, dst_indices, name="Platonic Icosahedral Graph" + ) + if inplace: + return create_using._become(G) + return G + + +@networkx_algorithm +def krackhardt_kite_graph(create_using=None): + graph_class, inplace = _create_using_class(create_using) + if graph_class.is_directed(): + raise nx.NetworkXError("Directed Graph not supported") + # fmt: off + src_indices = cp.array( + [ + 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 3, 4, 4, 4, 5, 5, 5, 5, + 5, 6, 6, 6, 6, 6, 7, 7, 7, 8, 8, 9, + ], + index_dtype, + ) + dst_indices = cp.array( + [ + 1, 2, 3, 5, 0, 3, 4, 6, 0, 3, 5, 0, 1, 2, 4, 5, 6, 1, 3, 6, 0, 2, 3, 6, + 7, 1, 3, 4, 5, 7, 5, 6, 8, 7, 9, 8, + ], + index_dtype, + ) + # fmt: on + G = graph_class.from_coo( + 10, src_indices, dst_indices, name="Krackhardt Kite Social Network" + ) + if inplace: + return create_using._become(G) + return G + + +@networkx_algorithm +def moebius_kantor_graph(create_using=None): + # This can also be defined w.r.t. LCF_graph + graph_class, inplace = _create_using_class(create_using) + if graph_class.is_directed(): + raise nx.NetworkXError("Directed Graph not supported") + # fmt: off + src_indices = cp.array( + [ + 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6, 7, 7, 7, + 8, 8, 8, 9, 9, 9, 10, 10, 10, 11, 11, 11, 12, 12, 12, 13, 13, 13, 14, + 14, 14, 15, 15, 15, + ], + index_dtype, + ) + dst_indices = cp.array( + [ + 1, 5, 15, 0, 2, 12, 1, 3, 7, 2, 4, 14, 3, 5, 9, 0, 4, 6, 5, 7, 11, 2, 6, + 8, 7, 9, 13, 4, 8, 10, 9, 11, 15, 6, 10, 12, 1, 11, 13, 8, 12, 14, 3, 13, + 15, 0, 10, 14, + ], + index_dtype, + ) + # fmt: on + if graph_class.is_multigraph(): + src_indices_extra = cp.array( + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], + index_dtype, + ) + dst_indices_extra = cp.array( + [5, 12, 7, 14, 9, 0, 11, 2, 13, 4, 15, 6, 1, 8, 3, 10], + index_dtype, + ) + src_indices = cp.hstack((src_indices, src_indices_extra)) + dst_indices = cp.hstack((dst_indices, dst_indices_extra)) + G = graph_class.from_coo(16, src_indices, dst_indices, name="Moebius-Kantor Graph") + if inplace: + return create_using._become(G) + return G + + +@networkx_algorithm +def octahedral_graph(create_using=None): + graph_class, inplace = _create_using_class(create_using) + if graph_class.is_directed(): + raise nx.NetworkXError("Directed Graph not supported") + src_indices = cp.array( + [0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5], + index_dtype, + ) + dst_indices = cp.array( + [1, 2, 3, 4, 0, 2, 3, 5, 0, 1, 4, 5, 0, 1, 4, 5, 0, 2, 3, 5, 1, 2, 3, 4], + index_dtype, + ) + G = graph_class.from_coo( + 6, src_indices, dst_indices, name="Platonic Octahedral Graph" + ) + if inplace: + return create_using._become(G) + return G + + +@networkx_algorithm +def pappus_graph(): + # This can also be defined w.r.t. LCF_graph + # fmt: off + src_indices = cp.array( + [ + 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6, 7, 7, 7, + 8, 8, 8, 9, 9, 9, 10, 10, 10, 11, 11, 11, 12, 12, 12, 13, 13, 13, 14, 14, + 14, 15, 15, 15, 16, 16, 16, 17, 17, 17, + ], + index_dtype, + ) + dst_indices = cp.array( + [ + 1, 5, 17, 0, 2, 8, 1, 3, 13, 2, 4, 10, 3, 5, 15, 0, 4, 6, 5, 7, 11, 6, 8, + 14, 1, 7, 9, 8, 10, 16, 3, 9, 11, 6, 10, 12, 11, 13, 17, 2, 12, 14, 7, + 13, 15, 4, 14, 16, 9, 15, 17, 0, 12, 16, + ], + index_dtype, + ) + # fmt: on + return nxcg.Graph.from_coo(18, src_indices, dst_indices, name="Pappus Graph") + + +@networkx_algorithm +def petersen_graph(create_using=None): + graph_class, inplace = _create_using_class(create_using) + if graph_class.is_directed(): + raise nx.NetworkXError("Directed Graph not supported") + # fmt: off + src_indices = cp.array( + [ + 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6, 7, 7, 7, + 8, 8, 8, 9, 9, 9, + ], + index_dtype, + ) + dst_indices = cp.array( + [ + 1, 4, 5, 0, 2, 6, 1, 3, 7, 2, 4, 8, 0, 3, 9, 0, 7, 8, 1, 8, 9, 2, 5, 9, + 3, 5, 6, 4, 6, 7, + ], + index_dtype, + ) + # fmt: on + G = graph_class.from_coo(10, src_indices, dst_indices, name="Petersen Graph") + if inplace: + return create_using._become(G) + return G + + +@networkx_algorithm +def sedgewick_maze_graph(create_using=None): + graph_class, inplace = _create_using_class(create_using) + if graph_class.is_directed(): + src_indices = cp.array([0, 0, 0, 1, 2, 3, 3, 4, 4, 4], index_dtype) + dst_indices = cp.array([2, 5, 7, 7, 6, 4, 5, 5, 6, 7], index_dtype) + else: + src_indices = cp.array( + [0, 0, 0, 1, 2, 2, 3, 3, 4, 4, 4, 4, 5, 5, 5, 6, 6, 7, 7, 7], + index_dtype, + ) + dst_indices = cp.array( + [2, 5, 7, 7, 0, 6, 4, 5, 3, 5, 6, 7, 0, 3, 4, 2, 4, 0, 1, 4], + index_dtype, + ) + G = graph_class.from_coo(8, src_indices, dst_indices, name="Sedgewick Maze") + if inplace: + return create_using._become(G) + return G + + +@networkx_algorithm +def tetrahedral_graph(create_using=None): + # This can also be defined w.r.t. complete_graph + graph_class, inplace = _create_using_class(create_using) + src_indices = cp.array([0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3], index_dtype) + dst_indices = cp.array([1, 2, 3, 0, 2, 3, 0, 1, 3, 0, 1, 2], index_dtype) + name = ( + "Platonic Tetrahedral graph" + if _IS_NX32_OR_LESS + else "Platonic Tetrahedral Graph" + ) + G = graph_class.from_coo(4, src_indices, dst_indices, name=name) + if inplace: + return create_using._become(G) + return G + + +@networkx_algorithm +def truncated_cube_graph(create_using=None): + graph_class, inplace = _create_using_class(create_using) + if graph_class.is_directed(): + raise nx.NetworkXError("Directed Graph not supported") + # fmt: off + src_indices = cp.array( + [ + 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6, 7, 7, 7, + 8, 8, 8, 9, 9, 9, 10, 10, 10, 11, 11, 11, 12, 12, 12, 13, 13, 13, 14, 14, + 14, 15, 15, 15, 16, 16, 16, 17, 17, 17, 18, 18, 18, 19, 19, 19, 20, 20, + 20, 21, 21, 21, 22, 22, 22, 23, 23, 23, + ], + index_dtype, + ) + dst_indices = cp.array( + [ + 1, 2, 4, 0, 11, 14, 0, 3, 4, 2, 6, 8, 0, 2, 5, 4, 16, 18, 3, 7, 8, 6, 10, + 12, 3, 6, 9, 8, 17, 20, 7, 11, 12, 1, 10, 14, 7, 10, 13, 12, 21, 22, 1, + 11, 15, 14, 19, 23, 5, 17, 18, 9, 16, 20, 5, 16, 19, 15, 18, 23, 9, 17, + 21, 13, 20, 22, 13, 21, 23, 15, 19, 22, + ], + index_dtype, + ) + # fmt: on + G = graph_class.from_coo(24, src_indices, dst_indices, name="Truncated Cube Graph") + if inplace: + return create_using._become(G) + return G + + +@networkx_algorithm +def truncated_tetrahedron_graph(create_using=None): + graph_class, inplace = _create_using_class(create_using) + if graph_class.is_directed(): + src_indices = cp.array( + [0, 0, 0, 1, 1, 2, 3, 3, 4, 4, 5, 5, 6, 7, 8, 8, 9, 10], index_dtype + ) + dst_indices = cp.array( + [1, 2, 9, 2, 6, 3, 4, 11, 5, 11, 6, 7, 7, 8, 9, 10, 10, 11], index_dtype + ) + else: + # fmt: off + src_indices = cp.array( + [ + 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6, 7, 7, + 7, 8, 8, 8, 9, 9, 9, 10, 10, 10, 11, 11, 11, + ], + index_dtype, + ) + dst_indices = cp.array( + [ + 1, 2, 9, 0, 2, 6, 0, 1, 3, 2, 4, 11, 3, 5, 11, 4, 6, 7, 1, 5, 7, 5, + 6, 8, 7, 9, 10, 0, 8, 10, 8, 9, 11, 3, 4, 10, + ], + index_dtype, + ) + # fmt: on + G = graph_class.from_coo( + 12, src_indices, dst_indices, name="Truncated Tetrahedron Graph" + ) + if inplace: + return create_using._become(G) + return G + + +@networkx_algorithm +def tutte_graph(create_using=None): + graph_class, inplace = _create_using_class(create_using) + if graph_class.is_directed(): + raise nx.NetworkXError("Directed Graph not supported") + # fmt: off + src_indices = cp.array( + [ + 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6, 7, 7, 7, + 8, 8, 8, 9, 9, 9, 10, 10, 10, 11, 11, 11, 12, 12, 12, 13, 13, 13, 14, 14, + 14, 15, 15, 15, 16, 16, 16, 17, 17, 17, 18, 18, 18, 19, 19, 19, 20, 20, + 20, 21, 21, 21, 22, 22, 22, 23, 23, 23, 24, 24, 24, 25, 25, 25, 26, 26, + 26, 27, 27, 27, 28, 28, 28, 29, 29, 29, 30, 30, 30, 31, 31, 31, 32, 32, + 32, 33, 33, 33, 34, 34, 34, 35, 35, 35, 36, 36, 36, 37, 37, 37, 38, 38, + 38, 39, 39, 39, 40, 40, 40, 41, 41, 41, 42, 42, 42, 43, 43, 43, 44, 44, + 44, 45, 45, 45, + ], + index_dtype, + ) + dst_indices = cp.array( + [ + 1, 2, 3, 0, 4, 26, 0, 10, 11, 0, 18, 19, 1, 5, 33, 4, 6, 29, 5, 7, 27, 6, + 8, 14, 7, 9, 38, 8, 10, 37, 2, 9, 39, 2, 12, 39, 11, 13, 35, 12, 14, 15, + 7, 13, 34, 13, 16, 22, 15, 17, 44, 16, 18, 43, 3, 17, 45, 3, 20, 45, 19, + 21, 41, 20, 22, 23, 15, 21, 40, 21, 24, 27, 23, 25, 32, 24, 26, 31, 1, + 25, 33, 6, 23, 28, 27, 29, 32, 5, 28, 30, 29, 31, 33, 25, 30, 32, 24, 28, + 31, 4, 26, 30, 14, 35, 38, 12, 34, 36, 35, 37, 39, 9, 36, 38, 8, 34, 37, + 10, 11, 36, 22, 41, 44, 20, 40, 42, 41, 43, 45, 17, 42, 44, 16, 40, 43, + 18, 19, 42, + ], + index_dtype, + ) + # fmt: on + G = graph_class.from_coo(46, src_indices, dst_indices, name="Tutte's Graph") + if inplace: + return create_using._become(G) + return G diff --git a/python/nx-cugraph/nx_cugraph/generators/social.py b/python/nx-cugraph/nx_cugraph/generators/social.py new file mode 100644 index 00000000000..3c936d07af3 --- /dev/null +++ b/python/nx-cugraph/nx_cugraph/generators/social.py @@ -0,0 +1,294 @@ +# Copyright (c) 2023, NVIDIA CORPORATION. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import cupy as cp +import numpy as np + +import nx_cugraph as nxcg + +from ..utils import index_dtype, networkx_algorithm + +__all__ = [ + "davis_southern_women_graph", + "florentine_families_graph", + "karate_club_graph", + "les_miserables_graph", +] + + +@networkx_algorithm +def davis_southern_women_graph(): + # fmt: off + src_indices = cp.array( + [ + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, + 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 8, 8, 8, + 8, 9, 9, 9, 9, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, + 12, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 15, 15, + 16, 16, 17, 17, 18, 18, 18, 19, 19, 19, 20, 20, 20, 20, 20, 20, 21, 21, + 21, 21, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 27, 27, 27, 27, 27, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 30, 30, 30, + 31, 31, 31, + ], + index_dtype, + ) + dst_indices = cp.array( + [ + 18, 19, 20, 21, 22, 23, 25, 26, 18, 19, 20, 22, 23, 24, 25, 19, 20, 21, + 22, 23, 24, 25, 26, 18, 20, 21, 22, 23, 24, 25, 20, 21, 22, 24, 20, 22, + 23, 25, 22, 23, 24, 25, 23, 25, 26, 22, 24, 25, 26, 24, 25, 26, 29, 25, + 26, 27, 29, 25, 26, 27, 29, 30, 31, 24, 25, 26, 27, 29, 30, 31, 23, 24, + 26, 27, 28, 29, 30, 31, 24, 25, 27, 28, 29, 25, 26, 26, 28, 26, 28, 0, 1, + 3, 0, 1, 2, 0, 1, 2, 3, 4, 5, 0, 2, 3, 4, 0, 1, 2, 3, 4, 5, 6, 8, 0, 1, + 2, 3, 5, 6, 7, 13, 1, 2, 3, 4, 6, 8, 9, 12, 13, 14, 0, 1, 2, 3, 5, 6, 7, + 8, 9, 10, 11, 12, 14, 15, 0, 2, 7, 8, 9, 10, 11, 12, 13, 15, 16, 17, 10, + 11, 12, 13, 14, 13, 14, 16, 17, 9, 10, 11, 12, 13, 14, 11, 12, 13, 11, + 12, 13, + ], + index_dtype, + ) + bipartite = cp.array( + [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + ], + np.int8, + ) + women = [ + "Evelyn Jefferson", "Laura Mandeville", "Theresa Anderson", "Brenda Rogers", + "Charlotte McDowd", "Frances Anderson", "Eleanor Nye", "Pearl Oglethorpe", + "Ruth DeSand", "Verne Sanderson", "Myra Liddel", "Katherina Rogers", + "Sylvia Avondale", "Nora Fayette", "Helen Lloyd", "Dorothy Murchison", + "Olivia Carleton", "Flora Price", + ] + events = [ + "E1", "E2", "E3", "E4", "E5", "E6", "E7", "E8", "E9", "E10", "E11", "E12", + "E13", "E14", + ] + # fmt: on + return nxcg.Graph.from_coo( + 32, + src_indices, + dst_indices, + node_values={"bipartite": bipartite}, + id_to_key=women + events, + top=women, + bottom=events, + ) + + +@networkx_algorithm +def florentine_families_graph(): + # fmt: off + src_indices = cp.array( + [ + 0, 1, 1, 1, 2, 2, 3, 3, 3, 4, 4, 4, 5, 6, 6, 6, 6, 7, 8, 8, 8, 8, 8, 8, + 9, 10, 10, 10, 11, 11, 11, 12, 12, 13, 13, 13, 13, 14, 14, 14, + ], + index_dtype, + ) + dst_indices = cp.array( + [ + 8, 5, 6, 8, 4, 8, 6, 10, 13, 2, 10, 13, 1, 1, 3, 7, 14, 6, 0, 1, 2, 11, + 12, 14, 12, 3, 4, 13, 8, 13, 14, 8, 9, 3, 4, 10, 11, 6, 8, 11, + ], + index_dtype, + ) + nodes = [ + "Acciaiuoli", "Albizzi", "Barbadori", "Bischeri", "Castellani", "Ginori", + "Guadagni", "Lamberteschi", "Medici", "Pazzi", "Peruzzi", "Ridolfi", + "Salviati", "Strozzi", "Tornabuoni" + ] + # fmt: on + return nxcg.Graph.from_coo(15, src_indices, dst_indices, id_to_key=nodes) + + +@networkx_algorithm +def karate_club_graph(): + # fmt: off + src_indices = cp.array( + [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 4, 4, 4, 5, 5, 5, 5, + 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 8, 9, 9, 10, 10, 10, 11, 12, 12, 13, + 13, 13, 13, 13, 14, 14, 15, 15, 16, 16, 17, 17, 18, 18, 19, 19, 19, 20, + 20, 21, 21, 22, 22, 23, 23, 23, 23, 23, 24, 24, 24, 25, 25, 25, 26, 26, + 27, 27, 27, 27, 28, 28, 28, 29, 29, 29, 29, 30, 30, 30, 30, 31, 31, 31, + 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, + 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, + ], + index_dtype, + ) + dst_indices = cp.array( + [ + 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 13, 17, 19, 21, 31, 0, 2, 3, 7, 13, + 17, 19, 21, 30, 0, 1, 3, 7, 8, 9, 13, 27, 28, 32, 0, 1, 2, 7, 12, 13, 0, + 6, 10, 0, 6, 10, 16, 0, 4, 5, 16, 0, 1, 2, 3, 0, 2, 30, 32, 33, 2, 33, + 0, 4, 5, 0, 0, 3, 0, 1, 2, 3, 33, 32, 33, 32, 33, 5, 6, 0, 1, 32, 33, 0, + 1, 33, 32, 33, 0, 1, 32, 33, 25, 27, 29, 32, 33, 25, 27, 31, 23, 24, 31, + 29, 33, 2, 23, 24, 33, 2, 31, 33, 23, 26, 32, 33, 1, 8, 32, 33, 0, 24, + 25, 28, 32, 33, 2, 8, 14, 15, 18, 20, 22, 23, 29, 30, 31, 33, 8, 9, 13, + 14, 15, 18, 19, 20, 22, 23, 26, 27, 28, 29, 30, 31, 32, + ], + index_dtype, + ) + weights = cp.array( + [ + 4, 5, 3, 3, 3, 3, 2, 2, 2, 3, 1, 3, 2, 2, 2, 2, 4, 6, 3, 4, 5, 1, 2, 2, + 2, 5, 6, 3, 4, 5, 1, 3, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 2, 3, 3, 5, 3, 3, + 3, 2, 5, 3, 2, 4, 4, 3, 2, 5, 3, 3, 4, 1, 2, 2, 3, 3, 3, 1, 3, 3, 5, 3, + 3, 3, 3, 2, 3, 4, 3, 3, 2, 1, 1, 2, 2, 2, 1, 3, 1, 2, 2, 2, 3, 5, 4, 3, + 5, 4, 2, 3, 2, 5, 2, 7, 4, 2, 2, 4, 3, 4, 2, 2, 2, 3, 4, 4, 2, 2, 3, 3, + 3, 2, 2, 7, 2, 4, 4, 2, 3, 3, 3, 1, 3, 2, 5, 4, 3, 4, 5, 4, 2, 3, 2, 4, + 2, 1, 1, 3, 4, 2, 4, 2, 2, 3, 4, 5, + ], + np.int8, + ) + # For now, cupy doesn't handle str dtypes and we primarily handle cupy arrays. + # We try to support numpy arrays for node values, so let's use numpy here. + clubs = np.array([ + "Mr. Hi", "Mr. Hi", "Mr. Hi", "Mr. Hi", "Mr. Hi", "Mr. Hi", "Mr. Hi", + "Mr. Hi", "Mr. Hi", "Officer", "Mr. Hi", "Mr. Hi", "Mr. Hi", "Mr. Hi", + "Officer", "Officer", "Mr. Hi", "Mr. Hi", "Officer", "Mr. Hi", "Officer", + "Mr. Hi", "Officer", "Officer", "Officer", "Officer", "Officer", "Officer", + "Officer", "Officer", "Officer", "Officer", "Officer", "Officer", + ]) + # fmt: on + return nxcg.Graph.from_coo( + 34, + src_indices, + dst_indices, + edge_values={"weight": weights}, + node_values={"club": clubs}, + name="Zachary's Karate Club", + ) + + +@networkx_algorithm +def les_miserables_graph(): + # fmt: off + src_indices = cp.array( + [ + 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 7, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 10, 10, + 10, 10, 10, 10, 11, 12, 12, 12, 12, 12, 12, 13, 13, 14, 14, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 19, + 20, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 23, 23, 23, + 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, + 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28, 28, + 28, 28, 29, 29, 29, 29, 29, 29, 29, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 32, 33, 34, 34, 34, 34, 34, 34, 34, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 36, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, + 38, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, + 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 41, 42, 42, 42, 42, 42, + 42, 43, 44, 44, 44, 44, 44, 44, 44, 45, 45, 45, 45, 46, 46, 46, 46, 46, + 46, 46, 46, 46, 46, 46, 47, 47, 48, 48, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 50, 50, 50, 51, 51, 51, 51, + 51, 51, 51, 52, 53, 53, 54, 55, 55, 55, 55, 55, 55, 55, 56, 56, 56, 57, + 57, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 60, 60, 61, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 63, 64, + 65, 65, 66, 66, 66, 67, 67, 67, 67, 67, 67, 67, 67, 67, 68, 69, 69, 69, + 69, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 71, + 71, 71, 71, 71, 71, 71, 71, 71, 72, 72, 72, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 74, 74, 75, 75, 75, 76, 76, + 76, 76, 76, 76, 76, + ], + index_dtype, + ) + dst_indices = cp.array( + [ + 25, 58, 70, 9, 15, 25, 31, 37, 39, 58, 59, 70, 73, 6, 17, 21, 24, 30, 31, + 35, 40, 46, 49, 55, 67, 8, 10, 12, 16, 27, 39, 42, 73, 34, 49, 23, 26, + 27, 29, 44, 71, 76, 2, 17, 21, 24, 30, 31, 35, 40, 46, 49, 55, 67, 73, + 70, 3, 10, 12, 16, 42, 73, 1, 15, 25, 31, 37, 59, 70, 3, 8, 12, 16, 42, + 73, 62, 3, 8, 10, 16, 42, 73, 14, 31, 13, 31, 1, 9, 24, 25, 37, 39, 58, + 59, 70, 73, 3, 8, 10, 12, 42, 73, 2, 6, 21, 24, 30, 31, 35, 40, 46, 49, + 67, 34, 39, 45, 49, 51, 58, 70, 71, 72, 73, 75, 62, 62, 2, 6, 17, 24, 25, + 30, 31, 35, 40, 46, 49, 55, 67, 62, 5, 26, 27, 29, 44, 71, 76, 2, 6, 15, + 17, 21, 30, 31, 35, 39, 40, 46, 49, 55, 67, 73, 0, 1, 9, 15, 21, 37, 46, + 49, 58, 59, 70, 5, 23, 27, 29, 44, 71, 76, 3, 5, 23, 26, 29, 39, 44, 48, + 58, 65, 69, 70, 71, 73, 76, 36, 39, 60, 73, 5, 23, 26, 27, 44, 71, 76, 2, + 6, 17, 21, 24, 31, 35, 40, 46, 49, 67, 1, 2, 6, 9, 13, 14, 17, 21, 24, + 30, 35, 37, 39, 40, 46, 49, 53, 55, 59, 67, 70, 73, 62, 73, 4, 18, 45, + 47, 49, 51, 73, 2, 6, 17, 21, 24, 30, 31, 40, 55, 67, 28, 1, 9, 15, 25, + 31, 39, 58, 59, 70, 73, 73, 1, 3, 15, 18, 24, 27, 28, 31, 37, 58, 59, 69, + 70, 72, 73, 74, 75, 2, 6, 17, 21, 24, 30, 31, 35, 46, 49, 55, 67, 53, 3, + 8, 10, 12, 16, 73, 73, 5, 23, 26, 27, 29, 71, 76, 18, 34, 49, 51, 2, 6, + 17, 21, 24, 25, 30, 31, 40, 49, 61, 34, 58, 27, 73, 2, 4, 6, 17, 18, 21, + 24, 25, 30, 31, 34, 40, 45, 46, 51, 66, 70, 71, 73, 56, 62, 73, 18, 34, + 45, 49, 52, 57, 73, 51, 31, 41, 73, 2, 6, 21, 24, 31, 35, 40, 50, 62, 73, + 51, 66, 0, 1, 15, 18, 25, 27, 37, 39, 47, 70, 73, 1, 9, 15, 25, 31, 37, + 39, 70, 73, 28, 73, 46, 11, 19, 20, 22, 32, 50, 56, 63, 64, 73, 62, 62, + 27, 69, 49, 57, 70, 2, 6, 17, 21, 24, 30, 31, 35, 40, 73, 27, 39, 65, 73, + 0, 1, 7, 9, 15, 18, 25, 27, 31, 37, 39, 49, 58, 59, 66, 73, 5, 18, 23, + 26, 27, 29, 44, 49, 76, 18, 39, 73, 1, 3, 6, 8, 10, 12, 15, 16, 18, 24, + 27, 28, 31, 33, 34, 37, 38, 39, 42, 43, 48, 49, 50, 51, 54, 56, 58, 59, + 60, 62, 68, 69, 70, 72, 74, 75, 39, 73, 18, 39, 73, 5, 23, 26, 27, 29, + 44, 71, + ], + index_dtype, + ) + weights = cp.array( + [ + 2, 1, 2, 3, 4, 1, 1, 6, 2, 1, 2, 6, 1, 4, 5, 6, 4, 3, 5, 1, 5, 2, 1, 1, + 2, 1, 2, 1, 1, 1, 1, 2, 2, 1, 1, 3, 4, 3, 4, 4, 4, 3, 4, 9, 12, 10, 6, 5, + 3, 7, 1, 5, 1, 2, 1, 1, 1, 2, 2, 2, 2, 2, 3, 1, 1, 1, 3, 1, 3, 2, 2, 2, + 2, 3, 3, 1, 1, 2, 2, 2, 2, 2, 3, 2, 3, 2, 4, 1, 1, 1, 4, 1, 1, 2, 4, 1, + 1, 2, 2, 2, 2, 2, 5, 9, 13, 15, 5, 6, 1, 5, 2, 5, 2, 3, 1, 1, 21, 2, 4, + 1, 1, 2, 31, 1, 2, 1, 6, 12, 13, 17, 1, 6, 7, 2, 5, 2, 9, 1, 3, 1, 3, 3, + 4, 5, 3, 3, 4, 4, 10, 1, 15, 17, 6, 7, 3, 6, 5, 1, 7, 1, 4, 4, 2, 1, 1, + 1, 1, 1, 1, 5, 2, 1, 3, 4, 3, 3, 3, 4, 4, 3, 1, 3, 4, 3, 4, 5, 3, 2, 2, + 1, 2, 1, 3, 9, 4, 2, 1, 3, 8, 4, 5, 3, 4, 3, 3, 4, 3, 6, 5, 6, 6, 2, 1, + 5, 1, 1, 2, 1, 5, 5, 1, 2, 2, 6, 7, 7, 2, 1, 1, 1, 3, 1, 4, 2, 1, 1, 1, + 1, 1, 1, 1, 1, 3, 1, 1, 12, 9, 2, 1, 3, 1, 2, 3, 1, 1, 2, 1, 1, 2, 6, 3, + 4, 1, 1, 1, 1, 2, 5, 1, 1, 2, 1, 1, 1, 6, 5, 1, 1, 1, 1, 1, 1, 5, 1, 17, + 1, 1, 5, 7, 5, 5, 5, 5, 3, 2, 1, 2, 1, 2, 1, 2, 2, 3, 2, 2, 3, 1, 4, 3, + 4, 3, 3, 4, 3, 1, 1, 1, 2, 2, 1, 2, 2, 1, 1, 1, 1, 1, 1, 3, 1, 1, 2, 1, + 1, 1, 5, 5, 21, 9, 7, 5, 1, 4, 12, 2, 1, 1, 6, 1, 2, 1, 19, 6, 8, 3, 2, + 9, 2, 6, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 6, 10, 3, 1, 1, 1, 1, + 1, 4, 2, 2, 1, 1, 1, 13, 7, 2, 1, 2, 1, 1, 2, 1, 1, 1, 3, 1, 3, 1, 2, 1, + 1, 1, 8, 10, 1, 1, 5, 1, 1, 1, 2, 1, 1, 1, 2, 2, 2, 3, 4, 2, 1, 1, 2, 1, + 2, 1, 2, 3, 2, 6, 1, 3, 4, 1, 3, 1, 1, 5, 5, 2, 13, 1, 1, 12, 4, 1, 3, 4, + 3, 3, 4, 1, 3, 2, 1, 1, 1, 2, 1, 2, 3, 2, 1, 2, 31, 4, 9, 8, 1, 1, 2, 1, + 1, 17, 3, 1, 1, 19, 3, 2, 1, 3, 7, 1, 1, 5, 1, 3, 12, 1, 2, 3, 1, 2, 1, + 1, 3, 3, 4, 3, 4, 4, 3, 3, + ], + np.int8, + ) + nodes = [ + "Anzelma", "Babet", "Bahorel", "Bamatabois", "BaronessT", "Blacheville", + "Bossuet", "Boulatruelle", "Brevet", "Brujon", "Champmathieu", + "Champtercier", "Chenildieu", "Child1", "Child2", "Claquesous", + "Cochepaille", "Combeferre", "Cosette", "Count", "CountessDeLo", + "Courfeyrac", "Cravatte", "Dahlia", "Enjolras", "Eponine", "Fameuil", + "Fantine", "Fauchelevent", "Favourite", "Feuilly", "Gavroche", "Geborand", + "Gervais", "Gillenormand", "Grantaire", "Gribier", "Gueulemer", "Isabeau", + "Javert", "Joly", "Jondrette", "Judge", "Labarre", "Listolier", + "LtGillenormand", "Mabeuf", "Magnon", "Marguerite", "Marius", + "MlleBaptistine", "MlleGillenormand", "MlleVaubois", "MmeBurgon", "MmeDeR", + "MmeHucheloup", "MmeMagloire", "MmePontmercy", "MmeThenardier", + "Montparnasse", "MotherInnocent", "MotherPlutarch", "Myriel", "Napoleon", + "OldMan", "Perpetue", "Pontmercy", "Prouvaire", "Scaufflaire", "Simplice", + "Thenardier", "Tholomyes", "Toussaint", "Valjean", "Woman1", "Woman2", + "Zephine", + ] + # fmt: on + return nxcg.Graph.from_coo( + 77, src_indices, dst_indices, edge_values={"weight": weights}, id_to_key=nodes + ) diff --git a/python/nx-cugraph/nx_cugraph/interface.py b/python/nx-cugraph/nx_cugraph/interface.py index a7b88b72ec5..fd0b1483d73 100644 --- a/python/nx-cugraph/nx_cugraph/interface.py +++ b/python/nx-cugraph/nx_cugraph/interface.py @@ -65,6 +65,7 @@ def key(testpath): no_weights = "weighted implementation not currently supported" no_multigraph = "multigraphs not currently supported" louvain_different = "Louvain may be different due to RNG" + no_string_dtype = "string edge values not currently supported" xfail = {} @@ -187,6 +188,40 @@ def key(testpath): xfail[ key("test_louvain.py:test_threshold") ] = "Louvain does not support seed parameter" + if nxver.major == 3 and nxver.minor >= 2: + xfail.update( + { + key( + "test_convert_pandas.py:TestConvertPandas." + "test_from_edgelist_multi_attr_incl_target" + ): no_string_dtype, + key( + "test_convert_pandas.py:TestConvertPandas." + "test_from_edgelist_multidigraph_and_edge_attr" + ): no_string_dtype, + key( + "test_convert_pandas.py:TestConvertPandas." + "test_from_edgelist_int_attr_name" + ): no_string_dtype, + } + ) + if nxver.minor == 2: + different_iteration_order = "Different graph data iteration order" + xfail.update( + { + key( + "test_cycles.py:TestMinimumCycleBasis." + "test_gh6787_and_edge_attribute_names" + ): different_iteration_order, + key( + "test_euler.py:TestEulerianCircuit." + "test_eulerian_circuit_cycle" + ): different_iteration_order, + key( + "test_gml.py:TestGraph.test_special_float_label" + ): different_iteration_order, + } + ) for item in items: kset = set(item.keywords) diff --git a/python/nx-cugraph/nx_cugraph/tests/test_generators.py b/python/nx-cugraph/nx_cugraph/tests/test_generators.py new file mode 100644 index 00000000000..511f8dcd8e2 --- /dev/null +++ b/python/nx-cugraph/nx_cugraph/tests/test_generators.py @@ -0,0 +1,277 @@ +# Copyright (c) 2023, NVIDIA CORPORATION. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import networkx as nx +import numpy as np +import pytest +from packaging.version import parse + +import nx_cugraph as nxcg + +nxver = parse(nx.__version__) + + +def assert_graphs_equal(Gnx, Gcg): + assert isinstance(Gnx, nx.Graph) + assert isinstance(Gcg, nxcg.Graph) + assert Gnx.number_of_nodes() == Gcg.number_of_nodes() + assert Gnx.number_of_edges() == Gcg.number_of_edges() + assert Gnx.is_directed() == Gcg.is_directed() + assert Gnx.is_multigraph() == Gcg.is_multigraph() + G = nxcg.to_networkx(Gcg) + rv = nx.utils.graphs_equal(G, Gnx) + if not rv: + print("GRAPHS ARE NOT EQUAL!") + assert sorted(G) == sorted(Gnx) + assert sorted(G._adj) == sorted(Gnx._adj) + assert sorted(G._node) == sorted(Gnx._node) + for k in sorted(G._adj): + print(k, sorted(G._adj[k]), sorted(Gnx._adj[k])) + print(nx.to_scipy_sparse_array(G).todense()) + print(nx.to_scipy_sparse_array(Gnx).todense()) + print(G.graph) + print(Gnx.graph) + assert rv + + +if nxver.major == 3 and nxver.minor < 2: + pytest.skip("Need NetworkX >=3.2 to test generators", allow_module_level=True) + + +def compare(name, create_using, *args, is_vanilla=False): + exc1 = exc2 = None + func = getattr(nx, name) + if isinstance(create_using, nxcg.Graph): + nx_create_using = nxcg.to_networkx(create_using) + elif isinstance(create_using, type) and issubclass(create_using, nxcg.Graph): + nx_create_using = create_using.to_networkx_class() + elif isinstance(create_using, nx.Graph): + nx_create_using = create_using.copy() + else: + nx_create_using = create_using + try: + if is_vanilla: + G = func(*args) + else: + G = func(*args, create_using=nx_create_using) + except Exception as exc: + exc1 = exc + try: + if is_vanilla: + Gcg = func(*args, backend="cugraph") + else: + Gcg = func(*args, create_using=create_using, backend="cugraph") + except ZeroDivisionError: + raise + except NotImplementedError as exc: + if name in {"complete_multipartite_graph"}: # nx.__version__[:3] <= "3.2" + return + exc2 = exc + except Exception as exc: + if exc1 is None: # pragma: no cover (debug) + raise + exc2 = exc + if exc1 is not None or exc2 is not None: + assert type(exc1) is type(exc2) + else: + assert_graphs_equal(G, Gcg) + + +N = list(range(-1, 5)) +CREATE_USING = [nx.Graph, nx.DiGraph, nx.MultiGraph, nx.MultiDiGraph] +COMPLETE_CREATE_USING = [ + nx.Graph, + nx.DiGraph, + nx.MultiGraph, + nx.MultiDiGraph, + nxcg.Graph, + nxcg.DiGraph, + nxcg.MultiGraph, + nxcg.MultiDiGraph, + # These raise NotImplementedError + # nx.Graph(), + # nx.DiGraph(), + # nx.MultiGraph(), + # nx.MultiDiGraph(), + nxcg.Graph(), + nxcg.DiGraph(), + nxcg.MultiGraph(), + nxcg.MultiDiGraph(), + None, + object, # Bad input + 7, # Bad input +] +GENERATORS_NOARG = [ + # classic + "null_graph", + "trivial_graph", + # small + "bull_graph", + "chvatal_graph", + "cubical_graph", + "desargues_graph", + "diamond_graph", + "dodecahedral_graph", + "frucht_graph", + "heawood_graph", + "house_graph", + "house_x_graph", + "icosahedral_graph", + "krackhardt_kite_graph", + "moebius_kantor_graph", + "octahedral_graph", + "petersen_graph", + "sedgewick_maze_graph", + "tetrahedral_graph", + "truncated_cube_graph", + "truncated_tetrahedron_graph", + "tutte_graph", +] +GENERATORS_NOARG_VANILLA = [ + # classic + "complete_multipartite_graph", + # small + "pappus_graph", + # social + "davis_southern_women_graph", + "florentine_families_graph", + "karate_club_graph", + "les_miserables_graph", +] +GENERATORS_N = [ + # classic + "circular_ladder_graph", + "complete_graph", + "cycle_graph", + "empty_graph", + "ladder_graph", + "path_graph", + "star_graph", + "wheel_graph", +] +GENERATORS_M_N = [ + # classic + "barbell_graph", + "lollipop_graph", + "tadpole_graph", + # bipartite + "complete_bipartite_graph", +] +GENERATORS_M_N_VANILLA = [ + # classic + "complete_multipartite_graph", + "turan_graph", + # community + "caveman_graph", +] + + +@pytest.mark.parametrize("name", GENERATORS_NOARG) +@pytest.mark.parametrize("create_using", COMPLETE_CREATE_USING) +def test_generator_noarg(name, create_using): + print(name, create_using, type(create_using)) + if isinstance(create_using, nxcg.Graph) and name in { + # fmt: off + "bull_graph", "chvatal_graph", "cubical_graph", "diamond_graph", + "house_graph", "house_x_graph", "icosahedral_graph", "krackhardt_kite_graph", + "octahedral_graph", "petersen_graph", "truncated_cube_graph", "tutte_graph", + # fmt: on + }: + # The _raise_on_directed decorator used in networkx doesn't like our graphs. + if create_using.is_directed(): + with pytest.raises(AssertionError): + compare(name, create_using) + else: + with pytest.raises(TypeError): + compare(name, create_using) + else: + compare(name, create_using) + + +@pytest.mark.parametrize("name", GENERATORS_NOARG_VANILLA) +def test_generator_noarg_vanilla(name): + print(name) + compare(name, None, is_vanilla=True) + + +@pytest.mark.parametrize("name", GENERATORS_N) +@pytest.mark.parametrize("n", N) +@pytest.mark.parametrize("create_using", CREATE_USING) +def test_generator_n(name, n, create_using): + print(name, n, create_using) + compare(name, create_using, n) + + +@pytest.mark.parametrize("name", GENERATORS_N) +@pytest.mark.parametrize("n", [1, 4]) +@pytest.mark.parametrize("create_using", COMPLETE_CREATE_USING) +def test_generator_n_complete(name, n, create_using): + print(name, n, create_using) + compare(name, create_using, n) + + +@pytest.mark.parametrize("name", GENERATORS_M_N) +@pytest.mark.parametrize("create_using", CREATE_USING) +@pytest.mark.parametrize("m", N) +@pytest.mark.parametrize("n", N) +def test_generator_m_n(name, create_using, m, n): + print(name, m, n, create_using) + compare(name, create_using, m, n) + + +@pytest.mark.parametrize("name", GENERATORS_M_N_VANILLA) +@pytest.mark.parametrize("m", N) +@pytest.mark.parametrize("n", N) +def test_generator_m_n_vanilla(name, m, n): + print(name, m, n) + compare(name, None, m, n, is_vanilla=True) + + +@pytest.mark.parametrize("name", GENERATORS_M_N) +@pytest.mark.parametrize("create_using", COMPLETE_CREATE_USING) +@pytest.mark.parametrize("m", [4]) +@pytest.mark.parametrize("n", [4]) +def test_generator_m_n_complete(name, create_using, m, n): + print(name, m, n, create_using) + compare(name, create_using, m, n) + + +@pytest.mark.parametrize("name", GENERATORS_M_N_VANILLA) +@pytest.mark.parametrize("m", [4]) +@pytest.mark.parametrize("n", [4]) +def test_generator_m_n_complete_vanilla(name, m, n): + print(name, m, n) + compare(name, None, m, n, is_vanilla=True) + + +def test_bad_lollipop_graph(): + compare("lollipop_graph", None, [0, 1], [1, 2]) + + +def test_can_convert_karate_club(): + # Karate club graph has string node values. + # This really tests conversions, but it's here so we can use `assert_graphs_equal`. + G = nx.karate_club_graph() + G.add_node(0, foo="bar") # string dtype with a mask + G.add_node(1, object=object()) # haha + Gcg = nxcg.from_networkx(G, preserve_all_attrs=True) + assert_graphs_equal(G, Gcg) + Gnx = nxcg.to_networkx(Gcg) + assert nx.utils.graphs_equal(G, Gnx) + assert isinstance(Gcg.node_values["club"], np.ndarray) + assert Gcg.node_values["club"].dtype.kind == "U" + assert isinstance(Gcg.node_values["foo"], np.ndarray) + assert isinstance(Gcg.node_masks["foo"], np.ndarray) + assert Gcg.node_values["foo"].dtype.kind == "U" + assert isinstance(Gcg.node_values["object"], np.ndarray) + assert Gcg.node_values["object"].dtype.kind == "O" + assert isinstance(Gcg.node_masks["object"], np.ndarray) diff --git a/python/nx-cugraph/nx_cugraph/tests/test_utils.py b/python/nx-cugraph/nx_cugraph/tests/test_utils.py new file mode 100644 index 00000000000..fdd0c91995c --- /dev/null +++ b/python/nx-cugraph/nx_cugraph/tests/test_utils.py @@ -0,0 +1,87 @@ +# Copyright (c) 2023, NVIDIA CORPORATION. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import numpy as np +import pytest + +from nx_cugraph.utils import _get_int_dtype + + +def test_get_int_dtype(): + uint8 = np.dtype(np.uint8) + uint16 = np.dtype(np.uint16) + uint32 = np.dtype(np.uint32) + uint64 = np.dtype(np.uint64) + # signed + cur = np.iinfo(np.int8) + for val in [cur.min, cur.min + 1, -1, 0, 1, cur.max - 1, cur.max]: + assert _get_int_dtype(val) == np.int8 + assert _get_int_dtype(val, signed=True) == np.int8 + if val >= 0: + assert _get_int_dtype(val, unsigned=True) == np.uint8 + assert _get_int_dtype(val + 1, unsigned=True) == np.uint8 + prev = cur + cur = np.iinfo(np.int16) + for val in [cur.min, cur.min + 1, prev.min - 1, prev.max + 1, cur.max - 1, cur.max]: + assert _get_int_dtype(val) != prev.dtype + assert _get_int_dtype(val, signed=True) == np.int16 + if val >= 0: + assert _get_int_dtype(val, unsigned=True) in {uint8, uint16} + assert _get_int_dtype(val + 1, unsigned=True) in {uint8, uint16} + prev = cur + cur = np.iinfo(np.int32) + for val in [cur.min, cur.min + 1, prev.min - 1, prev.max + 1, cur.max - 1, cur.max]: + assert _get_int_dtype(val) != prev.dtype + assert _get_int_dtype(val, signed=True) == np.int32 + if val >= 0: + assert _get_int_dtype(val, unsigned=True) in {uint16, uint32} + assert _get_int_dtype(val + 1, unsigned=True) in {uint16, uint32} + prev = cur + cur = np.iinfo(np.int64) + for val in [cur.min, cur.min + 1, prev.min - 1, prev.max + 1, cur.max - 1, cur.max]: + assert _get_int_dtype(val) != prev.dtype + assert _get_int_dtype(val, signed=True) == np.int64 + if val >= 0: + assert _get_int_dtype(val, unsigned=True) in {uint32, uint64} + assert _get_int_dtype(val + 1, unsigned=True) in {uint32, uint64} + with pytest.raises(ValueError, match="Value is too"): + _get_int_dtype(cur.min - 1, signed=True) + with pytest.raises(ValueError, match="Value is too"): + _get_int_dtype(cur.max + 1, signed=True) + + # unsigned + cur = np.iinfo(np.uint8) + for val in [0, 1, cur.max - 1, cur.max]: + assert _get_int_dtype(val) == (np.uint8 if val > 1 else np.int8) + assert _get_int_dtype(val, unsigned=True) == np.uint8 + assert _get_int_dtype(cur.max + 1) == np.int16 + cur = np.iinfo(np.uint16) + for val in [cur.max - 1, cur.max]: + assert _get_int_dtype(val, unsigned=True) == np.uint16 + assert _get_int_dtype(cur.max + 1) == np.int32 + cur = np.iinfo(np.uint32) + for val in [cur.max - 1, cur.max]: + assert _get_int_dtype(val, unsigned=True) == np.uint32 + assert _get_int_dtype(cur.max + 1) == np.int64 + cur = np.iinfo(np.uint64) + for val in [cur.max - 1, cur.max]: + assert _get_int_dtype(val, unsigned=True) == np.uint64 + with pytest.raises(ValueError, match="Value is incompatible"): + _get_int_dtype(cur.min - 1, unsigned=True) + with pytest.raises(ValueError, match="Value is too"): + _get_int_dtype(cur.max + 1, unsigned=True) + + # API + with pytest.raises(TypeError, match="incompatible"): + _get_int_dtype(7, signed=True, unsigned=True) + assert _get_int_dtype(7, signed=True, unsigned=False) == np.int8 + assert _get_int_dtype(7, signed=False, unsigned=True) == np.uint8 diff --git a/python/nx-cugraph/nx_cugraph/typing.py b/python/nx-cugraph/nx_cugraph/typing.py index d3045ab4656..b419a9085e0 100644 --- a/python/nx-cugraph/nx_cugraph/typing.py +++ b/python/nx-cugraph/nx_cugraph/typing.py @@ -15,6 +15,9 @@ from collections.abc import Hashable from typing import TypeVar +import cupy as cp +import numpy as np + AttrKey = TypeVar("AttrKey", bound=Hashable) EdgeKey = TypeVar("EdgeKey", bound=Hashable) NodeKey = TypeVar("NodeKey", bound=Hashable) @@ -23,3 +26,8 @@ NodeValue = TypeVar("NodeValue") IndexValue = TypeVar("IndexValue") Dtype = TypeVar("Dtype") + + +class any_ndarray: + def __class_getitem__(cls, item): + return cp.ndarray[item] | np.ndarray[item] diff --git a/python/nx-cugraph/nx_cugraph/utils/misc.py b/python/nx-cugraph/nx_cugraph/utils/misc.py index 72e4094b8b7..9683df5e7f9 100644 --- a/python/nx-cugraph/nx_cugraph/utils/misc.py +++ b/python/nx-cugraph/nx_cugraph/utils/misc.py @@ -12,13 +12,19 @@ # limitations under the License. from __future__ import annotations +import itertools import operator as op import sys from random import Random +from typing import SupportsIndex import cupy as cp +import numpy as np -__all__ = ["_groupby", "_seed_to_int"] +__all__ = ["index_dtype", "_groupby", "_seed_to_int", "_get_int_dtype"] + +# This may switch to np.uint32 at some point +index_dtype = np.int32 def _groupby(groups: cp.ndarray, values: cp.ndarray) -> dict[int, cp.ndarray]: @@ -58,3 +64,67 @@ def _seed_to_int(seed: int | Random | None) -> int: if isinstance(seed, Random): return seed.randint(0, sys.maxsize) return op.index(seed) # Ensure seed is integral + + +def _get_int_dtype( + val: SupportsIndex, *, signed: bool | None = None, unsigned: bool | None = None +): + """Determine the smallest integer dtype that can store the integer ``val``. + + If signed or unsigned are unspecified, then signed integers are preferred + unless the value can be represented by a smaller unsigned integer. + + Raises + ------ + ValueError : If the value cannot be represented with an int dtype. + """ + # This is similar in spirit to `np.min_scalar_type` + if signed is not None: + if unsigned is not None and (not signed) is (not unsigned): + raise TypeError( + f"signed (={signed}) and unsigned (={unsigned}) keyword arguments " + "are incompatible." + ) + signed = bool(signed) + unsigned = not signed + elif unsigned is not None: + unsigned = bool(unsigned) + signed = not unsigned + + val = op.index(val) # Ensure val is integral + if val < 0: + if unsigned: + raise ValueError(f"Value is incompatible with unsigned int: {val}.") + signed = True + unsigned = False + + if signed is not False: + # Number of bytes (and a power of two) + signed_nbytes = (val + (val < 0)).bit_length() // 8 + 1 + signed_nbytes = next( + filter( + signed_nbytes.__le__, + itertools.accumulate(itertools.repeat(2), op.mul, initial=1), + ) + ) + if unsigned is not False: + # Number of bytes (and a power of two) + unsigned_nbytes = (val.bit_length() + 7) // 8 + unsigned_nbytes = next( + filter( + unsigned_nbytes.__le__, + itertools.accumulate(itertools.repeat(2), op.mul, initial=1), + ) + ) + if signed is None and unsigned is None: + # Prefer signed int if same size + signed = signed_nbytes <= unsigned_nbytes + + if signed: + dtype_string = f"i{signed_nbytes}" + else: + dtype_string = f"u{unsigned_nbytes}" + try: + return np.dtype(dtype_string) + except TypeError as exc: + raise ValueError("Value is too large to store as integer: {val}") from exc diff --git a/python/nx-cugraph/pyproject.toml b/python/nx-cugraph/pyproject.toml index 9fec8fa0242..3d3029da232 100644 --- a/python/nx-cugraph/pyproject.toml +++ b/python/nx-cugraph/pyproject.toml @@ -160,7 +160,8 @@ ignore = [ # "SIM300", # Yoda conditions are discouraged, use ... instead (Note: we're not this picky) # "SIM401", # Use dict.get ... instead of if-else-block (Note: if-else better for coverage and sometimes clearer) # "TRY004", # Prefer `TypeError` exception for invalid type (Note: good advice, but not worth the nuisance) - # "TRY200", # Use `raise from` to specify exception cause (Note: sometimes okay to raise original exception) + "B904", # Bare `raise` inside exception clause (like TRY200; sometimes okay) + "TRY200", # Use `raise from` to specify exception cause (Note: sometimes okay to raise original exception) # Intentionally ignored "A003", # Class attribute ... is shadowing a python builtin From 24845ca319fb3922f514ef98844f1e1acb5e0123 Mon Sep 17 00:00:00 2001 From: Tingyu Wang Date: Tue, 31 Oct 2023 15:38:05 -0400 Subject: [PATCH 066/111] Skip certain `cugraph-pyg` tests when `torch_sparse` is not available (#3962) The CSC (CSR) code path in cugraph-pyg requires `torch_sparse` package. However, `torch_sparse` does not seem to work out of box for rockylinux8. This PR fixes such [CI failures](https://github.com/rapidsai/cugraph/actions/runs/6691094105/job/18177667677). Closes https://github.com/rapidsai/graph_dl/issues/343 Authors: - Tingyu Wang (https://github.com/tingyu66) Approvers: - Naim (https://github.com/naimnv) - Alex Barghi (https://github.com/alexbarghi-nv) URL: https://github.com/rapidsai/cugraph/pull/3962 --- .../cugraph-pyg/cugraph_pyg/tests/test_cugraph_loader.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/python/cugraph-pyg/cugraph_pyg/tests/test_cugraph_loader.py b/python/cugraph-pyg/cugraph_pyg/tests/test_cugraph_loader.py index 03274948158..836b30c9df7 100644 --- a/python/cugraph-pyg/cugraph_pyg/tests/test_cugraph_loader.py +++ b/python/cugraph-pyg/cugraph_pyg/tests/test_cugraph_loader.py @@ -29,6 +29,7 @@ torch = import_optional("torch") torch_geometric = import_optional("torch_geometric") +torch_sparse = import_optional("torch_sparse") trim_to_layer = import_optional("torch_geometric.utils.trim_to_layer") @@ -200,6 +201,9 @@ def test_cugraph_loader_from_disk_subset(): @pytest.mark.skipif(isinstance(torch, MissingModule), reason="torch not available") +@pytest.mark.skipif( + isinstance(torch_sparse, MissingModule), reason="torch-sparse not available" +) def test_cugraph_loader_from_disk_subset_csr(): m = [2, 9, 99, 82, 11, 13] n = torch.arange(1, 1 + len(m), dtype=torch.int32) @@ -332,6 +336,9 @@ def test_cugraph_loader_e2e_coo(): @pytest.mark.skipif(isinstance(torch, MissingModule), reason="torch not available") +@pytest.mark.skipif( + isinstance(torch_sparse, MissingModule), reason="torch-sparse not available" +) @pytest.mark.parametrize("framework", ["pyg", "cugraph-ops"]) def test_cugraph_loader_e2e_csc(framework): m = [2, 9, 99, 82, 9, 3, 18, 1, 12] From eb1e515553ef133f0ea484a663b1b3ff82b1d5c6 Mon Sep 17 00:00:00 2001 From: Erik Welch Date: Tue, 31 Oct 2023 17:50:52 -0500 Subject: [PATCH 067/111] nx-cugraph: add CC for undirected graphs to fix k-truss (#3965) Fixes #3963 and add `connected_components`, `is_connected`, `node_connected_component`, and `number_connected_components`. Also updated `_groupby` to handle groups that are not consecutive integers starting with 0. Also, `plc.weakly_connected_components` does not handle isolated nodes well, and I needed to handle this at the Python layer as was done in #3897 Authors: - Erik Welch (https://github.com/eriknw) Approvers: - Rick Ratzel (https://github.com/rlratzel) URL: https://github.com/rapidsai/cugraph/pull/3965 --- python/nx-cugraph/_nx_cugraph/__init__.py | 8 ++ .../nx_cugraph/algorithms/__init__.py | 3 +- .../algorithms/community/louvain.py | 2 +- .../algorithms/components/__init__.py | 13 ++ .../algorithms/components/connected.py | 130 ++++++++++++++++++ .../nx-cugraph/nx_cugraph/algorithms/core.py | 10 ++ python/nx-cugraph/nx_cugraph/classes/graph.py | 5 + python/nx-cugraph/nx_cugraph/interface.py | 9 ++ .../nx_cugraph/tests/test_ktruss.py | 30 ++++ .../nx-cugraph/nx_cugraph/utils/decorators.py | 5 +- python/nx-cugraph/nx_cugraph/utils/misc.py | 44 ++++-- python/nx-cugraph/pyproject.toml | 1 + 12 files changed, 243 insertions(+), 17 deletions(-) create mode 100644 python/nx-cugraph/nx_cugraph/algorithms/components/__init__.py create mode 100644 python/nx-cugraph/nx_cugraph/algorithms/components/connected.py create mode 100644 python/nx-cugraph/nx_cugraph/tests/test_ktruss.py diff --git a/python/nx-cugraph/_nx_cugraph/__init__.py b/python/nx-cugraph/_nx_cugraph/__init__.py index af1df04644c..8ef976aabf1 100644 --- a/python/nx-cugraph/_nx_cugraph/__init__.py +++ b/python/nx-cugraph/_nx_cugraph/__init__.py @@ -38,6 +38,7 @@ "complete_bipartite_graph", "complete_graph", "complete_multipartite_graph", + "connected_components", "cubical_graph", "cycle_graph", "davis_southern_women_graph", @@ -56,6 +57,7 @@ "house_x_graph", "icosahedral_graph", "in_degree_centrality", + "is_connected", "is_isolate", "isolates", "k_truss", @@ -66,7 +68,9 @@ "lollipop_graph", "louvain_communities", "moebius_kantor_graph", + "node_connected_component", "null_graph", + "number_connected_components", "number_of_isolates", "number_of_selfloops", "octahedral_graph", @@ -91,6 +95,10 @@ "betweenness_centrality": "`weight` parameter is not yet supported.", "edge_betweenness_centrality": "`weight` parameter is not yet supported.", "from_pandas_edgelist": "cudf.DataFrame inputs also supported.", + "k_truss": ( + "Currently raises `NotImplementedError` for graphs with more than one connected\n" + "component when k >= 3. We expect to fix this soon." + ), "louvain_communities": "`seed` parameter is currently ignored.", # END: extra_docstrings }, diff --git a/python/nx-cugraph/nx_cugraph/algorithms/__init__.py b/python/nx-cugraph/nx_cugraph/algorithms/__init__.py index 69feb8f6437..87b1967fa93 100644 --- a/python/nx-cugraph/nx_cugraph/algorithms/__init__.py +++ b/python/nx-cugraph/nx_cugraph/algorithms/__init__.py @@ -10,8 +10,9 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -from . import bipartite, centrality, community +from . import bipartite, centrality, community, components from .bipartite import complete_bipartite_graph from .centrality import * +from .components import * from .core import * from .isolate import * diff --git a/python/nx-cugraph/nx_cugraph/algorithms/community/louvain.py b/python/nx-cugraph/nx_cugraph/algorithms/community/louvain.py index 62261d109a2..45a3429d2ee 100644 --- a/python/nx-cugraph/nx_cugraph/algorithms/community/louvain.py +++ b/python/nx-cugraph/nx_cugraph/algorithms/community/louvain.py @@ -62,7 +62,7 @@ def louvain_communities( resolution=resolution, do_expensive_check=False, ) - groups = _groupby(clusters, vertices) + groups = _groupby(clusters, vertices, groups_are_canonical=True) rv = [set(G._nodearray_to_list(node_ids)) for node_ids in groups.values()] # TODO: PLC doesn't handle isolated vertices yet, so this is a temporary fix isolates = _isolates(G) diff --git a/python/nx-cugraph/nx_cugraph/algorithms/components/__init__.py b/python/nx-cugraph/nx_cugraph/algorithms/components/__init__.py new file mode 100644 index 00000000000..26816ef3692 --- /dev/null +++ b/python/nx-cugraph/nx_cugraph/algorithms/components/__init__.py @@ -0,0 +1,13 @@ +# Copyright (c) 2023, NVIDIA CORPORATION. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +from .connected import * diff --git a/python/nx-cugraph/nx_cugraph/algorithms/components/connected.py b/python/nx-cugraph/nx_cugraph/algorithms/components/connected.py new file mode 100644 index 00000000000..41f3457d542 --- /dev/null +++ b/python/nx-cugraph/nx_cugraph/algorithms/components/connected.py @@ -0,0 +1,130 @@ +# Copyright (c) 2023, NVIDIA CORPORATION. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import itertools + +import cupy as cp +import networkx as nx +import pylibcugraph as plc + +from nx_cugraph.convert import _to_undirected_graph +from nx_cugraph.utils import _groupby, networkx_algorithm, not_implemented_for + +from ..isolate import _isolates + +__all__ = [ + "number_connected_components", + "connected_components", + "is_connected", + "node_connected_component", +] + + +@not_implemented_for("directed") +@networkx_algorithm +def number_connected_components(G): + return sum(1 for _ in connected_components(G)) + # PREFERRED IMPLEMENTATION, BUT PLC DOES NOT HANDLE ISOLATED VERTICES WELL + # G = _to_undirected_graph(G) + # unused_node_ids, labels = plc.weakly_connected_components( + # resource_handle=plc.ResourceHandle(), + # graph=G._get_plc_graph(), + # offsets=None, + # indices=None, + # weights=None, + # labels=None, + # do_expensive_check=False, + # ) + # return cp.unique(labels).size + + +@number_connected_components._can_run +def _(G): + # NetworkX <= 3.2.1 does not check directedness for us + try: + return not G.is_directed() + except Exception: + return False + + +@not_implemented_for("directed") +@networkx_algorithm +def connected_components(G): + G = _to_undirected_graph(G) + if G.src_indices.size == 0: + # TODO: PLC doesn't handle empty graphs (or isolated nodes) gracefully! + return [{key} for key in G._nodeiter_to_iter(range(len(G)))] + node_ids, labels = plc.weakly_connected_components( + resource_handle=plc.ResourceHandle(), + graph=G._get_plc_graph(), + offsets=None, + indices=None, + weights=None, + labels=None, + do_expensive_check=False, + ) + groups = _groupby(labels, node_ids) + it = (G._nodearray_to_set(connected_ids) for connected_ids in groups.values()) + # TODO: PLC doesn't handle isolated vertices yet, so this is a temporary fix + isolates = _isolates(G) + if isolates.size > 0: + isolates = isolates[isolates > node_ids.max()] + if isolates.size > 0: + it = itertools.chain( + it, ({node} for node in G._nodearray_to_list(isolates)) + ) + return it + + +@not_implemented_for("directed") +@networkx_algorithm +def is_connected(G): + G = _to_undirected_graph(G) + if len(G) == 0: + raise nx.NetworkXPointlessConcept( + "Connectivity is undefined for the null graph." + ) + for community in connected_components(G): + return len(community) == len(G) + raise RuntimeError # pragma: no cover + # PREFERRED IMPLEMENTATION, BUT PLC DOES NOT HANDLE ISOLATED VERTICES WELL + # unused_node_ids, labels = plc.weakly_connected_components( + # resource_handle=plc.ResourceHandle(), + # graph=G._get_plc_graph(), + # offsets=None, + # indices=None, + # weights=None, + # labels=None, + # do_expensive_check=False, + # ) + # return labels.size == len(G) and cp.unique(labels).size == 1 + + +@not_implemented_for("directed") +@networkx_algorithm +def node_connected_component(G, n): + # We could also do plain BFS from n + G = _to_undirected_graph(G) + node_id = n if G.key_to_id is None else G.key_to_id[n] + node_ids, labels = plc.weakly_connected_components( + resource_handle=plc.ResourceHandle(), + graph=G._get_plc_graph(), + offsets=None, + indices=None, + weights=None, + labels=None, + do_expensive_check=False, + ) + indices = cp.nonzero(node_ids == node_id)[0] + if indices.size == 0: + return {n} + return G._nodearray_to_set(node_ids[labels == labels[indices[0]]]) diff --git a/python/nx-cugraph/nx_cugraph/algorithms/core.py b/python/nx-cugraph/nx_cugraph/algorithms/core.py index 33e79793553..2219388bc58 100644 --- a/python/nx-cugraph/nx_cugraph/algorithms/core.py +++ b/python/nx-cugraph/nx_cugraph/algorithms/core.py @@ -24,6 +24,10 @@ @not_implemented_for("multigraph") @networkx_algorithm def k_truss(G, k): + """ + Currently raises `NotImplementedError` for graphs with more than one connected + component when k >= 3. We expect to fix this soon. + """ if is_nx := isinstance(G, nx.Graph): G = nxcg.from_networkx(G, preserve_all_attrs=True) if nxcg.number_of_selfloops(G) > 0: @@ -31,6 +35,7 @@ def k_truss(G, k): "Input graph has self loops which is not permitted; " "Consider using G.remove_edges_from(nx.selfloop_edges(G))." ) + # TODO: create renumbering helper function(s) if k < 3: # k-truss graph is comprised of nodes incident on k-2 triangles, so k<3 is a @@ -49,6 +54,11 @@ def k_truss(G, k): # Renumber step 1: edge values (no changes needed) edge_values = {key: val.copy() for key, val in G.edge_values.items()} edge_masks = {key: val.copy() for key, val in G.edge_masks.items()} + elif (ncc := nxcg.number_connected_components(G)) > 1: + raise NotImplementedError( + "nx_cugraph.k_truss does not yet work on graphs with more than one " + f"connected component (this graph has {ncc}). We expect to fix this soon." + ) else: edge_dtype = _get_int_dtype(G.src_indices.size - 1) edge_indices = cp.arange(G.src_indices.size, dtype=edge_dtype) diff --git a/python/nx-cugraph/nx_cugraph/classes/graph.py b/python/nx-cugraph/nx_cugraph/classes/graph.py index 2048c4c3d72..23004651fc5 100644 --- a/python/nx-cugraph/nx_cugraph/classes/graph.py +++ b/python/nx-cugraph/nx_cugraph/classes/graph.py @@ -692,6 +692,11 @@ def _nodearray_to_list(self, node_ids: cp.ndarray[IndexValue]) -> list[NodeKey]: return node_ids.tolist() return list(self._nodeiter_to_iter(node_ids.tolist())) + def _nodearray_to_set(self, node_ids: cp.ndarray[IndexValue]) -> set[NodeKey]: + if self.key_to_id is None: + return set(node_ids.tolist()) + return set(self._nodeiter_to_iter(node_ids.tolist())) + def _nodearray_to_dict( self, values: cp.ndarray[NodeValue] ) -> dict[NodeKey, NodeValue]: diff --git a/python/nx-cugraph/nx_cugraph/interface.py b/python/nx-cugraph/nx_cugraph/interface.py index fd0b1483d73..875f8621021 100644 --- a/python/nx-cugraph/nx_cugraph/interface.py +++ b/python/nx-cugraph/nx_cugraph/interface.py @@ -223,11 +223,20 @@ def key(testpath): } ) + too_slow = "Too slow to run" + skip = { + key("test_tree_isomorphism.py:test_positive"): too_slow, + key("test_tree_isomorphism.py:test_negative"): too_slow, + } + for item in items: kset = set(item.keywords) for (test_name, keywords), reason in xfail.items(): if item.name == test_name and keywords.issubset(kset): item.add_marker(pytest.mark.xfail(reason=reason)) + for (test_name, keywords), reason in skip.items(): + if item.name == test_name and keywords.issubset(kset): + item.add_marker(pytest.mark.skip(reason=reason)) @classmethod def can_run(cls, name, args, kwargs): diff --git a/python/nx-cugraph/nx_cugraph/tests/test_ktruss.py b/python/nx-cugraph/nx_cugraph/tests/test_ktruss.py new file mode 100644 index 00000000000..a3e4cee3124 --- /dev/null +++ b/python/nx-cugraph/nx_cugraph/tests/test_ktruss.py @@ -0,0 +1,30 @@ +# Copyright (c) 2023, NVIDIA CORPORATION. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import networkx as nx +import pytest + +import nx_cugraph as nxcg + + +@pytest.mark.parametrize( + "get_graph", [nx.florentine_families_graph, nx.les_miserables_graph] +) +def test_k_truss(get_graph): + Gnx = get_graph() + Gcg = nxcg.from_networkx(Gnx, preserve_all_attrs=True) + for k in range(10): + Hnx = nx.k_truss(Gnx, k) + Hcg = nxcg.k_truss(Gcg, k) + assert nx.utils.graphs_equal(Hnx, nxcg.to_networkx(Hcg)) + if Hnx.number_of_edges() == 0: + break diff --git a/python/nx-cugraph/nx_cugraph/utils/decorators.py b/python/nx-cugraph/nx_cugraph/utils/decorators.py index 0f15d236ecd..0048aee51bb 100644 --- a/python/nx-cugraph/nx_cugraph/utils/decorators.py +++ b/python/nx-cugraph/nx_cugraph/utils/decorators.py @@ -13,6 +13,7 @@ from __future__ import annotations from functools import partial, update_wrapper +from textwrap import dedent from networkx.utils.decorators import nodes_or_number, not_implemented_for @@ -65,7 +66,9 @@ def __new__( ) instance.extra_params = extra_params # The docstring on our function is added to the NetworkX docstring. - instance.extra_doc = func.__doc__ + instance.extra_doc = ( + dedent(func.__doc__.lstrip("\n").rstrip()) if func.__doc__ else None + ) # Copy __doc__ from NetworkX if instance.name in _registered_algorithms: instance.__doc__ = _registered_algorithms[instance.name].__doc__ diff --git a/python/nx-cugraph/nx_cugraph/utils/misc.py b/python/nx-cugraph/nx_cugraph/utils/misc.py index 9683df5e7f9..26f023bdcec 100644 --- a/python/nx-cugraph/nx_cugraph/utils/misc.py +++ b/python/nx-cugraph/nx_cugraph/utils/misc.py @@ -21,40 +21,56 @@ import cupy as cp import numpy as np +try: + from itertools import pairwise # Python >=3.10 +except ImportError: + + def pairwise(it): + it = iter(it) + for prev in it: + for cur in it: + yield (prev, cur) + prev = cur + + __all__ = ["index_dtype", "_groupby", "_seed_to_int", "_get_int_dtype"] # This may switch to np.uint32 at some point index_dtype = np.int32 -def _groupby(groups: cp.ndarray, values: cp.ndarray) -> dict[int, cp.ndarray]: +def _groupby( + groups: cp.ndarray, values: cp.ndarray, groups_are_canonical: bool = False +) -> dict[int, cp.ndarray]: """Perform a groupby operation given an array of group IDs and array of values. Parameters ---------- groups : cp.ndarray Array that holds the group IDs. - Group IDs are assumed to be consecutive integers from 0. values : cp.ndarray Array of values to be grouped according to groups. Must be the same size as groups array. + groups_are_canonical : bool, default False + Whether the group IDs are consecutive integers beginning with 0. Returns ------- dict with group IDs as keys and cp.ndarray as values. """ - # It would actually be easy to support groups that aren't consecutive integers, - # but let's wait until we need it to implement it. - sorted_groups = cp.argsort(groups) - sorted_values = values[sorted_groups] - rv = {} - start = 0 - for i, end in enumerate( - [*(cp.nonzero(cp.diff(groups[sorted_groups]))[0] + 1).tolist(), groups.size] - ): - rv[i] = sorted_values[start:end] - start = end - return rv + if groups.size == 0: + return {} + sort_indices = cp.argsort(groups) + sorted_groups = groups[sort_indices] + sorted_values = values[sort_indices] + prepend = 1 if groups_are_canonical else sorted_groups[0] + 1 + left_bounds = cp.nonzero(cp.diff(sorted_groups, prepend=prepend))[0] + boundaries = pairwise(itertools.chain(left_bounds.tolist(), [groups.size])) + if groups_are_canonical: + it = enumerate(boundaries) + else: + it = zip(sorted_groups[left_bounds].tolist(), boundaries) + return {group: sorted_values[start:end] for group, (start, end) in it} def _seed_to_int(seed: int | Random | None) -> int: diff --git a/python/nx-cugraph/pyproject.toml b/python/nx-cugraph/pyproject.toml index 3d3029da232..7e51efd4fe4 100644 --- a/python/nx-cugraph/pyproject.toml +++ b/python/nx-cugraph/pyproject.toml @@ -218,6 +218,7 @@ ignore = [ # Allow assert, print, RNG, and no docstring "nx_cugraph/**/tests/*py" = ["S101", "S311", "T201", "D103", "D100"] "_nx_cugraph/__init__.py" = ["E501"] +"nx_cugraph/algorithms/**/*py" = ["D205", "D401"] # Allow flexible docstrings for algorithms [tool.ruff.flake8-annotations] mypy-init-return = true From 0a905630c990235783f77c461691a983f97afc9f Mon Sep 17 00:00:00 2001 From: Seunghwa Kang <45857425+seunghwak@users.noreply.github.com> Date: Wed, 1 Nov 2023 11:21:45 -0700 Subject: [PATCH 068/111] Cut peak memory footprint in graph creation (#3966) This limits memory footprint (especially in single-GPU or multi-GPU with a small number of GPUs) to the size of edge list * 1.5 + alpha (alpha to store O(V) data, V: # vertices). Authors: - Seunghwa Kang (https://github.com/seunghwak) Approvers: - Naim (https://github.com/naimnv) - Chuck Hastings (https://github.com/ChuckHastings) URL: https://github.com/rapidsai/cugraph/pull/3966 --- cpp/src/c_api/capi_helper.cu | 2 +- .../create_graph_from_edgelist_impl.cuh | 425 ++++++++---------- cpp/src/structure/detail/structure_utils.cuh | 281 ++++++------ cpp/src/structure/induced_subgraph_impl.cuh | 2 + cpp/src/structure/renumber_edgelist_impl.cuh | 100 +++-- cpp/tests/community/mg_egonet_test.cu | 1 + .../structure/mg_induced_subgraph_test.cu | 1 + cpp/tests/utilities/test_utilities_impl.cuh | 61 ++- 8 files changed, 415 insertions(+), 458 deletions(-) diff --git a/cpp/src/c_api/capi_helper.cu b/cpp/src/c_api/capi_helper.cu index af0163b0512..0ee49f87265 100644 --- a/cpp/src/c_api/capi_helper.cu +++ b/cpp/src/c_api/capi_helper.cu @@ -44,7 +44,7 @@ shuffle_vertex_ids_and_offsets(raft::handle_t const& handle, thrust::make_zip_iterator(ids.end(), vertices.end())); auto return_offsets = cugraph::detail::compute_sparse_offsets( - ids.begin(), ids.end(), size_t{0}, size_t{offsets.size() - 1}, handle.get_stream()); + ids.begin(), ids.end(), size_t{0}, size_t{offsets.size() - 1}, true, handle.get_stream()); return std::make_tuple(std::move(vertices), std::move(return_offsets)); } diff --git a/cpp/src/structure/create_graph_from_edgelist_impl.cuh b/cpp/src/structure/create_graph_from_edgelist_impl.cuh index 0d4b12a3e38..8dd587e1661 100644 --- a/cpp/src/structure/create_graph_from_edgelist_impl.cuh +++ b/cpp/src/structure/create_graph_from_edgelist_impl.cuh @@ -510,7 +510,18 @@ create_graph_from_edgelist_impl( auto use_dcs = num_segments_per_vertex_partition > (detail::num_sparse_segments_per_vertex_partition + 2); - // 4. compress edge list (COO) to CSR (or CSC) or CSR + DCSR (CSC + DCSC) hybrid + // 4. sort and compress edge list (COO) to CSR (or CSC) or CSR + DCSR (CSC + DCSC) hybrid + + auto total_global_mem = handle.get_device_properties().totalGlobalMem; + size_t element_size = sizeof(vertex_t) * 2; + if (edgelist_weights) { element_size += sizeof(weight_t); } + if (edgelist_edge_ids) { element_size += sizeof(edge_id_t); } + if (edgelist_edge_types) { element_size += sizeof(edge_type_t); } + auto constexpr mem_frugal_ratio = + 0.25; // if the expected temporary buffer size exceeds the mem_frugal_ratio of the + // total_global_mem, switch to the memory frugal approach + auto mem_frugal_threshold = + static_cast(static_cast(total_global_mem / element_size) * mem_frugal_ratio); std::vector> edge_partition_offsets; std::vector> edge_partition_indices; @@ -559,154 +570,139 @@ create_graph_from_edgelist_impl( if (edgelist_weights) { if (edgelist_edge_ids) { if (edgelist_edge_types) { - auto edge_value_first = - thrust::make_zip_iterator((*edge_partition_edgelist_weights)[i].begin(), - (*edge_partition_edgelist_edge_ids)[i].begin(), - (*edge_partition_edgelist_edge_types)[i].begin()); std::forward_as_tuple( offsets, indices, std::tie(weights, edge_ids, edge_types), dcs_nzd_vertices) = - detail::compress_edgelist( - edge_partition_edgelist_srcs[i].begin(), - edge_partition_edgelist_srcs[i].end(), - edge_partition_edgelist_dsts[i].begin(), - edge_value_first, + detail::sort_and_compress_edgelist, + store_transposed>( + std::move(edge_partition_edgelist_srcs[i]), + std::move(edge_partition_edgelist_dsts[i]), + std::make_tuple(std::move((*edge_partition_edgelist_weights)[i]), + std::move((*edge_partition_edgelist_edge_ids)[i]), + std::move((*edge_partition_edgelist_edge_types)[i])), major_range_first, major_hypersparse_first, major_range_last, minor_range_first, minor_range_last, + mem_frugal_threshold, handle.get_stream()); } else { - auto edge_value_first = - thrust::make_zip_iterator((*edge_partition_edgelist_weights)[i].begin(), - (*edge_partition_edgelist_edge_ids)[i].begin()); std::forward_as_tuple(offsets, indices, std::tie(weights, edge_ids), dcs_nzd_vertices) = - detail::compress_edgelist( - edge_partition_edgelist_srcs[i].begin(), - edge_partition_edgelist_srcs[i].end(), - edge_partition_edgelist_dsts[i].begin(), - edge_value_first, + detail::sort_and_compress_edgelist, + store_transposed>( + std::move(edge_partition_edgelist_srcs[i]), + std::move(edge_partition_edgelist_dsts[i]), + std::make_tuple(std::move((*edge_partition_edgelist_weights)[i]), + std::move((*edge_partition_edgelist_edge_ids)[i])), major_range_first, major_hypersparse_first, major_range_last, minor_range_first, minor_range_last, + mem_frugal_threshold, handle.get_stream()); } } else { if (edgelist_edge_types) { - auto edge_value_first = - thrust::make_zip_iterator((*edge_partition_edgelist_weights)[i].begin(), - (*edge_partition_edgelist_edge_types)[i].begin()); std::forward_as_tuple(offsets, indices, std::tie(weights, edge_types), dcs_nzd_vertices) = - detail::compress_edgelist( - edge_partition_edgelist_srcs[i].begin(), - edge_partition_edgelist_srcs[i].end(), - edge_partition_edgelist_dsts[i].begin(), - edge_value_first, + detail::sort_and_compress_edgelist, + store_transposed>( + std::move(edge_partition_edgelist_srcs[i]), + std::move(edge_partition_edgelist_dsts[i]), + std::make_tuple(std::move((*edge_partition_edgelist_weights)[i]), + std::move((*edge_partition_edgelist_edge_types)[i])), major_range_first, major_hypersparse_first, major_range_last, minor_range_first, minor_range_last, + mem_frugal_threshold, handle.get_stream()); } else { - auto edge_value_first = (*edge_partition_edgelist_weights)[i].begin(); std::forward_as_tuple(offsets, indices, weights, dcs_nzd_vertices) = - detail::compress_edgelist( - edge_partition_edgelist_srcs[i].begin(), - edge_partition_edgelist_srcs[i].end(), - edge_partition_edgelist_dsts[i].begin(), - edge_value_first, + detail::sort_and_compress_edgelist( + std::move(edge_partition_edgelist_srcs[i]), + std::move(edge_partition_edgelist_dsts[i]), + std::move((*edge_partition_edgelist_weights)[i]), major_range_first, major_hypersparse_first, major_range_last, minor_range_first, minor_range_last, + mem_frugal_threshold, handle.get_stream()); } } } else { if (edgelist_edge_ids) { if (edgelist_edge_types) { - auto edge_value_first = - thrust::make_zip_iterator((*edge_partition_edgelist_edge_ids)[i].begin(), - (*edge_partition_edgelist_edge_types)[i].begin()); std::forward_as_tuple( offsets, indices, std::tie(edge_ids, edge_types), dcs_nzd_vertices) = - detail::compress_edgelist( - edge_partition_edgelist_srcs[i].begin(), - edge_partition_edgelist_srcs[i].end(), - edge_partition_edgelist_dsts[i].begin(), - edge_value_first, + detail::sort_and_compress_edgelist, + store_transposed>( + std::move(edge_partition_edgelist_srcs[i]), + std::move(edge_partition_edgelist_dsts[i]), + std::make_tuple(std::move((*edge_partition_edgelist_edge_ids)[i]), + std::move((*edge_partition_edgelist_edge_types)[i])), major_range_first, major_hypersparse_first, major_range_last, minor_range_first, minor_range_last, + mem_frugal_threshold, handle.get_stream()); } else { - auto edge_value_first = (*edge_partition_edgelist_edge_ids)[i].begin(); std::forward_as_tuple(offsets, indices, edge_ids, dcs_nzd_vertices) = - detail::compress_edgelist( - edge_partition_edgelist_srcs[i].begin(), - edge_partition_edgelist_srcs[i].end(), - edge_partition_edgelist_dsts[i].begin(), - edge_value_first, + detail::sort_and_compress_edgelist( + std::move(edge_partition_edgelist_srcs[i]), + std::move(edge_partition_edgelist_dsts[i]), + std::move((*edge_partition_edgelist_edge_ids)[i]), major_range_first, major_hypersparse_first, major_range_last, minor_range_first, minor_range_last, + mem_frugal_threshold, handle.get_stream()); } } else { if (edgelist_edge_types) { - auto edge_value_first = (*edge_partition_edgelist_edge_types)[i].begin(); std::forward_as_tuple(offsets, indices, edge_types, dcs_nzd_vertices) = - detail::compress_edgelist( - edge_partition_edgelist_srcs[i].begin(), - edge_partition_edgelist_srcs[i].end(), - edge_partition_edgelist_dsts[i].begin(), - edge_value_first, + detail::sort_and_compress_edgelist( + std::move(edge_partition_edgelist_srcs[i]), + std::move(edge_partition_edgelist_dsts[i]), + std::move((*edge_partition_edgelist_edge_types)[i]), major_range_first, major_hypersparse_first, major_range_last, minor_range_first, minor_range_last, + mem_frugal_threshold, handle.get_stream()); } else { std::forward_as_tuple(offsets, indices, dcs_nzd_vertices) = - detail::compress_edgelist( - edge_partition_edgelist_srcs[i].begin(), - edge_partition_edgelist_srcs[i].end(), - edge_partition_edgelist_dsts[i].begin(), + detail::sort_and_compress_edgelist( + std::move(edge_partition_edgelist_srcs[i]), + std::move(edge_partition_edgelist_dsts[i]), major_range_first, major_hypersparse_first, major_range_last, minor_range_first, minor_range_last, + mem_frugal_threshold, handle.get_stream()); } } } - edge_partition_edgelist_srcs[i].resize(0, handle.get_stream()); - edge_partition_edgelist_srcs[i].shrink_to_fit(handle.get_stream()); - edge_partition_edgelist_dsts[i].resize(0, handle.get_stream()); - edge_partition_edgelist_dsts[i].shrink_to_fit(handle.get_stream()); - if (edge_partition_edgelist_weights) { - (*edge_partition_edgelist_weights)[i].resize(0, handle.get_stream()); - (*edge_partition_edgelist_weights)[i].shrink_to_fit(handle.get_stream()); - } - if (edge_partition_edgelist_edge_ids) { - (*edge_partition_edgelist_edge_ids)[i].resize(0, handle.get_stream()); - (*edge_partition_edgelist_edge_ids)[i].shrink_to_fit(handle.get_stream()); - } - if (edge_partition_edgelist_edge_types) { - (*edge_partition_edgelist_edge_types)[i].resize(0, handle.get_stream()); - (*edge_partition_edgelist_edge_types)[i].shrink_to_fit(handle.get_stream()); - } edge_partition_offsets.push_back(std::move(offsets)); edge_partition_indices.push_back(std::move(indices)); if (edge_partition_weights) { (*edge_partition_weights).push_back(std::move(*weights)); } @@ -954,6 +950,17 @@ create_graph_from_edgelist_impl( // convert edge list (COO) to compressed sparse format (CSR or CSC) + auto total_global_mem = handle.get_device_properties().totalGlobalMem; + size_t element_size = sizeof(vertex_t) * 2; + if (edgelist_weights) { element_size += sizeof(weight_t); } + if (edgelist_edge_ids) { element_size += sizeof(edge_id_t); } + if (edgelist_edge_types) { element_size += sizeof(edge_type_t); } + auto constexpr mem_frugal_ratio = + 0.25; // if the expected temporary buffer size exceeds the mem_frugal_ratio of the + // total_global_mem, switch to the memory frugal approach + auto mem_frugal_threshold = + static_cast(static_cast(total_global_mem / element_size) * mem_frugal_ratio); + rmm::device_uvector offsets(size_t{0}, handle.get_stream()); rmm::device_uvector indices(size_t{0}, handle.get_stream()); std::optional> weights{std::nullopt}; @@ -963,202 +970,130 @@ create_graph_from_edgelist_impl( if (edgelist_weights) { if (edgelist_edge_ids) { if (edgelist_edge_types) { - auto edge_value_first = thrust::make_zip_iterator((*edgelist_weights).begin(), - (*edgelist_edge_ids).begin(), - (*edgelist_edge_types).begin()); std::forward_as_tuple(offsets, indices, std::tie(weights, ids, types), std::ignore) = - detail::compress_edgelist(edgelist_srcs.begin(), - edgelist_srcs.end(), - edgelist_dsts.begin(), - edge_value_first, - vertex_t{0}, - std::optional{std::nullopt}, - num_vertices, - vertex_t{0}, - num_vertices, - handle.get_stream()); + detail::sort_and_compress_edgelist, + store_transposed>( + std::move(edgelist_srcs), + std::move(edgelist_dsts), + std::make_tuple(std::move(*edgelist_weights), + std::move(*edgelist_edge_ids), + std::move(*edgelist_edge_types)), + vertex_t{0}, + std::optional{std::nullopt}, + num_vertices, + vertex_t{0}, + num_vertices, + mem_frugal_threshold, + handle.get_stream()); } else { - auto edge_value_first = - thrust::make_zip_iterator((*edgelist_weights).begin(), (*edgelist_edge_ids).begin()); std::forward_as_tuple(offsets, indices, std::tie(weights, ids), std::ignore) = - detail::compress_edgelist(edgelist_srcs.begin(), - edgelist_srcs.end(), - edgelist_dsts.begin(), - edge_value_first, - vertex_t{0}, - std::optional{std::nullopt}, - num_vertices, - vertex_t{0}, - num_vertices, - handle.get_stream()); + detail::sort_and_compress_edgelist, + store_transposed>( + std::move(edgelist_srcs), + std::move(edgelist_dsts), + std::make_tuple(std::move(*edgelist_weights), std::move(*edgelist_edge_ids)), + vertex_t{0}, + std::optional{std::nullopt}, + num_vertices, + vertex_t{0}, + num_vertices, + mem_frugal_threshold, + handle.get_stream()); } } else { if (edgelist_edge_types) { - auto edge_value_first = - thrust::make_zip_iterator((*edgelist_weights).begin(), (*edgelist_edge_types).begin()); std::forward_as_tuple(offsets, indices, std::tie(weights, types), std::ignore) = - detail::compress_edgelist(edgelist_srcs.begin(), - edgelist_srcs.end(), - edgelist_dsts.begin(), - edge_value_first, - vertex_t{0}, - std::optional{std::nullopt}, - num_vertices, - vertex_t{0}, - num_vertices, - handle.get_stream()); + detail::sort_and_compress_edgelist, + store_transposed>( + std::move(edgelist_srcs), + std::move(edgelist_dsts), + std::make_tuple(std::move(*edgelist_weights), std::move(*edgelist_edge_types)), + vertex_t{0}, + std::optional{std::nullopt}, + num_vertices, + vertex_t{0}, + num_vertices, + mem_frugal_threshold, + handle.get_stream()); } else { - auto edge_value_first = (*edgelist_weights).begin(); std::forward_as_tuple(offsets, indices, weights, std::ignore) = - detail::compress_edgelist(edgelist_srcs.begin(), - edgelist_srcs.end(), - edgelist_dsts.begin(), - edge_value_first, - vertex_t{0}, - std::optional{std::nullopt}, - num_vertices, - vertex_t{0}, - num_vertices, - handle.get_stream()); + detail::sort_and_compress_edgelist( + std::move(edgelist_srcs), + std::move(edgelist_dsts), + std::move(*edgelist_weights), + vertex_t{0}, + std::optional{std::nullopt}, + num_vertices, + vertex_t{0}, + num_vertices, + mem_frugal_threshold, + handle.get_stream()); } } } else { if (edgelist_edge_ids) { if (edgelist_edge_types) { - auto edge_value_first = - thrust::make_zip_iterator((*edgelist_edge_ids).begin(), (*edgelist_edge_types).begin()); std::forward_as_tuple(offsets, indices, std::tie(ids, types), std::ignore) = - detail::compress_edgelist(edgelist_srcs.begin(), - edgelist_srcs.end(), - edgelist_dsts.begin(), - edge_value_first, - vertex_t{0}, - std::optional{std::nullopt}, - num_vertices, - vertex_t{0}, - num_vertices, - handle.get_stream()); + detail::sort_and_compress_edgelist, + store_transposed>( + std::move(edgelist_srcs), + std::move(edgelist_dsts), + std::make_tuple(std::move(*edgelist_edge_ids), std::move(*edgelist_edge_types)), + vertex_t{0}, + std::optional{std::nullopt}, + num_vertices, + vertex_t{0}, + num_vertices, + mem_frugal_threshold, + handle.get_stream()); } else { - auto edge_value_first = (*edgelist_edge_ids).begin(); std::forward_as_tuple(offsets, indices, ids, std::ignore) = - detail::compress_edgelist(edgelist_srcs.begin(), - edgelist_srcs.end(), - edgelist_dsts.begin(), - edge_value_first, - vertex_t{0}, - std::optional{std::nullopt}, - num_vertices, - vertex_t{0}, - num_vertices, - handle.get_stream()); + detail::sort_and_compress_edgelist( + std::move(edgelist_srcs), + std::move(edgelist_dsts), + std::move(*edgelist_edge_ids), + vertex_t{0}, + std::optional{std::nullopt}, + num_vertices, + vertex_t{0}, + num_vertices, + mem_frugal_threshold, + handle.get_stream()); } } else { if (edgelist_edge_types) { - auto edge_value_first = (*edgelist_edge_types).begin(); std::forward_as_tuple(offsets, indices, types, std::ignore) = - detail::compress_edgelist(edgelist_srcs.begin(), - edgelist_srcs.end(), - edgelist_dsts.begin(), - edge_value_first, - vertex_t{0}, - std::optional{std::nullopt}, - num_vertices, - vertex_t{0}, - num_vertices, - handle.get_stream()); + detail::sort_and_compress_edgelist( + std::move(edgelist_srcs), + std::move(edgelist_dsts), + std::move(*edgelist_edge_types), + vertex_t{0}, + std::optional{std::nullopt}, + num_vertices, + vertex_t{0}, + num_vertices, + mem_frugal_threshold, + handle.get_stream()); } else { std::forward_as_tuple(offsets, indices, std::ignore) = - detail::compress_edgelist(edgelist_srcs.begin(), - edgelist_srcs.end(), - edgelist_dsts.begin(), - vertex_t{0}, - std::optional{std::nullopt}, - num_vertices, - vertex_t{0}, - num_vertices, - handle.get_stream()); - } - } - } - - edgelist_srcs.resize(0, handle.get_stream()); - edgelist_srcs.shrink_to_fit(handle.get_stream()); - edgelist_dsts.resize(0, handle.get_stream()); - edgelist_dsts.shrink_to_fit(handle.get_stream()); - if (edgelist_weights) { - (*edgelist_weights).resize(0, handle.get_stream()); - (*edgelist_weights).shrink_to_fit(handle.get_stream()); - } - if (edgelist_edge_ids) { - (*edgelist_edge_ids).resize(0, handle.get_stream()); - (*edgelist_edge_ids).shrink_to_fit(handle.get_stream()); - } - if (edgelist_edge_types) { - (*edgelist_edge_types).resize(0, handle.get_stream()); - (*edgelist_edge_types).shrink_to_fit(handle.get_stream()); - } - - // segmented sort neighbors - - if (weights) { - if (ids) { - if (types) { - detail::sort_adjacency_list( - handle, - raft::device_span(offsets.data(), offsets.size()), - indices.begin(), - indices.end(), - thrust::make_zip_iterator((*weights).begin(), (*ids).begin(), (*types).begin())); - } else { - detail::sort_adjacency_list(handle, - raft::device_span(offsets.data(), offsets.size()), - indices.begin(), - indices.end(), - thrust::make_zip_iterator((*weights).begin(), (*ids).begin())); - } - } else { - if (types) { - detail::sort_adjacency_list( - handle, - raft::device_span(offsets.data(), offsets.size()), - indices.begin(), - indices.end(), - thrust::make_zip_iterator((*weights).begin(), (*types).begin())); - } else { - detail::sort_adjacency_list(handle, - raft::device_span(offsets.data(), offsets.size()), - indices.begin(), - indices.end(), - (*weights).begin()); - } - } - } else { - if (ids) { - if (types) { - detail::sort_adjacency_list(handle, - raft::device_span(offsets.data(), offsets.size()), - indices.begin(), - indices.end(), - thrust::make_zip_iterator((*ids).begin(), (*types).begin())); - } else { - detail::sort_adjacency_list(handle, - raft::device_span(offsets.data(), offsets.size()), - indices.begin(), - indices.end(), - (*ids).begin()); - } - } else { - if (types) { - detail::sort_adjacency_list(handle, - raft::device_span(offsets.data(), offsets.size()), - indices.begin(), - indices.end(), - (*types).begin()); - } else { - detail::sort_adjacency_list(handle, - raft::device_span(offsets.data(), offsets.size()), - indices.begin(), - indices.end()); + detail::sort_and_compress_edgelist( + std::move(edgelist_srcs), + std::move(edgelist_dsts), + vertex_t{0}, + std::optional{std::nullopt}, + num_vertices, + vertex_t{0}, + num_vertices, + mem_frugal_threshold, + handle.get_stream()); } } } diff --git a/cpp/src/structure/detail/structure_utils.cuh b/cpp/src/structure/detail/structure_utils.cuh index f57b549e1ef..01fbccaa53e 100644 --- a/cpp/src/structure/detail/structure_utils.cuh +++ b/cpp/src/structure/detail/structure_utils.cuh @@ -47,57 +47,38 @@ namespace cugraph { namespace detail { -template -struct update_edge_t { - raft::device_span offsets{}; - raft::device_span indices{}; - EdgeValueIterator edge_value_first{}; - vertex_t major_range_first{}; - - __device__ void operator()(typename thrust::iterator_traits::value_type e) const - { - auto s = thrust::get<0>(e); - auto d = thrust::get<1>(e); - auto major = store_transposed ? d : s; - auto minor = store_transposed ? s : d; - auto start = offsets[major - major_range_first]; - auto degree = offsets[(major - major_range_first) + 1] - start; - auto idx = - atomicAdd(&indices[start + degree - 1], vertex_t{1}); // use the last element as a counter - // FIXME: we can actually store minor - minor_range_first instead of minor to save memory if - // minor can be larger than 32 bit but minor - minor_range_first fits within 32 bit - indices[start + idx] = minor; // overwrite the counter only if idx == degree - 1 (no race) - if constexpr (!std::is_same_v) { - auto value = thrust::get<2>(e); - *(edge_value_first + (start + idx)) = value; - } - } -}; - template rmm::device_uvector compute_sparse_offsets( VertexIterator edgelist_major_first, VertexIterator edgelist_major_last, typename thrust::iterator_traits::value_type major_range_first, typename thrust::iterator_traits::value_type major_range_last, + bool edgelist_major_sorted, rmm::cuda_stream_view stream_view) { rmm::device_uvector offsets((major_range_last - major_range_first) + 1, stream_view); - thrust::fill(rmm::exec_policy(stream_view), offsets.begin(), offsets.end(), edge_t{0}); - - auto offset_view = raft::device_span(offsets.data(), offsets.size()); - thrust::for_each(rmm::exec_policy(stream_view), - edgelist_major_first, - edgelist_major_last, - [offset_view, major_range_first] __device__(auto v) { - atomicAdd(&offset_view[v - major_range_first], edge_t{1}); - }); - thrust::exclusive_scan( - rmm::exec_policy(stream_view), offsets.begin(), offsets.end(), offsets.begin()); + if (edgelist_major_sorted) { + offsets.set_element_to_zero_async(0, stream_view); + thrust::upper_bound(rmm::exec_policy(stream_view), + edgelist_major_first, + edgelist_major_last, + thrust::make_counting_iterator(major_range_first), + thrust::make_counting_iterator(major_range_last), + offsets.begin() + 1); + } else { + thrust::fill(rmm::exec_policy(stream_view), offsets.begin(), offsets.end(), edge_t{0}); + + auto offset_view = raft::device_span(offsets.data(), offsets.size()); + thrust::for_each(rmm::exec_policy(stream_view), + edgelist_major_first, + edgelist_major_last, + [offset_view, major_range_first] __device__(auto v) { + atomicAdd(&offset_view[v - major_range_first], edge_t{1}); + }); + + thrust::exclusive_scan( + rmm::exec_policy(stream_view), offsets.begin(), offsets.end(), offsets.begin()); + } return offsets; } @@ -156,61 +137,77 @@ std::tuple, rmm::device_uvector> compress_ } // compress edge list (COO) to CSR (or CSC) or CSR + DCSR (CSC + DCSC) hybrid -template -std::tuple< - rmm::device_uvector, - rmm::device_uvector::value_type>, - decltype(allocate_dataframe_buffer::value_type>(size_t{0}, rmm::cuda_stream_view{})), - std::optional::value_type>>> -compress_edgelist( - VertexIterator edgelist_src_first, - VertexIterator edgelist_src_last, - VertexIterator edgelist_dst_first, - EdgeValueIterator edge_value_first, - typename thrust::iterator_traits::value_type major_range_first, - std::optional::value_type> - major_hypersparse_first, - typename thrust::iterator_traits::value_type major_range_last, - typename thrust::iterator_traits::value_type /* minor_range_first */, - typename thrust::iterator_traits::value_type /* minor_range_last */, +template +std::tuple, + rmm::device_uvector, + decltype(allocate_dataframe_buffer(size_t{0}, rmm::cuda_stream_view{})), + std::optional>> +sort_and_compress_edgelist( + rmm::device_uvector&& edgelist_srcs, + rmm::device_uvector&& edgelist_dsts, + decltype(allocate_dataframe_buffer(0, rmm::cuda_stream_view{}))&& edgelist_values, + vertex_t major_range_first, + std::optional major_hypersparse_first, + vertex_t major_range_last, + vertex_t /* minor_range_first */, + vertex_t /* minor_range_last */, + size_t mem_frugal_threshold, rmm::cuda_stream_view stream_view) { - using vertex_t = std::remove_cv_t::value_type>; - using edge_value_t = - std::remove_cv_t::value_type>; - - auto number_of_edges = - static_cast(thrust::distance(edgelist_src_first, edgelist_src_last)); - - auto offsets = compute_sparse_offsets( - store_transposed ? edgelist_dst_first : edgelist_src_first, - store_transposed ? edgelist_dst_first + number_of_edges : edgelist_src_last, - major_range_first, - major_range_last, - stream_view); - - rmm::device_uvector indices(number_of_edges, stream_view); - thrust::fill(rmm::exec_policy(stream_view), indices.begin(), indices.end(), vertex_t{0}); - auto values = allocate_dataframe_buffer(number_of_edges, stream_view); - - auto offset_view = raft::device_span(offsets.data(), offsets.size()); - auto index_view = raft::device_span(indices.data(), indices.size()); - auto edge_first = thrust::make_zip_iterator( - thrust::make_tuple(edgelist_src_first, edgelist_dst_first, edge_value_first)); - thrust::for_each( - rmm::exec_policy(stream_view), - edge_first, - edge_first + number_of_edges, - update_edge_t{ - offset_view, index_view, get_dataframe_buffer_begin(values), major_range_first}); + auto edgelist_majors = std::move(store_transposed ? edgelist_dsts : edgelist_srcs); + auto edgelist_minors = std::move(store_transposed ? edgelist_srcs : edgelist_dsts); + + rmm::device_uvector offsets(0, stream_view); + rmm::device_uvector indices(0, stream_view); + auto values = allocate_dataframe_buffer(0, stream_view); + auto pair_first = thrust::make_zip_iterator(edgelist_majors.begin(), edgelist_minors.begin()); + if (edgelist_minors.size() > mem_frugal_threshold) { + offsets = compute_sparse_offsets(edgelist_majors.begin(), + edgelist_majors.end(), + major_range_first, + major_range_last, + false, + stream_view); + + auto pivot = major_range_first + static_cast(thrust::distance( + offsets.begin(), + thrust::lower_bound(rmm::exec_policy(stream_view), + offsets.begin(), + offsets.end(), + edgelist_minors.size() / 2))); + auto second_first = + detail::mem_frugal_partition(pair_first, + pair_first + edgelist_minors.size(), + get_dataframe_buffer_begin(edgelist_values), + thrust_tuple_get, 0>{}, + pivot, + stream_view); + thrust::sort_by_key(rmm::exec_policy(stream_view), + pair_first, + std::get<0>(second_first), + get_dataframe_buffer_begin(edgelist_values)); + thrust::sort_by_key(rmm::exec_policy(stream_view), + std::get<0>(second_first), + pair_first + edgelist_minors.size(), + std::get<1>(second_first)); + } else { + thrust::sort_by_key(rmm::exec_policy(stream_view), + pair_first, + pair_first + edgelist_minors.size(), + get_dataframe_buffer_begin(edgelist_values)); + + offsets = compute_sparse_offsets(edgelist_majors.begin(), + edgelist_majors.end(), + major_range_first, + major_range_last, + true, + stream_view); + } + indices = std::move(edgelist_minors); + values = std::move(edgelist_values); + + edgelist_majors.resize(0, stream_view); + edgelist_majors.shrink_to_fit(stream_view); std::optional> dcs_nzd_vertices{std::nullopt}; if (major_hypersparse_first) { @@ -226,47 +223,61 @@ compress_edgelist( } // compress edge list (COO) to CSR (or CSC) or CSR + DCSR (CSC + DCSC) hybrid -template -std::tuple< - rmm::device_uvector, - rmm::device_uvector::value_type>, - std::optional::value_type>>> -compress_edgelist( - VertexIterator edgelist_src_first, - VertexIterator edgelist_src_last, - VertexIterator edgelist_dst_first, - typename thrust::iterator_traits::value_type major_range_first, - std::optional::value_type> - major_hypersparse_first, - typename thrust::iterator_traits::value_type major_range_last, - typename thrust::iterator_traits::value_type /* minor_range_first */, - typename thrust::iterator_traits::value_type /* minor_range_last */, - rmm::cuda_stream_view stream_view) +template +std::tuple, + rmm::device_uvector, + std::optional>> +sort_and_compress_edgelist(rmm::device_uvector&& edgelist_srcs, + rmm::device_uvector&& edgelist_dsts, + vertex_t major_range_first, + std::optional major_hypersparse_first, + vertex_t major_range_last, + vertex_t /* minor_range_first */, + vertex_t /* minor_range_last */, + size_t mem_frugal_threshold, + rmm::cuda_stream_view stream_view) { - using vertex_t = std::remove_cv_t::value_type>; - - auto number_of_edges = - static_cast(thrust::distance(edgelist_src_first, edgelist_src_last)); - - auto offsets = compute_sparse_offsets( - store_transposed ? edgelist_dst_first : edgelist_src_first, - store_transposed ? edgelist_dst_first + number_of_edges : edgelist_src_last, - major_range_first, - major_range_last, - stream_view); - - rmm::device_uvector indices(number_of_edges, stream_view); - thrust::fill(rmm::exec_policy(stream_view), indices.begin(), indices.end(), vertex_t{0}); - - auto offset_view = raft::device_span(offsets.data(), offsets.size()); - auto index_view = raft::device_span(indices.data(), indices.size()); - auto edge_first = - thrust::make_zip_iterator(thrust::make_tuple(edgelist_src_first, edgelist_dst_first)); - thrust::for_each(rmm::exec_policy(stream_view), - edge_first, - edge_first + number_of_edges, - update_edge_t{ - offset_view, index_view, static_cast(nullptr), major_range_first}); + auto edgelist_majors = std::move(store_transposed ? edgelist_dsts : edgelist_srcs); + auto edgelist_minors = std::move(store_transposed ? edgelist_srcs : edgelist_dsts); + + rmm::device_uvector offsets(0, stream_view); + rmm::device_uvector indices(0, stream_view); + auto edge_first = thrust::make_zip_iterator(edgelist_majors.begin(), edgelist_minors.begin()); + if (edgelist_minors.size() > mem_frugal_threshold) { + offsets = compute_sparse_offsets(edgelist_majors.begin(), + edgelist_majors.end(), + major_range_first, + major_range_last, + false, + stream_view); + + auto pivot = major_range_first + static_cast(thrust::distance( + offsets.begin(), + thrust::lower_bound(rmm::exec_policy(stream_view), + offsets.begin(), + offsets.end(), + edgelist_minors.size() / 2))); + auto second_first = + detail::mem_frugal_partition(edge_first, + edge_first + edgelist_minors.size(), + thrust_tuple_get, 0>{}, + pivot, + stream_view); + thrust::sort(rmm::exec_policy(stream_view), edge_first, second_first); + thrust::sort(rmm::exec_policy(stream_view), second_first, edge_first + edgelist_minors.size()); + } else { + thrust::sort(rmm::exec_policy(stream_view), edge_first, edge_first + edgelist_minors.size()); + offsets = compute_sparse_offsets(edgelist_majors.begin(), + edgelist_majors.end(), + major_range_first, + major_range_last, + true, + stream_view); + } + indices = std::move(edgelist_minors); + + edgelist_majors.resize(0, stream_view); + edgelist_majors.shrink_to_fit(stream_view); std::optional> dcs_nzd_vertices{std::nullopt}; if (major_hypersparse_first) { diff --git a/cpp/src/structure/induced_subgraph_impl.cuh b/cpp/src/structure/induced_subgraph_impl.cuh index 950cca5828d..18e1af32a71 100644 --- a/cpp/src/structure/induced_subgraph_impl.cuh +++ b/cpp/src/structure/induced_subgraph_impl.cuh @@ -196,6 +196,7 @@ extract_induced_subgraphs( graph_ids_v.end(), size_t{0}, size_t{subgraph_offsets.size() - 1}, + true, handle.get_stream()); dst_subgraph_offsets = @@ -290,6 +291,7 @@ extract_induced_subgraphs( subgraph_edge_graph_ids.end(), size_t{0}, size_t{subgraph_offsets.size() - 1}, + true, handle.get_stream()); #ifdef TIMING diff --git a/cpp/src/structure/renumber_edgelist_impl.cuh b/cpp/src/structure/renumber_edgelist_impl.cuh index 6bc19ff4fe1..09a4dae6c64 100644 --- a/cpp/src/structure/renumber_edgelist_impl.cuh +++ b/cpp/src/structure/renumber_edgelist_impl.cuh @@ -367,18 +367,19 @@ std::tuple, std::vector, vertex_t> compu rmm::device_uvector sorted_local_vertex_degrees(0, handle.get_stream()); std::optional> stream_pool_indices{ std::nullopt}; // FIXME: move this inside the if statement + + auto constexpr num_chunks = size_t{ + 2}; // tuning parameter, this trade-offs # binary searches (up to num_chunks times more binary + // searches can be necessary if num_unique_majors << edgelist_edge_counts[i]) and temporary + // buffer requirement (cut by num_chunks times), currently set to 2 to avoid peak memory + // usage happening in this part (especially when minor_comm_size is small) + if constexpr (multi_gpu) { auto& comm = handle.get_comms(); auto& minor_comm = handle.get_subcomm(cugraph::partition_manager::minor_comm_name()); auto const minor_comm_rank = minor_comm.get_rank(); auto const minor_comm_size = minor_comm.get_size(); - auto constexpr num_chunks = size_t{ - 2}; // tuning parameter, this trade-offs # binary searches (up to num_chunks times more - // binary searches can be necessary if num_unique_majors << edgelist_edge_counts[i]) and - // temporary buffer requirement (cut by num_chunks times), currently set to 2 to avoid - // peak memory usage happening in this part (especially when minor_comm_size is small) - assert(edgelist_majors.size() == minor_comm_size); auto edge_partition_major_range_sizes = @@ -433,29 +434,30 @@ std::tuple, std::vector, vertex_t> compu sorted_major_degrees.end(), edge_t{0}); - rmm::device_uvector tmp_majors( + rmm::device_uvector tmp_majors(0, loop_stream); + tmp_majors.reserve( (static_cast(edgelist_edge_counts[i]) + (num_chunks - 1)) / num_chunks, - handle.get_stream()); + loop_stream); size_t offset{0}; for (size_t j = 0; j < num_chunks; ++j) { size_t this_chunk_size = - std::min(tmp_majors.size(), static_cast(edgelist_edge_counts[i]) - offset); + std::min(tmp_majors.capacity(), static_cast(edgelist_edge_counts[i]) - offset); + tmp_majors.resize(this_chunk_size, loop_stream); thrust::copy(rmm::exec_policy(loop_stream), edgelist_majors[i] + offset, - edgelist_majors[i] + offset + this_chunk_size, + edgelist_majors[i] + offset + tmp_majors.size(), tmp_majors.begin()); - thrust::sort( - rmm::exec_policy(loop_stream), tmp_majors.begin(), tmp_majors.begin() + this_chunk_size); + thrust::sort(rmm::exec_policy(loop_stream), tmp_majors.begin(), tmp_majors.end()); auto num_unique_majors = thrust::count_if(rmm::exec_policy(loop_stream), thrust::make_counting_iterator(size_t{0}), - thrust::make_counting_iterator(this_chunk_size), + thrust::make_counting_iterator(tmp_majors.size()), is_first_in_run_t{tmp_majors.data()}); rmm::device_uvector tmp_keys(num_unique_majors, loop_stream); rmm::device_uvector tmp_values(num_unique_majors, loop_stream); thrust::reduce_by_key(rmm::exec_policy(loop_stream), tmp_majors.begin(), - tmp_majors.begin() + this_chunk_size, + tmp_majors.end(), thrust::make_constant_iterator(edge_t{1}), tmp_keys.begin(), tmp_values.begin()); @@ -486,44 +488,50 @@ std::tuple, std::vector, vertex_t> compu } else { assert(edgelist_majors.size() == 1); - rmm::device_uvector tmp_majors(edgelist_edge_counts[0], handle.get_stream()); - thrust::copy(handle.get_thrust_policy(), - edgelist_majors[0], - edgelist_majors[0] + edgelist_edge_counts[0], - tmp_majors.begin()); - thrust::sort(handle.get_thrust_policy(), tmp_majors.begin(), tmp_majors.end()); - auto num_unique_majors = - thrust::count_if(handle.get_thrust_policy(), - thrust::make_counting_iterator(size_t{0}), - thrust::make_counting_iterator(tmp_majors.size()), - is_first_in_run_t{tmp_majors.data()}); - rmm::device_uvector tmp_keys(num_unique_majors, handle.get_stream()); - rmm::device_uvector tmp_values(num_unique_majors, handle.get_stream()); - thrust::reduce_by_key(handle.get_thrust_policy(), - tmp_majors.begin(), - tmp_majors.end(), - thrust::make_constant_iterator(edge_t{1}), - tmp_keys.begin(), - tmp_values.begin()); - - tmp_majors.resize(0, handle.get_stream()); - tmp_majors.shrink_to_fit(handle.get_stream()); - sorted_local_vertex_degrees.resize(sorted_local_vertices.size(), handle.get_stream()); thrust::fill(handle.get_thrust_policy(), sorted_local_vertex_degrees.begin(), sorted_local_vertex_degrees.end(), edge_t{0}); - auto kv_pair_first = - thrust::make_zip_iterator(thrust::make_tuple(tmp_keys.begin(), tmp_values.begin())); - thrust::for_each(handle.get_thrust_policy(), - kv_pair_first, - kv_pair_first + tmp_keys.size(), - search_and_increment_degree_t{ - sorted_local_vertices.data(), - static_cast(sorted_local_vertices.size()), - sorted_local_vertex_degrees.data()}); + rmm::device_uvector tmp_majors(0, handle.get_stream()); + tmp_majors.reserve(static_cast(edgelist_edge_counts[0] + (num_chunks - 1)) / num_chunks, + handle.get_stream()); + size_t offset{0}; + for (size_t i = 0; i < num_chunks; ++i) { + size_t this_chunk_size = + std::min(tmp_majors.capacity(), static_cast(edgelist_edge_counts[0]) - offset); + tmp_majors.resize(this_chunk_size, handle.get_stream()); + thrust::copy(handle.get_thrust_policy(), + edgelist_majors[0] + offset, + edgelist_majors[0] + offset + tmp_majors.size(), + tmp_majors.begin()); + thrust::sort(handle.get_thrust_policy(), tmp_majors.begin(), tmp_majors.end()); + auto num_unique_majors = + thrust::count_if(handle.get_thrust_policy(), + thrust::make_counting_iterator(size_t{0}), + thrust::make_counting_iterator(tmp_majors.size()), + is_first_in_run_t{tmp_majors.data()}); + rmm::device_uvector tmp_keys(num_unique_majors, handle.get_stream()); + rmm::device_uvector tmp_values(num_unique_majors, handle.get_stream()); + thrust::reduce_by_key(handle.get_thrust_policy(), + tmp_majors.begin(), + tmp_majors.end(), + thrust::make_constant_iterator(edge_t{1}), + tmp_keys.begin(), + tmp_values.begin()); + + auto kv_pair_first = + thrust::make_zip_iterator(thrust::make_tuple(tmp_keys.begin(), tmp_values.begin())); + thrust::for_each(handle.get_thrust_policy(), + kv_pair_first, + kv_pair_first + tmp_keys.size(), + search_and_increment_degree_t{ + sorted_local_vertices.data(), + static_cast(sorted_local_vertices.size()), + sorted_local_vertex_degrees.data()}); + offset += this_chunk_size; + } } // 4. sort local vertices by degree (descending) diff --git a/cpp/tests/community/mg_egonet_test.cu b/cpp/tests/community/mg_egonet_test.cu index 42a2bba1181..6660eac3cad 100644 --- a/cpp/tests/community/mg_egonet_test.cu +++ b/cpp/tests/community/mg_egonet_test.cu @@ -215,6 +215,7 @@ class Tests_MGEgonet graph_ids_v.end(), size_t{0}, d_mg_edgelist_offsets.size() - 1, + true, handle_->get_stream()); auto [d_reference_src, d_reference_dst, d_reference_wgt, d_reference_offsets] = diff --git a/cpp/tests/structure/mg_induced_subgraph_test.cu b/cpp/tests/structure/mg_induced_subgraph_test.cu index 3f3db7c5278..b7bd22dfa63 100644 --- a/cpp/tests/structure/mg_induced_subgraph_test.cu +++ b/cpp/tests/structure/mg_induced_subgraph_test.cu @@ -210,6 +210,7 @@ class Tests_MGInducedSubgraph graph_ids_v.end(), size_t{0}, size_t{d_subgraph_offsets.size() - 1}, + true, handle_->get_stream()); auto [sg_graph, sg_edge_weights, sg_number_map] = cugraph::test::mg_graph_to_sg_graph( diff --git a/cpp/tests/utilities/test_utilities_impl.cuh b/cpp/tests/utilities/test_utilities_impl.cuh index 3025ca7908b..856c50ad35f 100644 --- a/cpp/tests/utilities/test_utilities_impl.cuh +++ b/cpp/tests/utilities/test_utilities_impl.cuh @@ -183,43 +183,42 @@ graph_to_host_csr( } } + auto total_global_mem = handle.get_device_properties().totalGlobalMem; + size_t element_size = sizeof(vertex_t) * 2; + if (d_wgt) { element_size += sizeof(weight_t); } + auto constexpr mem_frugal_ratio = + 0.25; // if the expected temporary buffer size exceeds the mem_frugal_ratio of the + // total_global_mem, switch to the memory frugal approach + auto mem_frugal_threshold = + static_cast(static_cast(total_global_mem / element_size) * mem_frugal_ratio); + rmm::device_uvector d_offsets(0, handle.get_stream()); if (d_wgt) { std::tie(d_offsets, d_dst, *d_wgt, std::ignore) = - detail::compress_edgelist(d_src.begin(), - d_src.end(), - d_dst.begin(), - d_wgt->begin(), - vertex_t{0}, - std::optional{std::nullopt}, - graph_view.number_of_vertices(), - vertex_t{0}, - graph_view.number_of_vertices(), - handle.get_stream()); - - // segmented sort neighbors - detail::sort_adjacency_list(handle, - raft::device_span(d_offsets.data(), d_offsets.size()), - d_dst.begin(), - d_dst.end(), - d_wgt->begin()); + detail::sort_and_compress_edgelist( + std::move(d_src), + std::move(d_dst), + std::move(*d_wgt), + vertex_t{0}, + std::optional{std::nullopt}, + graph_view.number_of_vertices(), + vertex_t{0}, + graph_view.number_of_vertices(), + mem_frugal_threshold, + handle.get_stream()); } else { std::tie(d_offsets, d_dst, std::ignore) = - detail::compress_edgelist(d_src.begin(), - d_src.end(), - d_dst.begin(), - vertex_t{0}, - std::optional{std::nullopt}, - graph_view.number_of_vertices(), - vertex_t{0}, - graph_view.number_of_vertices(), - handle.get_stream()); - // segmented sort neighbors - detail::sort_adjacency_list(handle, - raft::device_span(d_offsets.data(), d_offsets.size()), - d_dst.begin(), - d_dst.end()); + detail::sort_and_compress_edgelist( + std::move(d_src), + std::move(d_dst), + vertex_t{0}, + std::optional{std::nullopt}, + graph_view.number_of_vertices(), + vertex_t{0}, + graph_view.number_of_vertices(), + mem_frugal_threshold, + handle.get_stream()); } return std::make_tuple( From 5c0bc8a19fc3f9904541de6fb9bde95495298eb4 Mon Sep 17 00:00:00 2001 From: Alex Barghi <105237337+alexbarghi-nv@users.noreply.github.com> Date: Wed, 1 Nov 2023 15:41:03 -0400 Subject: [PATCH 069/111] [BUG] Check if Dask has quit to avoid throwing an exception and triggering a segfault on ddp exit (#3961) Currently, when training with ddp, if dask exits before the `CuGraphStore` is cleaned up, an exception is thrown, which causes ddp to quit with an error, which then causes a segfault, making users think that the workflow has failed when it has actually succeeded. This bug gracefully displays a warning if the dask dataset can't be deleted, which resolves this issue. Authors: - Alex Barghi (https://github.com/alexbarghi-nv) Approvers: - Vibhu Jawa (https://github.com/VibhuJawa) - Tingyu Wang (https://github.com/tingyu66) - Rick Ratzel (https://github.com/rlratzel) URL: https://github.com/rapidsai/cugraph/pull/3961 --- python/cugraph-pyg/cugraph_pyg/data/cugraph_store.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/python/cugraph-pyg/cugraph_pyg/data/cugraph_store.py b/python/cugraph-pyg/cugraph_pyg/data/cugraph_store.py index fd2172e6ade..6192cd621d5 100644 --- a/python/cugraph-pyg/cugraph_pyg/data/cugraph_store.py +++ b/python/cugraph-pyg/cugraph_pyg/data/cugraph_store.py @@ -320,7 +320,13 @@ def __init__( def __del__(self): if self.__is_graph_owner: if isinstance(self.__graph._plc_graph, dict): - distributed.get_client().unpublish_dataset("cugraph_graph") + try: + distributed.get_client().unpublish_dataset("cugraph_graph") + except TypeError: + warnings.warn( + "Could not unpublish graph dataset, most likely because" + " dask has already shut down." + ) del self.__graph def __make_offsets(self, input_dict): From f4bcdc2667a15e2e5031987550322a9d29d8f713 Mon Sep 17 00:00:00 2001 From: Divye Gala Date: Wed, 1 Nov 2023 19:19:22 -0400 Subject: [PATCH 070/111] Setup Consistent Nightly Versions for Pip and Conda (#3933) See https://github.com/rapidsai/rmm/pull/1347 Authors: - Divye Gala (https://github.com/divyegala) Approvers: - AJ Schmidt (https://github.com/ajschmidt8) - Chuck Hastings (https://github.com/ChuckHastings) - Rick Ratzel (https://github.com/rlratzel) URL: https://github.com/rapidsai/cugraph/pull/3933 --- VERSION | 1 + ci/build_cpp.sh | 4 ++- ci/build_python.sh | 15 ++++++++++ ci/build_wheel.sh | 10 ++++--- ci/release/update-version.sh | 22 +++----------- conda/recipes/cugraph-dgl/meta.yaml | 4 +-- conda/recipes/cugraph-pyg/meta.yaml | 4 +-- conda/recipes/cugraph-service/meta.yaml | 4 +-- conda/recipes/cugraph/meta.yaml | 4 +-- conda/recipes/libcugraph/meta.yaml | 4 +-- conda/recipes/nx-cugraph/meta.yaml | 4 +-- conda/recipes/pylibcugraph/meta.yaml | 4 +-- python/cugraph-dgl/cugraph_dgl/VERSION | 1 + python/cugraph-dgl/cugraph_dgl/__init__.py | 2 +- python/cugraph-dgl/cugraph_dgl/_version.py | 26 +++++++++++++++++ python/cugraph-dgl/pyproject.toml | 5 +++- python/cugraph-dgl/setup.py | 7 +++-- python/cugraph-pyg/cugraph_pyg/VERSION | 1 + python/cugraph-pyg/cugraph_pyg/__init__.py | 2 +- python/cugraph-pyg/cugraph_pyg/_version.py | 26 +++++++++++++++++ python/cugraph-pyg/pyproject.toml | 5 +++- python/cugraph-pyg/setup.py | 4 ++- .../client/cugraph_service_client/VERSION | 1 + .../client/cugraph_service_client/__init__.py | 2 +- .../client/cugraph_service_client/_version.py | 29 +++++++++++++++++++ python/cugraph-service/client/pyproject.toml | 5 +++- python/cugraph-service/client/setup.py | 7 +++-- .../server/cugraph_service_server/VERSION | 1 + .../server/cugraph_service_server/__init__.py | 2 +- .../server/cugraph_service_server/_version.py | 29 +++++++++++++++++++ python/cugraph-service/server/pyproject.toml | 6 ++-- python/cugraph-service/server/setup.py | 4 ++- python/cugraph/cugraph/VERSION | 1 + python/cugraph/cugraph/__init__.py | 2 +- python/cugraph/cugraph/_version.py | 26 +++++++++++++++++ python/cugraph/pyproject.toml | 5 +++- python/cugraph/setup.py | 2 +- python/nx-cugraph/nx_cugraph/VERSION | 1 + python/nx-cugraph/nx_cugraph/__init__.py | 2 +- python/nx-cugraph/nx_cugraph/_version.py | 26 +++++++++++++++++ python/nx-cugraph/pyproject.toml | 5 +++- python/nx-cugraph/setup.py | 7 +++-- python/pylibcugraph/pylibcugraph/VERSION | 1 + python/pylibcugraph/pylibcugraph/__init__.py | 2 +- python/pylibcugraph/pylibcugraph/_version.py | 26 +++++++++++++++++ python/pylibcugraph/pyproject.toml | 5 +++- python/pylibcugraph/setup.py | 2 +- 47 files changed, 296 insertions(+), 62 deletions(-) create mode 100644 VERSION create mode 120000 python/cugraph-dgl/cugraph_dgl/VERSION create mode 100644 python/cugraph-dgl/cugraph_dgl/_version.py create mode 120000 python/cugraph-pyg/cugraph_pyg/VERSION create mode 100644 python/cugraph-pyg/cugraph_pyg/_version.py create mode 120000 python/cugraph-service/client/cugraph_service_client/VERSION create mode 100644 python/cugraph-service/client/cugraph_service_client/_version.py create mode 120000 python/cugraph-service/server/cugraph_service_server/VERSION create mode 100644 python/cugraph-service/server/cugraph_service_server/_version.py create mode 120000 python/cugraph/cugraph/VERSION create mode 100644 python/cugraph/cugraph/_version.py create mode 120000 python/nx-cugraph/nx_cugraph/VERSION create mode 100644 python/nx-cugraph/nx_cugraph/_version.py create mode 120000 python/pylibcugraph/pylibcugraph/VERSION create mode 100644 python/pylibcugraph/pylibcugraph/_version.py diff --git a/VERSION b/VERSION new file mode 100644 index 00000000000..a193fff41e8 --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +23.12.00 diff --git a/ci/build_cpp.sh b/ci/build_cpp.sh index 3fb72cac08b..d0d13f99448 100755 --- a/ci/build_cpp.sh +++ b/ci/build_cpp.sh @@ -9,8 +9,10 @@ export CMAKE_GENERATOR=Ninja rapids-print-env +version=$(rapids-generate-version) + rapids-logger "Begin cpp build" -rapids-conda-retry mambabuild conda/recipes/libcugraph +RAPIDS_PACKAGE_VERSION=${version} rapids-conda-retry mambabuild conda/recipes/libcugraph rapids-upload-conda-to-s3 cpp diff --git a/ci/build_python.sh b/ci/build_python.sh index 62eb6c2ccec..90a40c539ff 100755 --- a/ci/build_python.sh +++ b/ci/build_python.sh @@ -11,8 +11,19 @@ rapids-print-env CPP_CHANNEL=$(rapids-download-conda-from-s3 cpp) +version=$(rapids-generate-version) +git_commit=$(git rev-parse HEAD) +export RAPIDS_PACKAGE_VERSION=${version} +echo "${version}" > VERSION + rapids-logger "Begin py build" +package_dir="python" +for package_name in pylibcugraph cugraph nx-cugraph cugraph-pyg cugraph-dgl; do + underscore_package_name=$(echo "${package_name}" | tr "-" "_") + sed -i "/^__git_commit__/ s/= .*/= \"${git_commit}\"/g" "${package_dir}/${package_name}/${underscore_package_name}/_version.py" +done + # TODO: Remove `--no-test` flags once importing on a CPU # node works correctly rapids-conda-retry mambabuild \ @@ -40,6 +51,10 @@ rapids-conda-retry mambabuild \ # built on each CUDA platform to ensure they are included in each set of # artifacts, since test scripts only install from one set of artifacts based on # the CUDA version used for the test run. +version_file_cugraph_service_client="python/cugraph-service/client/cugraph_service_client/_version.py" +sed -i "/^__git_commit__/ s/= .*/= \"${git_commit}\"/g" ${version_file_cugraph_service_client} +version_file_cugraph_service_server="python/cugraph-service/server/cugraph_service_server/_version.py" +sed -i "/^__git_commit__/ s/= .*/= \"${git_commit}\"/g" ${version_file_cugraph_service_server} rapids-conda-retry mambabuild \ --no-test \ --channel "${CPP_CHANNEL}" \ diff --git a/ci/build_wheel.sh b/ci/build_wheel.sh index 821aa25c1b9..c888c908056 100755 --- a/ci/build_wheel.sh +++ b/ci/build_wheel.sh @@ -5,13 +5,13 @@ set -euo pipefail package_name=$1 package_dir=$2 +underscore_package_name=$(echo "${package_name}" | tr "-" "_") source rapids-configure-sccache source rapids-date-string -# Use gha-tools rapids-pip-wheel-version to generate wheel version then -# update the necessary files -version_override="$(rapids-pip-wheel-version ${RAPIDS_DATE_STRING})" +version=$(rapids-generate-version) +git_commit=$(git rev-parse HEAD) RAPIDS_PY_CUDA_SUFFIX="$(rapids-wheel-ctk-name-gen ${RAPIDS_CUDA_VERSION})" @@ -21,9 +21,11 @@ PACKAGE_CUDA_SUFFIX="-${RAPIDS_PY_CUDA_SUFFIX}" # Patch project metadata files to include the CUDA version suffix and version override. pyproject_file="${package_dir}/pyproject.toml" +version_file="${package_dir}/${underscore_package_name}/_version.py" -sed -i "s/^version = .*/version = \"${version_override}\"/g" ${pyproject_file} sed -i "s/name = \"${package_name}\"/name = \"${package_name}${PACKAGE_CUDA_SUFFIX}\"/g" ${pyproject_file} +echo "${version}" > VERSION +sed -i "/^__git_commit__ / s/= .*/= \"${git_commit}\"/g" ${version_file} # For nightlies we want to ensure that we're pulling in alphas as well. The # easiest way to do so is to augment the spec with a constraint containing a diff --git a/ci/release/update-version.sh b/ci/release/update-version.sh index ad6426b66ff..d3dbed6ae46 100755 --- a/ci/release/update-version.sh +++ b/ci/release/update-version.sh @@ -54,24 +54,10 @@ sed_runner "s/set(cugraph_version .*)/set(cugraph_version ${NEXT_FULL_TAG})/g" p sed_runner 's/version = .*/version = '"'${NEXT_SHORT_TAG}'"'/g' docs/cugraph/source/conf.py sed_runner 's/release = .*/release = '"'${NEXT_FULL_TAG}'"'/g' docs/cugraph/source/conf.py -# Python __init__.py updates -sed_runner "s/__version__ = .*/__version__ = \"${NEXT_FULL_TAG}\"/g" python/cugraph/cugraph/__init__.py -sed_runner "s/__version__ = .*/__version__ = \"${NEXT_FULL_TAG}\"/g" python/cugraph-dgl/cugraph_dgl/__init__.py -sed_runner "s/__version__ = .*/__version__ = \"${NEXT_FULL_TAG}\"/g" python/cugraph-pyg/cugraph_pyg/__init__.py -sed_runner "s/__version__ = .*/__version__ = \"${NEXT_FULL_TAG}\"/g" python/cugraph-service/client/cugraph_service_client/__init__.py -sed_runner "s/__version__ = .*/__version__ = \"${NEXT_FULL_TAG}\"/g" python/cugraph-service/server/cugraph_service_server/__init__.py -sed_runner "s/__version__ = .*/__version__ = \"${NEXT_FULL_TAG}\"/g" python/pylibcugraph/pylibcugraph/__init__.py -sed_runner "s/__version__ = .*/__version__ = \"${NEXT_FULL_TAG}\"/g" python/nx-cugraph/nx_cugraph/__init__.py -sed_runner "s/__version__ = .*/__version__ = \"${NEXT_FULL_TAG}\"/g" python/nx-cugraph/_nx_cugraph/__init__.py - -# Python pyproject.toml updates -sed_runner "s/^version = .*/version = \"${NEXT_FULL_TAG}\"/g" python/cugraph/pyproject.toml -sed_runner "s/^version = .*/version = \"${NEXT_FULL_TAG}\"/g" python/cugraph-dgl/pyproject.toml -sed_runner "s/^version = .*/version = \"${NEXT_FULL_TAG}\"/g" python/cugraph-pyg/pyproject.toml -sed_runner "s/^version = .*/version = \"${NEXT_FULL_TAG}\"/g" python/cugraph-service/client/pyproject.toml -sed_runner "s/^version = .*/version = \"${NEXT_FULL_TAG}\"/g" python/cugraph-service/server/pyproject.toml -sed_runner "s/^version = .*/version = \"${NEXT_FULL_TAG}\"/g" python/pylibcugraph/pyproject.toml -sed_runner "s/^version = .*/version = \"${NEXT_FULL_TAG}\"/g" python/nx-cugraph/pyproject.toml +# Centralized version file update +# NOTE: Any script that runs in CI will need to use gha-tool `rapids-generate-version` +# and echo it to `VERSION` file to get an alpha spec of the current version +echo "${NEXT_FULL_TAG}" > VERSION # Wheel testing script sed_runner "s/branch-.*/branch-${NEXT_SHORT_TAG}/g" ci/test_wheel_cugraph.sh diff --git a/conda/recipes/cugraph-dgl/meta.yaml b/conda/recipes/cugraph-dgl/meta.yaml index 9e9fcd2faf1..bb85734098a 100644 --- a/conda/recipes/cugraph-dgl/meta.yaml +++ b/conda/recipes/cugraph-dgl/meta.yaml @@ -1,6 +1,6 @@ # Copyright (c) 2023, NVIDIA CORPORATION. -{% set version = environ.get('GIT_DESCRIBE_TAG', '0.0.0.dev').lstrip('v') %} +{% set version = environ['RAPIDS_PACKAGE_VERSION'].lstrip('v') + environ.get('VERSION_SUFFIX', '') %} {% set minor_version = version.split('.')[0] + '.' + version.split('.')[1] %} {% set py_version = environ['CONDA_PY'] %} {% set date_string = environ['RAPIDS_DATE_STRING'] %} @@ -10,7 +10,7 @@ package: version: {{ version }} source: - git_url: ../../.. + path: ../../.. build: number: {{ GIT_DESCRIBE_NUMBER }} diff --git a/conda/recipes/cugraph-pyg/meta.yaml b/conda/recipes/cugraph-pyg/meta.yaml index f4b2e9a4ee9..2714dcfa55a 100644 --- a/conda/recipes/cugraph-pyg/meta.yaml +++ b/conda/recipes/cugraph-pyg/meta.yaml @@ -1,6 +1,6 @@ # Copyright (c) 2022-2023, NVIDIA CORPORATION. -{% set version = environ.get('GIT_DESCRIBE_TAG', '0.0.0.dev').lstrip('v') %} +{% set version = environ['RAPIDS_PACKAGE_VERSION'].lstrip('v') + environ.get('VERSION_SUFFIX', '') %} {% set minor_version = version.split('.')[0] + '.' + version.split('.')[1] %} {% set py_version = environ['CONDA_PY'] %} {% set date_string = environ['RAPIDS_DATE_STRING'] %} @@ -10,7 +10,7 @@ package: version: {{ version }} source: - git_url: ../../.. + path: ../../.. build: number: {{ GIT_DESCRIBE_NUMBER }} diff --git a/conda/recipes/cugraph-service/meta.yaml b/conda/recipes/cugraph-service/meta.yaml index 3d001e83e1e..ae8074ba7d3 100644 --- a/conda/recipes/cugraph-service/meta.yaml +++ b/conda/recipes/cugraph-service/meta.yaml @@ -1,6 +1,6 @@ # Copyright (c) 2018-2023, NVIDIA CORPORATION. -{% set version = environ.get('GIT_DESCRIBE_TAG', '0.0.0.dev').lstrip('v') %} +{% set version = environ['RAPIDS_PACKAGE_VERSION'].lstrip('v') + environ.get('VERSION_SUFFIX', '') %} {% set minor_version = version.split('.')[0] + '.' + version.split('.')[1] %} {% set py_version = environ['CONDA_PY'] %} {% set date_string = environ['RAPIDS_DATE_STRING'] %} @@ -9,7 +9,7 @@ package: name: cugraph-service-split source: - git_url: ../../.. + path: ../../.. outputs: - name: cugraph-service-client diff --git a/conda/recipes/cugraph/meta.yaml b/conda/recipes/cugraph/meta.yaml index 3691db508d9..65403bc8d73 100644 --- a/conda/recipes/cugraph/meta.yaml +++ b/conda/recipes/cugraph/meta.yaml @@ -1,6 +1,6 @@ # Copyright (c) 2018-2023, NVIDIA CORPORATION. -{% set version = environ.get('GIT_DESCRIBE_TAG', '0.0.0.dev').lstrip('v') %} +{% set version = environ['RAPIDS_PACKAGE_VERSION'].lstrip('v') + environ.get('VERSION_SUFFIX', '') %} {% set minor_version = version.split('.')[0] + '.' + version.split('.')[1] %} {% set py_version = environ['CONDA_PY'] %} {% set cuda_version = '.'.join(environ['RAPIDS_CUDA_VERSION'].split('.')[:2]) %} @@ -12,7 +12,7 @@ package: version: {{ version }} source: - git_url: ../../.. + path: ../../.. build: number: {{ GIT_DESCRIBE_NUMBER }} diff --git a/conda/recipes/libcugraph/meta.yaml b/conda/recipes/libcugraph/meta.yaml index 83c82adf703..66f72e6b6b5 100644 --- a/conda/recipes/libcugraph/meta.yaml +++ b/conda/recipes/libcugraph/meta.yaml @@ -1,6 +1,6 @@ # Copyright (c) 2018-2023, NVIDIA CORPORATION. -{% set version = environ.get('GIT_DESCRIBE_TAG', '0.0.0.dev').lstrip('v') %} +{% set version = environ['RAPIDS_PACKAGE_VERSION'].lstrip('v') + environ.get('VERSION_SUFFIX', '') %} {% set minor_version = version.split('.')[0] + '.' + version.split('.')[1] %} {% set cuda_version = '.'.join(environ['RAPIDS_CUDA_VERSION'].split('.')[:2]) %} {% set cuda_major = cuda_version.split('.')[0] %} @@ -10,7 +10,7 @@ package: name: libcugraph-split source: - git_url: ../../.. + path: ../../.. build: script_env: diff --git a/conda/recipes/nx-cugraph/meta.yaml b/conda/recipes/nx-cugraph/meta.yaml index 556d72e8548..cdb7bc13c23 100644 --- a/conda/recipes/nx-cugraph/meta.yaml +++ b/conda/recipes/nx-cugraph/meta.yaml @@ -1,6 +1,6 @@ # Copyright (c) 2023, NVIDIA CORPORATION. -{% set version = environ.get('GIT_DESCRIBE_TAG', '0.0.0.dev').lstrip('v') %} +{% set version = environ['RAPIDS_PACKAGE_VERSION'].lstrip('v') + environ.get('VERSION_SUFFIX', '') %} {% set minor_version = version.split('.')[0] + '.' + version.split('.')[1] %} {% set py_version = environ['CONDA_PY'] %} {% set date_string = environ['RAPIDS_DATE_STRING'] %} @@ -10,7 +10,7 @@ package: version: {{ version }} source: - git_url: ../../.. + path: ../../.. build: number: {{ GIT_DESCRIBE_NUMBER }} diff --git a/conda/recipes/pylibcugraph/meta.yaml b/conda/recipes/pylibcugraph/meta.yaml index 083998be053..ad59c4de66f 100644 --- a/conda/recipes/pylibcugraph/meta.yaml +++ b/conda/recipes/pylibcugraph/meta.yaml @@ -1,6 +1,6 @@ # Copyright (c) 2023, NVIDIA CORPORATION. -{% set version = environ.get('GIT_DESCRIBE_TAG', '0.0.0.dev').lstrip('v') %} +{% set version = environ['RAPIDS_PACKAGE_VERSION'].lstrip('v') + environ.get('VERSION_SUFFIX', '') %} {% set minor_version = version.split('.')[0] + '.' + version.split('.')[1] %} {% set py_version = environ['CONDA_PY'] %} {% set cuda_version = '.'.join(environ['RAPIDS_CUDA_VERSION'].split('.')[:2]) %} @@ -12,7 +12,7 @@ package: version: {{ version }} source: - git_url: ../../.. + path: ../../.. build: number: {{ GIT_DESCRIBE_NUMBER }} diff --git a/python/cugraph-dgl/cugraph_dgl/VERSION b/python/cugraph-dgl/cugraph_dgl/VERSION new file mode 120000 index 00000000000..d62dc733efd --- /dev/null +++ b/python/cugraph-dgl/cugraph_dgl/VERSION @@ -0,0 +1 @@ +../../../VERSION \ No newline at end of file diff --git a/python/cugraph-dgl/cugraph_dgl/__init__.py b/python/cugraph-dgl/cugraph_dgl/__init__.py index 74be4fdea3f..03ff50896a4 100644 --- a/python/cugraph-dgl/cugraph_dgl/__init__.py +++ b/python/cugraph-dgl/cugraph_dgl/__init__.py @@ -20,4 +20,4 @@ import cugraph_dgl.dataloading import cugraph_dgl.nn -__version__ = "23.12.00" +from cugraph_dgl._version import __git_commit__, __version__ diff --git a/python/cugraph-dgl/cugraph_dgl/_version.py b/python/cugraph-dgl/cugraph_dgl/_version.py new file mode 100644 index 00000000000..f95a4705467 --- /dev/null +++ b/python/cugraph-dgl/cugraph_dgl/_version.py @@ -0,0 +1,26 @@ +# Copyright (c) 2023, NVIDIA CORPORATION. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import importlib.resources + +# Read VERSION file from the module that is symlinked to VERSION file +# in the root of the repo at build time or copied to the moudle at +# installation. VERSION is a separate file that allows CI build-time scripts +# to update version info (including commit hashes) without modifying +# source files. +__version__ = ( + importlib.resources.files("cugraph_dgl").joinpath("VERSION").read_text().strip() +) +__git_commit__ = "" diff --git a/python/cugraph-dgl/pyproject.toml b/python/cugraph-dgl/pyproject.toml index fa9e1c5abe5..eff7a20f0aa 100644 --- a/python/cugraph-dgl/pyproject.toml +++ b/python/cugraph-dgl/pyproject.toml @@ -10,7 +10,7 @@ build-backend = "setuptools.build_meta" [project] name = "cugraph-dgl" -version = "23.12.00" +dynamic = ["version"] description = "cugraph extensions for DGL" readme = { file = "README.md", content-type = "text/markdown" } authors = [ @@ -35,6 +35,9 @@ Documentation = "https://docs.rapids.ai/api/cugraph/stable/" [tool.setuptools] license-files = ["LICENSE"] +[tool.setuptools.dynamic] +version = {file = "cugraph_dgl/VERSION"} + [tool.setuptools.packages.find] include = [ "cugraph_dgl*", diff --git a/python/cugraph-dgl/setup.py b/python/cugraph-dgl/setup.py index 6991b23b0fb..afb8002af42 100644 --- a/python/cugraph-dgl/setup.py +++ b/python/cugraph-dgl/setup.py @@ -11,6 +11,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -from setuptools import setup +from setuptools import find_packages, setup -setup() +packages = find_packages(include=["cugraph_dgl*"]) +setup( + package_data={key: ["VERSION"] for key in packages}, +) diff --git a/python/cugraph-pyg/cugraph_pyg/VERSION b/python/cugraph-pyg/cugraph_pyg/VERSION new file mode 120000 index 00000000000..d62dc733efd --- /dev/null +++ b/python/cugraph-pyg/cugraph_pyg/VERSION @@ -0,0 +1 @@ +../../../VERSION \ No newline at end of file diff --git a/python/cugraph-pyg/cugraph_pyg/__init__.py b/python/cugraph-pyg/cugraph_pyg/__init__.py index ecd2f271a00..719751c966a 100644 --- a/python/cugraph-pyg/cugraph_pyg/__init__.py +++ b/python/cugraph-pyg/cugraph_pyg/__init__.py @@ -11,4 +11,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "23.12.00" +from cugraph_pyg._version import __git_commit__, __version__ diff --git a/python/cugraph-pyg/cugraph_pyg/_version.py b/python/cugraph-pyg/cugraph_pyg/_version.py new file mode 100644 index 00000000000..963052da909 --- /dev/null +++ b/python/cugraph-pyg/cugraph_pyg/_version.py @@ -0,0 +1,26 @@ +# Copyright (c) 2023, NVIDIA CORPORATION. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import importlib.resources + +# Read VERSION file from the module that is symlinked to VERSION file +# in the root of the repo at build time or copied to the moudle at +# installation. VERSION is a separate file that allows CI build-time scripts +# to update version info (including commit hashes) without modifying +# source files. +__version__ = ( + importlib.resources.files("cugraph_pyg").joinpath("VERSION").read_text().strip() +) +__git_commit__ = "" diff --git a/python/cugraph-pyg/pyproject.toml b/python/cugraph-pyg/pyproject.toml index 84d30221d55..95b1fa27402 100644 --- a/python/cugraph-pyg/pyproject.toml +++ b/python/cugraph-pyg/pyproject.toml @@ -12,7 +12,7 @@ testpaths = ["cugraph_pyg/tests"] [project] name = "cugraph_pyg" -version = "23.12.00" +dynamic = ["version"] description = "cugraph_pyg - PyG support for cuGraph massive-scale, ultra-fast GPU graph analytics." authors = [ { name = "NVIDIA Corporation" }, @@ -38,6 +38,9 @@ Documentation = "https://docs.rapids.ai/api/cugraph/stable/" [tool.setuptools] license-files = ["LICENSE"] +[tool.setuptools.dynamic] +version = {file = "cugraph_pyg/VERSION"} + [tool.setuptools.packages.find] include = [ "cugraph_pyg*", diff --git a/python/cugraph-pyg/setup.py b/python/cugraph-pyg/setup.py index 1f7db1d3772..50f023050bf 100644 --- a/python/cugraph-pyg/setup.py +++ b/python/cugraph-pyg/setup.py @@ -14,7 +14,7 @@ import os import shutil -from setuptools import Command, setup +from setuptools import Command, find_packages, setup from setuputils import get_environment_option @@ -59,6 +59,8 @@ def run(self): os.system("rm -rf *.egg-info") +packages = find_packages(include=["cugraph_pyg*"]) setup( cmdclass={"clean": CleanCommand}, + package_data={key: ["VERSION"] for key in packages}, ) diff --git a/python/cugraph-service/client/cugraph_service_client/VERSION b/python/cugraph-service/client/cugraph_service_client/VERSION new file mode 120000 index 00000000000..a4e948506b8 --- /dev/null +++ b/python/cugraph-service/client/cugraph_service_client/VERSION @@ -0,0 +1 @@ +../../../../VERSION \ No newline at end of file diff --git a/python/cugraph-service/client/cugraph_service_client/__init__.py b/python/cugraph-service/client/cugraph_service_client/__init__.py index a0361abedd3..a9a96ae6c16 100644 --- a/python/cugraph-service/client/cugraph_service_client/__init__.py +++ b/python/cugraph-service/client/cugraph_service_client/__init__.py @@ -35,4 +35,4 @@ from cugraph_service_client.client import CugraphServiceClient from cugraph_service_client.remote_graph import RemoteGraph -__version__ = "23.12.00" +from cugraph_service_client._version import __git_commit__, __version__ diff --git a/python/cugraph-service/client/cugraph_service_client/_version.py b/python/cugraph-service/client/cugraph_service_client/_version.py new file mode 100644 index 00000000000..344361973bb --- /dev/null +++ b/python/cugraph-service/client/cugraph_service_client/_version.py @@ -0,0 +1,29 @@ +# Copyright (c) 2023, NVIDIA CORPORATION. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import importlib.resources + +# Read VERSION file from the module that is symlinked to VERSION file +# in the root of the repo at build time or copied to the moudle at +# installation. VERSION is a separate file that allows CI build-time scripts +# to update version info (including commit hashes) without modifying +# source files. +__version__ = ( + importlib.resources.files("cugraph_service_client") + .joinpath("VERSION") + .read_text() + .strip() +) +__git_commit__ = "" diff --git a/python/cugraph-service/client/pyproject.toml b/python/cugraph-service/client/pyproject.toml index 7f702252f02..59539693877 100644 --- a/python/cugraph-service/client/pyproject.toml +++ b/python/cugraph-service/client/pyproject.toml @@ -10,7 +10,7 @@ build-backend = "setuptools.build_meta" [project] name = "cugraph-service-client" -version = "23.12.00" +dynamic = ["version"] description = "cuGraph Service client" readme = { file = "README.md", content-type = "text/markdown" } authors = [ @@ -35,6 +35,9 @@ Documentation = "https://docs.rapids.ai/api/cugraph/stable/" [tool.setuptools] license-files = ["LICENSE"] +[tool.setuptools.dynamic] +version = {file = "cugraph_service_client/VERSION"} + [tool.setuptools.packages.find] include = [ "cugraph_service_client", diff --git a/python/cugraph-service/client/setup.py b/python/cugraph-service/client/setup.py index 811a12c50b7..61c758cef4a 100644 --- a/python/cugraph-service/client/setup.py +++ b/python/cugraph-service/client/setup.py @@ -11,6 +11,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -from setuptools import setup +from setuptools import find_packages, setup -setup() +packages = find_packages(include=["cugraph_service_client*"]) +setup( + package_data={key: ["VERSION"] for key in packages}, +) diff --git a/python/cugraph-service/server/cugraph_service_server/VERSION b/python/cugraph-service/server/cugraph_service_server/VERSION new file mode 120000 index 00000000000..a4e948506b8 --- /dev/null +++ b/python/cugraph-service/server/cugraph_service_server/VERSION @@ -0,0 +1 @@ +../../../../VERSION \ No newline at end of file diff --git a/python/cugraph-service/server/cugraph_service_server/__init__.py b/python/cugraph-service/server/cugraph_service_server/__init__.py index 87d35005195..02473f0ea47 100644 --- a/python/cugraph-service/server/cugraph_service_server/__init__.py +++ b/python/cugraph-service/server/cugraph_service_server/__init__.py @@ -61,4 +61,4 @@ def start_server_blocking( server.serve() # blocks until Ctrl-C (kill -2) -__version__ = "23.12.00" +from cugraph_service_server._version import __git_commit__, __version__ diff --git a/python/cugraph-service/server/cugraph_service_server/_version.py b/python/cugraph-service/server/cugraph_service_server/_version.py new file mode 100644 index 00000000000..7da31f78767 --- /dev/null +++ b/python/cugraph-service/server/cugraph_service_server/_version.py @@ -0,0 +1,29 @@ +# Copyright (c) 2023, NVIDIA CORPORATION. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import importlib.resources + +# Read VERSION file from the module that is symlinked to VERSION file +# in the root of the repo at build time or copied to the moudle at +# installation. VERSION is a separate file that allows CI build-time scripts +# to update version info (including commit hashes) without modifying +# source files. +__version__ = ( + importlib.resources.files("cugraph_service_server") + .joinpath("VERSION") + .read_text() + .strip() +) +__git_commit__ = "" diff --git a/python/cugraph-service/server/pyproject.toml b/python/cugraph-service/server/pyproject.toml index 3c77cf01c2c..f50b33b3f15 100644 --- a/python/cugraph-service/server/pyproject.toml +++ b/python/cugraph-service/server/pyproject.toml @@ -10,7 +10,7 @@ build-backend = "setuptools.build_meta" [project] name = "cugraph-service-server" -version = "23.12.00" +dynamic = ["version", "entry-points"] description = "cuGraph Service server" readme = { file = "README.md", content-type = "text/markdown" } authors = [ @@ -39,7 +39,6 @@ classifiers = [ "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", ] -dynamic = ["entry-points"] [project.optional-dependencies] test = [ @@ -62,6 +61,9 @@ Documentation = "https://docs.rapids.ai/api/cugraph/stable/" [tool.setuptools] license-files = ["LICENSE"] +[tool.setuptools.dynamic] +version = {file = "cugraph_service_server/VERSION"} + [tool.setuptools.packages.find] include = [ "cugraph_service_server", diff --git a/python/cugraph-service/server/setup.py b/python/cugraph-service/server/setup.py index 5203b76c659..91864168e2c 100644 --- a/python/cugraph-service/server/setup.py +++ b/python/cugraph-service/server/setup.py @@ -11,12 +11,14 @@ # See the License for the specific language governing permissions and # limitations under the License. -from setuptools import setup +from setuptools import find_packages, setup +packages = find_packages(include=["cugraph_service_server*"]) setup( entry_points={ "console_scripts": [ "cugraph-service-server=cugraph_service_server.__main__:main" ], }, + package_data={key: ["VERSION"] for key in packages}, ) diff --git a/python/cugraph/cugraph/VERSION b/python/cugraph/cugraph/VERSION new file mode 120000 index 00000000000..d62dc733efd --- /dev/null +++ b/python/cugraph/cugraph/VERSION @@ -0,0 +1 @@ +../../../VERSION \ No newline at end of file diff --git a/python/cugraph/cugraph/__init__.py b/python/cugraph/cugraph/__init__.py index f3a335183f3..f635d215696 100644 --- a/python/cugraph/cugraph/__init__.py +++ b/python/cugraph/cugraph/__init__.py @@ -120,4 +120,4 @@ from cugraph import exceptions -__version__ = "23.12.00" +from cugraph._version import __git_commit__, __version__ diff --git a/python/cugraph/cugraph/_version.py b/python/cugraph/cugraph/_version.py new file mode 100644 index 00000000000..710afb87e29 --- /dev/null +++ b/python/cugraph/cugraph/_version.py @@ -0,0 +1,26 @@ +# Copyright (c) 2023, NVIDIA CORPORATION. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import importlib.resources + +# Read VERSION file from the module that is symlinked to VERSION file +# in the root of the repo at build time or copied to the moudle at +# installation. VERSION is a separate file that allows CI build-time scripts +# to update version info (including commit hashes) without modifying +# source files. +__version__ = ( + importlib.resources.files("cugraph").joinpath("VERSION").read_text().strip() +) +__git_commit__ = "" diff --git a/python/cugraph/pyproject.toml b/python/cugraph/pyproject.toml index 5e17be40e7b..aaa301fa05f 100644 --- a/python/cugraph/pyproject.toml +++ b/python/cugraph/pyproject.toml @@ -20,7 +20,7 @@ testpaths = ["cugraph/tests"] [project] name = "cugraph" -version = "23.12.00" +dynamic = ["version"] description = "cuGraph - RAPIDS GPU Graph Analytics" readme = { file = "README.md", content-type = "text/markdown" } authors = [ @@ -69,3 +69,6 @@ Documentation = "https://docs.rapids.ai/api/cugraph/stable/" [tool.setuptools] license-files = ["LICENSE"] + +[tool.setuptools.dynamic] +version = {file = "cugraph/VERSION"} diff --git a/python/cugraph/setup.py b/python/cugraph/setup.py index aa3a5fb56a7..81916444cfd 100644 --- a/python/cugraph/setup.py +++ b/python/cugraph/setup.py @@ -46,7 +46,7 @@ def run(self): packages = find_packages(include=["cugraph*"]) setup( packages=packages, - package_data={key: ["*.pxd", "*.yaml"] for key in packages}, + package_data={key: ["VERSION", "*.pxd", "*.yaml"] for key in packages}, cmdclass={"clean": CleanCommand}, zip_safe=False, ) diff --git a/python/nx-cugraph/nx_cugraph/VERSION b/python/nx-cugraph/nx_cugraph/VERSION new file mode 120000 index 00000000000..d62dc733efd --- /dev/null +++ b/python/nx-cugraph/nx_cugraph/VERSION @@ -0,0 +1 @@ +../../../VERSION \ No newline at end of file diff --git a/python/nx-cugraph/nx_cugraph/__init__.py b/python/nx-cugraph/nx_cugraph/__init__.py index 25d44212264..3a8f0996e9c 100644 --- a/python/nx-cugraph/nx_cugraph/__init__.py +++ b/python/nx-cugraph/nx_cugraph/__init__.py @@ -29,4 +29,4 @@ from . import algorithms from .algorithms import * -__version__ = "23.12.00" +from nx_cugraph._version import __git_commit__, __version__ diff --git a/python/nx-cugraph/nx_cugraph/_version.py b/python/nx-cugraph/nx_cugraph/_version.py new file mode 100644 index 00000000000..868a2e19475 --- /dev/null +++ b/python/nx-cugraph/nx_cugraph/_version.py @@ -0,0 +1,26 @@ +# Copyright (c) 2023, NVIDIA CORPORATION. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import importlib.resources + +# Read VERSION file from the module that is symlinked to VERSION file +# in the root of the repo at build time or copied to the moudle at +# installation. VERSION is a separate file that allows CI build-time scripts +# to update version info (including commit hashes) without modifying +# source files. +__version__ = ( + importlib.resources.files("nx_cugraph").joinpath("VERSION").read_text().strip() +) +__git_commit__ = "" diff --git a/python/nx-cugraph/pyproject.toml b/python/nx-cugraph/pyproject.toml index 7e51efd4fe4..f309f4797a7 100644 --- a/python/nx-cugraph/pyproject.toml +++ b/python/nx-cugraph/pyproject.toml @@ -10,7 +10,7 @@ build-backend = "setuptools.build_meta" [project] name = "nx-cugraph" -version = "23.12.00" +dynamic = ["version"] description = "cugraph backend for NetworkX" readme = { file = "README.md", content-type = "text/markdown" } authors = [ @@ -61,6 +61,9 @@ cugraph = "_nx_cugraph:get_info" [tool.setuptools] license-files = ["LICENSE"] +[tool.setuptools.dynamic] +version = {file = "nx_cugraph/VERSION"} + [tool.setuptools.packages.find] include = [ "nx_cugraph*", diff --git a/python/nx-cugraph/setup.py b/python/nx-cugraph/setup.py index 87c0e10646d..c4ab535923b 100644 --- a/python/nx-cugraph/setup.py +++ b/python/nx-cugraph/setup.py @@ -10,6 +10,9 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -from setuptools import setup +from setuptools import find_packages, setup -setup() +packages = find_packages(include=["nx_cugraph*"]) +setup( + package_data={key: ["VERSION"] for key in packages}, +) diff --git a/python/pylibcugraph/pylibcugraph/VERSION b/python/pylibcugraph/pylibcugraph/VERSION new file mode 120000 index 00000000000..d62dc733efd --- /dev/null +++ b/python/pylibcugraph/pylibcugraph/VERSION @@ -0,0 +1 @@ +../../../VERSION \ No newline at end of file diff --git a/python/pylibcugraph/pylibcugraph/__init__.py b/python/pylibcugraph/pylibcugraph/__init__.py index 2aec0b98a25..30f1c2d0fb1 100644 --- a/python/pylibcugraph/pylibcugraph/__init__.py +++ b/python/pylibcugraph/pylibcugraph/__init__.py @@ -98,4 +98,4 @@ from pylibcugraph import exceptions -__version__ = "23.12.00" +from pylibcugraph._version import __git_commit__, __version__ diff --git a/python/pylibcugraph/pylibcugraph/_version.py b/python/pylibcugraph/pylibcugraph/_version.py new file mode 100644 index 00000000000..5dca7e48b3f --- /dev/null +++ b/python/pylibcugraph/pylibcugraph/_version.py @@ -0,0 +1,26 @@ +# Copyright (c) 2023, NVIDIA CORPORATION. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import importlib.resources + +# Read VERSION file from the module that is symlinked to VERSION file +# in the root of the repo at build time or copied to the moudle at +# installation. VERSION is a separate file that allows CI build-time scripts +# to update version info (including commit hashes) without modifying +# source files. +__version__ = ( + importlib.resources.files("pylibcugraph").joinpath("VERSION").read_text().strip() +) +__git_commit__ = "" diff --git a/python/pylibcugraph/pyproject.toml b/python/pylibcugraph/pyproject.toml index f1b439debd2..96f5ec84efb 100644 --- a/python/pylibcugraph/pyproject.toml +++ b/python/pylibcugraph/pyproject.toml @@ -19,7 +19,7 @@ testpaths = ["pylibcugraph/tests"] [project] name = "pylibcugraph" -version = "23.12.00" +dynamic = ["version"] description = "pylibcugraph - Python bindings for the libcugraph cuGraph C/C++/CUDA library" readme = { file = "README.md", content-type = "text/markdown" } authors = [ @@ -56,3 +56,6 @@ Documentation = "https://docs.rapids.ai/api/cugraph/stable/" [tool.setuptools] license-files = ["LICENSE"] + +[tool.setuptools.dynamic] +version = {file = "pylibcugraph/VERSION"} diff --git a/python/pylibcugraph/setup.py b/python/pylibcugraph/setup.py index f1a419f31bb..a6c1bda3b5b 100644 --- a/python/pylibcugraph/setup.py +++ b/python/pylibcugraph/setup.py @@ -54,7 +54,7 @@ def exclude_libcxx_symlink(cmake_manifest): packages = find_packages(include=["pylibcugraph*"]) setup( packages=packages, - package_data={key: ["*.pxd"] for key in packages}, + package_data={key: ["VERSION", "*.pxd"] for key in packages}, cmake_process_manifest_hook=exclude_libcxx_symlink, cmdclass={"clean": CleanCommand}, zip_safe=False, From 2bdb7358878a8f1e2405b1f5c3e7938d54a67759 Mon Sep 17 00:00:00 2001 From: Tingyu Wang Date: Fri, 3 Nov 2023 15:20:32 -0400 Subject: [PATCH 071/111] Skip certain `cugraph-pyg` tests when torch-sparse is not available (#3970) A follow-up PR to fix the test failure again on rockylinux (see [log](https://github.com/rapidsai/cugraph/actions/runs/6718664898/job/18258783035#step:7:9297)). `import_optional` only catches `ModuleNotFoundError`, however, it threw an `OSError` on rockylinux. Let's just catch all exceptions during imports in this case. Closes https://github.com/rapidsai/graph_dl/issues/373 Authors: - Tingyu Wang (https://github.com/tingyu66) Approvers: - Rick Ratzel (https://github.com/rlratzel) - Brad Rees (https://github.com/BradReesWork) URL: https://github.com/rapidsai/cugraph/pull/3970 --- .../cugraph_pyg/tests/test_cugraph_loader.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/python/cugraph-pyg/cugraph_pyg/tests/test_cugraph_loader.py b/python/cugraph-pyg/cugraph_pyg/tests/test_cugraph_loader.py index 836b30c9df7..853836dc2a6 100644 --- a/python/cugraph-pyg/cugraph_pyg/tests/test_cugraph_loader.py +++ b/python/cugraph-pyg/cugraph_pyg/tests/test_cugraph_loader.py @@ -29,9 +29,15 @@ torch = import_optional("torch") torch_geometric = import_optional("torch_geometric") -torch_sparse = import_optional("torch_sparse") trim_to_layer = import_optional("torch_geometric.utils.trim_to_layer") +try: + import torch_sparse # noqa: F401 + + HAS_TORCH_SPARSE = True +except: # noqa: E722 + HAS_TORCH_SPARSE = False + @pytest.mark.skipif(isinstance(torch, MissingModule), reason="torch not available") def test_cugraph_loader_basic(karate_gnn): @@ -201,9 +207,7 @@ def test_cugraph_loader_from_disk_subset(): @pytest.mark.skipif(isinstance(torch, MissingModule), reason="torch not available") -@pytest.mark.skipif( - isinstance(torch_sparse, MissingModule), reason="torch-sparse not available" -) +@pytest.mark.skipif(not HAS_TORCH_SPARSE, reason="torch-sparse not available") def test_cugraph_loader_from_disk_subset_csr(): m = [2, 9, 99, 82, 11, 13] n = torch.arange(1, 1 + len(m), dtype=torch.int32) @@ -336,9 +340,7 @@ def test_cugraph_loader_e2e_coo(): @pytest.mark.skipif(isinstance(torch, MissingModule), reason="torch not available") -@pytest.mark.skipif( - isinstance(torch_sparse, MissingModule), reason="torch-sparse not available" -) +@pytest.mark.skipif(not HAS_TORCH_SPARSE, reason="torch-sparse not available") @pytest.mark.parametrize("framework", ["pyg", "cugraph-ops"]) def test_cugraph_loader_e2e_csc(framework): m = [2, 9, 99, 82, 9, 3, 18, 1, 12] From 586451da8cbdf54e04a420725982c0d60de42356 Mon Sep 17 00:00:00 2001 From: Chuck Hastings <45364586+ChuckHastings@users.noreply.github.com> Date: Mon, 6 Nov 2023 10:28:05 -0500 Subject: [PATCH 072/111] MTMG multi node (#3932) This PR extends the MTMG framework to support a multi-node configuration. Multi-node configuration of MTMG assumes some externally available mechanism for communicating configuration parameters and the NCCL unique id between the processes that will participate. For testing purposes we assume a shared file system where we can create a directory and read/write configuration information rather than constructing a more complex communication mechanism (NFS is sufficient for testing). Closes https://github.com/rapidsai/graph_dl/issues/329 Authors: - Chuck Hastings (https://github.com/ChuckHastings) - Ralph Liu (https://github.com/nv-rliu) Approvers: - Joseph Nke (https://github.com/jnke2016) - Seunghwa Kang (https://github.com/seunghwak) URL: https://github.com/rapidsai/cugraph/pull/3932 --- cpp/include/cugraph/mtmg/handle.hpp | 8 - cpp/include/cugraph/mtmg/instance_manager.hpp | 27 +- cpp/include/cugraph/mtmg/resource_manager.hpp | 124 +++-- cpp/tests/CMakeLists.txt | 10 + cpp/tests/mtmg/multi_node_threaded_test.cu | 516 ++++++++++++++++++ 5 files changed, 626 insertions(+), 59 deletions(-) create mode 100644 cpp/tests/mtmg/multi_node_threaded_test.cu diff --git a/cpp/include/cugraph/mtmg/handle.hpp b/cpp/include/cugraph/mtmg/handle.hpp index efdec3f0775..6223de1781d 100644 --- a/cpp/include/cugraph/mtmg/handle.hpp +++ b/cpp/include/cugraph/mtmg/handle.hpp @@ -111,14 +111,6 @@ class handle_t { */ int get_size() const { return raft_handle_.get_comms().get_size(); } - /** - * @brief Get number of local gpus - * - * @return number of local gpus - */ - // FIXME: wrong for multi-node - int get_local_size() const { return raft_handle_.get_comms().get_size(); } - /** * @brief Get gpu rank * diff --git a/cpp/include/cugraph/mtmg/instance_manager.hpp b/cpp/include/cugraph/mtmg/instance_manager.hpp index 687c5ddbf02..f819a5a0abe 100644 --- a/cpp/include/cugraph/mtmg/instance_manager.hpp +++ b/cpp/include/cugraph/mtmg/instance_manager.hpp @@ -18,7 +18,7 @@ #include -#include +#include #include @@ -37,16 +37,27 @@ class instance_manager_t { */ instance_manager_t(std::vector>&& handles, std::vector>&& nccl_comms, - std::vector&& device_ids, - int local_gpu_count) + std::vector&& device_ids) : thread_counter_{0}, raft_handle_{std::move(handles)}, nccl_comms_{std::move(nccl_comms)}, - device_ids_{std::move(device_ids)}, - local_gpu_count_{local_gpu_count} + device_ids_{std::move(device_ids)} { } + ~instance_manager_t() + { + int current_device{}; + RAFT_CUDA_TRY(cudaGetDevice(¤t_device)); + + for (size_t i = 0; i < nccl_comms_.size(); ++i) { + RAFT_CUDA_TRY(cudaSetDevice(device_ids_[i].value())); + RAFT_NCCL_TRY(ncclCommDestroy(*nccl_comms_[i])); + } + + RAFT_CUDA_TRY(cudaSetDevice(current_device)); + } + /** * @brief Get handle * @@ -79,7 +90,7 @@ class instance_manager_t { /** * @brief Number of local GPUs in the instance */ - int get_local_gpu_count() { return local_gpu_count_; } + int get_local_gpu_count() { return static_cast(raft_handle_.size()); } private: // FIXME: Should this be an std::map<> where the key is the rank? @@ -87,9 +98,11 @@ class instance_manager_t { // (or no) GPUs, so mapping rank to a handle might be a challenge // std::vector> raft_handle_{}; + + // FIXME: Explore what RAFT changes might be desired to allow the ncclComm_t + // to be managed by RAFT instead of cugraph::mtmg std::vector> nccl_comms_{}; std::vector device_ids_{}; - int local_gpu_count_{}; std::atomic thread_counter_{0}; }; diff --git a/cpp/include/cugraph/mtmg/resource_manager.hpp b/cpp/include/cugraph/mtmg/resource_manager.hpp index e1e1d7ffc9d..127944cf7ba 100644 --- a/cpp/include/cugraph/mtmg/resource_manager.hpp +++ b/cpp/include/cugraph/mtmg/resource_manager.hpp @@ -41,6 +41,11 @@ namespace mtmg { * register_local_gpu (or register_remote_gpu once we support a multi-node * configuration) to allocate resources that can be used in the mtmg space. * + * Each GPU in the cluster should be given a unique global rank, an integer + * that will be used to reference the GPU within the resource manager. It + * is recommended that the GPUs be numbered sequentially from 0, although this + * is not required. + * * When we want to execute some graph computations, we need to create an instance for execution. * Based on how big a subset of the desired compute resources is desired, we can allocate some * number of GPUs to the problem (up to the total set of managed resources). @@ -48,7 +53,7 @@ namespace mtmg { * The returned instance can be used to create a graph, execute one or more algorithms, etc. Once * we are done the caller can delete the instance. * - * At the moment, the caller is assumed to be responsible for scheduling use of the resources. + * The caller is assumed to be responsible for scheduling use of the resources. * * For our first release, we will only consider a single node multi-GPU configuration, so the remote * GPU methods are currently disabled via ifdef. @@ -63,25 +68,28 @@ class resource_manager_t { /** * @brief add a local GPU to the resource manager. * - * @param rank The rank to assign to the local GPU - * @param device_id The device_id corresponding to this rank + * @param global_rank The global rank to assign to the local GPU + * @param local_device_id The local device_id corresponding to this rank */ - void register_local_gpu(int rank, rmm::cuda_device_id device_id) + void register_local_gpu(int global_rank, rmm::cuda_device_id local_device_id) { std::lock_guard lock(lock_); - CUGRAPH_EXPECTS(local_rank_map_.find(rank) == local_rank_map_.end(), - "cannot register same rank multiple times"); + CUGRAPH_EXPECTS(remote_rank_set_.find(global_rank) == remote_rank_set_.end(), + "cannot register same global_rank as local and remote"); + CUGRAPH_EXPECTS(local_rank_map_.find(global_rank) == local_rank_map_.end(), + "cannot register same global_rank multiple times"); int num_gpus_this_node; RAFT_CUDA_TRY(cudaGetDeviceCount(&num_gpus_this_node)); - CUGRAPH_EXPECTS((device_id.value() >= 0) && (device_id.value() < num_gpus_this_node), - "device id out of range"); + CUGRAPH_EXPECTS( + (local_device_id.value() >= 0) && (local_device_id.value() < num_gpus_this_node), + "local device id out of range"); - local_rank_map_.insert(std::pair(rank, device_id)); + local_rank_map_.insert(std::pair(global_rank, local_device_id)); - RAFT_CUDA_TRY(cudaSetDevice(device_id.value())); + RAFT_CUDA_TRY(cudaSetDevice(local_device_id.value())); // FIXME: There is a bug in the cuda_memory_resource that results in a Hang. // using the pool resource as a work-around. @@ -89,23 +97,43 @@ class resource_manager_t { // There is a deprecated environment variable: NCCL_LAUNCH_MODE=GROUP // which should temporarily work around this problem. // + // Further NOTE: multi-node requires the NCCL_LAUNCH_MODE=GROUP feature + // to be enabled even with the pool memory resource. + // // Ultimately there should be some RMM parameters passed into this function // (or the constructor of the object) to configure this behavior #if 0 auto per_device_it = per_device_rmm_resources_.insert( - std::pair{rank, std::make_shared()}); + std::pair{global_rank, std::make_shared()}); #else auto const [free, total] = rmm::detail::available_device_memory(); auto const min_alloc = rmm::detail::align_down(std::min(free, total / 6), rmm::detail::CUDA_ALLOCATION_ALIGNMENT); auto per_device_it = per_device_rmm_resources_.insert( - std::pair{rank, + std::pair{global_rank, rmm::mr::make_owning_wrapper( std::make_shared(), min_alloc)}); #endif - rmm::mr::set_per_device_resource(device_id, per_device_it.first->second.get()); + rmm::mr::set_per_device_resource(local_device_id, per_device_it.first->second.get()); + } + + /** + * @brief add a remote GPU to the resource manager. + * + * @param global_rank The global rank to assign to the remote GPU + */ + void register_remote_gpu(int global_rank) + { + std::lock_guard lock(lock_); + + CUGRAPH_EXPECTS(local_rank_map_.find(global_rank) == local_rank_map_.end(), + "cannot register same global_rank as local and remote"); + CUGRAPH_EXPECTS(remote_rank_set_.find(global_rank) == remote_rank_set_.end(), + "cannot register same global_rank multiple times"); + + remote_rank_set_.insert(global_rank); } /** @@ -130,29 +158,36 @@ class resource_manager_t { ncclUniqueId instance_manager_id, size_t n_streams = 16) const { - std::for_each( - ranks_to_include.begin(), ranks_to_include.end(), [local_ranks = local_rank_map_](int rank) { - CUGRAPH_EXPECTS(local_ranks.find(rank) != local_ranks.end(), - "requesting inclusion of an invalid rank"); - }); + std::vector local_ranks_to_include; + + std::copy_if(ranks_to_include.begin(), + ranks_to_include.end(), + std::back_inserter(local_ranks_to_include), + [&local_ranks = local_rank_map_](int rank) { + return (local_ranks.find(rank) != local_ranks.end()); + }); + // FIXME: Explore what RAFT changes might be desired to allow the ncclComm_t + // to be managed by RAFT instead of cugraph::mtmg std::vector> nccl_comms{}; std::vector> handles{}; std::vector device_ids{}; - nccl_comms.reserve(ranks_to_include.size()); - handles.reserve(ranks_to_include.size()); - device_ids.reserve(ranks_to_include.size()); + nccl_comms.reserve(local_ranks_to_include.size()); + handles.reserve(local_ranks_to_include.size()); + device_ids.reserve(local_ranks_to_include.size()); - // FIXME: not quite right for multi-node auto gpu_row_comm_size = static_cast(sqrt(static_cast(ranks_to_include.size()))); while (ranks_to_include.size() % gpu_row_comm_size != 0) { --gpu_row_comm_size; } - // FIXME: not quite right for multi-node - for (size_t i = 0; i < ranks_to_include.size(); ++i) { - int rank = ranks_to_include[i]; + int current_device{}; + RAFT_CUDA_TRY(cudaGetDevice(¤t_device)); + RAFT_NCCL_TRY(ncclGroupStart()); + + for (size_t i = 0; i < local_ranks_to_include.size(); ++i) { + int rank = local_ranks_to_include[i]; auto pos = local_rank_map_.find(rank); RAFT_CUDA_TRY(cudaSetDevice(pos->second.value())); @@ -162,26 +197,28 @@ class resource_manager_t { std::make_shared(n_streams), per_device_rmm_resources_.find(rank)->second)); device_ids.push_back(pos->second); + + RAFT_NCCL_TRY( + ncclCommInitRank(nccl_comms[i].get(), ranks_to_include.size(), instance_manager_id, rank)); + raft::comms::build_comms_nccl_only( + handles[i].get(), *nccl_comms[i], ranks_to_include.size(), rank); } + RAFT_NCCL_TRY(ncclGroupEnd()); + RAFT_CUDA_TRY(cudaSetDevice(current_device)); std::vector running_threads; - for (size_t i = 0; i < ranks_to_include.size(); ++i) { + for (size_t i = 0; i < local_ranks_to_include.size(); ++i) { running_threads.emplace_back([instance_manager_id, idx = i, gpu_row_comm_size, comm_size = ranks_to_include.size(), - &ranks_to_include, - &local_rank_map = local_rank_map_, + &local_ranks_to_include, + &device_ids, &nccl_comms, &handles]() { - int rank = ranks_to_include[idx]; - auto pos = local_rank_map.find(rank); - RAFT_CUDA_TRY(cudaSetDevice(pos->second.value())); - - NCCL_TRY(ncclCommInitRank(nccl_comms[idx].get(), comm_size, instance_manager_id, rank)); - - raft::comms::build_comms_nccl_only(handles[idx].get(), *nccl_comms[idx], comm_size, rank); + int rank = local_ranks_to_include[idx]; + RAFT_CUDA_TRY(cudaSetDevice(device_ids[idx].value())); cugraph::partition_manager::init_subcomm(*handles[idx], gpu_row_comm_size); }); @@ -189,9 +226,8 @@ class resource_manager_t { std::for_each(running_threads.begin(), running_threads.end(), [](auto& t) { t.join(); }); - // FIXME: Update for multi-node return std::make_unique( - std::move(handles), std::move(nccl_comms), std::move(device_ids), ranks_to_include.size()); + std::move(handles), std::move(nccl_comms), std::move(device_ids)); } /** @@ -203,24 +239,24 @@ class resource_manager_t { { std::lock_guard lock(lock_); - // - // C++20 mechanism: - // return std::vector{ std::views::keys(local_rank_map_).begin(), - // std::views::keys(local_rank_map_).end() }; - // Would need a bit more complicated to handle remote_rank_map_ also - // - std::vector registered_ranks(local_rank_map_.size()); + std::vector registered_ranks(local_rank_map_.size() + remote_rank_set_.size()); std::transform( local_rank_map_.begin(), local_rank_map_.end(), registered_ranks.begin(), [](auto pair) { return pair.first; }); + std::copy(remote_rank_set_.begin(), + remote_rank_set_.end(), + registered_ranks.begin() + local_rank_map_.size()); + + std::sort(registered_ranks.begin(), registered_ranks.end()); return registered_ranks; } private: mutable std::mutex lock_{}; std::map local_rank_map_{}; + std::set remote_rank_set_{}; std::map> per_device_rmm_resources_{}; }; diff --git a/cpp/tests/CMakeLists.txt b/cpp/tests/CMakeLists.txt index 6775ed2eb16..2f69cf9cb0d 100644 --- a/cpp/tests/CMakeLists.txt +++ b/cpp/tests/CMakeLists.txt @@ -680,6 +680,16 @@ if(BUILD_CUGRAPH_MG_TESTS) ConfigureCTestMG(MG_CAPI_TWO_HOP_NEIGHBORS_TEST c_api/mg_two_hop_neighbors_test.c) rapids_test_install_relocatable(INSTALL_COMPONENT_SET testing_mg DESTINATION bin/gtests/libcugraph_mg) + + ############################################################################################### + # - Multi-node MTMG tests --------------------------------------------------------------------- + ConfigureTest(MTMG_MULTINODE_TEST mtmg/multi_node_threaded_test.cu utilities/mg_utilities.cpp) + target_link_libraries(MTMG_MULTINODE_TEST + PRIVATE + cugraphmgtestutil + UCP::UCP + ) + endif() ################################################################################################### diff --git a/cpp/tests/mtmg/multi_node_threaded_test.cu b/cpp/tests/mtmg/multi_node_threaded_test.cu new file mode 100644 index 00000000000..e5a7de07781 --- /dev/null +++ b/cpp/tests/mtmg/multi_node_threaded_test.cu @@ -0,0 +1,516 @@ +/* + * Copyright (c) 2023, NVIDIA CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include + +#include +#include +#include + +#include +#include + +struct Multithreaded_Usecase { + bool test_weighted{false}; + bool check_correctness{true}; +}; + +// Global variable defining resource manager +static cugraph::mtmg::resource_manager_t g_resource_manager{}; +static int g_node_rank{-1}; +static int g_num_nodes{-1}; + +template +class Tests_Multithreaded + : public ::testing::TestWithParam> { + public: + Tests_Multithreaded() {} + + virtual void SetUp() {} + virtual void TearDown() {} + + std::vector get_gpu_list() + { + int num_gpus_per_node{1}; + RAFT_CUDA_TRY(cudaGetDeviceCount(&num_gpus_per_node)); + + std::vector gpu_list(num_gpus_per_node); + std::iota(gpu_list.begin(), gpu_list.end(), 0); + + return gpu_list; + } + + template + void run_current_test( + std::tuple const& param, + std::vector gpu_list) + { + using edge_type_t = int32_t; + + constexpr bool renumber = true; + constexpr bool do_expensive_check = false; + + auto [multithreaded_usecase, input_usecase] = param; + + raft::handle_t handle{}; + + result_t constexpr alpha{0.85}; + result_t constexpr epsilon{1e-6}; + + size_t device_buffer_size{64 * 1024 * 1024}; + size_t thread_buffer_size{4 * 1024 * 1024}; + + int num_local_gpus = gpu_list.size(); + int num_threads = num_local_gpus * 4; + + ncclUniqueId instance_manager_id{}; + + if (g_node_rank == 0) RAFT_NCCL_TRY(ncclGetUniqueId(&instance_manager_id)); + + RAFT_MPI_TRY( + MPI_Bcast(&instance_manager_id, sizeof(instance_manager_id), MPI_CHAR, 0, MPI_COMM_WORLD)); + + auto instance_manager = g_resource_manager.create_instance_manager( + g_resource_manager.registered_ranks(), instance_manager_id); + + cugraph::mtmg::edgelist_t edgelist; + cugraph::mtmg::graph_t graph; + cugraph::mtmg::graph_view_t graph_view; + cugraph::mtmg::vertex_result_t pageranks; + std::optional> renumber_map = + std::make_optional>(); + + auto edge_weights = multithreaded_usecase.test_weighted + ? std::make_optional, + weight_t>>() + : std::nullopt; + + // + // Simulate graph creation by spawning threads to walk through the + // local COO and add edges + // + std::vector running_threads; + + // Initialize shared edgelist object, one per GPU + for (int i = 0; i < num_local_gpus; ++i) { + running_threads.emplace_back([&instance_manager, + &edgelist, + device_buffer_size, + use_weight = true, + use_edge_id = false, + use_edge_type = false]() { + auto thread_handle = instance_manager->get_handle(); + + edgelist.set(thread_handle, device_buffer_size, use_weight, use_edge_id, use_edge_type); + }); + } + + // Wait for CPU threads to complete + std::for_each(running_threads.begin(), running_threads.end(), [](auto& t) { t.join(); }); + running_threads.resize(0); + instance_manager->reset_threads(); + + // Load SG edge list + auto [d_src_v, d_dst_v, d_weights_v, d_vertices_v, is_symmetric] = + input_usecase.template construct_edgelist( + handle, multithreaded_usecase.test_weighted, false, false); + + auto h_src_v = cugraph::test::to_host(handle, d_src_v); + auto h_dst_v = cugraph::test::to_host(handle, d_dst_v); + auto h_weights_v = cugraph::test::to_host(handle, d_weights_v); + auto unique_vertices = cugraph::test::to_host(handle, d_vertices_v); + + // Load edgelist from different threads. We'll use more threads than GPUs here + for (int i = 0; i < num_threads; ++i) { + running_threads.emplace_back([&instance_manager, + thread_buffer_size, + &edgelist, + &h_src_v, + &h_dst_v, + &h_weights_v, + starting_edge_offset = g_node_rank * num_threads + i, + stride = g_num_nodes * num_threads]() { + auto thread_handle = instance_manager->get_handle(); + cugraph::mtmg::per_thread_edgelist_t + per_thread_edgelist(edgelist.get(thread_handle), thread_buffer_size); + + for (size_t j = starting_edge_offset; j < h_src_v.size(); j += stride) { + per_thread_edgelist.append( + thread_handle, + h_src_v[j], + h_dst_v[j], + h_weights_v ? std::make_optional((*h_weights_v)[j]) : std::nullopt, + std::nullopt, + std::nullopt); + } + + per_thread_edgelist.flush(thread_handle); + }); + } + + // Wait for CPU threads to complete + std::for_each(running_threads.begin(), running_threads.end(), [](auto& t) { t.join(); }); + running_threads.resize(0); + instance_manager->reset_threads(); + + for (int i = 0; i < num_local_gpus; ++i) { + running_threads.emplace_back([&instance_manager, + &graph, + &edge_weights, + &edgelist, + &renumber_map, + &pageranks, + &h_src_v, // debugging + is_symmetric = is_symmetric, + renumber, + do_expensive_check]() { + auto thread_handle = instance_manager->get_handle(); + + if (thread_handle.get_thread_rank() > 0) return; + + std::optional, + edge_t>> + edge_ids{std::nullopt}; + std::optional, + int32_t>> + edge_types{std::nullopt}; + + edgelist.finalize_buffer(thread_handle); + edgelist.consolidate_and_shuffle(thread_handle, true); + + cugraph::mtmg:: + create_graph_from_edgelist( + thread_handle, + edgelist, + cugraph::graph_properties_t{is_symmetric, true}, + renumber, + graph, + edge_weights, + edge_ids, + edge_types, + renumber_map, + do_expensive_check); + }); + } + + // Wait for CPU threads to complete + std::for_each(running_threads.begin(), running_threads.end(), [](auto& t) { t.join(); }); + running_threads.resize(0); + instance_manager->reset_threads(); + + graph_view = graph.view(); + + for (int i = 0; i < num_threads; ++i) { + running_threads.emplace_back( + [&instance_manager, &graph_view, &edge_weights, &pageranks, alpha, epsilon]() { + auto thread_handle = instance_manager->get_handle(); + + if (thread_handle.get_thread_rank() > 0) return; + + auto [local_pageranks, metadata] = + cugraph::pagerank( + thread_handle.raft_handle(), + graph_view.get(thread_handle), + edge_weights ? std::make_optional(edge_weights->get(thread_handle).view()) + : std::nullopt, + std::nullopt, + std::nullopt, + std::nullopt, + alpha, + epsilon, + 500, + true); + + pageranks.set(thread_handle, std::move(local_pageranks)); + }); + } + + // Wait for CPU threads to complete + std::for_each(running_threads.begin(), running_threads.end(), [](auto& t) { t.join(); }); + running_threads.resize(0); + instance_manager->reset_threads(); + + std::vector, std::vector>> computed_pageranks_v; + std::mutex computed_pageranks_lock{}; + + auto pageranks_view = pageranks.view(); + auto renumber_map_view = renumber_map ? std::make_optional(renumber_map->view()) : std::nullopt; + + // Load computed_pageranks from different threads. + for (int i = 0; i < num_local_gpus; ++i) { + running_threads.emplace_back([&instance_manager, + &graph_view, + &renumber_map_view, + &pageranks_view, + &computed_pageranks_lock, + &computed_pageranks_v, + &h_src_v, + &h_dst_v, + &h_weights_v, + &unique_vertices, + i, + num_threads]() { + auto thread_handle = instance_manager->get_handle(); + + auto number_of_vertices = unique_vertices->size(); + + std::vector my_vertex_list; + my_vertex_list.reserve((number_of_vertices + num_threads - 1) / num_threads); + + for (size_t j = i; j < number_of_vertices; j += num_threads) { + my_vertex_list.push_back((*unique_vertices)[j]); + } + + rmm::device_uvector d_my_vertex_list(my_vertex_list.size(), + thread_handle.raft_handle().get_stream()); + raft::update_device(d_my_vertex_list.data(), + my_vertex_list.data(), + my_vertex_list.size(), + thread_handle.raft_handle().get_stream()); + + auto d_my_pageranks = pageranks_view.gather( + thread_handle, + raft::device_span{d_my_vertex_list.data(), d_my_vertex_list.size()}, + graph_view, + renumber_map_view); + + std::vector my_pageranks(d_my_pageranks.size()); + raft::update_host(my_pageranks.data(), + d_my_pageranks.data(), + d_my_pageranks.size(), + thread_handle.raft_handle().get_stream()); + + { + std::lock_guard lock(computed_pageranks_lock); + computed_pageranks_v.push_back( + std::make_tuple(std::move(my_vertex_list), std::move(my_pageranks))); + } + }); + } + + // Wait for CPU threads to complete + std::for_each(running_threads.begin(), running_threads.end(), [](auto& t) { t.join(); }); + running_threads.resize(0); + instance_manager->reset_threads(); + + if (multithreaded_usecase.check_correctness) { + // Want to compare the results in computed_pageranks_v with SG results + cugraph::graph_t sg_graph(handle); + std::optional< + cugraph::edge_property_t, weight_t>> + sg_edge_weights{std::nullopt}; + std::optional> sg_renumber_map{std::nullopt}; + + std::tie(sg_graph, sg_edge_weights, std::ignore, std::ignore, sg_renumber_map) = cugraph:: + create_graph_from_edgelist( + handle, + std::nullopt, + std::move(d_src_v), + std::move(d_dst_v), + std::move(d_weights_v), + std::nullopt, + std::nullopt, + cugraph::graph_properties_t{is_symmetric, true}, + true); + + auto [sg_pageranks, meta] = cugraph::pagerank( + handle, + sg_graph.view(), + sg_edge_weights ? std::make_optional(sg_edge_weights->view()) : std::nullopt, + std::nullopt, + std::nullopt, + std::nullopt, + alpha, + epsilon); + + auto h_sg_pageranks = cugraph::test::to_host(handle, sg_pageranks); + auto h_sg_renumber_map = cugraph::test::to_host(handle, sg_renumber_map); + auto compare_functor = cugraph::test::nearly_equal{ + weight_t{1e-3}, + weight_t{(weight_t{1} / static_cast(h_sg_pageranks.size())) * weight_t{1e-3}}}; + + std::for_each(computed_pageranks_v.begin(), + computed_pageranks_v.end(), + [h_sg_pageranks, compare_functor, h_sg_renumber_map](auto t1) { + std::for_each( + thrust::make_zip_iterator(std::get<0>(t1).begin(), std::get<1>(t1).begin()), + thrust::make_zip_iterator(std::get<0>(t1).end(), std::get<1>(t1).end()), + [h_sg_pageranks, compare_functor, h_sg_renumber_map](auto t2) { + vertex_t v = thrust::get<0>(t2); + weight_t pr = thrust::get<1>(t2); + + auto pos = + std::find(h_sg_renumber_map->begin(), h_sg_renumber_map->end(), v); + auto offset = std::distance(h_sg_renumber_map->begin(), pos); + + if (pos == h_sg_renumber_map->end()) { + ASSERT_TRUE(compare_functor(pr, weight_t{0})) + << "vertex " << v << ", SG result = " << h_sg_pageranks[offset] + << ", mtmg result = " << pr << ", not in renumber map"; + } else { + ASSERT_TRUE(compare_functor(pr, h_sg_pageranks[offset])) + << "vertex " << v << ", SG result = " << h_sg_pageranks[offset] + << ", mtmg result = " << pr + << ", renumber map = " << (*h_sg_renumber_map)[offset]; + } + }); + }); + } + } +}; + +using Tests_Multithreaded_File = Tests_Multithreaded; +using Tests_Multithreaded_Rmat = Tests_Multithreaded; + +// FIXME: add tests for type combinations +TEST_P(Tests_Multithreaded_File, CheckInt32Int32FloatFloat) +{ + run_current_test( + override_File_Usecase_with_cmd_line_arguments(GetParam()), get_gpu_list()); +} + +TEST_P(Tests_Multithreaded_Rmat, CheckInt32Int32FloatFloat) +{ + run_current_test( + override_Rmat_Usecase_with_cmd_line_arguments(GetParam()), get_gpu_list()); +} + +INSTANTIATE_TEST_SUITE_P(file_test, + Tests_Multithreaded_File, + ::testing::Combine( + // enable correctness checks + ::testing::Values(Multithreaded_Usecase{false, true}, + Multithreaded_Usecase{true, true}), + ::testing::Values(cugraph::test::File_Usecase("karate.csv"), + cugraph::test::File_Usecase("dolphins.csv")))); + +INSTANTIATE_TEST_SUITE_P( + rmat_small_test, + Tests_Multithreaded_Rmat, + ::testing::Combine( + // enable correctness checks + ::testing::Values(Multithreaded_Usecase{false, true}, Multithreaded_Usecase{true, true}), + ::testing::Values(cugraph::test::Rmat_Usecase(10, 16, 0.57, 0.19, 0.19, 0, false, false)))); + +INSTANTIATE_TEST_SUITE_P( + file_benchmark_test, /* note that the test filename can be overridden in benchmarking (with + --gtest_filter to select only the file_benchmark_test with a specific + vertex & edge type combination) by command line arguments and do not + include more than one File_Usecase that differ only in filename + (to avoid running same benchmarks more than once) */ + Tests_Multithreaded_File, + ::testing::Combine( + // disable correctness checks + ::testing::Values(Multithreaded_Usecase{false, false}, Multithreaded_Usecase{true, false}), + ::testing::Values(cugraph::test::File_Usecase("test/datasets/karate.mtx")))); + +INSTANTIATE_TEST_SUITE_P( + rmat_benchmark_test, /* note that scale & edge factor can be overridden in benchmarking (with + --gtest_filter to select only the rmat_benchmark_test with a specific + vertex & edge type combination) by command line arguments and do not + include more than one Rmat_Usecase that differ only in scale or edge + factor (to avoid running same benchmarks more than once) */ + Tests_Multithreaded_Rmat, + ::testing::Combine( + // disable correctness checks for large graphs + ::testing::Values(Multithreaded_Usecase{false, false}, Multithreaded_Usecase{true, false}), + ::testing::Values(cugraph::test::Rmat_Usecase(10, 16, 0.57, 0.19, 0.19, 0, false, false)))); + +// +// Need to customize the test configuration to support multi-node comms not using MPI +// +int main(int argc, char** argv) +{ + cugraph::test::initialize_mpi(argc, argv); + auto comm_rank = cugraph::test::query_mpi_comm_world_rank(); + auto comm_size = cugraph::test::query_mpi_comm_world_size(); + + ::testing::InitGoogleTest(&argc, argv); + auto const cmd_opts = parse_test_options(argc, argv); + auto const rmm_mode = cmd_opts["rmm_mode"].as(); + auto resource = cugraph::test::create_memory_resource(rmm_mode); + rmm::mr::set_current_device_resource(resource.get()); + cugraph::test::g_perf = cmd_opts["perf"].as(); + cugraph::test::g_rmat_scale = (cmd_opts.count("rmat_scale") > 0) + ? std::make_optional(cmd_opts["rmat_scale"].as()) + : std::nullopt; + cugraph::test::g_rmat_edge_factor = + (cmd_opts.count("rmat_edge_factor") > 0) + ? std::make_optional(cmd_opts["rmat_edge_factor"].as()) + : std::nullopt; + cugraph::test::g_test_file_name = + (cmd_opts.count("test_file_name") > 0) + ? std::make_optional(cmd_opts["test_file_name"].as()) + : std::nullopt; + + // + // Set global values for the test. Need to know the rank of this process, + // the comm size, number of GPUs per node, and the NCCL Id for rank 0. + // + int num_gpus_this_node{-1}; + std::vector num_gpus_per_node{}; + + g_node_rank = comm_rank; + g_num_nodes = comm_size; + + num_gpus_per_node.resize(comm_size); + + RAFT_CUDA_TRY(cudaGetDeviceCount(&num_gpus_this_node)); + RAFT_MPI_TRY(MPI_Allgather( + &num_gpus_this_node, 1, MPI_INT, num_gpus_per_node.data(), 1, MPI_INT, MPI_COMM_WORLD)); + + int node_rank{0}; + + for (int i = 0; i < comm_size; ++i) { + for (int j = 0; j < num_gpus_per_node[i]; ++j) { + if (i != comm_rank) + g_resource_manager.register_remote_gpu(node_rank++); + else + g_resource_manager.register_local_gpu(node_rank++, rmm::cuda_device_id{j}); + } + } + + auto result = RUN_ALL_TESTS(); + cugraph::test::finalize_mpi(); + return result; +} From a3cda5de57e7c5849c85befd5b27ccea21b10255 Mon Sep 17 00:00:00 2001 From: Alex Barghi <105237337+alexbarghi-nv@users.noreply.github.com> Date: Mon, 6 Nov 2023 21:28:06 -0500 Subject: [PATCH 073/111] [BUG] Restore the original default order of CSR, which does not reverse edges in cuGraph-PyG (#3980) OGB and many other datasets express the edge index in CSR order, but in release 23.10, the default order was changed to CSC, which broke downstream workflows. This PR restores the original default, which should be acceptable for the most common datasets, and familiar to most PyG users. Closes #3969 Authors: - Alex Barghi (https://github.com/alexbarghi-nv) Approvers: - Tingyu Wang (https://github.com/tingyu66) - Rick Ratzel (https://github.com/rlratzel) URL: https://github.com/rapidsai/cugraph/pull/3980 --- python/cugraph-pyg/cugraph_pyg/data/cugraph_store.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/python/cugraph-pyg/cugraph_pyg/data/cugraph_store.py b/python/cugraph-pyg/cugraph_pyg/data/cugraph_store.py index 6192cd621d5..d1b24543956 100644 --- a/python/cugraph-pyg/cugraph_pyg/data/cugraph_store.py +++ b/python/cugraph-pyg/cugraph_pyg/data/cugraph_store.py @@ -214,7 +214,7 @@ def __init__( num_nodes_dict: Dict[str, int], *, multi_gpu: bool = False, - order: str = "CSC", + order: str = "CSR", ): """ Constructs a new CuGraphStore from the provided @@ -260,11 +260,11 @@ def __init__( Whether the store should be backed by a multi-GPU graph. Requires dask to have been set up. - order: str (Optional ["CSR", "CSC"], default = CSC) - The order to use for sampling. Should nearly always be CSC - unless there is a specific expectation of "reverse" sampling. - It is also not uncommon to use CSR order for correctness - testing, which some cuGraph-PyG tests do. + order: str (Optional ["CSR", "CSC"], default = CSR) + The order to use for sampling. CSR corresponds to the + standard OGB dataset order that is usually used in PyG. + CSC order constructs the same graph as CSR, but with + edges in the opposite direction. """ if None in G: From ac5b981dabc0ca2645713164cc74367b4366cd8b Mon Sep 17 00:00:00 2001 From: Chuck Hastings <45364586+ChuckHastings@users.noreply.github.com> Date: Tue, 7 Nov 2023 15:17:16 -0500 Subject: [PATCH 074/111] Fix eigenvector testing and HITS testing discrepancies (#3979) Address a couple of bugs discovered in nx-cugraph testing. HITS should raise an exception if it fails to converge. Eigenvector computation was not quite right, nx-cugraph testing discovered an edge condition where it was obvious. Closes #3971 Closes #3972 Authors: - Chuck Hastings (https://github.com/ChuckHastings) Approvers: - Naim (https://github.com/naimnv) - Seunghwa Kang (https://github.com/seunghwak) - Rick Ratzel (https://github.com/rlratzel) URL: https://github.com/rapidsai/cugraph/pull/3979 --- .../eigenvector_centrality_impl.cuh | 10 +++++++++- cpp/src/link_analysis/hits_impl.cuh | 17 ++++++++++------- cpp/tests/c_api/eigenvector_centrality_test.c | 19 +++++++++++++++++++ .../eigenvector_centrality_test.cpp | 1 - cpp/tests/link_analysis/hits_test.cpp | 4 ++-- .../cugraph/tests/link_analysis/test_hits.py | 6 +++++- 6 files changed, 45 insertions(+), 12 deletions(-) diff --git a/cpp/src/centrality/eigenvector_centrality_impl.cuh b/cpp/src/centrality/eigenvector_centrality_impl.cuh index 291abf18455..8d1bea4004d 100644 --- a/cpp/src/centrality/eigenvector_centrality_impl.cuh +++ b/cpp/src/centrality/eigenvector_centrality_impl.cuh @@ -96,7 +96,8 @@ rmm::device_uvector eigenvector_centrality( centralities.end(), old_centralities.data()); - update_edge_src_property(handle, pull_graph_view, centralities.begin(), edge_src_centralities); + update_edge_src_property( + handle, pull_graph_view, old_centralities.begin(), edge_src_centralities); if (edge_weight_view) { per_v_transform_reduce_incoming_e( @@ -122,6 +123,13 @@ rmm::device_uvector eigenvector_centrality( centralities.begin()); } + thrust::transform(handle.get_thrust_policy(), + centralities.begin(), + centralities.end(), + old_centralities.begin(), + centralities.begin(), + thrust::plus()); + // Normalize the centralities auto hypotenuse = sqrt(transform_reduce_v( handle, diff --git a/cpp/src/link_analysis/hits_impl.cuh b/cpp/src/link_analysis/hits_impl.cuh index 9badb041218..674046745b1 100644 --- a/cpp/src/link_analysis/hits_impl.cuh +++ b/cpp/src/link_analysis/hits_impl.cuh @@ -112,7 +112,8 @@ std::tuple hits(raft::handle_t const& handle, prev_hubs + graph_view.local_vertex_partition_range_size(), result_t{1.0} / num_vertices); } - for (size_t iter = 0; iter < max_iterations; ++iter) { + size_t iter{0}; + while (true) { // Update current destination authorities property per_v_transform_reduce_incoming_e( handle, @@ -162,17 +163,19 @@ std::tuple hits(raft::handle_t const& handle, thrust::make_zip_iterator(thrust::make_tuple(curr_hubs, prev_hubs)), [] __device__(auto, auto val) { return std::abs(thrust::get<0>(val) - thrust::get<1>(val)); }, result_t{0}); - if (diff_sum < epsilon) { - final_iteration_count = iter; - std::swap(prev_hubs, curr_hubs); - break; - } update_edge_src_property(handle, graph_view, curr_hubs, prev_src_hubs); // Swap pointers for the next iteration // After this swap call, prev_hubs has the latest value of hubs std::swap(prev_hubs, curr_hubs); + iter++; + + if (diff_sum < epsilon) { + break; + } else if (iter >= max_iterations) { + CUGRAPH_FAIL("HITS failed to converge."); + } } if (normalize) { @@ -188,7 +191,7 @@ std::tuple hits(raft::handle_t const& handle, hubs); } - return std::make_tuple(diff_sum, final_iteration_count); + return std::make_tuple(diff_sum, iter); } } // namespace detail diff --git a/cpp/tests/c_api/eigenvector_centrality_test.c b/cpp/tests/c_api/eigenvector_centrality_test.c index 9fd2d2bee6f..8bc5971a70c 100644 --- a/cpp/tests/c_api/eigenvector_centrality_test.c +++ b/cpp/tests/c_api/eigenvector_centrality_test.c @@ -109,11 +109,30 @@ int test_eigenvector_centrality() h_src, h_dst, h_wgt, h_result, num_vertices, num_edges, TRUE, epsilon, max_iterations); } +int test_eigenvector_centrality_3971() +{ + size_t num_edges = 4; + size_t num_vertices = 3; + + vertex_t h_src[] = {0, 1, 1, 2}; + vertex_t h_dst[] = {1, 0, 2, 1}; + weight_t h_wgt[] = {1.0f, 1.0f, 1.0f, 1.0f}; + weight_t h_result[] = {0.5, 0.707107, 0.5}; + + double epsilon = 1e-6; + size_t max_iterations = 1000; + + // Eigenvector centrality wants store_transposed = TRUE + return generic_eigenvector_centrality_test( + h_src, h_dst, h_wgt, h_result, num_vertices, num_edges, TRUE, epsilon, max_iterations); +} + /******************************************************************************/ int main(int argc, char** argv) { int result = 0; result |= RUN_TEST(test_eigenvector_centrality); + result |= RUN_TEST(test_eigenvector_centrality_3971); return result; } diff --git a/cpp/tests/centrality/eigenvector_centrality_test.cpp b/cpp/tests/centrality/eigenvector_centrality_test.cpp index 7cafcfbde85..6c3bd510abd 100644 --- a/cpp/tests/centrality/eigenvector_centrality_test.cpp +++ b/cpp/tests/centrality/eigenvector_centrality_test.cpp @@ -60,7 +60,6 @@ void eigenvector_centrality_reference(vertex_t const* src, size_t iter{0}; while (true) { std::copy(tmp_centralities.begin(), tmp_centralities.end(), old_centralities.begin()); - std::fill(tmp_centralities.begin(), tmp_centralities.end(), double{0}); for (size_t e = 0; e < num_edges; ++e) { auto w = weights ? (*weights)[e] : weight_t{1.0}; diff --git a/cpp/tests/link_analysis/hits_test.cpp b/cpp/tests/link_analysis/hits_test.cpp index 44fa619b503..d0e77769034 100644 --- a/cpp/tests/link_analysis/hits_test.cpp +++ b/cpp/tests/link_analysis/hits_test.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2022, NVIDIA CORPORATION. + * Copyright (c) 2020-2023, NVIDIA CORPORATION. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -176,7 +176,7 @@ class Tests_Hits : public ::testing::TestWithParam d_hubs(graph_view.local_vertex_partition_range_size(), handle.get_stream()); diff --git a/python/cugraph/cugraph/tests/link_analysis/test_hits.py b/python/cugraph/cugraph/tests/link_analysis/test_hits.py index 1c5a135e944..fcfd8cc5318 100644 --- a/python/cugraph/cugraph/tests/link_analysis/test_hits.py +++ b/python/cugraph/cugraph/tests/link_analysis/test_hits.py @@ -38,7 +38,11 @@ def setup_function(): fixture_params = gen_fixture_params_product( (datasets, "graph_file"), ([50], "max_iter"), - ([1.0e-6], "tol"), + # FIXME: Changed this from 1.0e-6 to 1.0e-5. NX defaults to + # FLOAT64 computation, cuGraph C++ defaults to whatever the edge weight + # is, cugraph python defaults that to FLOAT32. Does not converge at + # 1e-6 for larger graphs and FLOAT32. + ([1.0e-5], "tol"), ) From 663d95fab0a9e6312f93d1190d6dfd590b081cd5 Mon Sep 17 00:00:00 2001 From: Vibhu Jawa Date: Tue, 7 Nov 2023 16:44:18 -0800 Subject: [PATCH 075/111] [REVIEW]Optimize cugraph-DGL csc codepath (#3977) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR optimizes `cugraph-DGL` csc codepath and adds an end to end benchmark using cugraph-dgl. ```python3 sampled_dir = "/raid/vjawa/nov_1_bulksampling_benchmarks/ogbn_papers100M[2]_b512_f[10, 10, 10]" dataset = HomogenousBulkSamplerDataset(meta_json_d["total_num_nodes"], edge_dir=edge_dir, sparse_format="csc", return_type="cugraph_dgl.nn.SparseGraph") dataset.set_input_files(input_directory=sampled_dir+"/samples") dataloader = torch.utils.data.DataLoader(dataset, collate_fn=lambda x:x, shuffle=False, num_workers=0, batch_size=None) def run(dataloader): for input_nodes, output_nodes, blocks in dataloader: pass ``` ```python3 %%timeit run(dataloader) ``` With PR : ```python3 2.48 s ± 14 ms per loop (mean ± std. dev. of 7 runs, 1 loop each) ``` MAIN: ```python3 %%timeit 9.52 s ± 151 ms per loop (mean ± std. dev. of 7 runs, 1 loop each) ``` ### E2E Benchmarks: ``` python3 cugraph_dgl_benchmark.py ``` PR: ```python3 ... Epoch time = 70.41 seconds Time to create MFG = 3.37 seconds Time analysis for fanout = [10, 10, 10], batch_size = 512 mfg_creation_time_per_epoch = 3.37 seconds feature_time_per_epoch = 44.09 seconds m_fwd_time_per_epoch = 7.31 seconds m_bkwd_time_per_epoch = 15.60 seconds ``` MAIN: ```python3 .... Epoch time = 84.72 seconds Time to create MFG = 10.79 seconds Time analysis for fanout = [10, 10, 10], batch_size = 512 mfg_creation_time_per_epoch = 10.79 seconds feature_time_per_epoch = 47.09 seconds m_fwd_time_per_epoch = 8.24 seconds m_bkwd_time_per_epoch = 18.58 seconds ``` Authors: - Vibhu Jawa (https://github.com/VibhuJawa) Approvers: - Alex Barghi (https://github.com/alexbarghi-nv) - Tingyu Wang (https://github.com/tingyu66) URL: https://github.com/rapidsai/cugraph/pull/3977 --- .../scale-benchmarks/cugraph_dgl_benchmark.py | 152 ++++++++++++++++++ .../cugraph-dgl/scale-benchmarks/model.py | 17 +- .../bulk_sampling/cugraph_bulk_sampling.py | 55 +++++-- .../dataloading/utils/sampling_helpers.py | 71 ++++---- 4 files changed, 244 insertions(+), 51 deletions(-) create mode 100644 benchmarks/cugraph-dgl/scale-benchmarks/cugraph_dgl_benchmark.py diff --git a/benchmarks/cugraph-dgl/scale-benchmarks/cugraph_dgl_benchmark.py b/benchmarks/cugraph-dgl/scale-benchmarks/cugraph_dgl_benchmark.py new file mode 100644 index 00000000000..85f43b97b90 --- /dev/null +++ b/benchmarks/cugraph-dgl/scale-benchmarks/cugraph_dgl_benchmark.py @@ -0,0 +1,152 @@ +# Copyright (c) 2018-2023, NVIDIA CORPORATION. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import os + +os.environ["LIBCUDF_CUFILE_POLICY"] = "KVIKIO" +os.environ["KVIKIO_NTHREADS"] = "64" +os.environ["RAPIDS_NO_INITIALIZE"] = "1" +import json +import pandas as pd +import os +import time +from rmm.allocators.torch import rmm_torch_allocator +import rmm +import torch +from cugraph_dgl.dataloading import HomogenousBulkSamplerDataset +from model import run_1_epoch +from argparse import ArgumentParser +from load_graph_feats import load_node_labels, load_node_features + + +def create_dataloader(sampled_dir, total_num_nodes, sparse_format, return_type): + print("Creating dataloader", flush=True) + st = time.time() + dataset = HomogenousBulkSamplerDataset( + total_num_nodes, + edge_dir="in", + sparse_format=sparse_format, + return_type=return_type, + ) + + dataset.set_input_files(sampled_dir) + dataloader = torch.utils.data.DataLoader( + dataset, collate_fn=lambda x: x, shuffle=False, num_workers=0, batch_size=None + ) + et = time.time() + print(f"Time to create dataloader = {et - st:.2f} seconds", flush=True) + return dataloader + + +def setup_common_pool(): + rmm.reinitialize(initial_pool_size=5e9, pool_allocator=True) + torch.cuda.memory.change_current_allocator(rmm_torch_allocator) + + +def main(args): + print( + f"Running cugraph-dgl dataloading benchmark with the following parameters:\n" + f"Dataset path = {args.dataset_path}\n" + f"Sampling path = {args.sampling_path}\n" + ) + with open(os.path.join(args.dataset_path, "meta.json"), "r") as f: + input_meta = json.load(f) + + sampled_dirs = [ + os.path.join(args.sampling_path, f) for f in os.listdir(args.sampling_path) + ] + + time_ls = [] + for sampled_dir in sampled_dirs: + with open(os.path.join(sampled_dir, "output_meta.json"), "r") as f: + sampled_meta_d = json.load(f) + + replication_factor = sampled_meta_d["replication_factor"] + feat_load_st = time.time() + label_data = load_node_labels( + args.dataset_path, replication_factor, input_meta + )["paper"]["y"] + feat_data = feat_data = load_node_features( + args.dataset_path, replication_factor, node_type="paper" + ) + print( + f"Feature and label data loading took = {time.time()-feat_load_st}", + flush=True, + ) + + r_time_ls = e2e_benchmark(sampled_dir, feat_data, label_data, sampled_meta_d) + [x.update({"replication_factor": replication_factor}) for x in r_time_ls] + [x.update({"num_edges": sampled_meta_d["total_num_edges"]}) for x in r_time_ls] + time_ls.extend(r_time_ls) + + print( + f"Benchmark completed for replication factor = {replication_factor}\n{'=' * 30}", + flush=True, + ) + + df = pd.DataFrame(time_ls) + df.to_csv("cugraph_dgl_e2e_benchmark.csv", index=False) + print(f"Benchmark completed for all replication factors\n{'=' * 30}", flush=True) + + +def e2e_benchmark( + sampled_dir: str, feat: torch.Tensor, y: torch.Tensor, sampled_meta_d: dict +): + """ + Run the e2e_benchmark + Args: + sampled_dir: directory containing the sampled graph + feat: node features + y: node labels + sampled_meta_d: dictionary containing the sampled graph metadata + """ + time_ls = [] + + # TODO: Make this a parameter in bulk sampling script + sampled_meta_d["sparse_format"] = "csc" + sampled_dir = os.path.join(sampled_dir, "samples") + dataloader = create_dataloader( + sampled_dir, + sampled_meta_d["total_num_nodes"], + sampled_meta_d["sparse_format"], + return_type="cugraph_dgl.nn.SparseGraph", + ) + time_d = run_1_epoch( + dataloader, + feat, + y, + fanout=sampled_meta_d["fanout"], + batch_size=sampled_meta_d["batch_size"], + model_backend="cugraph_dgl", + ) + time_ls.append(time_d) + print("=" * 30) + return time_ls + + +def parse_arguments(): + parser = ArgumentParser() + parser.add_argument( + "--dataset_path", type=str, default="/raid/vjawa/ogbn_papers100M/" + ) + parser.add_argument( + "--sampling_path", + type=str, + default="/raid/vjawa/nov_1_bulksampling_benchmarks/", + ) + return parser.parse_args() + + +if __name__ == "__main__": + setup_common_pool() + arguments = parse_arguments() + main(arguments) diff --git a/benchmarks/cugraph-dgl/scale-benchmarks/model.py b/benchmarks/cugraph-dgl/scale-benchmarks/model.py index 08ae0e8b1ee..9a9dfe58f96 100644 --- a/benchmarks/cugraph-dgl/scale-benchmarks/model.py +++ b/benchmarks/cugraph-dgl/scale-benchmarks/model.py @@ -57,11 +57,11 @@ def create_model(feat_size, num_classes, num_layers, model_backend="dgl"): def train_model(model, dataloader, opt, feat, y): - times = {key: 0 for key in ["mfg_creation", "feature", "m_fwd", "m_bkwd"]} + times_d = {key: 0 for key in ["mfg_creation", "feature", "m_fwd", "m_bkwd"]} epoch_st = time.time() mfg_st = time.time() for input_nodes, output_nodes, blocks in dataloader: - times["mfg_creation"] += time.time() - mfg_st + times_d["mfg_creation"] += time.time() - mfg_st if feat is not None: fst = time.time() input_nodes = input_nodes.to("cpu") @@ -71,23 +71,24 @@ def train_model(model, dataloader, opt, feat, y): output_nodes = output_nodes["paper"] output_nodes = output_nodes.to(y.device) y_batch = y[output_nodes].to("cuda") - times["feature"] += time.time() - fst + times_d["feature"] += time.time() - fst m_fwd_st = time.time() y_hat = model(blocks, input_feat) - times["m_fwd"] += time.time() - m_fwd_st + times_d["m_fwd"] += time.time() - m_fwd_st m_bkwd_st = time.time() loss = F.cross_entropy(y_hat, y_batch) opt.zero_grad() loss.backward() opt.step() - times["m_bkwd"] += time.time() - m_bkwd_st + times_d["m_bkwd"] += time.time() - m_bkwd_st mfg_st = time.time() print(f"Epoch time = {time.time() - epoch_st:.2f} seconds") + print(f"Time to create MFG = {times_d['mfg_creation']:.2f} seconds") - return times + return times_d def analyze_time(dataloader, times, epoch_time, fanout, batch_size): @@ -119,6 +120,10 @@ def run_1_epoch(dataloader, feat, y, fanout, batch_size, model_backend): else: model = None opt = None + + # Warmup RUN + times = train_model(model, dataloader, opt, feat, y) + epoch_st = time.time() times = train_model(model, dataloader, opt, feat, y) epoch_time = time.time() - epoch_st diff --git a/benchmarks/cugraph/standalone/bulk_sampling/cugraph_bulk_sampling.py b/benchmarks/cugraph/standalone/bulk_sampling/cugraph_bulk_sampling.py index a8c0658767d..1ca5d6db637 100644 --- a/benchmarks/cugraph/standalone/bulk_sampling/cugraph_bulk_sampling.py +++ b/benchmarks/cugraph/standalone/bulk_sampling/cugraph_bulk_sampling.py @@ -22,7 +22,6 @@ get_allocation_counts_dask_lazy, sizeof_fmt, get_peak_output_ratio_across_workers, - restart_client, start_dask_client, stop_dask_client, enable_spilling, @@ -187,10 +186,10 @@ def sample_graph( output_path, seed=42, batch_size=500, - seeds_per_call=200000, + seeds_per_call=400000, batches_per_partition=100, fanout=[5, 5, 5], - persist=False, + sampling_kwargs={}, ): cupy.random.seed(seed) @@ -204,6 +203,7 @@ def sample_graph( seeds_per_call=seeds_per_call, batches_per_partition=batches_per_partition, log_level=logging.INFO, + **sampling_kwargs, ) n_workers = len(default_client().scheduler_info()["workers"]) @@ -469,6 +469,7 @@ def benchmark_cugraph_bulk_sampling( batch_size, seeds_per_call, fanout, + sampling_target_framework, reverse_edges=True, dataset_dir=".", replication_factor=1, @@ -564,17 +565,39 @@ def benchmark_cugraph_bulk_sampling( output_sample_path = os.path.join(output_subdir, "samples") os.makedirs(output_sample_path) - batches_per_partition = 200_000 // batch_size + if sampling_target_framework == "cugraph_dgl_csr": + sampling_kwargs = { + "deduplicate_sources": True, + "prior_sources_behavior": "carryover", + "renumber": True, + "compression": "CSR", + "compress_per_hop": True, + "use_legacy_names": False, + "include_hop_column": False, + } + else: + # FIXME: Update these arguments when CSC mode is fixed in cuGraph-PyG (release 24.02) + sampling_kwargs = { + "deduplicate_sources": True, + "prior_sources_behavior": "exclude", + "renumber": True, + "compression": "COO", + "compress_per_hop": False, + "use_legacy_names": False, + "include_hop_column": True, + } + + batches_per_partition = 400_000 // batch_size execution_time, allocation_counts = sample_graph( - G, - dask_label_df, - output_sample_path, + G=G, + label_df=dask_label_df, + output_path=output_sample_path, seed=seed, batch_size=batch_size, seeds_per_call=seeds_per_call, batches_per_partition=batches_per_partition, fanout=fanout, - persist=persist, + sampling_kwargs=sampling_kwargs, ) output_meta = { @@ -701,7 +724,13 @@ def get_args(): required=False, default=False, ) - + parser.add_argument( + "--sampling_target_framework", + type=str, + help="The target framework for sampling (i.e. cugraph_dgl_csr, cugraph_pyg_csc, ...)", + required=False, + default=None, + ) parser.add_argument( "--dask_worker_devices", type=str, @@ -738,6 +767,12 @@ def get_args(): logging.basicConfig() args = get_args() + if args.sampling_target_framework not in ["cugraph_dgl_csr", None]: + raise ValueError( + "sampling_target_framework must be one of cugraph_dgl_csr or None", + "Other frameworks are not supported at this time.", + ) + fanouts = [ [int(f) for f in fanout.split("_")] for fanout in args.fanouts.split(",") ] @@ -785,6 +820,7 @@ def get_args(): batch_size=batch_size, seeds_per_call=seeds_per_call, fanout=fanout, + sampling_target_framework=args.sampling_target_framework, dataset_dir=args.dataset_root, reverse_edges=args.reverse_edges, replication_factor=replication_factor, @@ -809,7 +845,6 @@ def get_args(): warnings.warn("An Exception Occurred!") print(e) traceback.print_exc() - restart_client(client) sleep(10) stats_df = pd.DataFrame( diff --git a/python/cugraph-dgl/cugraph_dgl/dataloading/utils/sampling_helpers.py b/python/cugraph-dgl/cugraph_dgl/dataloading/utils/sampling_helpers.py index a4f64668348..f674bece8be 100644 --- a/python/cugraph-dgl/cugraph_dgl/dataloading/utils/sampling_helpers.py +++ b/python/cugraph-dgl/cugraph_dgl/dataloading/utils/sampling_helpers.py @@ -14,7 +14,6 @@ from typing import List, Tuple, Dict, Optional from collections import defaultdict import cudf -import cupy from cugraph.utilities.utils import import_optional from cugraph_dgl.nn import SparseGraph @@ -444,53 +443,58 @@ def _process_sampled_df_csc( destinations, respectively. """ # dropna - major_offsets = df.major_offsets.dropna().values - label_hop_offsets = df.label_hop_offsets.dropna().values - renumber_map_offsets = df.renumber_map_offsets.dropna().values - renumber_map = df.map.dropna().values - minors = df.minors.dropna().values + major_offsets = cast_to_tensor(df.major_offsets.dropna()) + label_hop_offsets = cast_to_tensor(df.label_hop_offsets.dropna()) + renumber_map_offsets = cast_to_tensor(df.renumber_map_offsets.dropna()) + renumber_map = cast_to_tensor(df.map.dropna()) + minors = cast_to_tensor(df.minors.dropna()) - n_batches = renumber_map_offsets.size - 1 - n_hops = int((label_hop_offsets.size - 1) / n_batches) + n_batches = len(renumber_map_offsets) - 1 + n_hops = int((len(label_hop_offsets) - 1) / n_batches) # make global offsets local - major_offsets -= major_offsets[0] - label_hop_offsets -= label_hop_offsets[0] - renumber_map_offsets -= renumber_map_offsets[0] + # Have to make a clone as pytorch does not allow + # in-place operations on tensors + major_offsets -= major_offsets[0].clone() + label_hop_offsets -= label_hop_offsets[0].clone() + renumber_map_offsets -= renumber_map_offsets[0].clone() # get the sizes of each adjacency matrix (for MFGs) mfg_sizes = (label_hop_offsets[1:] - label_hop_offsets[:-1]).reshape( (n_batches, n_hops) ) n_nodes = renumber_map_offsets[1:] - renumber_map_offsets[:-1] - mfg_sizes = cupy.hstack((mfg_sizes, n_nodes.reshape(n_batches, -1))) + mfg_sizes = torch.hstack((mfg_sizes, n_nodes.reshape(n_batches, -1))) if reverse_hop_id: - mfg_sizes = mfg_sizes[:, ::-1] + mfg_sizes = mfg_sizes.flip(1) tensors_dict = {} renumber_map_list = [] + # Note: minors and major_offsets from BulkSampler are of type int32 + # and int64 respectively. Since pylibcugraphops binding code doesn't + # support distinct node and edge index type, we simply casting both + # to int32 for now. + minors = minors.int() + major_offsets = major_offsets.int() + # Note: We transfer tensors to CPU here to avoid the overhead of + # transferring them in each iteration of the for loop below. + major_offsets_cpu = major_offsets.to("cpu").numpy() + label_hop_offsets_cpu = label_hop_offsets.to("cpu").numpy() + for batch_id in range(n_batches): batch_dict = {} - for hop_id in range(n_hops): hop_dict = {} idx = batch_id * n_hops + hop_id # idx in label_hop_offsets - major_offsets_start = label_hop_offsets[idx].item() - major_offsets_end = label_hop_offsets[idx + 1].item() - minors_start = major_offsets[major_offsets_start].item() - minors_end = major_offsets[major_offsets_end].item() - # Note: minors and major_offsets from BulkSampler are of type int32 - # and int64 respectively. Since pylibcugraphops binding code doesn't - # support distinct node and edge index type, we simply casting both - # to int32 for now. - hop_dict["minors"] = torch.as_tensor( - minors[minors_start:minors_end], device="cuda" - ).int() - hop_dict["major_offsets"] = torch.as_tensor( + major_offsets_start = label_hop_offsets_cpu[idx] + major_offsets_end = label_hop_offsets_cpu[idx + 1] + minors_start = major_offsets_cpu[major_offsets_start] + minors_end = major_offsets_cpu[major_offsets_end] + hop_dict["minors"] = minors[minors_start:minors_end] + hop_dict["major_offsets"] = ( major_offsets[major_offsets_start : major_offsets_end + 1] - - major_offsets[major_offsets_start], - device="cuda", - ).int() + - major_offsets[major_offsets_start] + ) if reverse_hop_id: batch_dict[n_hops - 1 - hop_id] = hop_dict else: @@ -499,12 +503,9 @@ def _process_sampled_df_csc( tensors_dict[batch_id] = batch_dict renumber_map_list.append( - torch.as_tensor( - renumber_map[ - renumber_map_offsets[batch_id] : renumber_map_offsets[batch_id + 1] - ], - device="cuda", - ) + renumber_map[ + renumber_map_offsets[batch_id] : renumber_map_offsets[batch_id + 1] + ], ) return tensors_dict, renumber_map_list, mfg_sizes.tolist() From 9b9ce55070c42811a26d4ec1bed675d26962c36f Mon Sep 17 00:00:00 2001 From: brandon-b-miller <53796099+brandon-b-miller@users.noreply.github.com> Date: Wed, 8 Nov 2023 10:16:16 -0600 Subject: [PATCH 076/111] CuGraph compatibility fixes (#3973) This PR contains two changes. - A change that defers to `numpy.asarray` to process array objects that `cugraph` consumes - An update from using `unique` to use `drop_duplicates` in `simpleGraph.py`, which always always returns a Series to preserve pandas compatibility. Authors: - https://github.com/brandon-b-miller Approvers: - Rick Ratzel (https://github.com/rlratzel) URL: https://github.com/rapidsai/cugraph/pull/3973 --- python/cugraph/cugraph/structure/graph_classes.py | 3 +-- .../structure/graph_implementation/simpleGraph.py | 10 +++++++--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/python/cugraph/cugraph/structure/graph_classes.py b/python/cugraph/cugraph/structure/graph_classes.py index 6f6c7e5a26c..03efcba0307 100644 --- a/python/cugraph/cugraph/structure/graph_classes.py +++ b/python/cugraph/cugraph/structure/graph_classes.py @@ -469,8 +469,7 @@ def from_numpy_array(self, np_array, nodes=None): nodes: array-like or None, optional (default=None) A list of column names, acting as labels for nodes """ - if not isinstance(np_array, np.ndarray): - raise TypeError("np_array input is not a Numpy array") + np_array = np.asarray(np_array) if len(np_array.shape) != 2: raise ValueError("np_array is not a 2D matrix") diff --git a/python/cugraph/cugraph/structure/graph_implementation/simpleGraph.py b/python/cugraph/cugraph/structure/graph_implementation/simpleGraph.py index 2b23d3a26b7..22d82eb1796 100644 --- a/python/cugraph/cugraph/structure/graph_implementation/simpleGraph.py +++ b/python/cugraph/cugraph/structure/graph_implementation/simpleGraph.py @@ -1286,9 +1286,13 @@ def nodes(self): else: return df[df.columns[0]] else: - return cudf.concat( - [df[simpleGraphImpl.srcCol], df[simpleGraphImpl.dstCol]] - ).unique() + return ( + cudf.concat( + [df[simpleGraphImpl.srcCol], df[simpleGraphImpl.dstCol]] + ) + .drop_duplicates() + .reset_index(drop=True) + ) if self.adjlist is not None: return cudf.Series(np.arange(0, self.number_of_nodes())) From 77bb2917729926b4d923e0f68a0fb76fabc90e8a Mon Sep 17 00:00:00 2001 From: Rick Ratzel <3039903+rlratzel@users.noreply.github.com> Date: Wed, 8 Nov 2023 10:51:33 -0600 Subject: [PATCH 077/111] Updates README file to include nx-cugraph user documentation, adds nx-cugraph to main README (#3984) * Updates the nx-cugraph README file to include nx-cugraph user documentation * Adds nx-cugraph and new News section to main README * Updates software strack diagram to remove DGL and PyG since they are not currently using PropertyGraph or cugraph-service Authors: - Rick Ratzel (https://github.com/rlratzel) - Ralph Liu (https://github.com/nv-rliu) Approvers: - Brad Rees (https://github.com/BradReesWork) --- README.md | 14 +++- img/Stack2.png | Bin 457436 -> 285038 bytes python/nx-cugraph/README.md | 135 +++++++++++++++++++++++++++++++----- 3 files changed, 132 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 560d1483242..8026e4feb64 100644 --- a/README.md +++ b/README.md @@ -35,8 +35,18 @@ ----- +## News -## Table of content +___NEW!___ _[nx-cugraph](./python/nx-cugraph/README.md)_, a NetworkX backend that provides GPU acceleration to NetworkX with zero code change. +``` +> pip install nx-cugraph-cu11 --extra-index-url https://pypi.nvidia.com +> export NETWORKX_AUTOMATIC_BACKENDS=cugraph +``` +That's it. NetworkX now leverages cuGraph for accelerated graph algorithms. + +----- + +## Table of contents - Installation - [Getting cuGraph Packages](./docs/cugraph/source/installation/getting_cugraph.md) - [Building from Source](./docs/cugraph/source/installation/source_build.md) @@ -52,6 +62,7 @@ - [External Data Types](./readme_pages/data_types.md) - [pylibcugraph](./readme_pages/pylibcugraph.md) - [libcugraph (C/C++/CUDA)](./readme_pages/libcugraph.md) + - [nx-cugraph](./python/nx-cugraph/README.md) - [cugraph-service](./readme_pages/cugraph_service.md) - [cugraph-dgl](./readme_pages/cugraph_dgl.md) - [cugraph-ops](./readme_pages/cugraph_ops.md) @@ -116,6 +127,7 @@ df_page.sort_values('pagerank', ascending=False).head(10) * ArangoDB - a free and open-source native multi-model database system - https://www.arangodb.com/ * CuPy - "NumPy/SciPy-compatible Array Library for GPU-accelerated Computing with Python" - https://cupy.dev/ * Memgraph - In-memory Graph database - https://memgraph.com/ +* NetworkX (via [nx-cugraph](./python/nx-cugraph/README.md) backend) - an extremely popular, free and open-source package for the creation, manipulation, and study of the structure, dynamics, and functions of complex networks - https://networkx.org/ * PyGraphistry - free and open-source GPU graph ETL, AI, and visualization, including native RAPIDS & cuGraph support - http://github.com/graphistry/pygraphistry * ScanPy - a scalable toolkit for analyzing single-cell gene expression data - https://scanpy.readthedocs.io/en/stable/ diff --git a/img/Stack2.png b/img/Stack2.png index 132e85c9d154edb02d319803182e79654fbd345e..97f1979c2d86c3c7992a08411d6f2287e4bd8d6a 100644 GIT binary patch literal 285038 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�l9ml{1Knk8~CU4NoKIO#~k~2g@kLQh#^X?JRV{O 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
bltU$KK#|+vX9PO^L{E#jJoOjn=No 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

wR>xtus?tC77Y=xVa*a>=I}Y6(^i^WgE8; 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

R!ghxNE-*WHaqZxm<`HR?IbOYU|qHpl{xyN5Y${ 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�VL+jXp@S|Q9K~w1`2Jo?zlZM)LOGi=ZlII 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�hTuVex`s-s|#V~h@0@#fiV|SR)@j;vd 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 literal 457436 zcmeEvcU;f=`#vd3$BZJC5YeKPb~2*0Xb%k{r9HH#9V#U4GE!(sTN5eK(B4ClG$rlz zyIwiY_xs5?pL0(6LpRf~D^xUz`<=WjisRy%K==sb!2dlYQuch+q%R+zu$Lh-?u zT+8*&W;I2aG8vLSU$}#9-Z^F)x~1it@4PcSe@iL8opnZU7_jitXI7qxs}$+o;?SRNti2h zdxt0DcK^aPFuj=7-_!E8phKDFl%b9HFI-_}vi&kLD{X(>Lvytt-7iyE+{f|jb^$XP zLx^2-|Hn9a&!OO7cJHo}N1JNPix!bh(DJ7&33RX-ZGZ0LxaSwHgu1IwQ3zenZb#W` zSJMPm{<_f6W@0qwI?KK9mtCtU<>kQmtE|87k!_lP!)rz8+FzFl)I>joY8?G_LA!?H za^N+_f?t;e-jQr%?rJ{t%N|LVxafxyr1}?s-F~ND&aY3S{Ocac0o?H>@twae*T=iO zmPk$hb#by|<#PVDd;B;2vXXQM$+G6vuS>tKH(Xxlk(d1T>*9``(Y%Bs`qw>@Ye?n? zuIv7~Ql^K*U}d)XmpziXR5M3Je*5~bE)uOZz9(@R5)8K zGKUIh9d~o6aMloHu0ftP1er^PbE)v(#^GFpJZth|jw_rkX*kCf&YH)a;|ga@8qTG{ zxm5VCH_@DI@~@X{I!K$7O=cZ;bF#^7A;_G9JZlItmkQ@n;q=`8cbjTXHu=|#_w-^h zC!5TgzMWH$|1E9wQ(Vs}$g`$z=gc(!wtCE^Lee?eWY%#vC!5SR?&cKaSwoOH1$ovG zWG)rXrNVz3hjX&YtT~H0*d5C(;onkgbF#^7<8Dqi znRVRF$tKfs`M*c8a|-gTA;|wfsW8x$y#L2p0RJ#J=OmPW3l@H2jOHYiS@Rcj63V~N z{Qnu?b87P6Lf*ftwR5R(E*1XkO*A)z_pg_1I$)cVP-Y!>e=VUjEwdk7-=U~6Ie(1I zr-{VMElgVS5SP=cm!^`tXe7irxsQZbJm9*Mb@dp}E-u3wlN*McC)mVwo39@gPyMYV zqH_6*C8Kwp$=P1$z1=MNp+t&#cTakwqe$bPI{U^?<|%`YG31`9vG#?1<4&1{{GOvB z>P0iV8!n2i{CTAMzwkf}{rQ`j^=YWRN zx9BQGa!7`25A82JUPw(ePohTb*By={x&@ELkJ0_t9qq!%e&+*I*T!FWFbSg<9*flV zoc}~ve=PD0KDkbP*!-r|jbC>@&UFbqmM!8sGmEu9`zdkCt^5+H;=k^grq@h(EL5SK ze+Rq%c|3-a-3koV2>o?0cw!|U3q{GAS@$o0ENBVhuY^PH*S+9eTAVe$aG5uk7Jo&| z%%#N{Wd5JnqPevA&lCP%7vQ0wNv^bX*{~B)nw|zC&ME`{GXOPwAxW$?6zW?~CId1V+5KnX5;;-Q2=hEU_TAbNF z5}lJ=egR!MC%MdQ_xJ&GMhQ@Bayd&Pb@v&*K?cwG!Hs6XU&K z-)_%kjY(VCycd6m)+ia&@#XMK}BTet_?lUXksm!*{9Ia;ynf%}-o6}Z&zvWp+^wq}6i9zS+^*d-hQAn@2lay|GFFgIv3H!VAi2!bzI1FQqDAxL|R&! z)2JqXsLRB8-R_ZM&*q$UCPIZ2>`9GIqg|Y~Uq4i3INHCxa5lB1&w2Q*R?h1in{>(_ zE0U#VPrteU+?#)SHP2)*)i7A?FEzaaU2bh{_2swF9UH046uH-18ZyD1G0`96Q|WP@ zKFyccM6=GaR60mtv^^pw=hW`e>g4_kb-^L-+8Hj&hZUF$$Sqzq{Sk}GX%`0a_cond zmZ+LK*kFG@ENqLvzkf~SYO-a*-wxNA<PTrW&A7zq|YCHV( zBj=g+GM(G*4x_b=9_jXjTTVH4>a4%2Zk)22!M-zgsAc`+nxuLw^=s>Qi!}6lSThZ- zU#c9Xwpv-^jACbF=A`Kd-ha4Zrmdc<@rt%mcKIbgj{a@wt60hUtUx*|@^ zanxpL*RXwoKabh)N1xn=bo<)KM#sVW;<~!JjXjN-&O+ngO3nHTc$2pB@!OSN!u<^) zK2}+@=JN{}TWDx$t@qKi(@*QoS!Yy1I>W#;C&zH!UA=|VA4}6365e`Cc{a89hxHn= zBEu)18ip$9mW+gRcTj~4?e9n~a6EbPWSw=RW9*5%n=iO-mE2vjZdZ1wgjaJyrKZ8o zeKVvH??hnE<{3+y(DVzsbB@H~Q=$8-t275=G}9N$Mrvhdh*iIih{>Sco-uU%>i7e5 zuj;r{Nv}Q5$ji%9Wsa1`+{1sS9bwV|$D6a=ir>9EoSaKC{bhgJ@_%~Wu7y~p&)58a z`n~F|GK+7$kr0sE_4SdgBg;T%HPz%$?(P~P{fbCcqo*FErD4)nSFe z-cFx>M?ksq;WF;ttjdY%yS~1l+`j+JnKM_JJ=Sn;T)333YNKF70FhplVl?T;M+dhH zJu|9JOm4e(wK-&R?9|LxNPy+ehtT_S13%?vF3O{}AH9#LtFQC+_P*eyg}+c6yu7r` z#H_J8AkT|Awl&qxDaxfF>&89k{sHn*I>4A?X3r&z=ZDN$EC)xBWHNBGm#q-L=@S8c? zhK`@!-k92#&+K?vd3l2RF7u()w6wHKwRDHadp(_ng6-NLtr4;@eV>M{kTcZVqSMz} zaKWB!h9FgssQ2~)6+h)ZVWu6%b;%E7VoZ_@L%*@C<2BCL+-3fpj3)cpnU3abI}5jt z)o7$zy*f`p)tvGvn6*yF^&j5#w7V6(ftt2)E&udKc;^yXjt9Lu$2xvwSIUqy`@wGG zZR^(V)RjU;5h)bth?{(B`?6%I zkGs%lXWRvQfw(U>JRO*J8CEUdck#DvLK8IxF|UYZI{YbQ`^?wC028@UVs%v0K}y4M z*CY22c)vYRC^Fjlf_Iy@yTeFn2uBZVU76d3V=phhT0imS(i#Cvz4xCzoxa`YJf3~| z#hIBVEH%uZ>a#ZoPk)Z4B;>!X7a8+CxydB0_rOuR&kv4hX@&H=7x8}z{2C|O^KRr+ zXH{%(m5%4DEY}6<+f$hTV!uy&^3&hv@K&UQ>p4HA{-&ta#4Q?DJ=t(^$vUN>jV2kx zx1uY`v<2>{>94^;qD5}lFF(ECf2gO{* zp6RIYp7f3$|LGon+UL_={`*sDyN0MNe8~UP7<=0>*PVrY_m6UOS#?&uVCLPmD<@vR zwApnL!`8Y|zYEss9U8sCgK6PA4+hO}zWgwC+smJG$sKz#O{d|H52K>oyK0kiGtqkt z4Q5W#BRgNYvFYILNe!F#7dY*{wdrJJW8Lp(WQd6L`4wd&e!c1%%gR>8=@wjZ936NS z$Zv7e{`{Zb^z`Wad8zCOW0>q!dxmM*%JdC+q*?+?H!@*WvYF1UHwV=wg(ikt4&I~W zNM?*q?S3{i_G$O}@F1(28)^Mw*quo3uRi){F?3W!8KfW0FflgvSZAEPVW7Ky9TK`^ z_lg-#s<%nFHV6;zDB3uEa|bRaaYi?Ayd+d&s>4Mn-6Ug+-AsXV!Jrt_!dF#T=*e7}i$BDZ`tGhfCu?fYeP zUFG)V*?tP5>F@6k8p$V03QgwFD|3lA9`u5XPkrlY+zp7|@`g2rtxjGo`QvT%H zCvHqn9@Ad;=SMg7FQUA}b?9ohOluZgEl>mk>|5# z&t9lv-w@#F08r8U;F1?-$taIEq}g_L$BO?MteCbp{^ZK;2x5-jGCaC^+BVGEw#gKI zMDEyd-~E&n{#ssik{xE%Cw*QOIZyPx@I#O3c4@s6Uzlu&W^#DEX)e8W_7dK@>;2<3 zj(5anlxOCX9HjWh-(Iu%r>hoaCM7tSw7%wZ>&LVKR<|b_*H7Vj zZXTWl`{>x%*rPIE5s&Fd6>ioX1zWRB=!>X=l2Xpi&60BJZ&V+#J^0o++LO84Zm_~p zo&4m_0sc=`k}(xosWWup^wpYMs$nsiG5t@b&tziPCswEQ}5NU!HCd^{@zW454dj073V!Y`Szad z`bqsZ@@q3AnOu!me{rs^jY_*Xy`(dzW<@16&=Msqpoq=eBR@!xH_@$9p`w_G#a0@c`#CzyGzPYWtOMbe4Bkjdf8#+gpz&Agez(r7ieF8poH zJKgj%U%IU`1#h#fQyWx7x;=jUn8+QHueL=fqlHPx7zSRYYaDGDRLf=WHb9>an8wb< zJ8jQTd(Xej!OtX-dad*PC8uqaz;7h7!u<+#LN-rcSqEFy=o95%S zmOPo$jML)C^eH8sfaSw6HzLvZJwhLD?eNtr#L&x{dyTN2C4nkAfoxuEO10{`l49|R zI(ogP?`~(Ox-*p;#aPIfM~3b4Zf-W~N~mPWJNoC@^>3H3%^3Kyp50Jn`o+mn)Y zo4vn4lM^g#fBaKn5PL?H!r`xIN>jut`~n6u2Gard*7w*qjxP4xu9YDqx0y9+nM2FE z#>+&9-_tNT8RB|qGaIVw>GT5OX|Mk$sc+iTo4O%HOIvAF>5`vtW8i)gNgob(Iel>p z3kxaV9UGR?@x5#AAP6?@qmGJ!Q*3yq;-y@op2S4!iT!(+OVJq|~ghAK&VLP55}$t0XDy%L_^) z5&gPkGoz=`q=2JW1bfnU>J|q2(e(|rc;=#@HP=}bNk^}zcls=;B`{LKGwq%J;q|#P ztZu8nFWa|cdbnrR9@hT&#+y?o=bGN>(<~07SFT)H!+CuFd%IIK8DG6)vTtpZznL2l z5YRF)+9MlHF|7*z)2j&-$AZX&W?rCNNwOV{`-gWr{fe6QgBLcxH?}!#(X=m_?p>^)sQAjC$Kc+#)btR9bZ(+i zZB1>-=LB?6uRi2?d3_3To*3q{Y&&c&J;S>FU*7)3;%n=8IodlqbiOn_ztB_l?%i^N zuK`<0)BX9)ULc3BLm7^v$KT#&&*RxJ?c%?$#)N13sEfzFYa_cSB=2rYU%ZukYl+~- zo9_Z{k;{p#GQ7O{!DSaRG0`g|8x^mJuOJsw+ep9vb2{Zl`cN9zR+^i!?}=i_V+>cOSk`&-V|sc`Nu_=%??_iXeOAvpwd4 zqz?;{y+_{|R#PG2v?A1ryx3MHs*`VTpP>W%K9WmE@po_YU#}!>kINS3hA$jD7X9=x z;wwouOWrO+b-uOf;L_Y-q%0+<9i@2abOy$oZ*12R$vuW-X_0^9zrMv!H#co%veuF6 znKv)#A+h-BWn7sy9S#*=5v3Bl7wU^yU>~xeVw|pYL}BdK8*a$g0v@mZc_MT?NJ+>U zUFOa7Lj$9z7nO+(jqie=UV&+wYwmjIy3^V>iQBc+N--DfZ!a&sCw7(2V5Jw|nf6;c z{L(UX`r@;AdvY7MdD1dzAKbNRf4Yn7@uX7cynu$^a0{8$L~Eu$Ubuu-4{%s!Glj>c z^XIE7S_^~Ju{f~>EA~%Y*njocEOp{3u=kMm{mD~c)~3A{iH9I|WtCZ=y^4x2Ew6E8 zQsVSjT*M#!mqE?ZQj(?n8D!!;;psKfR2E4C6r$I<*XkltZ`K zKJyKJ`C`gr(s0y^S=K7D_m=O@Qyw5E4Aj4(g{aM;*f{f@K+f1CO~TFJ8)khj<)D86h^3cb;H;P>m zv}2dKIJJ~6D|Qs@L*2ZO&g%1v3zT*sy1jvVR*=Ob-uhb6hy|6fsn_3^7@zWD|&9?_viTiK_?cKY# z?8&ix>>AI?61iDe4wcG-cE{*Kd)8R-dp z_8^)Pm&oMj6_E7sTeLnj|M=#yQk4G$V1#n9(zFF3yS2bk_4DO(e`&8zO)@F~A)yc^wRQBj*Q{ytjiVP`{$K9nf3E*8K6ABa6ZP`4A6sZ@`(wcvw-G$eAzjXX^h`&?MvD)w z*9T+B5qqAFPQ;<=v@e(W2XV0j2ezEJtQcol&1GZdgh~0y#hofrI(+zgu9>jix9h_+>GT4Y;`PtYl*-ErKLB5U z;F5-B-^aJ%NK`0f`I`&p{}D+)WtboP@V|Va0ry8`;$-c+?#IhCk&yuJDs|N)oat>A ztF88Z)u?;-%-$qi*JIrRf3%bGdoLxIX=FI4qHrI9+I6&}Xy$=4>3=B@{ybfbSW@MzKZ9ZB~5@ohy zTVt4(so%uzHKvW%q zFVp^bne8ifTz#B-ZS8$*c!9#L&usG$X7mP9_hdsvM!J$3g({e~43{fweZIOqLp*=^ z>F>eCNC*L+>#Z9V6%@+oQ#za{$Cai1xr>45SDq2sy?eL&I$jxqpTCzQMsupbUy+iU z?J%CsN&m1Xe=Oub*i%1!%k@=cdgeD*HS)Ax`>_@4PR@_JGXempq^*6Y#XII`%00XO zc6~&8BG2SlpC>d!y#PsU8v*O?NXWU^`7$)yHOo*L^n$jlK0T4BbMIV;!&kS9%hoeE zzGnTtdvCkwKkgr>H4!`=in6iZY3MHaUYa0&ivwsG%3S6zP=Mhgmvf6*U#oP2aed8c zwdU9zG9}zgb{0jz39?-#XZVR;@xAuO%uKY2cLdI~^U>Vg^RFPsv~OmlApg>rDbXZ- z{K}7Iap5HomGUc5BiDRzXthy8TD8?7-5WMhY6JJWiA3|~MS23o&R1hqz-pe!k)n?_*ANTR&QT_xe{@WL9Zn(tI6kK@p zKR;xXA_{=9YURp%;6w!qzrla>ST!z8>ZMmzEunF*R~v<@UbfzO?0rQOlhs{)r>DmC z0%Qvp<}AhiziJKJa7a(@p=96!BE7mUl5dJD5-CM$HZ;kQ@Y+&gvDxaJSMmj}v7ih|EMu^;kSnR+AsmD3nYB*I4mjqq7O+;YV_trGtf3ao>prxl0Lz zfnDH9|FnJ*KBsR;1=X!WWV^?|$@h0u7RkG{7KOwBbGNmZM=*4V&k9>!Uq#&Ms)o(2 ze*i-pKT&{rXwHuzvGErgzPz|(@#4jDNVKp@v?x`=6Y(+Muav!emH2Hozl0rd?X}Bn zuGshmDx8v_Ri|m>23KO$Or0qmdSB5zv+WD3m2S=RI!FjGNhuYXlM@kJr2QYN#_0;! zeS27I+ew0ET^Y%vOO8QDXVth#DOz+}@Kh)=~qT?omkqF_Z+(kZ_xf3aR}%IZkHKw`$uSEb2cQyf4h;bxmA%VK!lWZW zI6pjmcdy4jkM+Ba*%$66+Q=nSYwkKKt35W}!nRYz=M()j(03n-=nijNiY7W-b*#KwG=+LO_tv`$dF@D#0GS6#|Z zE4~KDKA|8H$7gb(;_p>bb#j*?gSW$#=fN{ebaa3~M=hx@0)L<*SEjA+U}<%P(T>7D zMU}~kIT2lxKCY6BvMMN%pdHv~Gm%r|Da@v7#;9l$@*vTSK-g?C=SAI#_`x? zg(16RmhB$fVfDI3)|(4>YaSrN|NP z*FEU??|~`Y_dRLSkUg{RiAGu2-N4%2n7O;b|2*|AcJ&Ii--0)aEzaElYh5`k+4oQb zb7@yjy|=`{CI~dfUOWZTXV0BG)0)3l%(?s2+uPD;)GQ<1>Dwxz0%Sr&=mk%7IU^?R zzUDF4Hy67)jgNdTmEG+w67QG}o&ZoR8V(?7Rx50|g zExK^w!nUnj#nw!nyocOM%Woc3xM1C7nTgY|#A>woMOn*f4m>7qX-Le6+y&lO1c4Vz)ik^2{S8P~OO ziEP|>_Ejgx#;KOn`e0ULqZZsWae!Q%HqQaIB8=;w5y(qN`Z!fg+Se;+oIaVUcrplc zgKhBSVVY`{(j*x{qx7T?2BYGxD!r90gzM(FP^TJye`$SP4h_dq6;F|ohw63mj4(+D zmvHizlIIJB17L0`b=kZ@9T`6!JMCnFe|c2h*pgLrbdgXGQa{f0mVa-rjYufekF*%Y z>Eg~^vL+Wp&f259qB&AEj!qhQC=_K$uvKE}o5y%b9?3l@sY|g?e0oA6m(usuXM%BmjB$%kEY)a&x$&`{g3VFOpuqio~Nhi<(Cx#7L@RH5(Pup3oa zvJ=)(z8kH6q~;mz_b(fX3|}1ZTw%E=qZx^=S<~C?nUNipG4^z8R;;)SFSY&eSO2Av zJXAKMLG7K!9ntx?=Y`A(r^|2Z+n9$Z;^NhS8Z3k~6clbDm38?tPi0HoFq$WMDB_09 z#18Q58BKa>RF_03#f&lFAX`GxS*<}wT zc^D0!LhNxd)Xy4)<$DR3mb{JxcIZm*_X_9(lEj*dD;wiZc@oV9G?i^c@AJOaj-KcA zEux+QHKzpT5^*v1yUAkT*5@IsT)~&BOx;PWh zbSTw20G}AP$UH}qOW{%L?dyx1SeR1kd}$S%%3+W_75kP6pBT6BrG?qpBy*)j}DlsasXNe9x%R?8{HiA)nYJWr0w5 zxM0!JDw(IKW)+_tV9__~>m@4+Q3B?3#0uJ82M9d*`ucmB z;F}h+>Jm+rNymELC?=B=&CVrKRE1C!&^KfE?CT2Hbe#w5t@~fmXpMzmUEHGSqeE;5 zr`0}ud3@%nA>Y` zu4R#`JClwcCjoPo_|Z+@d?n&;#ij$vfw|loC-(i(Dg1d;@i!a68qu8`upBJQc4{~% zmRXK`(@$DEGCFE7h1fn-PX&^(Y?edeKzq3))fTCDAK&^I$tD_9a$s7h{0-OvDbPbo zSE<==v8i&^jwB6wX$f)@njhvkq7Q})e7X(g1>2$9?CL@a9@{no$Id$;c?w5+jH~l&7s3@|*cWKp@ZtGO1i8*yk)k zQh1!g(R8Fpg{dW+whn#n#;+fJH05X^z2g@&qy`^e-zdK15d(QP)=??Ju)4(Tx7l^g z3@7G)@_w-F>Eb~&6Pnw;fS2mXabIU_VgKRA!7?D`B)+Nr;d-9Z_reFLka5%o2=Z<3 z7w9N5fKC_4MJo}3WS{#cxhGMt_{Hha=HTJX%lgS&u#-(vKDk6JziA^S;hk({D2UtE zJpckUXG#56GZ4}Ax&8;#3B;XW6eOsOT7AUECRtM2ZRKW#3MHMzT?9ELcV_aH77h%b~}*#w7bs~e2!#X_9vzr$jBC~9 zy@PdD@}f?qw0c-;b8 z6k3Va$|@ZbkOp3=cZNzNw*w_f#maYXLY5fx&O%!G))`lnQ`;0O>Pmu!Am02_VHDs} z8``zXiSf~vgdOhkBqSv9;H_3~lgo~s2r$Wrr41T`0GP;wp7M0@y$i7NJ$$SfQB$#e z3ZjcYBWdlMbmwh2te~KfFMSP=?evzf7&~pxG&DaRygjVA0u8B7G`;mHs1jV0LPom5 zSf)XRy-WWlHUHOjb6unfLTuTXGs!&0>z1TkrwSu60F)wagPSJ^bOK-A-syUkW;@6! z)e#IgL|qu*HED>Cy1EpPRI{}a&&j}jC*>%S;ax|MXurF&rv_QACCcwYBfX%N0#>&E zv{g?-R7}RmA*|yK{k_fcK|;22uCA_17LPy|*W4Z+8q$MXO2By{4V6vf3aHyfC)93{E5Y`qc~i4#t$!Apq9Y9y6f@DL=~ zi-AO`oi|bZRF+6}g235>$-U70=01$(wYf0|KM|I-`~uPu#A^xa<^6reF%uILT$J@8 zXb_W*T4r5~QBR5{yyEoDQ=B2A8>MfUYOw#wR~f>6LaGo-I^WS(7kRV#^e62_DW-DF zNVDxz(*b`O(iZ#MY$8guX%7`z_j3at7MbKs$6UeZ*#@SoUT?f7MhQkyjWDCYpkg?F z&kme%u37L+@KMU>;klhB9Ar;^Af6}_^Kx`mK%LJDV0)}lOEL~Z+|-#{xdP~%Zpm>6 z&*-fou!VpLggyrw+ZDH0^%7ulphAYP+DD|X@_b)H?3_M6-AnM#fvW*)zp77`OE8ymLOXV@D zQ6MxrqFoCUVhES;Isl5_3-*SlSYreoE!T<5x&8!jWn$V7z-S%0GbA^xI1kOj59*nT z-W3h8K0=SepV%&|*RJ)uQWsD!2EJxoRh(Bt*QV&E$045Q_MeKpwWC1e4k(| zA=@1N`n&WyQxxP9hh{#|=ryb|J`}!W7vb-f{MI}wXtoYb_F)haJ=u@=77@2DOZwu) zi?TFZi}DC%SzTuYFGrsZKm@mO9pCUsLrvwrzO}-JRXfgfRuz9tGg~GCu>*vofN|AF zKEh`cuXn?|HUINUPsbBvmMfu6<~YAiiE@WU0RwVm4H2a$ZLzcWT(X(D~WdQ|7~4 zpQ4epA{>ff`T8RdM$+)ieI&1a*V97T2`&{(&#s1x>VyR0G~QpCY99BwG+Y_VErfH0 zC)&?b&80bhHaPtYSd}%-0-wcbf6&K_<;k8?HbH^}2YX8KOcJCO{1QjdruYo=3_$7W zKv6v6wMAM1f{HN4DJ#xSqOz(jnU}x!Kx!A@B~g$&<+6z<6SJDnqV*ceWf-Tn<@iS!_>dDCG@Q@EZJv}x%(Hxz|nGR?lT4zh`k*3LJ*_K})Ebv}SR31(e z%q*7i*jR1+aTWj~H33xph4bfMvsHoPO9lNFh9fPuLJ?G2;054V{_w~?-8~Kt0jj=@ z7$?$5vpE9(BaXc>(I^1c9Fy-pE}MWXP(lh3O6*d2~YYk(?NOEMNFakv3VtrxGwm>i(Y8E+i*H?Jdt{-O$+IRk-IjMC45K zD5AML8tJ4vnpb(>JlDz9eAeB8cyjk~(nY}tnN=Tar*82mTv3s*f2vKAELh;pW1!@z zi;Zz>co04w+ek^NrzpOJ*$Az%m+!GMLK@X%egbQX&ALEe3FXHMpFN7k!Ennb8P|U( zdwl|ufk#TR*CP~DDD`)M;nbz0q&Bhcn!1l3YH@fNFnGDsZ=qsr5_5PdRou$V1z7wAmm zjqBAA{3p{&Q3H+Ac#`DaLBq`z`vf<1I7IE6O{MrquI6v&9{Zi*tr zpx3BJYs|J`@Jkc5T5?6i1SL!$sX` zc?c^5=$O~B`3~00$jRS;=2c+yTXl%x$Yv2CUS&$s~$+~kfyF5VhCNhzP}*)dpQVLXChO| zvq7nL^4)sX=E2QGpsUfnjOf^m2ai<_Wk+q5xq}XtD4<*?UN2|vbG`(KERXDu@y%Z6 zJp;1|SdbDCghyA=WRcgNrU|Mc^a=g>JB!+214(kU0PP|Nwc;B{NgV0Xo>+9s`YfK^ zLlicvZGIOb(4a*WUs^U*KJ@l5_JZ< zYES)c=)E3KmT%pU$ba2!(ZIcg+dT=7`2pbMmZ1YEyV^W0Z{Uh;S<^ST7v=VZKikt; zwE!+IgM<-yw1Gev!N4kZpY`^n(zfZ00 z`1p8vip8n#Gzmh{w}`XU20CJv8If<5pus0t_h{z&t4plfdN|9fD#%HsO7lFik->{n zVP4{_-C#F+Uy2rpK%?rXuexdodl34ca#RJsil^_L!vl@S#X~+oWdXs zlPg#3c|+9or_=}hbC|4;*P^fy69cFrHNnxaNZ578<{Da%g3xdc-FTfvk;QMJ0-m8| z;8(w$rxf6!r{_1%j@=SVQL6pQ*y2iU`N}DB@{6&nu>F8aW4Qgdwqv#2?~Dh+s>?C7 z6n^IpqhBjCMfDY+dvsD;t-I?yK896avAqo!z?;~Mvvc5+*ExdQtaG%8zMSQ_efjFu z4?yq|mYc*ufA}UkXc`JgMo^oiq1{&A*>jHYe~p76^XEEw1%>#S;4Uju@Ulwxsbt+1 zj#uW^dtSR1AKeJIgu(MneaquX1S&$VN|Ibb+~&e4tM$oWYb>m4DC zm00{JF4~Bh!Gbm%3Tc9rMFhdHAfPH1t#@G5()u(>&uQI#G=jpXHjkbJz&Ifz(}z{L z@a|q>KZ4qcf;(Z>7!gsHWvf{1Y_CxWEn?6u3pFE>>?$GuLbm;elAK(p3PWGn{e#PV zs-oyZC4FRcV|9Qa=`U?pc_OOI5S&=4X)moSW2KHRGQp}#?MzoqdCNV?)YxnSG)>Ai zA=eO-2wz(ZB7i6IxR*h!y+ZJ{IP>GRL!w{NK`(V+@q6KNID2dy6P*mZN2NA-6Kc)# zPnwB_en@(!)F;d2WeJa5BLDY{)UaLKUuzt>&6uhnkUtK-ng@{CQ$OBY0D8F$4J(rV z#!~Hf`lGD@CV_H^W~FP)kg$nq45J3pO`A6LeZsjyx0YbB@7rf`fovf+JR4c*oT(()Yc9#s!1#;qV;~ey<&wUr|>qeEf0r zh#Gj5#P0hzQ;dUuDY1a|+fXD=^6(;GBH%?K<<$ zgtR+@=t(p9mG?JgEa$WNatB_NNE}jOn-+3qHLG1d^R8i#^>5gMEo(4t8V24&@IllN zN)YOd`DZXeCFmd$fDMT8B`(UIDM8d^1<~9}-G9g+Y}W>QpCb6S{yg z_!mC;A#4U@X-$S)--Rq;5^nXnt#uIGh+Z7XF_E&r!gBak!9>tblNNy zYQi3HX>2g(=zP~vxjX2?N8lD~PEUflB3s?7>MV_>%^oOQrgXcGy&%mEl<+03h z`t9?5VwzEhT>_7(Ns}BW=x8FdRbVE}WcVwD8buhcAR`1lwD{IpEl;?5ayett2mFb1 zH|41yWrJaj(0+cl0_~}?j(M!L$i$FNdS9Vn?7k`ChMbAhmFN^4x^wG|`|T$oyL$r@ zC5Oj(Nbh^0UJVfYfS71P?cjXr=-|MO0U*KQ0mF&TYi1wnGzk2tVG?~2Jp<0t z;zPU4+AwMh6xNb4(xq14hgH)ww2`drTK!Jp{EEA^!REcCJhXhh4S$1!8&o! zhXiRPSn*jc@s-(G!q9we?M}iLz^XIhWCLq!i-Or7{_mOHuC+WskhOe(!3NXy6d)yK z(Zwo(#TITTN0E&IWV4A(@kXeKJc6;TYANQU9%6t>`*$WQE~_VV(wIFb!>r|Eu0A?s zC9IY_@Uw7*-v`i@icmB_fUq{yw2^>O^wU+6xmqZ#0fFIw2i+q`B=%#0Md zaFaWs5heOxISQYLnH>S0arco2WC+R_>|SIG3I@LiryDWBg-;<5T+e-0B@y?;0g+5> zm*bbH5{R|}PF;Coim8n6$`trZQ)Uzrs8Zp6Fx3FW*XMbyDHCT<$Di*A6Tqob~Ev-#5 z3Hjr`;p%z@OOuIU0tOBA+shsityBJ|Xw@h5>3%I>*FHnPIYJmWUmC}Nw1}e(3KVgy zVgIyh$JMu=zRN&--c>p1d?NrmRHfy>8R@F;o0=J9)L(p}D7fCLIV?{CrZFRqj3#g@ zhLXW(k81h$%zV!DzHo&91)f% zh{v3^H9XQk1OT(rUZ9<5_w6@o`*Xoxa4@m5(3kT*R0w6iFqvMXFta>h(SC%!9b0|8 zz85Xe>CkNMb6oC9gh>khq@7Tb?NT9(;V~F|pL1a{a)lZo(2zwKOHhXSTfyiT26^e< zTy6apYA1~Mt9j`x(u`_%0sH3IUG(pbP>Q-TxOUA>U!sX3G@}`QNM^=EPhsvny2^D_ zK8wwN{cfi;2gjh@YFGzCXsBP|0tuZz_!IP7)8BKtgf`@D^AVv@z(2(_n_ipnR5(lh ziqeaAPaZ{ecTaMHFh%g`PzIZ$+&NzRY!87I?j&stpAmdBl?2y}CWOy>`FFXinWWYB zaHI-nZ7L*hfYi@$rY72^Y9l=Q@tVmG8k|OrhKGk!vZcLVV#7ZMK4Up@@4Tjc7Y{O! zEMc`mLB(c@L7HjD>T|U4&2T|}{8-O&)9(FE^nE(i;PSFbHErILLB`H+i3F5+sF`kO z8z~`fj`r8%BqTZW-&aPAk&8V9$!>&${2f}D&bDuwGfQZYeL5L zBWt0k&%ZZ-5ZOjB&-?Crxji=PeaT=Q5gi@q>IYSBpB9ehR{?`-?z#R$HzC6&vTuX!yPvf?QG)0e zTxck8PP7=t5G(_Z5HsTAbUf!EiG^D?*ybkQy&Mx0gLpro8I%Zg5CPwjjDGD9WUVOl zc7FTI(;96)mtGd$mtKze`gEomoi0&-`MDJCR{c|31B)(R}Jw| z0@$i?+Pbsqu8Ye&V)TNew{1ab7At%_#g~XKHQ#!S7>CIg>IxJ%a|jdK?|{68y>HCU zJ?NUs)|A%X&mnvM?FJW+;;Iq$}%OK3KB z!9`-wYXhT=6q{W*N+IDpAedu7$b*-Rw21wRBU)-&1J7co72{-6p=3pw`9eVyKNa;yoeC3l|nbm&B<*a%e+^D*Spz!u%xA$tw`@&8LId zd0_lF$3ccC>Y#h(=-X}wtN8;I^~WH^_g`8$Rs*7jT{DgEdHgG z7%hgrlVpy8i83UZs0&?Shm@dh(TgGb>IR(x6!YLAdT+2<3gBdN{gd5rM1$YLd3ZAJ z0VVsE35TMCdzIQ$V#G+K+$IKcU$GCZef>DFmP46$NjTQ1dF55dFBl?LqkLsjm%KvI zuqzYCwk?Ki87yQQ*#f|z65JIwsDkONOlh`?nOiz&B`n^zsRgLj5;HY4%rTrwIg=zAlS zN(>8wnX9hwy)&Gu4a~{e__4qj$pJ=tNe>;N&H0U7VSaDW)3F13%0Boo(+cuEVKyzL z=o}x7za1sGe9%x`wu>UDYaQUUO| z&7+ucp`Kdip&|m9h7_A0Crwk=Mv+C|9!S_9)PvhV9hCtv!#Z?`aFGdn4!`;a50Uzr zM6yY$-?S==^@$N>}TyDRyYlZ zxDZqDkk4vvch)3G!s;sz0AtjL2a`xWI6Cq*R+n-=V0`zpEF>Nxfp%EDl)xelrH5%&5v z78aH*H0e6jy8aigL)o#hyi9b^O~H$gW;j%2{Q*4O4Ci%f=sH1Y6*{t zi4UZy+^fDl!|;UGVCF@~T|#K=%gc&5ZS-_XactqAZVqcULM!mKz$HqDI1Z@h_rm;N z2Dsvz z&J!$7JaXAj3ao7Z_+X;}{E1wY-wMtSU?}u7$Ql-vC)#O!!j2>H`FrySxw9e+hc6&M z67g-+x5WmPl{i-gr*m9{OmiQ_C&GDh0^@~A^j^D+)IbZDw(uJ*T1hy9C-%AG@JdD4 zBF$4@pgc!jzD%BEvv&c0`;8?l&d60n*{yGunO~ayM*n?E>6$og$MKPV8GTlU`uxYp z3m8%~Q$%lng8E6^lg9xzGs)mQW|lra zP^%Q@g*{HVr;J;47Uh!+5C@VCcGt(gg}F{p`0xO|%5LuVebs}c04NX;dBMZ1 zuQ->m@ls3shixSYAeSU^*s%5^vf_dY!DA=jkeS+}4tx5llQHw0#MP%tuqa8%;V$wm zTT?kbg$I0{CdaJ}%u+Q(tn#_z{CQ1+%eh&tga(}8roq^5;uPM(Q^l`rz*yl4+KoBJ zA}g&Ah4o&>QM9xhw^+Wtwau$PebVn#aIF89FbS`m5vSdlWb0TJb}SDw<|n!i9yp-p zz7$!!-O8@?jsbo_#-SSC{$=n)=*~X@)Y8n#Ow0+_@wkE#M(D~g#8^j2%j7p(q!u{rVzxqV_I-ODuC-@$ z0Sii>e1H@F9dXnL<{p#M&}K8iQ0cAjvC_JuG~5%X&NB8%9s6zz;BbWvSPv1#u}N(|AMz4dBDKZmI*J$K%s1#uuuY&%TcV|8+Cf|IO!8d#1dKQZF4dN<~NENOEq zzbspSZ4;7sMUMOJHPP`rJ)| zc}Hcn5^&E=9=@gpCs4lwzwOsh2S-QZkgoKB8Uu#h8&lH=$w?$(H!kbD=+XM|IUeLK zx&eQ+J|v&Tzx^il?YwkfWE(q9m;=7XDIk?lo~h}rY+<%|j6t@xl@EcDDwaV|T$b^WiWK}9ipNg>b;mn%VHfOp}q*bdec z)R`QD#LxgoRxl0f}p*}7d) za51E~$8oNjs%gd)#2h7P?)VJ*fQ2enwWtFMNyZu=25UzGlOiPa@;?vj<`84~7=n8; z!Em|9f#|%^+}FR6y1yS|bg}=duNZRftS7TnsI189rQg6rYE4;k{8bS3Hkosz>`GT? zY@VwHQXAbBHS^gx|B5=}p1mt0*`vj-NZx(5;ML}Vbc;%#v=gljc}!B|6q6XqYVA%h zN*SDZ+VEOk*#3XL$qxeO?~u0m^7*5r{H)Ak^IVfyrrb}#Cdfn2%nZ(<^bo(yN1bgw z=2PLk-hGh=_QU1Q&dw4z+U}S%2DYRyvlti{_}lm6QYhHJ4E5^p!?7LzR@{&40h?CF znESfGhhpnR9M%|hT|QT}i{Ed@vAx*}UBoXwyXP$<`R?6K+1YWk)NBjoN&K zQ8rje?LJk%YP}wKwY|WTTX7QLfz8i)F;Lx}BYlp8gQL0Y|FHHZ&{(Kz-}uhfpi-s? z4Kk)k%7X?&MHwRVJVYu(Pg2Po8bnADiVTr?h%#iZC`rmZMiLdu6v>qDchi2?xBlzh z=X|UF?^%OkvbbS>*dGchu;KL(h>smmSJbbh?%ckjdmjivf;=DrQ zH$IK|PmRk2^97Sgs?gQP1;}jwsFDXwcFV$}6@l__1yNZ8sE_OFH1QBwNUF@dbip<~ z`fr==ia)VMxR2KMzhW~XW2XkWA~i;RF36%YZRsU1ua}T}^1N~ST)hD{N1xp~ht5y- z3aE2Fql)@*ND-lg*7l7yUU&?r0VC8Xp?72d+T6GpPgU+%VCtNkZitl@r&$n>A;iB| zS4VzDVQf7<3ULX0<1Ky+cAG=O5)z#oT$PJV0Z7!G&2O>8Bu1iopv77B@J>WT#OT+y zCLvXP&cFTf;D5_Rq+@g+Uh6YD+R!-DtJYVbsk3=#vwGLLR57dCvQtGMu_|C}zz3ra zf*8STjK_~3FU^N-;w6xOL2Di!gd;F0C@jcMDnS3&_5S?^JRl8Qo;5U3TKSH?x}~ng zv(eBW%Ba3Ah~QJ#2L*p}i?v$Odb}t6)(S2i=vW@E{`G6zl*NP~PvI2OPQJOp+j?(e zhjOZuh=y-sym9g#U2t#qO{qu`K{$e(Qs|bn@Bj`im13<@`GzuO+p~im#(o_#V|_k< zCM1-dhKP#yyqo@7Pg=G|XL|z&D66TP>V>auNIbv64I?8YEv%(4!B?*S$jyXrE);cE z2|ytC-eUn!LEhM6obTOQ{eql9roEjY#xyV->4OG1H;F0rrAWy%H?CvAIn?)V#<5Au z6!k}^%u3@x6>{TE-ufy^(r)kD_Vy}kv_PBjB2(`ClEMxzc?AjsQY4#i%XCx<&JDax zqVS0s>=qN#YR$D3L17fz6=&-_p$EizR^Kto@Lgjz0A zlVXg?B?wQjbicsK>--eZmWV z6AtWK&*yND~J=CJ@G6hUN3n<0pio;O*&Cy+S{Lkpki;7Q+br1 z|I(Y0#{ynH^5)zYr-n6NudJyNwZ1)Y2I!I?N?jw1{C(N9_dPv)L?EL4r)@PY)7Am* z>#}TRR*bTbb7=O5H{m2$wlU8*WE1ocq;x_-daH-~muAuSJ60LElu#n?t~-@=B%AIu zS^8-{=MowgjOMtLSk;d4Qhb*R3~pJ}HXSX^8{RkIyYNmBcRG9ebRaCIoarEHDMS>} zO|slf);Ekj%F9zk0qg+*aqJr;p(4o@bmV3u5Mo*4u?v8$d|g*+RQ&}Ez~);xD4gI% zt$V;_y=+p(_cWY1inqp#VjCz4%D7Q46}FPogB?<(aX?gga783u1^vH1WdHkr&+`(p zSQMW=I-^6|5%w6Orf8RRoc9-i#_Z47GHHeP`aj1(_R}~vOuUSsm`|_GQ|_mE=iS+*eq%A{Us!7g3CEML$$SUk1Ggo6f4NXXL=2R5bV^>OFu zj{Exq)~#)Ed?vO&{76Hy1sq@_2scrJaq?MZWo6eWa8aFl2f9Un*4q}kfbw!0|H*H} zNF%q6HC};ej zu|lC3D@*RFkN<*8qOWa3Rfqe`0zyUG`a8Txa0dRzDhRt&c0$~otF?12i=^w13{>&` zKnd*ct5OP)@f&?|K;^>g*RRR5jt@KY(W?xzQVY!AtTsY?kh-pEX|_O68F|H&d(4j? z7XSrHqVV-ndHyHH@AoPYod8CpOAkN2T2i=bfrNyFA{v*0JS`;3zlrMq*u`=7?Ae>R zNBycL&SS|Z-)fvH^Jc8gwKY}dt1WVI&__u0|9m3c9G6&x4aA=J?A^Q90@^###%DRv z7~8u(eBi+p?fq5D&d#nzJf5>iwcL3R|9pM9B?~QU9BwA-8yTo)deQt|x^l%Z)t6d} zg}Qjwc%U1|kkqEE@#%e7SVMz^%^N_p+2dlL0V7MNEa1e#xGc#(^~;|m8pifXms6A_ z%|?$qIy#1Q8wcA91mH)`o!FrqyeY0AzUKa`e%1qYs!;giJ7(fznz2H60ZGdqacV1z z)DuRWNQRX(_qIanU&5ryuAM3hlr^B(Rp0I zZpiFJ#a(^uHZ1*z2BHaY7 zT>;$e9w3XZ?rw{mo9M{8L5LycdlRM_a?!&Y=6?-^LYm~e@8Rsd2Qrz}b}ywJdd{M< z?B3{_E_A*cAe>^)TDZavRf`IyCt)CHTI|}*5q}6Ljt{nzCXPEu-KZ7&C$#!^VY9-E zq);rL-Dt!_lf#;*of2Y!1X>6E?3zrV;-p-+yBb}9j`jXP7HQX>r_m+T-?0uVzH;Tt z$-GHEmu#nP;A`@oE)WzZZ6T;!0f5GV$cgqV;W+f6JRlG`kpr9&qH3>M@6x5dyAq*{V%AIpT=}L^PxUpvqE{R8Y)`fiNG) z`Od|a=xyE#&L@ps3j%Il_1QUTpZ4@{OJ0O?!Jts3W^MFm=-N(lx3V^~;Ax=t{gC%)&IMts4u&H9G@ zMk)lUh+*OBs!lG)_hVz<_w77Bv!g&IPQ&yIazXXa0#v!8`v zm9iPu+t{3m4k5-$kJhph%mmbL#bL=D%YOX$@d7XKy2{{<hqJk{GvBnM;IGC6e;H0IQr~U@Ph5P>YBeO&2pW<3PJ%c85k*3K55Xe zFfDX6%Y5D14Tgb7S@WL4hCYsiSFCj&Ab`ov-kzeOzk7>L>Mij>#1D>uP_!D1%QP{<2Ir=>`xBaH1CORB!nsE|$L*u@Ql+)yBl9|duAIQ-9I;H@FZ|GxdriyCB z9~8tT3j&~GU1fr4f|@C01H+Pw5Ha;UEP#->s2d5fLg{Rz7jN`bqPw0+~3QjK9KGtqDhhA3Hp`m~+Slj))rP`%^rxGDL)B2Mhlr>`_n*ISvEgHx^#J=F$gtH=jNhk@f7=@|^d!V!U`9oeOHF5$58Q96V$3C4fVR+$q z_Ugz$*LG zPH8&aaO&z@(wT<`g(i7|DAePyL?irGRGMmjMZ5m=&UiZw)nsKvn^9)AIvG0x&3qU~R!@$dXy=L`$Z+|-1_r*x`N zCmLHk)IK?TPN4;@1#&4Ffw0#@bIrcp)$9*IC`N$b*!b&UWq@s|6^v^)V9Vh6sNDta zL93TsL+Z1zyBsNrDt^SjDXgoa2jTF&d39*vG8$;>wifsS+IgO@AMCt#9X4vU7^wek z{UOge;P8A?cbFHqIEM0C!+&{$!&S7Y@*&C3xf}hEkWhhewX!Eqo@BV9i2W!7yU>{q{X6Yff3Yj$iRSD_Qe@$ zQ9h!O@msS)103415Q(LJ92BM9Qd0i41|M$1R`wEK!7p5HNnr_1-#1qrzc?6|V#Xsn zA>h}Mc;AOnq)`tOY7GMk_tt<8H_v4}-OS&FWEXA2mu{H^`di`4*V%FR?YwAc3?k>k z2Y&x(IhHK^nLG7)=3M6hqxZwdxI|=f^!nxwEDSu4pP97v-kH4wBDW8}ESKinJAwaq z9NK>K(anQ6C=3k;a3iCnPavguC2x2|kbdDFm1v-GRMPBsF*KsJUZF+@o;=vyt z8(YQ3##SUo(uTO6&>|{!1I!<_n0WUKB)|9RP^(fQNq%W9%(1hy#?UTxzr5YIfkCuSXDH9(}&ww`Bc_$Lb>DKsc3 z$ZFHb4?H#+h`CdL$CJvAqe{qvTSe|6BBmW-y>srT-|fvvWhVIydScFD43b>fBnGX@25PW$oA4p{js{+#}Hs67zVmlC9cj|vLn?c z-VIN9Xl%KLkVX{z zo>41&qCw;dRy6T(vwT&ja0BBc5T2ZGNiQ*Zm7zpJA@D)M`04~{e5+`QG*%FL%^#DK z&z-7>W`mSa7;=2<=mi7>2-5eg=_y23R{m=6sjd0o%rZYB8hhfWsl*|ow^gGEsME_K zq4>%F z!L{b=>Ti`nMw)Yshm^!4YBv~_ZSK;Oa@N``6zkmJ!_l>aW{DnyT1lZaWI&}$0ez4J zbl0B~pK7cz5|d;tq7}8YvnL@1x1ky#>lTC(@cLCiTqE?e%LVGNZX|An0=D~{e)j^7 zUrkuV+kE@VjOSr;Xa=)P-QPe8OzD}sHn#GIahAFU2DShq$f}sY(G$Sux2oaNrg*d* ze@`HsOIUordjFU90$l<`)Sd30J34{4WO4lK(+r!2zPTcwH+zr)GLNi^h?K#F$;2{v zNGKf<3>YbVtP^88WV9b{9qULaF8J&Z&59Dd@1ye>CcodBbV@M|wTvY!kLq3j^;&7^ z(F(;u>9t>C<#zxml3+sQix>8ube!_Xh7Iya{Fs=KPicX|{^Uhw+(*>M?I%vfu;N?< z{Fs_j1_m6H#y-{wzY%!?^Dsr#pUV1#+8K&T&^I9=;nAZb>r6V`AHc_3&mtK{i(ZqWtex|Lhe0umZCI<3MIbq+7UoOTA=dbn#lsnrvk}Ddp z5rE&?cn*YmJjD|&osf&rg4`!Q7-+xi6DN*&$|3=!9F%S*I9-y>LgN?_3q?`ViW(Nc z;*7qXejIlk9Ubv=F!7T-)qig0Z~R>~t$D|@Wh^*+!qMW~$);O%Ip6s|2R4;mILSBa zeK+jpt!tRjZNfQJJ~)>HA+8o*3yYDlNZK>Z7zxy9ceKf7{;l3JF5ge&!KFfy+LDj) zVag^*Pad#*a__;O&^OJ6(-u+}Wbc`RFOem-sTfa=rAj?r0&NKU!#D9-(>#0uJ_C>mVXD0B& z%2T8Ox;~gtfg{WWaSmp~ZWXOT$>WdE=jj`&uKh#suN-%H+~h&;%@yytO&H7jYGMt{ z*c(z*>lxkvNnX03;l(|erXILWohtA2irlYI6c&!xP0GZP zKr)^3+-HkT419_Az0Gp4ug_YKnR1sSGErp~cQP2y4^ahLfFwQL{bGal84F-HF(GrW zs$&8zA;Z`a;Mqb+ir0dzrGX>*7T>JQP+!AltZ>bp2LHI9FUCX}7hMPtm}sg`nB@8C zM@VUG&*;6HZ13Q}28dmKaN*pz;CXLzhV47)B#gB!^gAUbEu)&Wc}jn*!>CcmaRO^+ z_*D}C>V*<|199SO5+)A*;b41Dpz~q>A$I9HnO6VHS1JR8I^1?PAMibm3M*{;wfb}ws;tx{jFDDBs)el1~P@ez9Cv28%tMb=IXbrP{p5>c{V=vJW`nDF@ zR-HO?CJ6X&C14{9xLdox9iWlD0qqXC!?L);Xlm;F?>K+0q(?@FQ;39ttL;aDrH1qYp6L+1_}aTbWyFO#r6 z42VY_StB0=j&4IlZm_4tW1OwIcI1p2yyRaC`=+p_Y5fiF5*9 zCGb;=+s)0os`ZOb+myo`DGc>LMM(FPPt^~+0>pOH_4%`FzlZs%M$- zsqbI()Ao4x8h5`8R)($X2*h6==r&#^&GV zKYB)y6nDLo(bJJZjdRh=^t8~=d=e|yjYh2I^~oerrL4UdFXEK?=H})~?%2Vu<$VG% zmN1ENnVFf*w^W{GLWEOjuS8#8ga-Yf#Bmk^(i4PE!VPdMUV_MzuQz@My$afcW!J7< zu}s4wBZ24(R{shl=QZ&JEYfE(4IEYYyjN3%MeJehed&hJo{52htFPz*zMmM5u;#Wl zo2r8V2KaFpYKsdJ`?Km(EBA+%ZTtQ1m5BLZE5VwYvY*j!Ul#~mN8Ttkwi86n;f7~O za(=>J;%x#zfHV@4l9H<<9UuxK!2A67$0fQ9|%i#DW>B9 z!Us3qH!TT4h|&T4BjVf~)ZXc?{Wdv@(>+~0bU%)#tC;m!b zi692(0hrQw(J_rcIjRH#CiWSc35;qA95nCWzemw!d>F3&jvb1xBdmvBcucM{s~i-d za}v43`TO%6`2(0p+$i0wMCZ~DW>4VNWU@e1NJZD$6jNp0*%K7Fr#FMh&3g0}whIg3 zjI8sHFCWEDwAxH)3tTGj%r*L#vuODzlg&Mb$86V+2Wa(;<-#T0nostHkrocF?0A=T z1V{dh#>cc1-3b#1hxIugv_TSoJ`)56%D#2z=I2{Du+TG?cL)W!0)k0C&{1B;UGY*A zNVk5Go1s5sS)>{ZZIM8#A)$Ha86a76##457qJ{OK%T|9hS0Y3}E}5E%m8@_7}nE3m=-8(nB$$2lJb58nPE2ps49S@ zFjG0f3>{S)9=QYs`lkuvTlQ{wa&|Jem$@K(ncB+8`jAv?WXBnaYsZ^(6*7MfOmykJ4q)zzy-?!xiqMFEr+3cjA>;}{c!TFed0 zU!FxfealO7em~S>H7>N8U0=SWhK~;q*Z8+>dW42ZU8O+l;eGt^iXhmt%i+c{jJRO` z8!h+z2l%UJ--oAsb?tRouAd7Ly(wg3Vq$JdLK*^Vq zsfgb51*%qAdwcr{l4mP76X=2-qiQaG$MX^cfoM2X-_>PVU=R14RCIcf@}zcq3FfxL*$*i z#Ns$JnN9R+NUdJ;u=3V!rw4wpBnYjeM03QVLhZKR*!m5=ofl;M1d|nlQl*TJF+?{2 z^l6N^8;p+XHbzX;F87uA-1!kl=g;~b$PAdtt2+mAnqqsnS3ohpp=5f*7b#Ry#_ayc?y11s>|bklO~&-P6Q-9=2>I;BgvTn&claPwsFZ}B9ntRCBEd@zW%uC88=VydWJOF)s z53|v_XCi^!xhHR4VD9kcy?{X_xUfG+SfvWW17j-W5PeBd9OrxeNecoE=GdQNW`8XRU)J2_D$gp% z<+Jo{fx{t)zPLrP8olOekUX z9m}};K&tSAN)M2%cY77FE0=U9Mn*N9x1Qf(a~PbB*e%8 z*=fx9C^I_|5Mm2%H1VTgCVO&C14NG82|#G&hziK*#N<8H%+v51rZ{&~%4 zd6v+KUiuH<>8dT(Jmn8+;?HOAeqXIe@gOlfxR=DX=&C2y#r1e`6FB zWOD*|V?>hDEnhD7YZUMf_iz@rZFvy)-T5c7YRjlMHaV=u1nA!&GShtvlhz>+glW&y<$Wr#lZkdfKKtDgjb zA?!*R=7oRwu-|XIJs&Oo@nure>&<9@xbaNRxBrVhQ1FCP(lUIpFh$bt)T!%`+**}f z=twxU{%dHP_N%Z=gwRlrbYbD1a7Y5IjF2Pn15>7?7M%HXjnJA{0djIaCS73@O|qR2 zavF%@V<<%HZx#m!2X+6Lu5AeLWoYT_Mx)?;;?xK*+6myZ_ON<21>I3Y2ts>iZSxGXled}sR(&avxY z4isVGmUlZRu@)z}M*7<)Z`D(qdBu*tD`H7G9*_d3bZLNxP;7p*0hcyeS zhhN8PKA!_^&ks!xOT#VRoFn~>X*V>v!vc|vAa8SG6@^WI^qm0ApMel6Z?aYxCs^Ux z4~(B`ROY;;3>gNpZXvo=a`neYyhX7mmncaaPw{de`}%>12vMU)z^obWlFwD8nCQbP z_Fc*hr0p@vj?>-OvGB(lshNSSG}gv8Ue>h_5VC?n#P=7`_WSh!@HYaoeX11ow+b!+ zVX%Kxd|k4OO-Etg9W0K=-pXASA;RG;Uz1JMZLt?*jkK#CS=NZ@_!vd~Zz4s)mP?E^ z(z>5uRAYfax4m#F`ge6iDF_qW!+Z=f?p#A8!*Cug2}y@$$VfH`Z=tR;ya7&Ahix6f%Hb zIP&${SmdhKi<)1W{vl*U%8d-577fh9?aK;jO&Afk{_eXeRZ;`qr(L3fI#wst0rfd>*(-B1py-4 zGe!B1)WF;Ev1>6X={xWo%Ziy@hZJ-};^2TjB|hoJ2QpHkgYCiSa6>ebba9H>(U8_X z06$@O`t-KKX8?oYkul~)z*xp}F-9gPCU%W)@Wxv|dLjj7K`RAM-+|Yn&IAfi6ZN&~ zc>P+HL(K9p$HzH04p#^b{IP+Mq;k9_IUWSl7@|n^l_Al9Y^;m%I`Ms)_?GPb*2{F9 zP-)<`L+B%sNMn?fs4VA??pRL(blt9~(QExN=DL@7ayE?Z7~ds0gx+2Wm}^Tm+Qoxy zT$Ul;8wgC)0!htM z9&R)=(M5M9U$j5vnAZN%%1UKy@UXlo?u(lGQYfD7l)Zg0Oa?Y8iK8R&nb?wb%PZ?h zLn;~5^M8ehHZB9AxbbS;qdD0|WqQ1Y_dXwWww<5dviCb{0$!R@I z22MZ{){hc@0Rkt!O6{fW#nx!-ftosTU-wgYH!pEQ0*JQicSD|{fu>N=bCbS;JwzZ2 z%4!5;Qw6RgAAlz3%K2RLxjOQDe_@h)yrnCZFI=^kVN2Klw|(LjI5qpz&CbCg6f!&| zApT@e`8LqQFQ&9goYTbxkKq^D#@ZwReHoeTi8ARu6CNVMHkZ2AU$VXvV~wAaon41h z5CQgl#CJAd2IvH%O^zdHQvjgiwZi4llB7)pIjdy#sNH`@0$rc+!4M&09rS39$1hqh z1RrkhL8_l_KZ)a&u;tL|KZ^xFVySCWo@)a;VB1yjBH(Dg{k#{`Tp*ew*cBV9Sp`V$ zoz0@It}Z5hhu&H~viL#&Zx)C}h*%WlKxtpo7pNX?0}ox!A@(BmhIu#L>=dz*7?eIE zdA!!mLry@n8-K`AWYo`~t_fm*rKHZUjs3l2K0nP0FNiXBe>rsfXN@Y`x-H78&f~p@ z*9*T~Kr?vzG4664a94wi%=Z1sbc*$h1UBiwo)<1Ascw_RzbV;A5h*D3cL#_~`lOMF z9{Njy*huc&X>B#OYy{u~nwxd3(4YbFMCu#ykHt_V)!9aXN2U!|04ues>(>Zxfj-v8 zu8Ig7!2=365>l_Qu+RcI=|rNcPJbVvSzF-l3q~2TnoXsiis?h{T#Sn}tU{o6bZkSK zjOVuuc%(o6CA-bf^NzM&k;q|(91AgSLP?0)oQerM14>L0nbPbjO(z;w#{oz*V17!d zq{!0I3aFgp@K`WmJ4aZplup!a``fp2Za;PD^({}uRFVXj-|t>gNkz$7m#E{{jxX~<~=4jHRPw+&9~$zbXtQD zHb~W~CWbMEt!N;}(XT&|*i0o)JvRR)7aRp4u+gC>yZ`8uf#XPvQaV@J4B7~tFnYAg_f39fuKFx{Ym~OOfAGq2X&L!?f~mG zB;6WHz;r?ffoFVh_z8`=O0*vl%f!LVNxUwxftGd$4wPG#hpfR zF-laRe$#*Bo%`xf@KmQ6N&vZI!sRVpNL2c6(z^68g>xkz(`podv~BkjcV@imY_N>` zs?Z_JXj8Hx>l`5P2few%RU+uV>aIOdv1vhejVQ9qMH|l#Jy}d_Mu!2ed(}ZaZF`G{ z;QPG%o+N;jGaqHb%CQCZJu<^MP5;OwpFpx;1sDZ#ue~%0=Zh;9jBl5{Q7?SSiWMP1 zBD6^}3MdMfv6eN*sZcbZU3PlLj+HDApk1`+YO9B>717(p zh(B#}c10*>E5A~aJ;V{V6X^Zt0XR=Nf$N)fx1`l5;{8~De+H>-wODWHMC=v#Oo#~& z%~%LWDlHHgv^YJeLz%`VHs&!LI!=y`3iyPxy*A_ho{5Nku_KIO_=gF^XQT<4}*_g~2LfKbz1x&`ugk@LW}##hqh%E-ty8hGzlAslW;;L zITRZ7p*wSE<=n)>6iWhZviy4hS$c&I;@d}*Xb3hJl!i6)V-{HZ;U-=KUu#*sFF~tq zc$ShBgt6xiE-o(YSZYB!ylvP<~*fcbqd>B+-@L+^D{n;Ijm}Lrl6dz^be!U7OHk z?Bi;bE35r9>zxg&5!-P6#{f+8EV1Kq4Gg}2%E|Fk@UHM?muwto7Vt!4XU#b%Jfl>A zJ4=W#>?!OzyP`;u)THcTW7|U42@R2hBw|OQo(8(dn@Gi3UHm;M+z_xBj1}*Wjo*d? zE2VKK?8#`>UnCo7-!mymYne77v`Kiw0l(E|!^LURqL=ZTLyApQ8hBK1l>D6B9QGJM zaU5Q1d|DBfcFK$UW?n!OjPM<5(m^mhJ%B%1ZMwo(-rY7K4?HFQ-zSr1Fo^c#}Y6gvB!D;n~$n zGtkD%eN^uozYlt!Ue$yOpDoMw{MpN?&`n;7Zg<%&?{a7W-35Zu@hvJEo)tU)bK-a} zR^h6sp6G4*C``SRTT!7;i*w+(&l87H0gnMM-jx^*d3t0<>7?pyz~%L}>w&(zthuZk z0mUfsQ}G0ztqEBTWB~dsdEX?6H^)rd51rT#5H}_`#h$R?Xub(HZiNOeYxg_{YsdBj z4mBawYl8PB(4mjafi2)oAf7~cxGuojEwIaPPxY~hLAqc})7z~ZH!1**T-S8;_8+=e zmJ|ljJdaq@QBm0w!{F`i3FGbypfW;{mY_aH$eOOyOnxFCE`0zff&HTbsmpYNL?dSf zc!qFu&=hWprw@lh{X|0#7Gz02k5`Y3jC_Dx{glSK%}bXp+a7L;fe%r$rrewcJ9h1= z#3OnSoQt!vo*yczQvIR{s29CMLqikkzr$)wh7&-Q8IqyAzs?MT7f;&l#YZOm(I1dU z$W1_N%4iAwmEjQ=0FH*;3w zgq1VNxFAR8xE{-uO9oA6F``{NrXRm?j!&K|(!cc=*%;Du#LP0E{(G1&T6_dS%k^C& z;0=NS${VDLDiKi*p`F7~H6EUv+uC^X6}|*EqqdbQ5~YB(=L2bkaEtl5Em|sCAq*pZlQ)qdlxQAwX+oX721eU4683 zE$D$cB^C;*MSBq6Ffq-R?sA8yr#?mZ5U{W1Hs(QU^r{0LMKJvt{U|_TP-kPIW%XPl&RWgmavR-oGyE-1PLsnPG5ZXp0re(k+`* z`jgEWd-m?#`|2ecC7WNs{=dCdg)3>uwvV3$B6TiR*Ed`QIrd!a)m-cKpFVcpZR81m zAhS9&MdTFn9&Yb)NC%@?0Xo;-w_fDoI97#Vg?TDPrbf7y4v&U5#>AMkv>?`ojC*)) zTjPn9sXzGEv-;;#xI4F>X?H^MaufYr|NJ0lYsiIyN_x5wqQ~1n=0T+EXyCrG^?Pab zMMCL+6ucub#O*+a-2gS&CJA#LOc;ugpW54SUmyx~@y7?&9N29zwR5fdo4Jqpae-iT zhl{y2?KbC8OR`^xSTpeNsl|-Av1qu_P1Dcr({C`!1Y%(jl#Y*DzbMD*J>bTJ`&u)t zW1jv;%&6mlCsgzmXwF`pG@g+#_^;eCyszJVW?3Iy9M_kfx$2Aoj2Ga~HpEPnE)sb8 z8HjmpfrG6e3I0JnwHAjwm@tc2ulx3tDhe_gB_7x`Z7B`&F2fx=E76fMjgL|Y3qHIz z_ArvZNzD_=&8v`Y%#mD8H7Mo6GRWdi8}@dt^6OWlkJS2`x#8wZ%iR#ukUVUvLDSUA zTUwf%Fj=B<98-qN407XbznC}>=b*31wyz2@&Epe#fH+OPb_+0gS#Q4g6o?Qc@~VGC%Ty08C7>yF96y#9jp&@W~^2EX;d^Q}%M7CH?d(02tu)9-R?xg*f839?D)l;6dV2q=l-0?_wUnh zG}yekXqFV(dymV?F=wpQFcs9x zh;9^9qU=|1A}(9fM-wn8|BSX~Dqe?QM|ug?g#-6}i5H057Bl(*w3%z6zc^&^DomhH z$_gdIfLSDVlGusba?;+r1k@D5bx(Srlat7SB#ywoKTUFiC|BR(BwEk=U*hw;|2WqYbg-Rqgd8lWk>A+1IXRb(E2pQslXXL9yV~^%0X=!N)cHJ=?)%oCYnP64v=Zrq;+cu^j5=QYk`TaZcIG$9bj$79=);G?w$UmztLL11AeaeedhmSv@PE{CeLiIe(02eMV%DkYc}KYU=)XC_}X*9w~C zC(#{K2iJsLy%Q6rZ;$vJEoMgL4`>4nO6A7Za*3BVG-%sgBd6N84*FLSnV&ymPa^0W zw2UB;9iA+Pc7L1W(BbwqPCth362yMw(;HjKLww>V-zHM8icK~+)K%aD5`Yd0{3xrJ$sdq?_5z=T1u6z< zq*6YrT|!YT6!rw8*B6-#kl5~gd!2Fh)x$naeBHV4&NGj#Zo5H*cv$iRJVQ7~GiffM z#am!O!(SQb9Uw)CvW&V+wc{`~HO0f{-=MM#dUKL_^Z?V3UPQ;UXYx}W)iC?|e)9rI z*cZitG}ngWfW(hp1U&;V^ZB(fGNg#k@5|STtLe-B6n_r9^w+M$z_KXO-gjj{Yr0Va zx4nI&hb{_ixHlmxV#Jp~0dKVt)IUl}4`Q>g6AbVYo}Sx;r7%~iC$Z#5F{yQZ{K!f< zF`W1C;hFBS(Si0|BRhpvxk)Znp}Z%mY?`QduV6!bKWxNdUhiJoVLI$4$p|*C@LQ(t zJPvxP2Q?cRh-TVS@^ek@um}SyD?j>$AYADeFb~KT=^$S6tG{Y$$9jTKC)+8W*pII9 zoG^SnepO=NZVBK0&&NQ{Eff=rZ{o*4m$3>iBS&u5KB&5g zevPCW#`QRt1IX{boikxPvn{_ytxA=6?D33?ZK@&rYi~e%pbx{|D|DPdDV0L7+}x`w zG9`X9hCiSqYrlrR{nk@?TxTx)*O84@>&W%%*BKrvlL3IVs6sz;Xsk6Spvmh-6mcM= z9#@^$`deDAvZJ>?^Mm9`5~l;9TPch?*3CJ5nRG}73Hn4Kw99^l*U_aYLCtd%|GJUc zEKPjakO#G$2(x}ofzFXw^YpiHM~9^o-Va^JD#izW2u4s3nO*PVu;94-nDlvoGxrJ# zG}ZQM@ZMwW4P@e#rwhK$z-T0U^Jbu0??8x-tkq4HJsiCsXWc%|7EHAkJy*PT_Ma>O zhu2?<9N6?i-Z&Kvy}tA5&i3ttzT*#e9gp08#4EV4I_V<+in@OBRWs|RhDukaMIA-Z zl!f(vDbl%3QI zH?gtm1^6pLs4s(|u&xB8n%XTexpjhgMxf86UzzZ7$K0<#%V-IsTJRSUh2U%_FquxD zA{@H0aPss@r|kPOi-~0sZ8>wZNzfItSc3QM7T=v!o$b81l4Q*cY$%@r zyZi#?F4FT~`+D#R`g_9)CJJ4wgiPhF=Xc3Au0JWQjyLepjc^wkiuC542ko1)Ia)v^ zOp9zNLKvCd&6>q*SCG63&>?f3$JrCp?oY`2pW=y+oVH;GR3MwjrDWM9P8uDFB9HP) zQ)$Pc$IWwVjfM&rLQZY#vJ~qZuTp2er5?P_msgLVqfvwi1Fp;y_=W2q=2!rmB4*i zPW13=Fv%tntMSpSS(6~H^I6D zlCjl`)hL(ByUnh#$^JXP>B)BRFcD&Cj3O(ewPFq)M9oYz~pv1t5m6PDUc ze{wSLEH8#e8FU^7ZXv?xXVz@pF zGjcisPifO0uObMiI#3r_)L|YDxDw(2jU8s(%wQ z7i`QgAGkQHO%(HDH$2iORmn8*G{{5M`{8C3?Cg{kb@>>I44M5+oO*({t_^)MSJa+f z@!+3Ck-lA@s$Zw%XudNA$n*;U1NY#Fi|0SzaKN8KJN{7ANtH#Bz#;7X@NBm3{E2R@ zUsK~LBscmprp*!Kn2-2vi^f8RZ~cb?pyr8lPHMUZ+F)|?b|B=y^1{l37)24W2?rPs zNt&SvUqkghc6`-25pPIz3z3hv-z`k;2Tn=drLunnElJz+gTZjWhX5>m2~;=LWog)> zr$v^5p(sqGDM;n*2NVC0spQ1FVkDfsXgCiSYvbT&d!Yo2H6XGm{7UZQ?GAAO#nU$k zmynni9M+@gRG}3OetgSjAF~HZ+(&}3mGIN*JE zMHR=1W|qiW6I|E_j`TfMhi2{c{Tx=?+Oi>o(ZQ$=U43lT2!3HQdo7>MO=Ky{#=|@M z-yM)Tph?3qUVOKuspO5bNm%E3Na=J=Gmj68(GjjNbzzDayUa3;yk(h$Quzx0-K4zkd@ciN5L615}cFi1iw+nG?h%g_k++2;#$tj4}?l1r9zKDSfkz`s@Oh__pC2H8ZI(WnUK@0at-!}1j z*PvdIYQ*a_5+CLM)cHZu&l5GpZZ7PI`>uN*haNpwQ3Dn~`4;z@V42v}be}Bb@Mz$M{Ticip)?CBV zNAZs;alPXc7}^OX;IjQv!~RcWmgd92`JCDha3XhyJUKs4&f+0m=Z4l;C~SyVF_3qi zXj-+?9&`%X{z<-*mNoad$hKh;JfB0osI%XfRfW7;2$))8K&2AUN9=w6&&*w>0V~8; z;e)Xos)9v1xQYNnQN7U$&BK;rRx!uN|^8G87qH0nutnGFWuveKma=z)~jf)Kr zE8*SV{=R9G$V;Przb{oQY z%7Yt#acl;%kiAX^ll0xM%>%*U=$Rs1yR=A*G1dj1v7TEt;uueZL1Fv4L%PSE4di8x z+sAD-wo;TyA*LE=urm3hTheI9vR+@bo9j;^i#^gmN}!!g0p{6;~9^jF;F587FBlCq4>{{rrYA?6L2^*IbVR5B^u{kbq|K>Q;?X zbLXK8%xg6VlMuOOh)N-dt3SSDkN1?5RDfHN?pqX6ClrR6o$e7LVql_=JY`VHf(+oh znsHdiE=R1DDN;HWv$*!&-~gSfSY8F7C=uutn2XrDZ1E3vNAJ|QmoNXZhF*tm)A-&u z#zD`g!aC*N*dS7FTJ)JZq8YSRx2!_?Ro_BaDrE6FUPw&iUaTQmjw$f&^c zKCzL2$q+du^VuuiayzCKn+FG~oyV!hRXhl`M4+cGRLDv8uT_~3W<{iL`Jgpg2-U-< z`b$JjF!YLclTPZ!{IHwGHE)vf*tK5XYn+QRp6B%KScV-jS=20kR(g7yPR8FEbK8}M z-qMa1d?b)L{wcd6OEIdZX$#l~I_QOZc>G0VV`x&OCQUbuU%pM@|$fe7k9y}6{V4=50RU?bO=_H%k`-$mJ-jXVTsLC#%d zR!k|7SJ46=zD^gtd-*KBi8zcC((NS!&6%;~@5$y{KINv%vQJ}(8w_#_n<}TJDWa4L zAO6&p)D|qaEc`jye;VU@^@^ioO|+t679XL58F$$|GaCblxpB8>$>O_nQ_X)W*O!#= z^55->J!3hS;RR2MduQKjG@8ro9$^oI9`mAu^iYS0EMpZKTGCHHAX5C}X{@xRJo=Ed zGQRxQE1ET&gFO5FGWexGmyz{(-m1GrS4Aig`IKgJa#NWQR3fKN%q=%|WTl~F;=PXY z)wWJaK+b9;K0*D9bitS;WJN*H!%F+_+s02S$m27t-b{Q~4}$*yxYgsxPdFyL(l602 ztZkcMCS6;$QjDglikBWV0K@j?r|=W^K=8)m7Wcn>HS_-&gH|UVjLbldS5Y!DmiD)( z{kz%gx>(K5UEj)~z*bcKt`u%e3BUczrTzF10bmOFyk@<7B{ce}7GlMtO*>S{{`Lc* zn15XAFUw!vvgT;07N5}b6;-Q|_nE=e{8=g}W8+Q}OW%$&wRtPUxV-+o2K@R;-k9Li zf75Y?=jS0=U4KEi$3Ner*VV_8%V~U<>Bc1{SLiv9wGD>b7BjkeZ@b5Y#f8LmQ--eM zUcBcpiazn}tg&@PWSS9NI%}KD-S*i=!T@bl??L|f-L?yighb4n1O8Wo%`x`yDYTg#>3 zWQ|cgigAjcSj5)6yLqZRHb6QMDcZC-%PRfRvy=T_Pfx#ioLOIh;b;U8yaE>Z@m)(u`ort%7|b2DfWk;lI~ zbJ@a1tfkYrv#zm;lf*%W0@7ai-Hhl69oNxd{9{PC@}Mo-MgXp%B|(~XZ(bvk>jh>& zsppl@)==`^@W?JCS4Q>{Q#D=5PM<7v*4LWGh}uEs!0x#kCbK6(Z8y~T2;^Z(h9ZZtX$C~?=759BMn0qa7NT!`*e-)A$4vLf9h;i4O7v{kCxQ}wq4 zZQ0+cH@VBfIE<=_OD5_f8)$n{VK^sOjbh?!shz=`&;{4_n^=10gx8HF6)r)Y_9U zMAr&_(G{|;PaGi$1NiQ`fpidbroU(sLC>kQLU>?99p1S$o%@3Esg>WoX7vvQPv@bIz=~ zGs*DZ*2H;MFOz)NLqGGw&qH>rCn#}clr7>Oe$r>W3v-=4b#gi%L471k?0}4M#q*|^ zPrWL54z9WagV_c{PL1}FJ6BbcZ^14(?CG}F2H_9H^OIoay}?74ma+QMeo)oUn{;Cp zg9yr^(pvLot#O(aJSH*BWjMJkU843Q_0Zp(JOPkdDoOugJVmjx`9-CN>U1rg={b1; zmfqt$bH}=~hoRbC7ty2%*Vs}N8SO9JKhJjAzGl@b`=iPU71jBHWX(YjjDBWrj?e$~ zH+Q6?6}vl*Z)VMXH0Aktjnw-cKH^b(NaYkzT|AC7p zYDsibcLvNH`sx+o#1=wu$&lo(88;HNc@%#x)2?$T0v@I8WljSF+LF!L*F;Olq^Br@ z%*i7G-(Dj2){=%*C7)4j!f$F1W!MEb?e7#c9wI=qA2Q*1^meHeGpOTmmk)m-QQ-$y z?;q48)(N|7wW!}c2%GjiGF8Bcl?IyyefUFbc!$o7A7TD1ni&Wvu$U+o*o{NGDP8YvlR zA@voz4oXHjETc1>xFF8U4>fCizA!ywQX4fK0;Ji3o~ z`V;N>l(JQyE}p9oHX42M3YU3dTF%MzS(x@}G5Z}J`+`zX#!^iupZ_#a`o#=_elAzY zTc3_cAvFk!INOte|CYf@v;LRH_~)~#{J6)Qe3t2E`dJF%e#(7}pjipQiRFBwB5>+nlOe+9gI0&@Hx-f#l1VlKABB|f`6CfeqSaOO6q8{7D~aNtKi#V3wxQWMb&xl<+_5KEm#`#(h>rZQA>v~u`>ua| z4*#jPo@#Vxl(k&Xe%F@O-JiP|cE@A_c#*Y#?T>S9m^C8OEzNcDA|2JGrlRiaX6WHD zK8k*_v2Q(us<|g`&SJ1@Oi91lW#hxXJ^u5>+hG>6zLD<;p{m<;mwf$f)xj6Uo>ZWm zrusHm@E|??XyO(UkqoMNLH`*!fzL)revpimW=LoD70XisEFA|?vF9hItRU5EwG~=> zbOV-hG*OMsl0d@}9sRhP_tegFxS8?-BYA8QmiIQ_QlI;sOjH*{c#~0cmq}szOFcLk z4p-2v-SvZ^esKScbc6QKgYxdu)q}F`%wl2BkhlDC7Vu7|F8o|>{s4^*ZdF;^D(JA zQGh1{9_6k=u4FFNIysj*U7|F;YNr^TM+lMz^du^58j|^7i_hwto(bTm*CVU|9Gs&I zs-SY7*u^pfPInwM0VSYIeG zNT+;}@&+*ufZqi5BBrkKYt_7YhO~+o=ziXouA4$flHCffR8Q>vRpXi?aCtx3A~&b{ zf2_<+GHh)|9@NPV;J2tWv@PN*{^|0LbMMUgi0#L(eXtmUV9D@ZKhbbW?5kb$Z+%iH z9~o^X62KBKolV6T>}6YDN7qc zMdvK5K!*g_Y;%t2rvG*)JA0R0@9yq>v@O~HF=j$3>lCB_64@Up7{@Z#G|VMlR5sIf z&h?^DORhh02YAHdwv{Onx+YHY0Gqk69e2MTeT5|8>1zwm9^L51FjX@9^Pv=h6<-4F zp&mIG4883QTu_6RhufaHCBbDG0xIs=t`VTHuemn@3m;%Q`gETpTv zdt`zJ-<8<%T_;iIB=j&VEr~=Q=+v@k-f^C5+`W0CeeRQ{`NKl1FBtqCjN+U8zoyr* z&5iZZAT#yUcIWu8@3BFR*SkYT8NOiM!KMqOreg7h-C{Zfq8u-CfteQP@O5aTYTpSq%U4bbvR8r zOSuToNoI4`?U@1af1Mh4(!np6#co?|>Zad@L>u|-$Fg%8jYm@8=lQz!GukQxFc>nC zjy(U)nk-RPcpGXy0fp4_gf*}g0G#2dHwjv4oMbt%LXe7*1FXwdpDfJ3n|wV%2OFxB z9Ak*Ig@3FX{3A{M@B2*eJl8MyQnmmJ)n+IVBbLy1g+UOiM~WDRW7zwkj|8IpIHT+< zA)Zsm(e%nvqJ0d@abLxC88ixLO5z5?2z;he!($6yLn=U3F!p=ag?pEJ@0Ao2nmv&qMYKomta0 z^SRds!L_Dog zf{5LVAAkrPxiyBCj`FjrRt`bVu!mN8{^6j(BVSj{rBTTp8ENNyDoh*1EyvRHEp3!) zSI}hgWs-`gp@_!w1K5lDt=T=loAWZCvG!Alg!kcYlEGFRs7dNyb`EE>TYT8+FyCLQ zFHhrzz8@8w>RlE8nd|+(-@?Coz7C8uJd%!#4~55Xpelc&r8bQMitHaD9J56P1@1K$ z%3)L~Q0r=T7O2Q%dp^8J$k1a%ViehvcLE+c(ykK{I!rF%2l*G0deHsx(rfJEkA`oY z0e|TI@{8O$!?`8H&1udr5mWV+XlB-AekTp3AUSg^z9QR<;QiqSCrBAl%Q`V4p||Gc z*LHJx&mb*zQ-y2S5!QoARVDm zwE?(T!#DZ}%I?nG572k8)t0)nV#YI~DJjTQC~hC%3o*L&TYqqEd7CkrygA$G$RD@l zrNgY0^D>P*ftZ(1_lGYFbpStPAsvD{SIN4C4zrTS7(E(WeOQD;ML5-nQ{ozN22BI1 ze`kaMkZkJm#;lP>=jtiaM1mvwla+UEcwNiKN2L}VLVwSvNQ{?a{dL8aoyz$~1f_gG z>9Qy(>OxTV zYTL=%XGSdHF@Ldq#Q!7E^*SP(0mJAJcPPRHeYZEKBxX*3F5S?lujo}M;>~!Qm_1qV zGN)m3jb%ty6d<)CB!u%>_`TozPVNGLSHzF*PA#pw!mXArwfLZon7P{Rd zkwBosKAiIc7g1H5Ejig%`+|pMD<_R~GS9@PywET!ijbWl>Acn)KvSUGIe+rx0^BvD z&q;|csINDGNB(~8nV?<8;3})EHDfX>tL1aYhmPOH7HuL^dFk{w1?9Co8vGMtMur&d z^iI0QaoTu;KxYa{Td~dr?Ru4w`5;YO-m^ySQQflaErvVD-jj+9dkf+y`DMkqgtNTz z{m)ET?N5tlqRQ?J-uRCliL799lO&r)qHW%T#=C(i>X8uMYczB?AH77nSQRwy9#59_ zcD7x`J6d&>jDEzuSFvMukhz2OqJTSg6!mAS3s$;y1zgJeSs9&Yp+R+V2Qya+WV9n* z{CP1rJb|EZ`I->zw*es7_RP=8*x@{EE41f2tNZYoRZd^=HDAHVVH1RjX%@?M(%|9r z1*fDr$6}pTV-^|xBYRUusgJw-wT+1I$U|j+<$`j7nF}E7OF`bT6xMPxBAb9LH<%p- z>tzZ}w@>UwWbX>4tj*6MYXjM(ap;n<)SYeg?7|0NwWkom!bXEJc+= zTIC0~G;&Q+)9eCsk2YxJ=wqTNg3B39dwG>^t*-!qUA~|gFyRQVk4;c5Ugd@utPDq% z*keZ*PsaYxqiVQ%l#9e^?h6ZvTiww1{Dj|FH|HhP-B)2UJ+XFEba9eYKTP&3p!9b2 zozj?{oc^|f?!u;%htD?P3D;}x9hC~X%LI0!zPl=NGZCO!zDXEBkNau+yblG*D%6Kp zqiw)ZS!_DrXyN-%zGI%8&&kjr1wZPwv3ItHvxdZ<|KZYxLpfAd8)H50rTZyd`C%|8 zXkFA5zIy1>*yByaG6z@qig_(&ZeF7p#N+Yvm#K?{}PM$Bw58y39M5EHod zu`K}^)ss(T~AK>xFKv7Zgt`!D@2Lz!sITArO-lq_aA2~b6)1e zB@=o1o*kz<-aae&YI4Ej#PT}L;XVJfKqxZ~K4)qq+0g#{XR;TZNF z7vuE)ry_&91qB~ZOV*uF?MiiU?>2TB*1XnnpDJcIJRE(HxVAxF1>`^AAbt+aH_GKr z)otfd^*O)4x`*6>19J_JDsn{-@Bq7sH%bH6h=<|I*Xv1ph{*hAu%<1Z^&yeTI3Suc zkEPtsM(ptxBzjRKE%vKrit5DQsqp6>X71*A3IGr3|E0b~UA__JVv_d<&92-LGFPn< zYhVPvCg@u%(}R(odKQT{M36}QXo&6Fj_C}*?P@%D}{TD#||9+wuatHX_z8Fl6 zn`GfSo5vb4Y6FF0%ZEXQXrXXYw)jlP z&683k;IHsy*9DcJDtYQ%BIc;=m8b8VWQ!))EP69pMfJzY{>z_`<9Q@K@~nCXvyzF7 z4?>QWEdm5^VISl0()u5j6m9YH$a#S6G1xgPsQRG;YnH3gV`hR%U> z8mz8FEFy4b4$T@yGn<=Wn;eoWHQ8X()ON5>4wPKm69hXQ=3JOP{M6ES?qUl%`BKBEorm9C6w-p?V9BQ*MUTR; zcPph#fvm=Kofst|qc4~;Bxx|I3EP5s+@>acEJKeT=+g{P!c1kH)~nXT3*1B&+U8U{L*&*;10cCkeB-Shmy z$UkrXZ)DTBPO%2r_VNSG>3D|a88(>-y~ImPh(YP$V!7jJQN_?no%y;Ry~m-=5+J>Z zP=s?h$`dzKH|$`PL)jR%GSPlqM73023`Rz{RY%JD`p>2)MTb68`K}hOo5}WDe2i#s z1z5h(2ozA~!(y!aWFco2&*Lf5u3hd&r$zgLgy$6Y$d4mVH;$d0U60UAr9d6x-Scvv zAHr8CYia)p0vXrN#K(Yj8}^|@B2~~~Nz;dgQqJGwHs~e4k|;#C>H*0Dqh2dvV0x$j z7FmHARCW0u8@R&J2;D~SMm|QzLhSc=#Ri?SF?F!4<+4ZxgXY)IpPxhYk0$lN8m}SB zG%{bd%8%G&Og?vE~VRiS( z?y5teh9W!;te^dOUh=0P<6Ymq8WVG@&bQqGz+_*)q}#5A4pV$nKMU}PPPXiJhP7VW!9@QBir1lyz`BNsVAr`!S!RCUJ~XD07w&kne7!tUhZUDzg>eCJ{8 zLwvcKu|ZVmUQ_+4X3K8^KktLCmLro?DTwhMwO6=tQI~Sk9m&78=H|M)xhj*oJzZ4Q z9b`3`gwZLGIe^&uq5g6M89i4m}#J&k6NFWU&jf`U{6EK>XVZ zXMZohKSeP6n`Ql1_a}x34XHJw2Ix17VOp4l)~DP}eB^zQQFt2?(JO&qz|E`bXX5XR z`{xsAG;!csWBfs6vDz|{b#P?&l?fH1WitL6s&wHY9>YEqGM2uRS^TLVLRb9W;E5xi z>L`00f`M!2Osa%~Bi!_4Ubppl?{A1W#wil)MvX1O?=GsT@wDCY)v}3g}rt652 zF>w}7Y9c;ZVB81rg<1c2ba_x=7W6(9MJI?XupYB7A(#+VT4T`lqwLKn1Jpo1Z#I4} z1mnhmI*Y^)oLj}1>Q63+oRM@lKUi6Ii3iS>Ps9V@_fE!yx2{w>3iZrU+{y^585Q+x ztu(yWNdH$Co`MqbhrUS6t}G;gZ>Z6l6qOrOPkl6>OsK2nzD^U=%mb^?)cXJ%%EZ=v zmF=S{)0I<=EN8uAJ(P*978$+{mUU~?Ts_LZ<=B46L8xY;HxQ}GW=K;Eb&|+V)`1}+ zDv;di6eg2(TPNuVo9afSq{0AoN{3#cCts-}ClT7%?KcfW>qS5-u69sD+-G)?^zXdp zCpkR*Pi@}xrO}5+nqARya@lb-TQ`x)i19u1ug);1v#5BEsN($-y?;{X2DM032ymCU z3{p`Yv*&Bk`2b~_daY9K!HtC7`~j0VFKUl81WYg^e`==CgH!>`9<3cspy3a^rLT`- zem1Z3tmcM{29rk26y!{IFcMvHzpkM;U4P8e+EpWten7p=^wXoEudtg7FKsrkXGn{MFxrB|R$+*h!fqOAG6J8Mm zKG*}_ieVT=*rKE}y}~dsQF;@R1@Q9|tpLwe+Y)*l$NbjIQ6tHC9?=ZYh9HF|R)c5Ium`SXY=8U-f>8_y>22I>l3rLIPx zOIIlIcl_=Qf_9Lk{Tg=2RWczYHhEc@zZii7#19o4Q}h(~H)t!BpMuOIMQcwv&w9lF zvj%MWlbgquwjywHXkRH`Ff3lKXvSq`C%bhVPd0CGWheBF@Jy|bmQx`@JWv|fDowXc zHXsQ__?1vG)HoH8ClUW zhO6}lu!34r{OOS#J^==)@WwF5R_|>*+}wFV>sE@j=eU5cw^+s+vLMNM6|<%GnsY{?x+L>g(Ot3p5Z7H<8z7GFq3OhZPVH(5)iALScy-{723~ z%}aJ0B#wtl*_Q+z3a2jLFVD{dg5mjh_r2;|U8rr{zpW2vdMM^`OC_TsB3Ai}xn7V) zNqq!Uo!yH-m1a?Yh&Eq=sFT;G45X2=2tkcELyGE51k|V;UStQk{Cu$rhk)vcJ&i?5 zF;d1W=e}%y%+URoa$Y5)+VoJbJU@T;*+W3nA9v5LdFB50f`LpGVl08J#jyypFB!6N zUbJ71tbqF9Hfk{szBv$q0eNnD*Z(2Mg>1lh^PM$FLXIhhXvk8nwIA>;Dwjpq{)y{e z_?nz3=jp_IWU7o{OUT)24B4sKepeC5ywR{YT-JU#HDul1#cWXe6Z8Q&Y!Xa*E75I= z8=o{<)K=D+Qe#9#<-)3u%@=t}hlqCw2CAP8yEeg)e*lv|ekt7ke9Zk(gwz;wUM~5> zaZxg6f=kfj;J8|udlq#{+RE4pQ&BzQ0yMc=2NGWKzMUPcmo&td`Mx_^miuxIbEV^# z>4)Z@Yo80+ji<9Q#xzlGeognMT-0I67cRS>c z)*+nQ3J~^~!cR4xpJDE~BP$ne7B(zrH>;rDP(l(RJ&@`WE~&XM6FMKQ1re882k=;$ zz+gWUhavrl29shMzw)!x2Mqt}g;_~?@FRaGm>1Rj#wPlT^t(#m#%d?lrM~wtBfelD zI6PIH2BSg2ecbxoLxpGUa}U{wAws&X^~po+AJ-?Q-1xv_?~bv~bzkb!62%}IAY~uM z1N+C1q)Y<<_auovn7U5p*q3UD4vpv8k^K{YS^QI)*%-0&FubY*r^qDQl`?Yxr<=+(espDMp3CFLt5CeIYq>DbNX z=EbK}KGn!0(LcgEGHZiDN;B*Cs18?FL zuIW<`!5bA5-;xOvIq0ntJC)87o)vfi&GJlT%wDhtVc+oFqnq7M?Ph|5X;5C9Im?hH z7iSGEpHyJ622mfypLwt(d|Z#LJC8qhw5Ie<>DkPws+nbeS+z7lX0Zqp5e$nju2kL? z5xWXhgEn3yKW@iTx15VelIwtsev`6m{*K57%xu8WaC~+zN_Q4bZfGtspl;J`)L)r# zg$yl0G;;kVdM^gp<6w1toP88P;#|*>l$!A-M1`T)#Iz9bz@J7YZLNGiS!M#smY{0R zHcwDIwM*$V+0EdAgm0ZimP}yK-Tb<&7lrgj(D4}%W2%$(>Tdo=$LpnMzzi{Lf6ZbO z2|0`c3`jXj4Xw74Rz2za)V^b`5`nJ#AUn)9Vi#F%rw8!DiqjaiP%QPNgE*2)5I zE=(6FgOwEl341XV{Ho%~(S5#7f-|SsXt6sw8w!{zk>jtD@&`4m--Gt~hM@iBHTgUM zC^=~8_sUbR{217B#o6uq>>MP$DoxO+Gs;vfdPK`f4{STVbpr+vy*8<=2#OfFiNQn@ za^a0%nqBZgjv9Yob4LZ0>}{0$;}xG{QjiE^Nxr(rX-qAh9(XM*da6O9C<&eTudHrC z&!O1zsKeOU_zBJ{KgVqU`X0_4@MbzD?&9Kw->+Q${ACK<;6S5uA0Usa^p*DuI>v{) z`2le50A}fJ3%1wS-uC0`(^Aq6;B>j5W!AD{byGd5csvr;GNGa{VNt$R0iT1g<#SmV z%-C&Agyo@;5Q&jCg5${)3vnJgd}I*X_I+q}5;vzcKG9)rc&<+-rMKEwE2`oTFolR6 z6C_4C;PQiBa1Lj9R)T$%=}L)!Hk_#K>REr*rpt^eRErOj4TmxYK<_}>AV~j2zTcW)BakwBK*xY=oNNOZk>)B4xyhNL zHR?#Cu&lWQlibRr5P^pt8e;ARWI7+^ps4u5BqOHm-#8@7`Ww?ea_4Z`mWA&mSv~V! z=!uY=KB)!fU~rbN+w)tPZ~TZFSt$Ggh8Bc9*3&3BF?WS=hyj13o9x8KpsZt1tW;kRQ8zq|^BoK(q z61F6Fy@vJB^(Fing|=>FK$^5@#GZS`5XpM=ZoeGqLnqk1i2R@#FDY$#Fp&9+7wPB( zVz&0JlEZTL5R~KktzEHcGydSvmpaxL9o+Nw4=;cQb8T2hv&lFmn{b63fIez-(N^|g zq+Pq-h7YNRkRr%y_eactqpU2N44PKt-8hCcT=yaN7L-23z=_y(pv=<}(=9;?;}{1e zqX%-bqr4q`(?Mrm1>ffnXMZr9@=U2Ex&>5Os_#6*vQ5p#qj)o`LrewWMz;J80h)x zOy&7=Rha0%N=*^5F~-u$i|L{uR`z8Td9fmjG`Z*xQnYVX6d#oORGi(}CKNK~HnPIV z4efD2jMN?bvdzC%n?X%aRure{br^*>=BGcJv~*5UXoW zmzdwnAiAOPUD~V@prOeQ611<4dBofmLFLL*15qo!N{!{)L*_>I?ynDntu|`uj%D|0 zWjD{owz?cgW!1J1LaoMEg)v}@T@h1HDZD?DKGda1f%VJJx#2}IX zkX+N*klY6zdxm`Zn#rE(n!!YXZXPtRm|TR1`@9>~N#)qX8?Ah|TFIu5v#PbLX}k78 z?aA%;icN=n8)H3IZ%<8exSgtuN%AZxv;8prqRnD7d#3q$Lmq^$a`#Px?5M7Gqhv8$HhR|5w0JU0snWG!J zI5U}{&MF1%iL$E$GyNA?8Vp`%kd|q!CX<*YS%vUG=_u-ILYbg}{z-&QL0cKBG*3b| zMkQzwH|E_Fzu9q7WG&F)`eL*?J&_tNrmF}@`x&KBbqbhoePUl~am3)z?zBb9=POXx zrRkmd`6WlEX^^(>q)0Wxs`oS6vOAJGyIhc$Z{e`$%Bs`$#5HM-Wyc7qWZc1++Ag@i ze=&Ny1BhVOkyLKP#4U{-HRGch%?zKX7dqT+qZR(gsGFxfH=2k4rpxN<--R(EIvTMg zqn_EA$*=R}^qFEc1CvBOX6a0y*d3AD$}fP4PZu3Sdh&-2`S#Q`M5GWvOQ!ybu;OyO z|5b2pX7MLYPnyz`WdlQls@kPC%!cLWsDi4G*I(sw?R=E#Vu{s4{kCE!N zln|xAFZW+yy1)MC>1Qs}Ts7R(gW0E>KyeCi=gGPP zaO5-m^iqN-+3!L)u3?u4^W-olO!~gtZ}TlA`c%v7Gxh&0cgW!x9dUUieK7e^m{D1F z$IOoS|4d;H1l~EGss7ZiKnx{8f0wqNtB6*$k{Rt?Mt=_-Qg*c%HQstHBDsnmiFyJ_ zWksT~Y<(0~8K!7yk*jGLOgdNMge24Rac zfqZ()hY^v8#qb|+a+b924uJ6*vGU{^c(I%6tMdNc(fxU*^PUm__w?!FZF@!ImHe=n zDz~)1C#@;9XB;GLZMC!h2VCoNQ~sU%fOh4%F6;+=Rc4+_Zd4difNp@*;0;Kb3u@02 z^&qj(hj1#%Ti{z)Y@*&0Ct>W9!KY`b-OuDlma&K_-iqn$$6OsRY>C9g`ycv3GDtuK z84OuAm>L1vkmxTRVR4h)vupd$%XEQ(Ou-W8BuMgaW1^B>d*5%>U#8EHX)rQ%`yO#` zYhhmrw=uqpDy$ihk%<;ZM<6om2mA=FHB`wW()fR?A$@`N#Br|%#kI& zun-#+rJWgkyiWm)Co8g+LmFgq7X={Ez_y`pRk$^Zl5_3{50qV;hbPZXKy~y0xSUf& zy8t}~d0Ata%eFmY?AJ^*0iPF9^VyPE*x;X*!_2e*`Ux#ts`Vx6Ncp#HKZR)p2AR_; zp4lEJC~&!q}JmXu=k9T3^f z_#+iQ{F`fvVwbEPCzKhnt+o%_#dMP_s110S|D_~3tnvaUij~t{D#TO#_lG#!Pt9yZ zRL{nJHElO!2Swj+E#{Coq|Eww(Uk)TdrHKkW^XLejZ@VR^?TH%ITHi>$hK0F%fr@m z5`8Eu9hrqE3@>yD?DYhmR@~dYz@#CJf~mNp*x9LuDIk*(+_HU;j($dkP`I_%9dlx2 zCo$Ow)q5{*5)YjyhmV3m$3tNz3zhAI^X8xYi9f|?g3ilPPvOR?*j>&iYuhj!Wr~zc zDr1uTpy2o9>&3GUGLU1kcGMS3B72c$HctwPbcqn0VR_bMyYhL$Oq$F%+s>A1F1fjr zoJ1COMj%nSHf2wZLoX@y$6o_K5%aPQY3Mab!CSahFQQvQ=j zuw@w{!Ir~|jTewYyoce7@)YXS`odk0q|x4ho+bajE^w)I*F?-UoHlYQ4-agPlr4&0 zLPIZi&K;~V>UH&#Ok1BN7%^>f!MIn#$IXu32)e^_91-lUx8jQIAp=+C#RiSHiJ2af zusrp^2Y<-_io|RCilDU(I0-5ZkP33UzSNF$Xf1VLTL*PxIwCNeeLp4%8F$Bo-}vdCH!-LpFDdrP&q|09M%0t-z* zt2#8nDxE_HZK=Y+u8Id<7*MEZ-9~38e+ClW&+51n?kw`*I7XsJ^685z(L_IV&5~om zV+KC#xq)ZMQRSQ`4EmaAx%vZZzQ!ZjuPogK>D7AD6$#^+07y{kIxXP&1^>dJ?MhJxm zWEBvM)SBxSQEPC6^*vMw?B?hr{5)vPSKvrUl195EMJvt|74G9QG9-);`@HLSS)e2> zu~o#wTp1$GQTGIzS>h^7+UkAxaGuHZ`AskyN(ckIz$-?+cvu2~P?Oa3oS1bU5x4l$ z?q% zP(SYg>9k(j749}YMVR7N`mrCiAgY8MuO;2&awqo?l`=0pvkDslIPahn@EWm%JVc@@ z=H&DRDRyW*%1op9Ys|IoGXp{H^lQGs$CB`ESyO2_+gM!ST|-)dpD}wm81*a(UhpSO zL1~aBi?0E`c_BwCOx-z;^pxR!yxEMo{%@Ye(swh84LB{`A>B+Qu+WkZ<`Kh4gVzyNe6=6C`gkOh_E~@fZuT zg=U&&TbM`N?^?x~u8q3b^|A`5;VGY7VBM&?F)DcCCoKo1Nv#XwHc_FQG;BJ6MOJ~( z)5`g9=)!GCvpdVag7x=_>t&+=0`YPeRkS%U;Oy9wQWFWh6XCH8Y3`KES&8BD0KTS* z{L5pud(6CCW#QduK-StKAyKj1QDU(pb|80mFc$6kxp(ZZ?>Kh@11VntagXwDCn7Q= z)%8^!4{dJ~Jq6ljPuh9N`KU4x{ZEc@Eg4w-x*@Dy0fYQcmItRVzZ(o!V&WT38fX2% zL|p~swXIP6G58i-8DElFo?z4o58&&TQ5?c}TFEqP@mdbNEp(&h> z{VKeV8&8>(V+9zzZ$G43-Zc!6LX>C@q=!5KMZF><8P|)*0#|WKC9W5XUq=)_B5n22 z13yR^2=9l1FOMfDFMup zXA(5G*=@x)cL6P}j-~g40g(f#Cv`ty%ntgb%Xoy}4(;!+S{0wD~e=XH; zd`vHX|0cGwq$6d5sV@WbAea0cpVsIHIh$j!F3v8Y*ZrW!@eR=GH17s|mCSi2&9Yf5 z5N>qk40D0D&v?-`ZG>WmTp0Mt9ibz$#_=@MuZ44Ityo_eLHR*O?$v--m+CDi65!C# zJlv7^{DUeUcd&DI(-_+wD0DW<+(cESh~7R&5eIZY#3wds(axW_p)Soz7%`>)d|9Ls zS>uY?1Vt1|GA%oW#6BDb%^S~iXlHj%s{Q){gn+us6A6`(M^N@t!FInqa~&L@C*ROd zR`9RG0VRJaIQeTS0TuYOZN#FAEBh)o9xEw^?Z_tLOsDrfkT5QIr|HS6Jw232sNz)H zcVauQZ#w-SJogFaKS&at5vZPLdUE{5ikokT4_ZM|V1qkO%4NXk`cypiu(o$u_t~ht@^i zgRjI6LGl|fM}~ex#rtDjL5vYIVTtb{SYVF6 z6wY%@aXTVI@Lm*%$ppfDVN{Sb>1dDvFq2hoG(_bB0$mBBlD#9SCv}<%IOqQCX8d*F z_`5UyufGu*cwXI)`l|Hn28lBDWdWE5MuD9#@g2V^5>?^MSld>F(iAOe0k|4lHry#8 zgR*GVtPE(Dv|{fzz-^f5L(_A8uT4kuD#ic`v*7CVNt* zf`(=rmvtIa0B|Bhui~jQ?L+Qw0}RN~Wf`5o;f@9qMAl7e(cwm|Hnb5_t(rEW^b$Yw z3`H;*N@g9|wiJGuD1W|nCe&oYNb4V1i`iH$y^4G|?8p_<+x@?rs1$%|at!>*J5870GT7_wk+ENVk*lY3K#$ zlW|>)_}oGm*^NOHj%3~D)C8JDYcll(D_XHNl_&=D1(K>z$;Cw;BvIy~B5WK)e5wQ=D>JE}LPyA?c=V=$;bAh2YXZkdT zFO`tMX+|!dLe|_X)(FW%_$KfXkVwRvxA+$?^RNNi#Fxe+ZI(cjP8(tbLzn}FL^+=1ymqHa2B9WXQjtD#d;Me(BLUWyTOw%k;P$ zk*7*~`Dy8wO61X?p7ve9>A>yxVx4Z0mFwW$^zU^RPlHyi=tCw<$SVQ-P#aIPYTD78 zHTbR*c-TCXdoB$veX^^2+u#)B!)*Tk{?7`aqT1e$McPL{DZ1wuN|VOM=i?W;Os1zs za(yvUs=1`E#<8w+Rq^>gmpUSrObyWsDFccrI&UO=?Ge-q*AQ9W zoU}4~dGwCqN){uB!{#HA%}Q#NBoW^>XQR4%uM~qkS%2jz%se~B-OX11LX0Lmj+N8X zgiQ@aw>meC8+osD8Lw~hu{zhfO1#}vobPV;2nTeYr81&39QlM&%^+>X9K9m=FHD@`#{~_8P!!fD_@CMOsJ(n{zoLGc`GD8y;msLw-hRPhe zVxo_f?&*aPH6Q1swZXPDzVE;Qd8gt2^e5|*1rpL*Jl6{gCtAxZy1zi8tPZFwE37}2 zx$$W?24F<%*(<(z8BeTDsSoY*j`60b$nMjr`qoqnOBSmlf5mc@1oo2Rkv#Z7d@+#T;O8x&SCV^RrPwG?Y-$$ z>Evh!={GMl9^KVoSE($n(#kd6&((S-cjBnGH zwqH6&UcQ+;4N4f_B2MQGeu?5_qRb0bLiMeR=2R$>i$GzjkLM1Q2toVTTa`LCAF2p0 z(Cg=n(jqsv+}z6<$inigH{$#p_ZLeKv_)B^+aoxWJa>P4>Vql19aF8cQ|n@%DNRJnPM#pzO{EQzkmf6J;7B`KlM}hZGp{Ch zYB;^?QLJdRx~?u8`KL@khXhJ4I-#(YibmUZ?as(Glf!FplijO%PnQTp&m0G+e%=|> z0#CLkSE_gcqL{XSRei~O$H1@71!Z(U^d1jV-}CEeX|23t42x}>yOGgs4`=E#^JiTB?Ql2DiFc(C=JEWB$xDa+Bk1v_qhu*VE_ zpryT~f~UmP7ge{CeX5>!dE`|%e*NGdNXNlnQD*xT@HGANZGD?npH|uGVc!32@2L)5 zliPRh6!FXcC?1{ca|`oV<0TgT+LEfPsr=QdHqj!J2{bWPB$nfOmrx)jw)6acC zxw+QF*IRbt(~@%J3-gU`+aU;d33hA`jG@~-bz5_KFB)3KU*RP%{w6g;>|N^m{pmqn-4AA4 zgaiaUAWhx23Geld$m^04n;wv1rEfk|o_@J#o2ie#fzY!|C((ECDg161({G_B#%u9% znq)V`&BUbKJu}yV0`Sv<`3)Z?RcR4hrM{f;Qwe2?1Fm7xjU(X3ZLV|5ND+G<2vzIT zicNk#7Gd|cam9hatf&`U#UuC7KW!W65_ecsjNzWwCwp*Wj_Dh+`v7~UPYk>sR9hm| z89w^%Y!3DR77dmo8q5iWc1bJ$M0ChXOe)x%+L}A@S-5pDZ2H{&cX7CB*66MkNp=KN zC40dFx4u{?d>yw@UYZh(8|b(v zYTpZ|KXFa1Njrf;HeLj6^Ym7q?KXFZNBF9~HLJRfsb@`3_LXL}Iz4gkXUQFvAHt0M zm4jjgc5J5e8%c-`brQr(s6i|3>({T>Im!~lra{J_HzC`%y^|SfP-wif`sdTRN2BNt z_M_kT<_hUzDU>I-g_6agsExZNRTSpX;!)Lr%_I3xSgO}|F>Syd02RAkdxOZAvjxoB z`6!3Fwp=p!^*d+AhdUR5lo=yV0Xi@cR7oS_q@#DEDmW&%LJhqooidi{tevGsGha34OG4 zuPi$n0;i9u7_xIZb>6E=ZX4G%;VdpyLOfJ$NyzLqe{&`@@?3S45P*1SaM;E7I7#BVayD+%Uw}n6UFc`=F_ld6JD!|hch%_b`5wLD@ z87Y9+;z>qZ%N@frftg7)>mzT@alg%X<0M}g%vE8E5pJZe+PEEGfIVz_qNDr0s_4uS zc(q)?^OVf^*QeGm82C*e$T>PXp6gN%^CXZsQ1ZkgXd--3z3zw?cqh?%U7{zsQmvl$ z*S~+b1A>FV`HnH1cyEh@XMDDNsDk>RmAY&q=j~1K6)`NwmL+EUCN=0g6<{i4wPLy7 z7zd)hm;R||yl44G@YeM*ac8%l!fLp546fggjEt;4$AA-Urbq)q&XYuAlCkunKjIY= z9CifMl{M^EYl1NLO1^p3-ev-}n9HR;-%=ON=s2@o02z3^=8|9VElHN#>6CX0}%B6Mh@4Y_oS5K)RsMZF-!1XISxyVw16! zNe#?Y#9^77MY>Afj^kVT=rkG!Lr1=@DmfS2aB#|lKlD}Bp!2c4i^!MVNdpDKTjp3N z6csHdT;n=Wc|I%oisAVc+Zs;qCJyD#ls^~q*)5oP*Jh0FV6Z!OxP<{)1is|Iyb=bO zU3v>|5m$|=&&E@{0Deu8KEpps-FAQ;h2gKle|^L8xqgE20^S)w*~H7sYu%SUQYg=2 zcuVsBlP6CM#?fosV#{7}pwh5m7~9Z^-c%|*xS!NW_9e=Gi!laW z)esrJ@5vQVT}30g)03DI(>4z!^_1RI%79TVqBtuUL6CzF#D(A2&3rWq9ZaZJ_(~5b zBm^8*R^RVBzjma9VA1^CsqWlDoDx>31D(uk*6hBL4>M{Gd=gblmjA;SY+*Xk);33` z1F5JV_ofgjhs#&yyH302cDv3S^}k6V)4vHuPqBmXUKX%KpycogI96Hu55-0B^9P^a zb?(P&;{-qb%Hm~vISxMfQMP-D_o`FrYgTU1S(*zlE9w=l@ult3zn;H4ACrRXl!B|u zvo^nd=2?a4yRaV*bvy>*N*XDiqiL)22CoCV*>9> z#1r0T)gpCj7pnohQ5xTvQd`}oI@}5iSd2M9V>Pal#kj1kMQ3$?tv(23m)2H{hKdXs~3i>uB4=7 zBfmPg-`h$~H0t7?f9b#f;j7T0jt@EP-lGx%rv|L1zgzXAbQQO%f=zcv!J`EkXZtu+ zrTgWA&(xjhhg3$}>Rm|U@2MH!0$Q@D_NtY^P`>#~@?F;PyTDi6Ua0EQrGD_>!DaBL zb0ui_nX+$+cbe6>Ky>i(V%eX;=;w>Nys->{eDp)9>+SJ3(?G!w8>s}^H=r650jcUV z^8RPI{-x0Cxtns6y2mxN3CrLNtZxMM+T&)-){ zN#e7r+nO_K<*GYFntC4$FLW@Guzs~AI7$0kB;u|3qWj+edd%)CHyuz@TP{2Le2L9v zmEMf*1gZG;SL=MQ6PS{KN*3>1KV(c{?Ju57#Fmy7X6nJOPX%ep4!+sa4_Z*xT=2A? zfg=I}0c}#s5x_?M7{x9^5he;fbr=^Z2GDU_vxPtYFl-4({M9-k)tlZLVVF6c;6gpA zK`g;{yHf?_{tf9T>$yjk6%#Cy4<#aQQ?RvuD&9GRS)T$tsuY5{IFIt$+53j05&Fo>tfzJ1S7SZ5!g7jYkycPp(1tD_;JmAP=SswWMxAB|AAAV) zR>-CjD9THsPc4R1y&c(q9e$lZzm|Xe2fG7`Et|VzXg-BXsX>w20uJvQrfDR1O#dj| z3wBC&V(6t&o^bgr-eh8F;>KbnxQoGa!ve0AUrgZKz zy$yj<`H+Fx_TYHDeQO2zKtCX9!9wAz8{H6KS68`C4+u~Dv9p|Xaz}%%0E&bblC$7< zA_+xM*$|689eI0ZV(u7kDnN0`PjFX zqwCSbhYuA(ULac>fn6IoaaCwoWYlc`FQ7V|ZiVCr-;U|+O+Qp12HtLe3Fk&8XXt_O zdxxUIDj$3LQ2T1#{;=sEVb&;Tihu&QI1?Jl&&^(P2SQijUhvr&ktHWg-GNbsLxB&o z@AIvao9=0v+6b4f%x&#+34~mcnuq?@_mvVKwp2zgrKp+ize$x@3);KPkB;!x$Ox}D zWo{^W{aXEw-^5qz_Q2zXV7c!j-o{8?_!Aob*FPM{qOtTm`(-?B03DHnnw;+`RXb0m zf4Y(mgnJhZb?8FZ(U)94#uF_p=d?3M0>F!pe#m?uJRMkM3s!ydyMoY`HXKyM0AccYekn;Y?xJXV&EK);>_>ZTh&I+Go_%t!FSY*E zYp-qc%}imVa?%5@)|G=0pO4JH=4xBv>FDA4OugDgNRiu8rcpMiwaz@+0|7L^E-_58 zIAGojNh{(3zy!Np`?;R8dV}1V zS`EmRN%)qxpU@8eGG3%Sr(bVVaX$J8vS@#-@uXnlak6J-RU=@_KwXgF>8T$bMK~BH zP_+%`&;dt-OE(wkbkmey@(lF$}sBo9_Bl~O%^Mk^~R(MlTslS9FM ziQDMQl$HAXJud{K`XTXmo@C0N+-h_RBalx@(lNZ?jd!ErAxSv)Uwb?5b+}kDAJ+@F zB5)Mfci!d+0iF9+ZF5^2Rn-UVd_J?b!7{~r{&hZW3Ejq119w^h_phO~S#vufn}P1h z*4*O-cTWpm;loF8pImw%*=9j3uGi7pjd|Gw-ntsZg58`W|cm|qU-afvEJi^NYH-=q9nT6kI1~B7+cJZGk zF%}V#4XL^GD^Zh43|FZWQGKA~<%S!>&!mNnURj82`_-#KFP28hY!9XE%W_rY;pt(8 z(nN3cd?A7V=r=g`HUV3$d*U1rN}YXcX-? zydx}V1F0cbu0IV{un0MMHKtszS5#C~F(VrDq3Lp`POI~IT&f*a~VvT zC1Im`cJA8>4U#m(XfR52P8!TiIjV=Og~23(L#x`sDBLcddY)3fdcM~5; zKEj;AF0kIY0BYbO$N<^V0;yYNabgKMF$MZ{V{h*K;;oxYvp@}q%gG!gf?G-4shPPr8$q0!#aPN2xQZ&(0*fdV(ZKW z%dnGtOf_qZ)zr|4^!Y#EDFCQN4r>T^m{J25mLPVkH)I~p1cM3Ql95S42f1Sd6t%M0 zqPr?cJ~(iZq9wxA*C8H$Vl5?HlM(8MwV_y_3yPUP0wHRntG8}nt8V|w+(Qe0^|+m< zFo9I8nA3pSp9kN^-daJtZ57vy)^Q6-AK?DDH=&3QfNDlVOKWTOx55+i-Q(Wg)V=%p z?qSbOisX>nFMZ`CD=Z`wyW2_tpWDokX5NCu1i~hJ${)=}6w@z=*m&WWU#Kz<=#hDc za8HBkEZ}?64;Jh}s`#F7p!d9?>@GyWYjIz|c@1lnwj}5Tl#9|GH;P) zBOeqoR3T?u^y`26?dsp)2)-Qj$1x7e-^8<|0pbN}CmGLF&P3okj z!`Gyufh#&NFnJRsE+o9EOceJbw_fanXsGN{MCCkAcbKX5I_iA3(8^Ns`>A5kai57E z7Ywg?{Cf15L;5FqRFWh2;)$s_??6x`q)Sw4kJaY*SBj$Tqzsx|tv7vaIs6i}yZkLZ zRgW&ihoU@ZG|jd;Ua;`Y8ors3tqFXC>lkZy=HDu%|Mo#?k~s0=U>X&;u?T|_qEuL| z)?rVI<1y#mGkpSrCk8S&&wYDtjk%SM0y@1blWmBnV zn(H#5U(UIdm%s}r!atIth=?4vI8T2TcJoj@*TQr&<%{G~lawpqu1Bk)cQAuTd#|^a z*piXjz{nu{Gcb?O8_%?0|3CpGSI>Qi(C?2~_|7w$k6<~xS_XW;rlzn###8tB!x?s?eK}eMsx!FrJmvM$S7QdD%vNl%qWfk` z{)dd+F%v;h;JRlqnl`%y|D6~5^DY0|4`Lyd%&1iv!@km}zer<{^pA=SzLHE%WCJtZ zAxO)Qw`*Fh7ZUR7PRQIu$^tE`k`Oo2`YLtlwT-d7uyHT9#h|lR*5|l4`%y|CFrd-x zt0%E|6MZZ%zK;+SGM*o^)zQ~!9J74*vMxH3Tb4+F%WJgfw68kYUphTeS{~)?e6>fu zf0lRi$NTVJ-<*~-?%@boK~dB6WK)eq37R`C0VX~(T}Jf43{6f|j1pR0kw4^?ZnG({ z4WV$VrF<^EU?C(x7thm{nBoiM;go$PA78gXHOS=!ftiTq%V$1+Vr97BAHVY7p5jc6 z;(lQ%*!I2B6W?BHwqp!6H~=mi{sA*V>~GSLnKWT>o)18G;DHwOdkM8CKYkF&&u{Ev z>rs>?es*Qc0o2KMQQEh!RXcY~)4<`=_=U=#0=xvX+VGMb7dh6`hS_0Nt?&u- zM{M_pN=p){lb$VaGA_VdB2Dx`H^U76a<6r+>5_Wu72OlhV4e-6^g#Aq`RvAL;FR#A zh{+^_whO)unkCw;h^E+7dM2+E3cW2f6Sx2W7xr)8Y?#SRJ=gAoE)D5!5`0KP?(qk0 z1=L+DF_;i<>rQQ&`kqEw?^)KgdI=O|#5~6EYpE_t*AM@(c}L*(utPesVqoH ztda41M|9?|-{6jVW-lI_MZK7@POQi({L3p6DVFm4v%*;VEMAr{EH(QP|jvS zQP7Up(Px&=nlu=dxPMGfp4h9^Pfa!u`P&akzw}`k0Os)EBirv+cMRPl(uo$r+O*2_ zIjV+?_b3uljtD79hhi`K#NRKuSjy9+SUNSqPgY1T{sEpq_%LIYR$FB1_i7=NqvS4q zpdmVwHsk7p^&LOrTw7*)PL#u_+SOP~FY3y zt96gdLvX((QNpY&JiR^0Vy2Bvh{bo{eBT%>+Gre}s@%6#Id!6=mJ!mU#qM>UYVW_h z8gnRV5tMM7|3LkCT50#jK1uPPDxm*ef;~%} z@991Jn1~iS_Qscd%(yj)X7j@MNRh~IL5N|G?wf~S6sv^wwimlrCT|Qs3{oL$S+RG zZenSR*gx|bIvpsc6 z5;^P}N|WtZ2IryVFb}3GQ^g0&2%>pTf9v+`YKc2w4KK@Hh`p&mfF@|9njKB9qG?m0?B`%n#$QDO&a@Z{L2O zl;VGR`p1QCXugTulsm7n$)a2Jum8;NkH`H8Gh}x`CX6b(cQXN;y6sLtBdW}PKXNpzyJG%Fz(}yeT8!T@iD8d zfAOFH?Jt-G;zcCC>3AEbpH8u<85tND(9=GwelA{aIsO!+()KQ42^L0AHx(Al9L7Wn zYM3}Q-ufKtE@Cn|A0t*3LHYmoFaD3m;=YpmF@RB(uAq%y$=X>F%}m^N-?I!4;~?vz zUE!`Y_8-3*)ntts8g3l9X(IQ*L^6~-m)fAe) zd=%tcoTMQbJ^PGWXT#1}p4cB>KzSWF;{lz~|`LvL5T!62iR2zRIPw_AQ(C_cDY!|7d z?x_Z!NIQv8r2l>J_;>u0+I0tNTK*qep|OaE2gE{0p+)O` zbOYG|zlqhykIx+->c*{c`t^YT5dswX%tZ-UNlIbuKP-Y9;^XVvKT8Jl1ZoU>wG1M^*2pnC&%gk*SCc;6 z=gz*-Wq?_gAd(H^gn3N}SL4csp2-A6T^kU&gx)*7ZF%O=>6d^ztKJJV8MJ8<8=L%_ z<*{Ceu+pLPvuMNxEJOrjaD59*Lr6@%V0ZxPpkt1KLN$-k0a0UbNU(*%`E92<8m6Yb zHdG~|9S2N21zf&S5sXxNrf!OAKo){jI=4}!h`TsmCkJ#nXgkjLL2fdN9->FI@OKN0 z1nchRq)UTkhOY?sn&@>P%@$Rl5;sinQ3EhKrBEE-L$s=0Ju_a+C(f@e$$)4y@-Si(A8yfLI+|0euc`xu)xHzBN<^2 zNGzfAFf|Zx$7urW)bR5KM0Rxa^wUclZ-|=Si(nccwVXb47GTZS?*OYK+CY>t@vN9$ zB<03&3@!!$woI}ulvkgZrA^6r<7{VVkQ5kVX18fLYeM91zZK#kotLo4{m&acQL8v! zH&4T8UhN1HUG={ELHt^bG{$Wf`5V1}cD)aQU6jxTv4 ztmcb#p}dqLO!&_}F+uc2M+n2wZAmpd47X`llp??jh*qkqDu@03X*RaI=FEmh=))MZ z08%_t_Bl)IeC_I0&sO1WjW*47h~@*DTGX=X&!7LjRuX1{JRhyD1gO#z1ORqu*cmLG zp9|b%!<)euudbK6xtlq~z(qKfR1XAyVE(#4y~kS6n=sVp{> zl*n`ik?BtYxFMm9rwv9i3g;)oc2`o!Gk`6qd$Ez6ymQ3@j9Iu$vK>rm$vsvO=xGdS zwwKD={G|;RP~6=fE){kq_5Axz{mKAAVM>=Hv(4ff?2#%&4aj%_ZvT4S`;M_IIE6_aC6A zoVkS3wh?)AVj=G_q7b8WFrcCII<%;prT6x{^OSS{RMcUWhJ+6U) zvHa5wYi$OA74f`d(=!jM;Q^o}+eiRJ*!!Lj?QmF zpocI49bW>d7-AU7OgJnqDW9xHR!P>oF-!4|7ppQfmmIFIXmSB^ze2T1>M+ZbKxi7! zA-rkxgek|StOKx7i#Rg}Gul6|jZh*X z_}4}L@`ng#R34o+AlY;dV}NE#2&z=wZ3yTzUEhB_{38Mo1z2Q#O92n;l8u9@G zm<^rsNC@ZUdf}O|c_>j)QJ7(-0?72n{2G*}pN@y#IxK>32|XN{NKb3Gu}+|_18Z#C zJ-7@Lq-8Kca!sITqO)haDhDb8jWevAAC-iz$vX<&&26nN2nJPkxFR{}UFv{QCJqf` z`{WRpc|dW7_`~Rd6(7J?5`$)04nL9t?8z_1 z@)f1LYO3M(F(=q{BKiw%Bi#@!ZA|fjnQIM*fti9;0?vv2>3hNN(}WgbYWR51>bS*p zkayn0)9Wyj$9i1@C}Jl8x>klXKo)vQibw%|a`(;d#Gj9Df^gbTi6C9b&F&&s_sz1X+AA+U^{F1Rb)*F2GmMJ zG*K0y_n%x}>sgoi5M>@m%6>-?!4k8j0fy3(5G9HmBr7lutOmnRBl(TMR3P(rlPMmy z3|^7c?(4*{2??$gYhN63Lo6>G(z%HN9^ew_h3VzLg@7uwbKg45(6($y$N}#Z&RpSi z=tPM^;gqikt~4i-+($;9w1e5==jN4kie4r0BwV7qEaCPUDq2N|$d{WYx2?@!21p>Y z@`CJl-MRXAY5=y2Q5asH;lk=~QG9MhFeiwyRxoa*yDK}2?SUYpxXlnN2 zK(HplfXYq%kbvekf*3^r=0LqjciX`#xd19ZYH$%c=>1cCsGf2;% zztp>!rTjPnGQ|e`cnZ4eX}2X1+-5znFwsrkx1WrgRg!=4(-8EAQW!e9e;vX_ zJlfloVTs%zuG04Q%0GY&9x;Py{wN^Kc+&wwHc$ZaI@#3E6HZj_u_?mf4*HPUA7n6w zZ9sR$2m`>O%hz-Ufv=j`$_v`OWiUh~J9FTVJ*?3v1jzx7r^f)TZ~A-={HYV%v&@1F z`6~ja#Rk+jVhsxcvL3Yi@(>voU9w<$O%8`f5dQp`EV+UPs3|>9+-2Y@L zjgflW@$yJ=gbRLhM14IscK>kN!l&8+WS%%QcSGxe>A4(cG)8>XM@50Nsp37`8X!-Y z^_-E3IfP+p;0R2baCxK^>wdw*w4sJXH!By?&!VPBaG5oCJm;&Kf~kL#y{ zy%5EZusLX9VXvKevNHJ@%Bbta3(3MnL`0LzJ?s3)+&>)oN)knw@|+7|@j3{kdTHGNqbgnT7gMXO^A$hy{dkf4N;J#>x;nNtOusoC?SoE8fiYJs5}#Ezev*c0Bs z$iLiPhopDC7DdZsgRBvHDURe;G+BYE%Gt4!$h=L{tld&$xVyI`H*JQ*lRq#!sop`r zB7&>9VK+y^QP=j215!>;EhPepYY@bSh_f|UDvljM_~`F?z4Ep(gaM^0ecSy=gK7h9 zxg#DL722-y`5!-NmmmWP##M}2mv8oO=PVylJ|Se5!W~o)a0gJKB{U%89W?<)j!fnM zCXVr z0v5=mBaL55Z{#bhSisM@Ieot}D4?J;iU3BU{ng$Q`NubW&`L(#WQR$ifI1F%F;(XwA%CFQd+MOvWEZ))c+QPTRHgL^+#NuSP| zse-BCT^f3Z#sgfWay>DZD7#q`Cg%C9gJ^2J8s(>xk7C zU0(6I9swQ;>DLPt$crCgSWE~dtb{7i^i8-ecs6Jb@a4V7dKQF$#OE|{ERl$wsJO}N z)%#HNLfLRRK$ZGj&(}JckF88N4zt((+O&Qs#yiD9D#mkwXY}O|jC%v^QJmN`fE4BW zyEa-6zmddf_=}FxwJ%ZHhf(>(NVNyrtmM>gW=Zc`Y0idCHdVI^Y z(GRAB)v*{T&Ze1ySHhx@NfFC0AM#-z*5?6+W^#(_z&7dGw-~L>9>cyY@F%{roCerf z79eZP@25Vtf{c1WrhzFV7GeJwcK7vO^qX;j`vQ&GY`&%b!BfZb;aQG6bHX)!OsruKvXd_@?_RqbOE)WqYE|V zN$Z%Miwrr)+{Dix|MowLst_BstnK6YxTFU2!cFxao5Us1XyODM1XX*0%4&s`t$dxn z#30Z0gLn_2j zwPM>FWpG?#ykiyYW%6^wjyq+E6S(hKcemS$5y93k%I%L8v*{m_#B@V8LrjjQPzjiEm%=lc4+p zqs*s`nxX>Mz^$1~BD4Z92s3pLNfn_#T)n}IvBk0ev{g`91C&`&Ao=>~m(V){rnpOC zr7$b#2{qhzO;}LA@Y1h^`p64r)w6v765#TB&D89XF45C$60%w4^<#Q6x0(c59WmTL z(%|0`JB?g3G+j0VD_YnWUVic=1y-dhlXtIwvj0^B6-20%3-9KGQtY0_l(Nvu`J9o z?=%Zm=Q>|ZALF;N)Vsb3f&+`6Fk#^wvNERBjfBm>*IlK*aePU+>k*lLo4QKCIY&lD-uznc`DUQYJfOK^>q+u=i%Jp3bjw_ayZ2CDJH!OtO1xfax@fOFxyvXoj!3dMk zYvf2~PI?l{jp@m3u9%u5Ofp$LQ?I*^oK?U_Ai+KoaRT(*MgOC`_~r&RDN!bp8iw+S zf9+E#M;F6<8wuD_wEl(>sqEpA6F_)S!2ZOOyZ5XNaO4Ek9YeGJT}&uv)YST+1N;jg zL<|R7A+++@cVg>j2$51w>0kqFUzG0r6caa|i2<=*o6cSia!~%QK z$zA(MUs6zIxh7Ta3Lh3mw)c|b0SH(&LIYGhp=Hl6EL z?>vG7P#J-J(6Gk=_@(c1>79Gp)wUk#tse`=$TtsV5H?}~5cY_hpapEvx<6G~o0L8P zNsed@AHZ7r5DYLftE0=z$mj-nP>fjhPXPx|n*iNqnKmR1oC5)xdg9GnX^$|Ghje5J zf$%{|5rIlo^@f4-fUC~QHp~!Mk zleka^IcqdoIbRX=?lILh7s4~);ax|HvuOs@qnk4XliA8$%KA_z4VIF4jd zSk%qeL;eoJ*iS+Vc%5;kx4KlP9`y>$JUtlA_A@YenDGgrWC5IZO{kB+_{+|%wo&;R z>}2Akl0h%qEh0B$FIr0!*m*H`FzIeJqUJ3lY}QeJt*KByfr@BF@B*-yb>|0cx_{uA zRqB~dwy| z5oX%D6692Ac%XnmqSrfoBlJL& zbNV_*hnPZwq9=C?jIAPu;lQ88PaVmRl5v|>#zg%03&tqLrJCm39{iy2^<@2s$a=SG=O@QzB|`P-+rJQ`jh z;w26Yv>j7WHIoBw)4a0BnQ-4y65yGUO8&2gE6s=yFex(JVDiAWVR?hAdi%7x(`MUsRqr9j(WGzT1_?}~Ei&fMrM_@vRR5*X4S!K&6 zCCn5y!rGA6*A2J3#HHV|4NvfDdMlNR8wriYBH!e$sUR83oHXPnG)h2Z#<*Wat#m`D;4-2Qnex6bE9E!{k|3Zyi-$b|N( zB1bx)&6_y1L8iCGC1OqzA+rw%%(GTyRYr=B2nl?D3`imrp)Nw}r=5%68kl(%X8#J^ zEO9R3>ja>#eTrK0Tro!OvN_ql(xB|2W!vll7oTt3W!`Vg<0Nx;@k? zY!~15Ht-iwaPPHDh;2 z-6I+QRC}uVprB-&3#ZWmI5QvN{)k{r?%kt7y1`ioxjsGFX>LohIjH{VB*an^KwRhs z18PKX34?Eu>k6Ju<>z3l!d6Xh#*HF6aXrsGG4Fb2hj!O%>o4P42vcdg_jMW;60i?E;NTBk^stn2#0cxF+GSy@}-~PnU{8TXB zaiAK6AR<5>J1S?eL$Ng(YO_#u8rwoe8ZGLUQcpqq zeKZE8%n}Mvql%@RdjiZcJjim=&tX1E3h9<3JOxnU(UrLQqwV-<^+;Ov;4nfXFl?iS z?)Tl~K}0O&#z&nmK+18`((jnDn^Nh?RCXWs-st^;5%M7W8!aQ5Ejv_Dub~u1^`cj0 zgZATVca{?1<*Cj#L?#y=C1k`9h3m{RAHw7PfTfh2meAV2BRpbrAm9QXM5EMZc1V1_ z*%$jo*0ah% z#+pFaSw}+e1N|&(;3PHdHF`t>3&N$1xf38jVFH{Z{mAky`oRiD<`I)8b-1NpMmg4x zrm1N|OM2Zza~H^x#uPz!Spv*@`guE4Ml0wdef=4J){3A0hvp9qSU7egVrU9F!C8V4 zlkZZ7AEfs|-<$AFT9=c4b%(28209U+*#IC9hebNOLNao)BzF>9Hh`pMv@IdWjDbiN z*&>uau}HjWcmfA3TR*ccEi<9+0)Np2{!|7nj@A5lD7Q_^$T>Epjp*F@ng$fgJXctx zW|29*IP!a2&F5ArZ&??O4kn?<0%>qxsO$W+48yP1|fTc zU@C$Oy8)1s1%|Ts(~|2JTQ1MB?|E)HVK- z%vXQ-o5U-i((-eEvd`W_cp37=k;WG@%+KUtH>i0iJwgTPySt&^!>V-;xS>@wH@)^n zLWIf$F=s}4iHm8d?wj!l^hKTVa6LE{1eXiO0u0{qdle_pZ=S>QzanH}ap1xXpWHQM zMs=IzW7g}sZSPl_`R?ws&x*6Cq?1oOT}9N_ZBVRn=j}R3Y(wXh%hw1F78m>19-=bvATvo# zaB08Tgr3*+CGz=U-P{C+K1J0Y7!o;1J#jl7e3Ywc+A#H54NU&A;Iz<@Sphq^fcENb z@AqFC-@TLeW7=eAaovr{pTKGtjF`;rx842r{Ly6oGQKG+^q@+2k1tgZ0prGTOWXsr z=>p`yhL}1|4n6E!y8qPJMV6gGDa|@@18ytGcxP+772$^x#KGozL8EP}kO}lUoOeH< z+9|bbg5>HI`^YSE$9Ti34a1C?Jo#jvD;tv7$#W~@eWSy} z&iQ{Z3-|y*W^!XAGlsn1$cYz zU7HZje$Yp+tjjYY)PTz#PlxsJFby}w^aISkxL!KO5RRmi59!Dr7;c_1z{VWQ@Pd^zpxtt{#bw zfdMk#?_-vx6LwkY0z&@wJ-mpe+Hpa0cu2#2M%vkMA7iekY1HyFJ1jJo;GkyxVz7d6 zVpK*}Ryp)t-{$fF4^S%W=AUci?{STpz*;41jGPdFy7z>U!Xh@^8EMl(BAogJ#W!?F zaD)jWLK?SKOZ>o3gv?F-yJMHQ2bhLp6XM%>A15UxrJDY@U~Oc607u#Hbj6OP6YnWTj&ITsd@9BEE_ zl;ReOs$a~6m>^}u#}9FH5pg(E4?;dAYRNV~e8=^-4B{`l@LfYt%ov~{m=k-9oTxkR zb2AuJe?SxuE1{lvTk+=-kp~B#qTd∋u5!=Z-6_%yXY+Rns*;qJ*b^!gc7M=GgIO zZf<7#eR2^)CNGh7hB`mU-|Hk_CtR>pCJ$l_4R7Ya;@c2wJ>k&MkQ0=BeAogxDq|9! z{2OCNgh$9Y1^gHuvc}0~TWFbfVEK}OA)9(`rlMKz_EeDGq?Pecj9dXq?TL5fbk^|h zM&aEqWwNFw78G)on<1|mmGlgk;!KVb$IiMVB3GYeMMe;C$%&jidj}I)MB-9gcY`Aq z3lU$eE(Wldmx+g>=H*!{V=TCmH=>E}0{rC<&2-Y~6;%*|#Aj43Z6(me;Nu_qq;*+w zBn`QeeBi(x;B(CRe0Z;`Z?Waxd)H>d7;KC z2G^c=UnhP{b?RMH6Ct`s3Ra3g?4Y-?^+MQCi1q1gL~ugSzm=V~9F*@T=GzZV&PZ+D zBu|-D{bbh5%UiXj33H#4+U)KjVf{VekZASw;3kOrwjox!hn{DjW1%@X21AQ9J1TKm z)mWeBPbx%k$!%KOphFdS&!3p(!}g!q#a1FY;Fc2|eL5&O*irG>`#t+4C?{+KE$j{? znp-1w1I4$OP@;o2=#z|;CI|7>=l^xQzbzI~?m`^Ee&M(C)^>M-aILA)5?fRnlS_Ok zG&1R+(XHM776d^WxV~)iDz~tw3?sYpER1f^B}Xbdm-%q4(G6!NZ7vp{h-S%7c_xP| zGW1B_+v&1V>(`_dKE@q3-Dv;$`F_rRmOIFrhR#q{h;r0CTNhI{)FRMu^c8Ve2LwMj;I2U-)DaQman;b7P7q>gg8@Y##q_e%Mz5*gi=U? zHdTD9joc!{O^La>7Q;N$Te{j{=Bx4$CvB1aum~J^ZtegoKA-&HcTr0#|EIT4w!vlFKbV-!V6^|z-4MT^A9vB3iBv|Y|+>h)!En;qadMaqdZ z#6USavWzD6vQ7laZLWa zmb`lReEY|A&5f#mtN~_jZmvu2@s#}8xG~RRWXA0;>HX@kpH)N7x+l*JK-&g>v}Pzi zM4lePW`CCXSa@sI*XU6-;WQs6eS)e-2U;9B82g%zv8km+z9r2Mh>gB``@NrVrYe@Z z5-j}lC-?<>yocYvUMQ0XO{wA$F3*fnMqqcC6hdTVz;GIT=%(BK{X+GmBXr0w*XKVz zdOVV8qXz!<^AYOcC%pg87Wi?qynt!4^#vW>{(>LDFHJv#m9?!w(Z`(G+t zy!yo{QiGtteBKM9c33r#!z-kd`EFQz13aX}Y4L}kxAi@|f8(TNQ&9CcnIVXtywJ$D zMQiAgm6nQ*q`i&|zKA0mTi2&L@{Ep~+2z4()3D`qx4)ofax=>?$EKn>qq+xg6xbdd zs*9$n5CyAcpZAtp+87f#oixOoXt`T0X6x@#h z?U&ooYfKzL#WHU~*2Iq0r-L|`s^%)wJ|{CRVmr`Fn|8Zp&G+9W8-uYwj1>uT73uh) zG$PkKzm?v`5;0cz6~TDORUcMI!hV3<{B?k6au*YZZnNoOmtBLy%8=V`!LZC7lF7rv zQ)O;+XGXtZpWue{TzN40b#LC49DCI@@)i?3#gtUgPE~Rm`F-$2(gor z+=r1w@Avixr9p~?z#KcRkvoll%w46EvBn*eHza3fYWk@~(KJoj@Wx|oA<>8zFKDx( zBd0mD?(RQjZf52?oBH(WQwE8z+iQe{4tGR{2hWaPn<*%tpSR*4Zsy6{)WL4gp(Dtu zT%%CkTA>VK$#X+)8h~M^nl4z+^$%!Q|)7$q?61`u%KvWTY(38XH<_hkSt%DDIEKc3l68AkX$(tFM@h>!ni~CPf-1;$Z5+z^UmLF9(?7;5{H&Qe32LQcHn)&7@v}a zu!uUMT^b44Q~U>~`8mqt?4Q*e)?2z1Qo9L-r?c4MOm%w)3e}^z?V??grb3^J21J3k z{y;*kA8SinSFlB6V{c*d7Z0`DT9$Oy^goW z2sR%mgtNo#5pPbpYC1}kY$-U}nUyU3BHyWy57%;Y)}ahj40q2QMtGM!Jc)6XhOWlC zs}b5x95FgzW-6j@Mbp>6jFzA{6$+jbMYQJ=4p$&h)f3HJA=$F9?R<|f0Zq_BPaG~? z1B1ltm-M!Vs3>2=pGWV0-jUl3Sq*U7GLQJ($Gvsxa2!+`bI{jAlIi6WVzCTVH^01@ z8W~rrAn!vHeA42jSRFoQef4f=ZEfxFNCG+HWQqg8*K@HLYgZp|boj>|$KW4EmR1uA zhH`jMMx63i(;({s0amjKLtHL3^ugVcOFQJ=3x`tOmHzPZ%>k1_@{%uK$tp@aH z9X)oeJlgSsMB0%EZXla9Bk8mA{zS zx>%aSIt*{0Y*&>Rx)f1RP>>KfaGNE!N_w*p`17}K`5!h$$ftce*Yk4-a&S)nAlWj+ zOHS^LUEccm?^+J$5EKux61rm#D5*hZ|M4hbj*`GToj{^2#rMdLd|ie{F=U7P0>mzC z74lzxJT@Iwe=0Oml+V{t-S(GD`MxIP2TVPp)K^*UZ>BFrlWqM?dNsf6+sT-GOK-WVXex8U?pJiS8r>Xo942+i;Ox!iNe_#rs6? zm>jAnx9r6>FBc(?1N5JSHQ}+%#$ThluW#&(jm<^t<++VAP%*u0ZuadIx^(H1VeSt# zj=x@dtLRXPxPVa>XpZL5PJ%0W5uG3KEkH{w6n%jNbJ&?E`ll9knBlqHbFFAaDgR>eSw_R7F zDZjtR4?oB%Z%hNh?S!cf&2b*TcAu-L?q#Z?Ey|KiqZ!|0l@*O@80?`2Pg?e}eqA`}wXSzy0C= zj`n}y27V<`5M+>W2Adl`&7rnz6YEi~mqnBc=P;PP>8xy}k%1<=Yum~3Y zAfu~w&#Er*8$!!Y5=jq(Jc=W?8O^DsxiG$XbKn&(sq1h8uyV@=Y5sC=HE?lgvsXu3C&SoaM zQRdkAemaIPZ+7<_voKn^{YYPq?C)awx(4V2aDWBA zY7-&zb&r~vzD#8Gkgk83rjWDdm^BL7YcuTC$C(tHh(FTc*FDsbKQVOU?D}Np`KiU&qY9eplC=Vt3mSO{3*GmI=oL-xaONzj#!% z>RGhzHNjn{f0rL^UC?jThLi_m)HO{#=^|XjX-@WT^PWHWJW3UJWldMWnMzw-+A{?T zy!b-C-xu-i2RiDzt5o<|h6&egp&}E(jm>oZxbf)d@Q{nzw~zd|Gru!!n0jI;Or)1) zY=up4$Yx0RLnb5sdRlJy#Z;hN;KER8L#kx5Tf%W01=#*E5jU~ih~h;B|D|+(Be?Is zs(^aRsojDFkGZjT&%JXD{5;#(z5n9t--Sb+ zWOZ-lmJ-ds*{R=rMne%Xt1fl5e_#E7vAVA@>I`INqV^a64ub#hzwi(#QXYEKo3_n- z{kA&)n|E4;y=e%4V)MU5l|PEr0deCm7e@cT<)ilj-cYkj^aMIs+sW>1CKf@^^KwD@ z^|%Cr&`^WPhr7E8tUI&(`hURAZTt7%vkGYmPy^Y<`gbJT3Iyej(r@(Pv~LeKxpy*i z)RxlFa!4izefU2!m$EJ>%?#7a0{`BGZ};xs)*dT`AlIg|k0Lk1{D1jcOdMF>D7^!Z z-2WTR`sEs8r7#0a$NjGU@1=zZMU9J%Gub-*>b^unqjy{=c)x_TbrxnYNPX?Goi#=3 zb%xqfKS={~m5}q!wUR}blYUoR0?xDCG_fIL6KV=F`r86jxw+h8JFLb#7HV`x zx@6o`o2jv|%TmA;^euci#{Ku*j_AO7o__gyK5MV8qwsMK`pBmzippunE1!vx6QAri7sf$*Qw{f!$ptH|&ZJDj(OYIE z_8|Jz;y&H~O!Rh z{JRF+FSaC6WIf%hN#c6tzIXLpr+#_fm%hI6cZ8F#si^oT)17WL2K*rPe_sG?1cM_wLjy!aJJXe_(~An+bJ& z(J2`o!#_4JbVq!?H;Yy1ir9K1o5jtN23>q%Nhyybe9OioWlqBLGgspK7077Uil4Kt zB`#A?lvcMmwd<}VN<2K)Jd@tDT<2!_-s5D;`0=hnF}Q6-9joySgEL1MTBa+jKWJep zS>5+_h-p*nA8roYZ2;-ZV%NPcnjL>W`n=TV(*UW-y(KQLpkvh0BWVQBUa+X@T3neY2&cpGChRwc^aB z332bxG|g1wy7wu@^QzI0>sHxXJ6IY0syV4{OI{CgA6W@#<4tR-%XH^Up(5)v>ojte z_`Kjgmtvq7aHvWPF4W)Tt>YQJg=vWheJj9eWpy1C;Ba*rKT7LyE|chzD` zL0bhK&&~K0DuHWjS@3h%!uap}kJNX}9|J-mr<>7&m%pSsZyG|nt zo6po>I^r%O%`eV<+&>XHk``eV1wMFkoF{K$vL}dm(P|h%vbcplLTjS`;d+nEVN*kr5FnmjE%wIy6DO{{M?5XVvKHB%zx+w^1D_Dp3 zU1uBg${zi*ZoqHx%dC{dD=PA;nh|>Ac2|>LXYaym@~2cLzcg|Pq@^n>Dc!zsQM~UH zr*$Po$qN^?BY4k4rKN`_Kf!oGwtdxn-|mAes>qRV$R@56!H)@RlMGmo%%1W|E+4dC zdrkgI`oMs;fco8P;l7eZe={jMzB+lu<+lA&Eo{~e&YaqnBfaTw?5PvW*qGjH<8JaIolh3Crv{=vUE!XFN5=S3I%M#)@`d5uG!VcSRw#~mWznG#VHE^)R zrv)ATs2p_rX-|&lES1J7K;k;1dd}zT-y&T4OHIG|Vrg$I@y3D=3gnFzt;uQxlk)-- zc~{hPmbp(B1`d-Zca{;3_M2Gnk$?I0a8NM!BcD3NQ@vBqsMh8NR%~8i$0>c#c=DK_ zQBT8dzuV67;q<-J;`E2YQiq(|a@Ok?DuWJ2S7wB}?j)lXldfgAZou~(^Uh)`DefFA zw3pG;Hw;zcF^NeydiRO9H}Q4yI-aGSIc8=?6Cb7>W}d5a4F*P+cV`;VE>u)MRC2US z5_@$siFYw>VTQ4e*QaH*GHM_&XDq{lKL=Nn_-=n``Kl&sjPYF7`;5tBw{}RTYFfXV z5^$CwR>NnMAXtv?mA1<~+Es7D%*0o#KIb?&w%kqJnz*VMG}nfXtwx1um1hhEXurBF zdHt?ZNZyRZ>MqsFj*_5-NJ2EI3aRS?03k@h$KW9~^w>*k|9JX5_YsAR$ z&h5vqdRIsI_%S`j`HPxOW?@ zdBL4K4JKI`txV=vl;Wo`4TY*^B!hkK#VNO8P_yeL!F^~v)4G`d2X#AtsA_16)yTo+ z&VsY+y}lFfRgw#%_pw5>e6=nJ958g-Uo+{|#~DzfA$1twqZ4gVgsw{!{@f6VvE-b&9EFG+eEJ@3FgoXq1kLO8YKW)*aV{ zF4fZI>Oj)^Ui*D6m`}34C@`_vD<#Z`LEyvT_)MkTKxxgV+56FWVMb8KX5P5#a;D9M^ddt=C0s%6;^6RB zVpQ>5-^@a&Od*kXkT!*$c0zb{n9b#tS4Ub)y^d%fh^I#qwCa#-Ek(2Ixx=<>L?5&j zA7*S*hJoVp%ww0{4DA)mwsm3tft~f3TzshNbJVKL`B_%AXO$kS&1I=oo?7b%?BC zl8nSYHR^9whXU+fMfn=ZxcOV>GQUQZX9+PRWbN~6>pf%m)*Z`CR2E;bvJ9OpXN?z^ zZ>3-pOFmZ2J1?M4htb2YkY}ru$5LUR9)(MuQ&cTVzyZijS_wzK)=sq44$1#A<*ZO( zvi@w+b42QK=x*{O*WRa{VRRd$Sp9fm%=&$<$2?6>cMdaxIA_g@P>j1MdHa=$ zOD*^>tT3x_cE!OZ<>6O=vbQGf>Yn%BQHlJb$2RqO`nmUBal%ww!WK z)(6#(({1&t!}NeuoJ}bVpw#^VMm6_4uWdBEgyfOpgCgtdr+^M_=2X;;_PT+x z$GY9fr3UMsZP|limRf_%GStH+kJ1=4DpQYWmpAR5*3_%Y*U<|(WZl7DKEO9${yoMt zseAhHT^A>967T1DTFZ_|&Rg|F^&PC=g3R3qe2z<)URlOxQC{}mqO@>sY?Zwc!c3f#yi|;tX`E!Oi!3SV+b_Zm$gFP)b7w*jMjG5O zNj9b#TtlM5KawAC{&PhD`;7~qZK@#1Y$MHU?E4s+^*mhn-X|IZxBZN#W2&u%QiIxf z`Q|bW7|{ZLYnkYcK$A89DaWbSM=U(H%)z0q+EU744<=ckPovGalPAOlCMZ~XM%$P| z(el*+FSAYWq!kf#a?h8A?F<&5h&NbnDZ5memKJYddFgmbx387J{>aK8dT#mfoz0eo zO%6_{2F?qWOT8%bbP?9*UhADPWiel@HWBy!L16N3(45~ z+p@nn6%C~Y$7ik3*nKJPAXeb@t8@a~`IO|?yZrr z{xsfEbOM4>dT<;by(y|$F@1L}L(Z<38h1o0ME8j1^|j1~Ph91Dv8%3<@2cJq=55iK`ka1?GV2+=RU1qSx0*H9b9%}ebM(xv1od* zAf9Y$gn_fx?Vv69c<0pWkQhOpc1&S)a6a9Dq!Q6|40(8Mj>%F2Pw?n;L*M#o{^@D) zWd@FS)qDmjrf}lhGq~?Xxla^ClSXeBhZcV0<2>udL0y_>j}$^Rb5g z)4Hjp1S;la^ULw;b05S75UZ)-ihUsX-gREHS5;wqu9#Wjj@Hj7zTlY`@Oz+QOI<^< z7|!ZA7)KZpF+o%(Uy~C>h@v;Lq$pf_L2Fr}iB__G&KPVgdEK}{;q6o5(z}L~h z8``Zhcf_Wpx7Dw(Nc%$h5>KY}wX+@!ZI!uW0xrSiYlDPNcMK-zcF%C*MNSQ*rD<^T zWw{ep_u>^^%4ShP zJ6ni!QwN`9Ie2{(#j~TAuR)uqv&S?qF0~yQAgz z&xkIOVIg$6J51hlQSiq5W}nF}xe4YF=_iR{3kAzPzGs$JB|Kaz^BVM=EeuzbyRsIo zCtqs{1fJo@KW8E0t$f?>N|MrQQrF6>Gf6#@E`<4gXk9jcYU+bpB!T1YHfAedF!O%$ ze{j7^iBIS#7*pws(dR#0r#v}#3EdudT)kj=f3)afL6Wy=C~9Vj__A9&c$_m${0mvu zrsouhZXM+0!^@l+TV6XkJNf*jEtLqya`PL(FzKiw9P`~cU zVt;4nnc(jFcH*9b_0Z@w(}S{Uv^WpYcRQtebImBE2pH`+Qc)KYbxXtY$GC(Gs119* z6g|sb5>*q3`nht4lrT}VxP+?|wsZE&?NoSLcdc^++46`@%6eOU40nY#1Tmc>z9SX5 z9!#sVu$NYPDH%^p1Q=3+u>EQ|NAv{$%M+@DHrgvg zujb3od)TO4e>v(Q-g?={Zb99_X4+KU)@P%GzLS8HRI9gy@IzF}jSp!<&m{sVVi{!vNaqL|(3%}y>(Wef-(&`*!eY6_gD_xQsd7s*auX$hlYqp*H@7-&7P}PSP zwx`TW8XqIG>3;X_5d9!;+DseV_=Dp?6P}QzR|}Q`qNNxk^uVk4Rx2S*yyvqD=4z=G z^s2Cu@)xr0uej`pmK$7ZhZccbDkK{z1z;FUY4MV2YC?x&oG-O|tK5#hPebBQKFGZM zhSg%g!1#TK$7}$PN$gz;snkAY)9N&hlhjPv39ODUiX!Nh2m%)&`1d0xiQ-qyn|l^b zd49PgYqtI&Mq5H(mDHbnb&V7Kv43X=?Xgz0u5rvLsjAt0aKn6L-F}^-dO9DRF?1Qe zy;}Aka%uvpf+4AB>vk$@sdYM>l)M}7W0v-5`76{px095aC=?`{CQMCDH>6V1#1HW2 z%~-a0PSD`gSJ}mwKEKbovt6inHC?xkFM3`|ZMprm`Wq!Jy~XP3dpkq43~o(Rq`|vF z%qRKLI#xEKQ^1z-XiK8vcw_z2@d|w#hFbr8J6Ej^=k_|+M>64u1yq+mdfs_{W<(9b zHp|+$`I({RTGoT|PrRu`Q}4fl)ccb0tgVqmQE<7&qju`Nb3@vfmd!baZjb()IHEp? zVh9HL7xU-S_FJE{Q!2oWSX*SBQI{H~nwhMn#&U(q<>WgiCHWAJCn0F>lkWES>Z8f? zx&ny1+E`wWNb=21-?Z72&MG0geq{aC>sRKcndSCf=7Ki;?mboeA+;y%qO+5|BIi)kie-e;_3TgC)4gWgJ07avbJgVzn*`$`lp2?zZO^c!3IcD9oHpBb@< zOINOYHPbCnC%(hNA=lu29y`BrAD8ZfvOGM6#meEr&mZlb>$%`ywU27iQb)dwv%ry%5PEIn4_S)B9<0Gu{*2GVZgG|I9#&4ua@&X&gnb4 zbc#pDKOC@K*^<0_GG38=F~;G(szZCf1cencW0(dYBi>af0XL3NGyUq&{5Xrb2zBip z#%lI0b-Rv#h_c=ZmfOd@5vOWB%6ba!aQM78{IPbDrOK5%Vf;~rS`(ka2A^;bRN>wI z!lJbOdZM_(s_ja*E>b!Wov=#%<6QfMcaOlTqbE-IixPI9c^USD4AvBb21+PDT>|3% zTl;CoX5Of)DvrnKhuJWb5XUK&wb>q!onE}g(#`sC5*^P^n#?tuhvfDONy=<)^qA*m zd!umxkypgHK=))D*P+jO4()TT5$gX52U+s-Z zVgG-Oy=6e$O_%M96C@BEf?IG6-nf(C8r*|B!8N!CcZWc58h3YhcX!u@=62qh_ug~v zoM+~I`u?y3h7Y%9!UCL-2c zc|Pr3Wc{;mbx7)m!pGYi$dr-ziRF9zhmdB|(m!7N^9TJK`kvMnKnV?J)Z&t|Rqper zcn(88-@9z2v6*0j>TNUhS7YR2#{s|RcpSdT>d)YZaiLJs$|vD1!LMdn1QEY8l~eQ+ zszIIm93n4ESC`T05#hJe6Q71wtGBEKrkgub0Pdy_$yxseas0>M2NHrQ+jzQpT+j!9 zH}OXg=YnpGFe$)-9DMy6?cq9_Rohyna+Zns<>Fz51XS704m$>s=JSuI6WOGiomBtg zTagvQ^{nH2G(Nmpb=zka(Zj5t!tajnBf~-lwq_L061CbO$2I)P#&w^c!(Htc!0)dn z8gw#n*`80x=Q81i8!rciNH5!C&YEimD8w~?#be>=B<)|~$Ziwbk162l{C@-lltT1u za;4It*1EH#*OON+W?5Kv{Gg;1p}Ron1ZHOT*$0*S#sz`&it%zDP@DU3H-qEN z^H*z|G+df-Uklr6CQnjX5va=Hu~O&lZ$AH8jWP###GoM_-zV)a9T9-(4W+wwQ-9xm zIl6*7esvaF9!1Fpb$Q^d5@p-)i z-(LT@#BS{V#OMIrMZcA_`|rB|e0feGdcy~}9}uL5?g}Gj*!ODU1zKtB1oPU8D9 z#ei}X&febfY-co+)e-@8n(0VX?_gNom(^2Bp#I;EmHsbi4^?J!eQZNU+AY_jPzUy%; z{Ndog<3C#`3db1ECah7TX|)_#zPBNWHD7fa&UXZc&730s;vW)=M7O_gnA;IjpHnRT zRKLN=Anxq&Pb*oQOrb?x20d_7>AL<`OWRQXkSJeUSo-rG{`w!A~WU@_jNWAx^%41{n(6NJLfG% z`XB>lY3>3-F?rb^#2A0Uxy4ccp!FPR%_e>Nt=cXU0uv)bTvz!A`9k=b2OdUjx&B3{ z;>&=7rv8RNLM@jFP~{2_%1+#;(sPvZ&yu|lx* zN&^>A0vU)CB;{24!poZ0Rtc_$l*Rtrc6v0)+YzY_q7JXyjZ$ASUV*aRroYJSKb={f z=3Eb>n^8y@;lp2MHUO3~TDvElfk9ZxdzbI&-J<2=OSC&->jug3Z!CR|o9o>_g1F~a zN3QqAM>U&Uw*ZscD75=A1(+ROvh<0`jnyD;2vSrmtsEdGY*&BIs-qbbGPg}rO4Hx;q^k`Z8~72VU;TfGH8-UV0o9+wM+54fylMfYs| z?cvEk(*P{Y4?(XHkWyJv7lyI9f16ymb0M7@FhKG3b- z;s{`VMejO5dQW%+cp9qqIhgR&0PHSk+Gh}wK070$-n>Fi*M=_rVY&HK8Q!F#-uLon zVBk9S;8ybi5;eH`2U_&J4&a3Usr|p>qKUSmQD2MI``)obSeweYz340_84vw^*>~P~ zu{I!*@a&2wd;Y4sfuoBI;-eH&tfikiOGe72`}_c4#|oP563CZqrI4<1q~sQmTWK>^ zPq+$W--RDOPYMq)WmFl(OyrYNRr8L=I4%j6Wd>b6eip17-aBImakJaGkrh z2SKC!0!0-8do?-qTrd(a3}*V@64aVI-7k_ZaQ7FqC8vfdh%?Ws-7n(tm=M{|{{l6} zB)Mqf<0Lu8!zV)1V$Sz=9gsFnTV`N%6WQGwJKp%?6^}wYlR6?}!&5uulYj{ps?-jQ zic2hjdvK@#r>$j=^Pilj`c6uKZ>p{3n!cNg$psk=q(0?4Y`=^QFR!(~ z@iks#VUJRxxBzWI6JNMXzx(?K1LwvsVrZn2*QherTGL+zhhV61Z2rYO_h0_Q$Gnwhk0ldU9(cyGyKxy`s+pbMI|`>HVnKQuGfObp9a(6#dN;%(&n~R zoh`c|r=JQakYW1#_0vKb^Bd#DT5fjl9l$5{rz?LY}aVhZ{K)_rJMP?R6$02_+WUdMvE7E{3AuTyO(^E9U7&fwjqm&U&9=mVcL}yZS@E;+z5q8Q{IYjcv-ed( z;#uzE&^qE`{cT*?w;8cyDFLR7zwwO50b+C&O*C#WF?r3V!7%UJOz<8HIDmrRi0=&i z5J7S9f9RIazlvxBo&L1l3?*IztJh-NC(N|(U8hlEUft*pW$yVvSsNQkso5PbOhJ7> zKemlVP|VK$T{ay<{44S8o!y_d2%LW??OYZIv z`O<$A4HmW~glvow5qMr+NV0`)a8|SF$67O3pVD)~1@f-y#oGy(9sY%v1$;pubQH_A zh-du7t7vsFVqjBqt(nu2EUyN*K74{Cq2B$4cM`LB6MvR^c;<T(ZBvXlFDVCacixF_8Oa~7%+sXYR`y@`|G=?B&YuY7f1qd~ z6UwT$zP4LW(&ZTYq>TD~L>-l3SV5%wP9981h743{?-S{TLL%8Y%6brh#~Cm5CY6 zhly9)J76~MDz^*b3kZl4gw23B#vJf;zWU8iSEg;QOkSC5FCGwOmq?9Gx~AtCv-;RCU82cN`iaY`n&6%vag9J z-fMIydJg^D*Eowc9uSYp=VM5N|3@3KK=e>?dI7dOOr(4VS9S#ra%$Kzi7b2EUPar7~hoh;Bn0P!rAJ#t;*-N}W>pt3a@mcT%#9 zU*{#fR@(0Z!aJdF-;{7y&qg|YOkcLASa6e$vZ}q`njh!ee3RBYvRS-4p05IYHw0QN zv(2CjfH`UsE1YzVGWyV8fSY)bjhG3~#akgUO)?&6-(T>S-Ncy^xd zTdu3tdeZl_%fJgW&CAeJfC+Bkesf*6bhd=|guw7VocFcaL4h#Mn=&$LiiRuh;Bkko z?_G}2Li0i*eEQ?GP4YnS+N*+M>wkbSFs-+~w;kQ#H!cXWZpF4YD;ANjKi4IEdmc&d z?E%=1mp&^l%b~xiSj$JCy@HQ~c>V@=GKLtw_|vM}jZIwWxlm=9buC`kTaPbwfUmhd z;dqX8ZO`scRH}w%W*$~IzKVG1;h@{TT3k>F=y7w_{_dpmU7#!nMR;;~(|}(WG#T$@A+xx;&eiN&^y&U<>lq~4l?e0RTfp?kp6Nq67i0p*oxIvk^o=L< z+v|N|iKtw=+u!A@@{1`d>dchTzSngqX!tuY)!mpD4^W`oyDlcsXq0-{ul%5sR^GTM z%&j-=xT5~`D(5pEw~#D2Qxj{p!3o^d20T<&YK02gS`;q}p8Rju8-RW9$6*DduOC7A z>z;K~Ux6k=K&FRqz@n;(91RB+8JsB62N)#s-8WHMqz^yIk%(n4QjDtD>K*JHlWm5Q z7&Y4-^wv{3Y0ho4%_{MRK7%vtT(g=tw9n4hZumP4VPyS~SfM|$h9U(%4+HdV)-N8f zk$EJf4U9NCv1*{Ud%K_$*og(82#4qTob1t*P1BbZvOb68mo)Oo9Lk|d<|4~%){=e= zlvo^s!-gT*EaWu==IwRTNrY3hhDG+WnO~PvOHOH-s0U2_D61=(@0rJ)V@wW(Jxh*u z#{mO#U~IFl1u{6V?eK$)t1-!`*zMuMGXi1dQlgM$18xW5zle)z2DS6>4kW5CCs5i5 zj*_xe$yd!+d^BXxBPokzf$Nd~)CrdYg=O%cU-Dmns7m!q8sw9>s_z{qd2(%p$se4- z595{mNy<)0>_iTk0e(rq(DeU+f0CqATCq#N#|p2J`beW)=R0-i%K(GC|1JZcTUcov zFY=LcRp2vX9_;6+__1Xg=%BvTw?OTpiYcPX&uRo=IForZ8?xGnRL%x0H3z%~m4ZqF zfG_&->fD?n?e~`pMKSg&Xru16Tty z%)~dyt1$~v1TqcZsO*hUd(i_FbW-kE8jbtabuM|9d6tg7HS~pU0VKo;NFC`-W7hsy zuRv%)!{Is~z2nI!`%2%|)-gC88H4ZBHngJ{)MwphO$}*G_Idh9VX9?<_ZsPNEVn04 zxA%*S5nVH4{0tTKvJ<_{ZyeXwM`Sz1#S; z88?V2MzeJQkV!C^HXGSc@l+5vW%gBAL=YG-8PKDU{p{jlWWa(sju&5VkwmG+FKS+~IpPspap zRHI4dKlaRjU1xs9dTYT>p#vzBAy|4kZx9eFCs$=n zZo2lZLi&tG!5OMk^~WZM!}nWe>9|i_met<_TA_+El;L1l!xM`_k}p#%x{2Ncv%btv zDlrPIrsOD1b0~?L2saOwKhkTL5#pa6TKr6^K(=U`rLf9A^Pa#L3OgqSsUx!;y8DF#K{`mlAu>L2+b+AEAUI^K zsu)_RQn3+&7Q+X_CEz*Pi}H8|bc@b@HX1nG{)oyeq4LavEg80PR!jg5C+Qe6?d&ka z>a#?CFe;3S=`bQ=uW`WP-5ZJ0by-n}=o~`CT+zNFuYLITNtx=;FrBSu27t;j?@sfZ z6IoBs?=HD`jb%Bip)^7K|hLx8%Fw`)Kn-e)@;Az zknDYdjsgq3Q%2937i2_-@m|HIcm2o4F?sefA(~Pe3qGR#Y6ew8j6TbK*+BXG-zo}Nq}{y!!R+1C1tu;H$%wL^t-bGF7WLY>BIxa4uQy`)xabpe;_oAx$gn1;pRY!Dy09yRoNxd{ z_NB(HVNj<5K)*~vZXuiXO*~*5kzVNYVuMM@PsDzND1V1X;?I=Fj4jCFTGQwn2T{oQ zNR*U#x#nkTzc9H_I_yFg3%_FE3$t>1Rdl;@fW4;${az0;l;373a62Cx8LYuX4;tQ< z_NTXN+=Cikx@?1odBkXa2Naj9jQ>=8<<(%i1g|OW&XS58!pZ$Nq7aMC;muEB%=?pL z@Ps4ZWVT|zVUvb+C3mEDfR&!muiu|6Y=B7auq#Gjuu~@=vQ`BVgYFwNN?s`@V0t$8 z5rIU15P8Vaj#+<&aLe1khzM@K!xS&9X#4O+!<(ftKA_oS|n{c9y@eXVK9 zse9_1A6|pzr$v(zd3iG!*-a|{+QBE`2lG_#WT~I;i~3(O?0qvn&{W6u6USj!w-h+g z_=)exe0@>SSz%e9{=HV!@{*}f#JtR>^ZK6gi?8^ihq!Rbds|0Kpvu0y*e8_)~C5 zqCV$Nkm*{{Owu6l7lB1ExG<2Ef82A3@m?2B*vfUi^_;Xmesi|L_YJ-{Z6iXys>uaU z$UF&5dTpfS$m-tvKh&S|WgxbGZ0ETJMEG!%PZ42;%o6CX3=HU2tm6F;boJnNN(}(~ zNCkEOqmE)tK?)6IcOIBoDqY!jRe|f5fSw#tKk47FC0&aarTYl;S?vr8aOJvqO*Kev#DeNMo}7ZDM-m`w6R-O^4zxJt#2Cf&tt~>YNQMoSa)h<~6k= z+$|y=b~;*2=7g6&UPg9c8~j?;agAlFlm^iTRDrU+Y)N##!Xsd5di8TlG!F1_+0h_+ zGGz@HS-pO@`#w%%lQtS>YCBgt5{eWnB@D%bJ+V(F5B^7U`y_ygx!LRVYgz2dGE)E#dnKm%YY@APRmeB4ER|m@d~AXsVf16`TRwv$yS!Z+x8ANR&J% z!d?g6~V zKV3NG#_+1E+CbueTu(U}WE@Z{&CF$9d)+{;l~Bq6;JT-19hZgCVzgp$+P~|`5S+;q z*z-_tK{y74Up-?-4Dds<=TG=gv0YoKvNP`zg+$!mYxr2pf6fSj6k3#bk-ZX+?D-&X zpEXO(>t8pBAmmR&TT1u$cfvLxrHJT;lDl6VzN*4$9Am05x?*c0&if|z^bU5q1e-KP zr^IB2j8jgFtgRbv?vK2uO)}Y%aw|&JgGhoiO~b=!5AP)8i-m5KF9tx4vAVcDR^AT) zlY9dySA9a&I-;L}lVS9cvEV=S*>oXo;4CgY7Oe1$Uy}#BfhSu7KL-kQk*>b_DLoQV zv16W9ix-HfhVTk%tRo`KJQ<;VRU&zQH5BjF2`K29+xU1&crkf_-cLb(5cvW?en-9K z^~IrA36oYfMkl9L+F&Q(bwQLDyhcbE#(lIPw~}wn+_|A9$T?2GN^?bG>=2z_Cqz#{ z=c7lC@Z&^d^Jt4&?grD#b)KbkrlE`;&tkMhRG`zueOgJ42z-Z6piG%YC_5k zgO}OXyS;}lW5Ot^PDVO>_vitC#HDs|#`AQf`dN9IrEDYT|^K7IWBs1!6^- zQE!A{z;>I29J6=$V8ianyP1nubBvFG{x2FltpAqbX9L4!{Q}*>zM6UFReG!CfNtUg zF%`2T;a83 zTXns3g;uC0HHOXW(}h5#-an(OvmgUf=6)87<=oQlZCA{%kKn^>L*uCqRD89161Z2> z8Y&Xsp_Dg+1pA>)#`+8#Clcm_rjHVU_2m12>4V>3y)^l4xDBEvQkSH*++n9YBuZ=A zedwC_3(?pZ8r9gsA3>6mnYoO4e5$XB z(7C8g`CW_yd3LsTkATMF+$!vNH-Tk=(dsJ&6Gc1$l3VS=>`2_&y4_e)K7f|* zJW67sC+}9j!iHL|WphN#mA;z=X!Q{(Qz!a>5XogQ66_-=*r=LhUB9ukfUZJ(a&{6t zH$b9RVdg9_r};ih3cJqxCd?E+>h*gg>xuj29r7$SVe8C~J%c&C$pt%#k1-;2>ktaj zi474QGliOm?#HU^T8~WZ%@q&1lNr?WD=gWJM^;qJW-@JB+jL2@lp`k*D6_q39g0YV@Q=B?D!1N#`|k+|k+OeP&9h9D z_w$}MC`61&L@f5eD$8=kVf?_&NCk7pKSh2%6?15h>U(2%K|hU`UU6<+cy@b2e{?3K zLbbm-H2sC+P2v^rDzxaBH6+e(coDJRwq$u5$&AU1B$|uxbu4LT*6u`juZXrl5sBQ# zGPmFl61Qb|h9X~>5WcpC0z)`uyX~pVVEeyEPH^BOgBnb!b0%wnQH_edtJ>ixE<5o6J^fb8pc0PMX))e0l^y{HQW>RkD9YDQLuHqY9 zVH6E_K`QmEsOeGbG+vI(`|S6IK0UeBH-SELw$)=JlB?UsoAUou12$*LC-c|_$i6W$ zdLMT>Y{#l^^0yW_LEX=#Ega&arp^29G;H}CznIaalRL&0GfQPJHu-Hyf51O)d(B%5jd8=1Ac5;_wh3`PUbLv|2bNGDQbiA<*E3-z zzQpMX<#kx;vpaDeF-{8fR~m0>BE^L|&EhIG<&L(=P^RJ3!%@3Y|?Cz)vTA91E zKAvFjKlZ5F9gIwk1yFb$srBj`0#%UzNc7aqfv`v$k2e{m9c0-Ua6zMAVup(x(ifZa z=B)5juM+~yF)n52s^fSh8-3ZsJ+c2xo1kH?#1p_@SI?Jdp=)ca*60`=s_yYRZda8S z8mXv?B)EPQhyN|&u?u72ttC=k5OkU%d=n1&&{=W(@l?lP$b%QjJkJ@)AxEZbB7#$* z{@5y^@*K7^qskk4?@nTbq9|-)q;Fzbw~G&*$QwN1_g@jQIj!UN6RD|?;l9&WHWZP* zoT0A3Ul)_a#x_RNTysiU3u~x^t8Bx+suoulwmYWLR*s14U637c=C)WfAE-tYW&3C|nhmG9oLFYnY50}D>pe$FB) z20oBDl?;IQVZ5_v3GQOWEqv&oi^li3E`DDG+s>$+zx$^lKc!QVY_f#h5wn|u2JJ0$ zMTypfI=ZGk+G&%I@g3t-fYY!#9;{AQ?)mnhybDMzAo=}w=`o$5qzwui@38NFN6uV0upc%QIDNSf*Tgz0*G}JiCWTujo2F+GgN`V18>0ZK-KB#bee|tiRtkca`rR z82hJk_HPQ%Nk;yQ#66fI4Fkh37K=(U!Vpc>`;$y!3!y#qaSN!zFRLBR{gcdaYl0^= z9TXL6)%Kmn7h2}Eg6LNf{6pUFo z``CIx=ZpS8%x#Oi%Yesgam@JnaKvD#dZ9@yLVjbA7yXzoI-OWE9Y6NNFgN~LdIfqv z${THF|CgiBmDY7WTlqDWq_cLZbuM$=r7h##R^1qyxUjwuT$>Y=Y;45Bae3mB=a2;z zk#T5#3Ban#6!ueq3x{z-VmLlO^Xu{`$0PYgFdmGSK}AyyftI+CmtenI!srBAuW`2L z7a?gi(-+)n#S2F)vwUnr#gkyE{@qv*8Pa<}a&a>6 zvAPDGgwwg6F(upadoOW#d>GHCnS|1Im8?gssw@kzMGa(KebzA?B+Y?!w5Itme0 zJmGeL;!{Eq1D~ixEqir%gl@7w9A_I$;+|zBKXJU+9sGnA3cEg~_av0i^nJ1k%!2=v z|8D1bN8$Z0mW_#cW{_wK27*kJn!b^K8qP)qu%YU_A>j=1fQ0)|P?n-v!45n70s$wj zS_Lu#whKM8j>qce?|$;ncW_AVq0WDU?H9Si&8wP`KaH=5X+D0>jfIW(b{9?fh|SSoVqy^jWpsTFm$MUT4Y2)H`DIan%%8SYgS(dH;F9bdlG&(+(6Wc1A zG2trTk0?vFm@7lblegdJMc?l@{-wM0{_(|~sMPFD4&KeLKk%1YNq26wjHsrk7nSH4 zw;@1E1=IG!Z{A^n429Db|0H>X@0+5>gI@b;+ND*(v)9v)Qr=8@fUQAjlVzl zNM8;`8x0J*>Hr=}L^#@TdS(G6qSBc#d8Y2#>PnIS4(brA1RXs+_nNoIaO|!}wZn(e zZ4_bw>FP4vkWSl_tmnh*o~ZR%ZTK+8!p^K&^>#@H+m~8FqbEy9+ic z01Hjk#}+pz;c{j(SSGv2uE-{ED|bdN3F$tHGj%|T;3MD*HXEP;OM-3>0JL(cpQhrc(2N2pMp#IjMJ0 z1m1SfTTIT)SSqd#FhId8ekXi10Z`OT{cb?yxlW>zZOGNzSTSjFU zWR`M~IHUSnJVcwOsLbS+W+Hi2e2bybna!t@EDmR<#9!ddC!1=DB=Eb!^CZO)3N*31 z#xbm0+cH>cMYmb(b{T_LewT-Q=x|F%FRk){ZSA=D7=hqnW2>szRq$xlxdBl9_4B73 zez}z?IH0?>Yd`W@g6PGcc zqCO67g%`x}aW~m-!Ff)V0B`~87{>TISbouQTcVNmE2FIf3QiOguk|M%m+h{#a_YAr ztf-a9h?KUD=f!+hz#elh{@Y-2&?icXh>yTMq7QQiv>W3x|y#(+)9a12O?2 zr5(`7>g2#sKh@ZkEkUaa(%*b@GevBg(#k2g2UFgk1~r)4Kj82vD~GQ^s?;|zvPlhA zg42WW<7JexKaPsYQ_TaBF&#uc^l66kP8eY!RKfC?z(Yv?T&_3n<}l;4=mPx;wgn!qCo?D4 zDREm+MA@J2WOC; zobv_}mYt-PMoNdL+NEW;n}EWuYnvqd(^P|bZKXl*NYqqQbIZivpKhOJm0guOJReZi zwrk46^yCt7TMQ+ZEB*`uSna+M?B-_P;RjyWk8#s?jEiiGmBzf{R19)qb7Uqvk|MW) zzk1lrCUVcc&7WH?MlCEs9P-0(+KVml5CRC8sWnui9q5bf(@_$K;}qt5DB#@)N69_> zk4)Qu*`NQaF<6-&lM^QIN^Qo_)~3j|9sL?Qnv?JCv4aUSA|vn_CL{PGicqe7Si9+t zPEiV!_XNvI>7EFS&rq+ntT6c`I#p*Zj#I@isqeFz@)*3iLx0%wkyxy<(}|+5t`nIuG9gbjUvBj+5TXKY8bgD78CwM$E@ubhdd%?Mi8|6%?$IEc2Hns%I7k+X1-pox4K~wM zJoC?rY8z<@*pxhhehT$}A*dy*THTi=qaJA5!S>83jnO*r@YjL-qJwr5|++664kM4Sy zqmb$UbZveRjm64`JcW~YY6R(BFfi?UBGap9Ye_;yG7N-NNH|QOME5U1v;K&hq%Mdi zo%afUe;wxU#7>e-!X}`72P?_plp7VAg1#XCxLD9BBeLef6TWSPT~+j(i4@YASQXXY z38qK-7fjNzGCaw@yL4C+_!`XGDMjKiAx0)hQ#zpLW0hUaNGAmYy|FI@#Y;=#ZrhK= z!+pUYAMzD?=+k=;PA_82Ku17LPGPC2n&e5EXsUKgZML@>PTJYwfYXntr6%IU>Kq?p z)mkEPNnO8fApQSbsYnOkE5g;F+Aa#AzHnXd# z9E`)YchV(fja)Ti^VyoF*308K!Ah4v}1yS{2RKePYEtBlJr!WM)Km?N_e(Z_80R+apf;*E{DLTK0ri9W8Xk$=D!!) z|35O&`A3Yb652iwdoz&kicO)9b7KQ1VrKEg1Lou_^f^8v&(xQ-WT(pq730`=c$s6b z-9MJFi`ta(#w=JLFmg!mO(|$NG!s5uq^7;llq_KCdn1;-NHcZ|;UI6($ds&>3k{M_ z6lRJlvDIg|A<1eoXha2 za;U#F9d^{?1MGpoQ;47W@7eAjGD581-rYVt^hhDvD&o3+n=uQ|uqrRKd{=u~`8>O9 z>7REtZN#x-KBQ`xqd+AN|J{I3vc^6x)(w?uy0EXPUs9A2iPqUM&jKVIC%YSn`$bt8 zMms8xE<#+sENazegxBDt`kR)I)R7e#CU5S9KW>;U{e1Mapdk2*VSJmnfN(?ukqz-0 z&y<)^?Vx*A>gRD{{mKuf)dmo}8JQW0me zm~zcdf*pC#LnxF3sJX}e2V;XrPN4#hXW>3R4_-*f$lpvk9Zk#V`^O}A)Q(O6Ci<58 z_KRZvd&ZxpQX+VTYqExCl)-52ZOG&h3;m~mjbw4bqn==JcRQ+wbE9(vfTy{N;sI`$ zrH4cq2wU)WK98XC824ryI*&dH8-3E^Vl*LI74)XOze3$hl@zL+=QPz=S{Z&2eti*h z@ceoeVLz0TtQsPD1EPSP&Z%e#E@dkKID(XM#SV}16PocZiAALgR;Q90y`yo2sSlDMauP5I^w?DV!bY$Ze~Ew6II>dQQu}wEPzuN@N2YjD$)I->wVB>ljU7ZP zRN}Dkj*S=h$4B0~xxWqQ$W&5D=OUP+WXE)b#474}u50QJ{EE?|@AuTpI~|%V-`z(k zaY{_B%7tZw;>IaSa8%4Ls}+{atVh?|J{>5)2nLK1xt~MCQc$W)Ll>mF{*{#2au8OC zOj`IaR!E|vbXEv|mS8Y+*CQ`iQett2DK&A`ZG}Jx*&FwiWFYN)+_F@3xxgrNA`Q(o2oqe6c7s9HWvLNU48CTY%%uv18*(@EVXm*fE_MCdT7^F!`pJ9NB8$UEx zR9B!p+z(0O)$))By5P8?m2Wtwp<~9Rtzuj;z=$0ZKTuoyCC8&~I_p#*mF16R+Y2iN zy>95in^_ZPMv#$y6P?toi z^E^L$=5}lV;cM$NVAY+c_9@z4b%il<4+__dN*KtUDIfubpTPaK5)MbIAbFKuR`Ia$ z@RYT66&g#uD|l4)SS+bb0C{`QX5Q3@J=-QG9m5EgJ!UY<*y>3LO3R=eWLPzzfw5Vu zn@;O~iMu4|@|A`wj3p@`%dPAngd~RkvJ(dJuP~%jH&YOlo{*e_oLgGv&SK5zp_j7H zSQMl2K~}y|etEUIyGRbXN(QAxNmRV^orT?U^WFs4v}lnc%vgt~*@Y=9zjO1o7bWKQ zv_ z(3x|4e(j2Szh)NVV>}7Y6mb@?Jf!6UeGu<>8{98R(>1%ttD-Nlo<7U zDY$|a-6j}egm49!$E0AW8)P-#8yhjl?Z;9V1W)(_%+CGNi^}3mm0n&aQGT2Sb@S(f zk?fZ*SAr}k4!UclHu)px4a%p4UvQS}2;ODf-u~#)kU9^NT97nUjfyZ3o2r4NRE%jY z7vb&}FS^L=MfryvL_-G6ryaa5-IU@clOf?gNU%)wBXLNc;i8d`WGt8LoJ95#lga{= zoNA&b8rt*wU~@JgxF-95Ud?RKn7>#o7m>5~))8XL6*{XUTWm0Sa-UaP96G1I%wY0X zo3KTcxld8x>8?XzV>;AZFh$=iU11mfYT5`Xm!z#zqKs_|@lTCxw&F_`#;3WdnU?omdYj^j zTj2haQ>{eLkQ9!!`7Gu$RRlo3U;#6y!xb|hD>Wnp5yC#g;xdkJ?&HLl9SvB_r``y8 zBXXN&_#6awDsblQ{J5RQ2dEd41Rn4gj8DK+s3p5BsW)-jbubVY*4zrqR!MMLNNqVr z3$?TNds=7M@3YJ7GbbhI9UF`9&61~}kO^{E(<*njZUT5tOvJ=Y_+3i+@>C#Zg;yYZ$ z=3J=EJQPM*$6%qU!xp;C%9fE0PFthN(~x()W@#<-W9Pn3W=-^!p_TOU1kpZ+pDC01 zBMx7Xuft;T?3KbBB9`~QL*lLfAlT&%VOkW6+gfo9zWu`Qrocu4x8oZ$=uGPBOiJ2S z2AeyfhAU{)E-iL`pl`>7Z@>6p{+b39}dRrRQIz8DiB_axyqzp5jVF zLVYQT2*kE2xA15gt%&+0%~*XDwfVMzprV|~!5LO^aJc_7Ee!no$>FjRPCTQZyx0EfE5@hdU>hC`m)YdV1qA3gM$L&40~|%v{odeIgx`RqA!9zhiC^BUOuN z3J3Hm`4b4ot6BhIYuF|)esJg#mdceO5YDJyd{|!=nPayi_PJIVvXf z3PM98m)t>wqTKk;pp=D~NaTH({ z;h;Q>qzYD2cZspDk{4Cn7vGj=^>1%j9L}32*CJLVC0R2>I!4ZH8DZGE1s{byf zhIErScX)pX2MT?HCFD7JXYyw^F-H|PB!pZ`fqASUnnwdM&)*Y9WJ#%i#1nrNMO$RH zSq{QtZ?PxS@WzmNm-)_hbP>5US`Y@3X5cbk8|l$76aqXkOGl;H{Q9(#dqYl&7*ft= zry$hmJo;wfGyFQ-q#pVIu=bW=b!E#IDDLhMED$6FcXx~665I(I+}&LR1a|@icXzh{ zf#9}rclWnwyWM?~bMN=w`?o*Xd#$N8t456)Q;ymMf>LTH0$@6JFzX+&5da{RFthXl zt09ntfj6Dwk`H?+oihAO$nFmWqJ*mK;7Wm7iwu{}(H(W2Zxo0*j^Wj%s)CF&q{D3X z%n^^|B42LbfHHBVW~bg_WUg%v6>BOZecmM)b2|*owl`z7Mf%c3L{W+$CT+}X`EJku z=?wMllma*~%bb(c%mk5kpHq$J=|2vP@IbBjR%SLCma>k>O5ve1k(8t!mG>Vn`81q6 zr9Hw`f0V!mrw+-No;T)=?kP6X1q%dAXRPfsI5_Xz=N=|{Lb7Ne076m@85QCA zDh-Da92}PBFV>--)pT&c!?6l)f*bm*HY@CHI%_Is7Zm^CJu)xrVI3pWn(|Z$Vioxs%q{N1|(2!Je(~yVqjKy6fi6Wy-QkYriRMPX4LQ$Aa3mpPJr~># z_Q(MV8c_#xF((|XgZ5vc(gA&SwMtPaEU8eo*6vHr;LCff5{0sVL#sQM;mB?eX-)&n zsd@j5Zb$|SF81!Eu8|<4#deg0pTr9hp4ASk^Y*sHJ2}@4Hq8Z-A-qxaJ7+EhJ)1-F zJeBvSgf`j?Nd&U&_PeK0f6dQKtUn}@l5}h^dU7=QjR*ueIChe(?sg7B0S#C-Y+B5* zqanruK&R?jwOj0xycfxAk0oJrK|U8V2&R^=v^#kZa5D0O`usAY-YOX36Wa!uGRi7y zEjj8=o*s?W%#1E7)+^Ker`Zon)+M(N2h`408&Ot$x|Y$Cfgb^dQ_9xz7J>4Gxp zZe^c(-k7sJd044cf|D~iFI9fz?25o5fOg-#$~px6=eVu?hNe3UKJVr@67X=D=r;dy zskcq9DQuVl%xTqpz(b@Lp1SRr!bmk3BPT|d;67=EHV+M z3&Y0&@3?DJcwc%}$GB|JYvMI!Pw*M*xAXFAT|m_Ez;wzEcpAR(f*$**W5cjn#jVq19#@rx& z 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 z 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 MXkN%TfBVt@0fYxuh5!Hn diff --git a/python/nx-cugraph/README.md b/python/nx-cugraph/README.md index ab267e5a756..148fb2f0993 100644 --- a/python/nx-cugraph/README.md +++ b/python/nx-cugraph/README.md @@ -2,31 +2,134 @@ ## Description [RAPIDS](https://rapids.ai) nx-cugraph is a [backend to NetworkX](https://networkx.org/documentation/stable/reference/classes/index.html#backends) -with minimal dependencies (`networkx`, `cupy`, and `pylibcugraph`) to run graph algorithms on the GPU. +to run supported algorithms with GPU acceleration. -### Contribute +## System Requirements -Follow instructions for [contributing to cugraph](https://github.com/rapidsai/cugraph/blob/branch-23.12 -and [building from source](https://docs.rapids.ai/api/cugraph/stable/installation/source_build/), then build nx-cugraph in develop (i.e., editable) mode: -``` -$ ./build.sh nx-cugraph --pydevelop -``` +nx-cugraph requires the following: + + * NVIDIA GPU, Pascal architecture or later + * CUDA 11.2, 11.4, 11.5, 11.8, or 12.0 + * Python versions 3.9, 3.10, or 3.11 + * NetworkX >= version 3.2 -### Run tests +More details about system requirements can be found in the [RAPIDS System Requirements documentation](https://docs.rapids.ai/install#system-req).. -Run nx-cugraph tests from `cugraph/python/nx-cugraph` directory: +## Installation + +nx-cugraph can be installed using either conda or pip. + +### conda ``` -$ pytest +conda install -c rapidsai-nightly -c conda-forge -c nvidia nx-cugraph ``` -Run nx-cugraph benchmarks: +### pip ``` -$ pytest --bench +python -m pip install nx-cugraph-cu11 --extra-index-url https://pypi.nvidia.com ``` -Run networkx tests (requires networkx version 3.2): +Notes: + + * Nightly wheel builds will not be available until the 23.12 release, therefore the index URL for the stable release version is being used in the pip install command above. + * Additional information relevant to installing any RAPIDS package can be found [here](https://rapids.ai/#quick-start). + +## Enabling nx-cugraph + +NetworkX will use nx-cugraph as the graph analytics backend if any of the +following are are used: + +### `NETWORKX_AUTOMATIC_BACKENDS` environment variable. +The `NETWORKX_AUTOMATIC_BACKENDS` environment variable can be used to have NetworkX automatically dispatch to specified backends an API is called that the backend supports. +Set `NETWORKX_AUTOMATIC_BACKENDS=cugraph` to use nx-cugraph to GPU accelerate supported APIs with no code changes. +Example: ``` -$ ./run_nx_tests.sh +bash> NETWORKX_AUTOMATIC_BACKENDS=cugraph python my_networkx_script.py ``` -Additional arguments may be passed to pytest such as: + +### `backend=` keyword argument +To explicitly specify a particular backend for an API, use the `backend=` +keyword argument. This argument takes precedence over the +`NETWORKX_AUTOMATIC_BACKENDS` environment variable. This requires anyone +running code that uses the `backend=` keyword argument to have the specified +backend installed. + +Example: ``` -$ ./run_nx_tests.sh -x --sw -k betweenness +nx.betweenness_centrality(cit_patents_graph, k=k, backend="cugraph") ``` + +### Type-based dispatching + +NetworkX also supports automatically dispatching to backends associated with +specific graph types. Like the `backend=` keyword argument example above, this +requires the user to write code for a specific backend, and therefore requires +the backend to be installed, but has the advantage of ensuring a particular +behavior without the potential for runtime conversions. + +To use type-based dispatching with nx-cugraph, the user must import the backend +directly in their code to access the utilities provided to create a Graph +instance specifically for the nx-cugraph backend. + +Example: +``` +import networkx as nx +import nx_cugraph as nxcg + +G = nx.Graph() +... +nxcg_G = nxcg.from_networkx(G) # conversion happens once here +nx.betweenness_centrality(nxcg_G, k=1000) # nxcg Graph type causes cugraph backend + # to be used, no conversion necessary +``` + +## Supported Algorithms + +The nx-cugraph backend to NetworkX connects +[pylibcugraph](../../readme_pages/pylibcugraph.md) (cuGraph's low-level python +interface to its CUDA-based graph analytics library) and +[CuPy](https://cupy.dev/) (a GPU-accelerated array library) to NetworkX's +familiar and easy-to-use API. + +Below is the list of algorithms (many listed using pylibcugraph names), +available today in pylibcugraph or implemented using CuPy, that are or will be +supported in nx-cugraph. + +| feature/algo | release/target version | +| ----- | ----- | +| analyze_clustering_edge_cut | ? | +| analyze_clustering_modularity | ? | +| analyze_clustering_ratio_cut | ? | +| balanced_cut_clustering | ? | +| betweenness_centrality | 23.10 | +| bfs | ? | +| core_number | ? | +| degree_centrality | 23.12 | +| ecg | ? | +| edge_betweenness_centrality | 23.10 | +| ego_graph | ? | +| eigenvector_centrality | 23.12 | +| get_two_hop_neighbors | ? | +| hits | 23.12 | +| in_degree_centrality | 23.12 | +| induced_subgraph | ? | +| jaccard_coefficients | ? | +| katz_centrality | 23.12 | +| k_core | ? | +| k_truss_subgraph | 23.12 | +| leiden | ? | +| louvain | 23.10 | +| node2vec | ? | +| out_degree_centrality | 23.12 | +| overlap_coefficients | ? | +| pagerank | 23.12 | +| personalized_pagerank | ? | +| sorensen_coefficients | ? | +| spectral_modularity_maximization | ? | +| sssp | 23.12 | +| strongly_connected_components | ? | +| triangle_count | ? | +| uniform_neighbor_sample | ? | +| uniform_random_walks | ? | +| weakly_connected_components | ? | + +To request nx-cugraph backend support for a NetworkX API that is not listed +above, visit the [cuGraph GitHub repo](https://github.com/rapidsai/cugraph). From c3b3ceec056723fc0ac9dfadeba7373e1e88387b Mon Sep 17 00:00:00 2001 From: Rick Ratzel <3039903+rlratzel@users.noreply.github.com> Date: Wed, 8 Nov 2023 11:07:29 -0600 Subject: [PATCH 078/111] Fixes typos, updates link to NX backend docs. (#3989) * Fixes typos (repeated word, extra periods) * Updates link to the current docs on NetworkX backends Authors: - Rick Ratzel (https://github.com/rlratzel) Approvers: - Ray Douglass (https://github.com/raydouglass) --- python/nx-cugraph/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/python/nx-cugraph/README.md b/python/nx-cugraph/README.md index 148fb2f0993..273a6112d77 100644 --- a/python/nx-cugraph/README.md +++ b/python/nx-cugraph/README.md @@ -1,7 +1,7 @@ # nx-cugraph ## Description -[RAPIDS](https://rapids.ai) nx-cugraph is a [backend to NetworkX](https://networkx.org/documentation/stable/reference/classes/index.html#backends) +[RAPIDS](https://rapids.ai) nx-cugraph is a [backend to NetworkX](https://networkx.org/documentation/stable/reference/utils.html#backends) to run supported algorithms with GPU acceleration. ## System Requirements @@ -13,7 +13,7 @@ nx-cugraph requires the following: * Python versions 3.9, 3.10, or 3.11 * NetworkX >= version 3.2 -More details about system requirements can be found in the [RAPIDS System Requirements documentation](https://docs.rapids.ai/install#system-req).. +More details about system requirements can be found in the [RAPIDS System Requirements documentation](https://docs.rapids.ai/install#system-req). ## Installation @@ -35,7 +35,7 @@ Notes: ## Enabling nx-cugraph NetworkX will use nx-cugraph as the graph analytics backend if any of the -following are are used: +following are used: ### `NETWORKX_AUTOMATIC_BACKENDS` environment variable. The `NETWORKX_AUTOMATIC_BACKENDS` environment variable can be used to have NetworkX automatically dispatch to specified backends an API is called that the backend supports. From ff943332512e6b5bd3bdbad4893ce46987fd9b1b Mon Sep 17 00:00:00 2001 From: Rick Ratzel <3039903+rlratzel@users.noreply.github.com> Date: Tue, 14 Nov 2023 09:29:03 -0600 Subject: [PATCH 079/111] Adds update-version.sh support for proper versioning to cu version suffix pacakges and nx-cugraph meta-data files. (#3994) Adds `update-version.sh` support for: * nx-cugraph meta-data files * `cugraph-pyg`, `cugraph-dgl` env yaml files * `*-cu11`, `*-cu12` wheel dependencies in dependencies.yaml Authors: - Rick Ratzel (https://github.com/rlratzel) Approvers: - Alex Barghi (https://github.com/alexbarghi-nv) - Brad Rees (https://github.com/BradReesWork) - Ray Douglass (https://github.com/raydouglass) --- ci/release/update-version.sh | 8 +++++++- python/nx-cugraph/_nx_cugraph/__init__.py | 1 + 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/ci/release/update-version.sh b/ci/release/update-version.sh index d3dbed6ae46..0f2d4a3b914 100755 --- a/ci/release/update-version.sh +++ b/ci/release/update-version.sh @@ -90,8 +90,9 @@ DEPENDENCIES=( ucx-py ) for DEP in "${DEPENDENCIES[@]}"; do - for FILE in dependencies.yaml conda/environments/*.yaml; do + for FILE in dependencies.yaml conda/environments/*.yaml python/cugraph-{pyg,dgl}/conda/*.yaml; do sed_runner "/-.* ${DEP}==/ s/==.*/==${NEXT_SHORT_TAG_PEP440}.*/g" ${FILE} + sed_runner "/-.* ${DEP}-cu[0-9][0-9]==/ s/==.*/==${NEXT_SHORT_TAG_PEP440}.*/g" ${FILE} sed_runner "/-.* ucx-py==/ s/==.*/==${NEXT_UCX_PY_VERSION}.*/g" ${FILE} done for FILE in python/**/pyproject.toml python/**/**/pyproject.toml; do @@ -108,6 +109,11 @@ sed_runner "/^ucx_py_version:$/ {n;s/.*/ - \"${NEXT_UCX_PY_VERSION}.*\"/}" cond sed_runner "/^ucx_py_version:$/ {n;s/.*/ - \"${NEXT_UCX_PY_VERSION}.*\"/}" conda/recipes/cugraph-service/conda_build_config.yaml sed_runner "/^ucx_py_version:$/ {n;s/.*/ - \"${NEXT_UCX_PY_VERSION}.*\"/}" conda/recipes/pylibcugraph/conda_build_config.yaml +# nx-cugraph NetworkX entry-point meta-data +sed_runner "s@branch-[0-9][0-9].[0-9][0-9]@branch-${NEXT_SHORT_TAG}@g" python/nx-cugraph/_nx_cugraph/__init__.py +# FIXME: can this use the standard VERSION file and update mechanism? +sed_runner "s/__version__ = .*/__version__ = \"${NEXT_FULL_TAG}\"/g" python/nx-cugraph/_nx_cugraph/__init__.py + # CI files for FILE in .github/workflows/*.yaml; do sed_runner "/shared-workflows/ s/@.*/@branch-${NEXT_SHORT_TAG}/g" "${FILE}" diff --git a/python/nx-cugraph/_nx_cugraph/__init__.py b/python/nx-cugraph/_nx_cugraph/__init__.py index 8ef976aabf1..26638d1e735 100644 --- a/python/nx-cugraph/_nx_cugraph/__init__.py +++ b/python/nx-cugraph/_nx_cugraph/__init__.py @@ -136,6 +136,7 @@ def get_info(): return d +# FIXME: can this use the standard VERSION file and update mechanism? __version__ = "23.12.00" if __name__ == "__main__": From 2f44a9cefa3116ac69d1f8c5cfdca87b50a083c5 Mon Sep 17 00:00:00 2001 From: Ray Douglass Date: Tue, 14 Nov 2023 11:31:50 -0500 Subject: [PATCH 080/111] v24.02 Updates [skip ci] --- .../cuda11.8-conda/devcontainer.json | 4 +- .devcontainer/cuda11.8-pip/devcontainer.json | 6 +- .../cuda12.0-conda/devcontainer.json | 4 +- .devcontainer/cuda12.0-pip/devcontainer.json | 6 +- .github/workflows/build.yaml | 24 +++---- .github/workflows/pr.yaml | 34 +++++----- .github/workflows/test.yaml | 10 +-- VERSION | 2 +- ci/build_docs.sh | 2 +- .../all_cuda-118_arch-x86_64.yaml | 28 ++++---- .../all_cuda-120_arch-x86_64.yaml | 28 ++++---- .../cugraph-service/conda_build_config.yaml | 2 +- conda/recipes/cugraph/conda_build_config.yaml | 2 +- .../pylibcugraph/conda_build_config.yaml | 2 +- cpp/CMakeLists.txt | 2 +- cpp/doxygen/Doxyfile | 2 +- cpp/libcugraph_etl/CMakeLists.txt | 2 +- dependencies.yaml | 66 +++++++++---------- docs/cugraph/source/conf.py | 4 +- fetch_rapids.cmake | 2 +- .../conda/cugraph_dgl_dev_cuda-118.yaml | 4 +- python/cugraph-dgl/pyproject.toml | 2 +- .../conda/cugraph_pyg_dev_cuda-118.yaml | 4 +- python/cugraph-pyg/pyproject.toml | 2 +- python/cugraph-service/server/pyproject.toml | 14 ++-- python/cugraph/CMakeLists.txt | 2 +- python/cugraph/pyproject.toml | 20 +++--- python/nx-cugraph/_nx_cugraph/__init__.py | 4 +- python/nx-cugraph/pyproject.toml | 2 +- python/pylibcugraph/CMakeLists.txt | 2 +- python/pylibcugraph/pyproject.toml | 10 +-- 31 files changed, 149 insertions(+), 149 deletions(-) diff --git a/.devcontainer/cuda11.8-conda/devcontainer.json b/.devcontainer/cuda11.8-conda/devcontainer.json index 83078a304ed..e48301e4d14 100644 --- a/.devcontainer/cuda11.8-conda/devcontainer.json +++ b/.devcontainer/cuda11.8-conda/devcontainer.json @@ -5,12 +5,12 @@ "args": { "CUDA": "11.8", "PYTHON_PACKAGE_MANAGER": "conda", - "BASE": "rapidsai/devcontainers:23.12-cpp-cuda11.8-mambaforge-ubuntu22.04" + "BASE": "rapidsai/devcontainers:24.02-cpp-cuda11.8-mambaforge-ubuntu22.04" } }, "hostRequirements": {"gpu": "optional"}, "features": { - "ghcr.io/rapidsai/devcontainers/features/rapids-build-utils:23.12": {} + "ghcr.io/rapidsai/devcontainers/features/rapids-build-utils:24.2": {} }, "overrideFeatureInstallOrder": [ "ghcr.io/rapidsai/devcontainers/features/rapids-build-utils" diff --git a/.devcontainer/cuda11.8-pip/devcontainer.json b/.devcontainer/cuda11.8-pip/devcontainer.json index d59742575b5..a57ea0d163b 100644 --- a/.devcontainer/cuda11.8-pip/devcontainer.json +++ b/.devcontainer/cuda11.8-pip/devcontainer.json @@ -5,15 +5,15 @@ "args": { "CUDA": "11.8", "PYTHON_PACKAGE_MANAGER": "pip", - "BASE": "rapidsai/devcontainers:23.12-cpp-llvm16-cuda11.8-ubuntu22.04" + "BASE": "rapidsai/devcontainers:24.02-cpp-llvm16-cuda11.8-ubuntu22.04" } }, "hostRequirements": {"gpu": "optional"}, "features": { - "ghcr.io/rapidsai/devcontainers/features/ucx:23.12": { + "ghcr.io/rapidsai/devcontainers/features/ucx:24.2": { "version": "1.14.1" }, - "ghcr.io/rapidsai/devcontainers/features/rapids-build-utils:23.12": {} + "ghcr.io/rapidsai/devcontainers/features/rapids-build-utils:24.2": {} }, "overrideFeatureInstallOrder": [ "ghcr.io/rapidsai/devcontainers/features/rapids-build-utils" diff --git a/.devcontainer/cuda12.0-conda/devcontainer.json b/.devcontainer/cuda12.0-conda/devcontainer.json index bc4a2cb6fb4..10ba2f8fd3d 100644 --- a/.devcontainer/cuda12.0-conda/devcontainer.json +++ b/.devcontainer/cuda12.0-conda/devcontainer.json @@ -5,12 +5,12 @@ "args": { "CUDA": "12.0", "PYTHON_PACKAGE_MANAGER": "conda", - "BASE": "rapidsai/devcontainers:23.12-cpp-mambaforge-ubuntu22.04" + "BASE": "rapidsai/devcontainers:24.02-cpp-mambaforge-ubuntu22.04" } }, "hostRequirements": {"gpu": "optional"}, "features": { - "ghcr.io/rapidsai/devcontainers/features/rapids-build-utils:23.12": {} + "ghcr.io/rapidsai/devcontainers/features/rapids-build-utils:24.2": {} }, "overrideFeatureInstallOrder": [ "ghcr.io/rapidsai/devcontainers/features/rapids-build-utils" diff --git a/.devcontainer/cuda12.0-pip/devcontainer.json b/.devcontainer/cuda12.0-pip/devcontainer.json index 3eacf726bf0..a112483a6db 100644 --- a/.devcontainer/cuda12.0-pip/devcontainer.json +++ b/.devcontainer/cuda12.0-pip/devcontainer.json @@ -5,15 +5,15 @@ "args": { "CUDA": "12.0", "PYTHON_PACKAGE_MANAGER": "pip", - "BASE": "rapidsai/devcontainers:23.12-cpp-llvm16-cuda12.0-ubuntu22.04" + "BASE": "rapidsai/devcontainers:24.02-cpp-llvm16-cuda12.0-ubuntu22.04" } }, "hostRequirements": {"gpu": "optional"}, "features": { - "ghcr.io/rapidsai/devcontainers/features/ucx:23.12": { + "ghcr.io/rapidsai/devcontainers/features/ucx:24.2": { "version": "1.14.1" }, - "ghcr.io/rapidsai/devcontainers/features/rapids-build-utils:23.12": {} + "ghcr.io/rapidsai/devcontainers/features/rapids-build-utils:24.2": {} }, "overrideFeatureInstallOrder": [ "ghcr.io/rapidsai/devcontainers/features/rapids-build-utils" diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index ccfdb826812..6f12a76dc18 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -28,7 +28,7 @@ concurrency: jobs: cpp-build: secrets: inherit - uses: rapidsai/shared-workflows/.github/workflows/conda-cpp-build.yaml@branch-23.12 + uses: rapidsai/shared-workflows/.github/workflows/conda-cpp-build.yaml@branch-24.02 with: build_type: ${{ inputs.build_type || 'branch' }} branch: ${{ inputs.branch }} @@ -38,7 +38,7 @@ jobs: python-build: needs: [cpp-build] secrets: inherit - uses: rapidsai/shared-workflows/.github/workflows/conda-python-build.yaml@branch-23.12 + uses: rapidsai/shared-workflows/.github/workflows/conda-python-build.yaml@branch-24.02 with: build_type: ${{ inputs.build_type || 'branch' }} branch: ${{ inputs.branch }} @@ -47,7 +47,7 @@ jobs: upload-conda: needs: [cpp-build, python-build] secrets: inherit - uses: rapidsai/shared-workflows/.github/workflows/conda-upload-packages.yaml@branch-23.12 + uses: rapidsai/shared-workflows/.github/workflows/conda-upload-packages.yaml@branch-24.02 with: build_type: ${{ inputs.build_type || 'branch' }} branch: ${{ inputs.branch }} @@ -57,7 +57,7 @@ jobs: if: github.ref_type == 'branch' needs: python-build secrets: inherit - uses: rapidsai/shared-workflows/.github/workflows/custom-job.yaml@branch-23.12 + uses: rapidsai/shared-workflows/.github/workflows/custom-job.yaml@branch-24.02 with: arch: "amd64" branch: ${{ inputs.branch }} @@ -69,7 +69,7 @@ jobs: sha: ${{ inputs.sha }} wheel-build-pylibcugraph: secrets: inherit - uses: rapidsai/shared-workflows/.github/workflows/wheels-build.yaml@branch-23.12 + uses: rapidsai/shared-workflows/.github/workflows/wheels-build.yaml@branch-24.02 with: build_type: ${{ inputs.build_type || 'branch' }} branch: ${{ inputs.branch }} @@ -77,13 +77,13 @@ jobs: date: ${{ inputs.date }} script: ci/build_wheel_pylibcugraph.sh extra-repo: rapidsai/cugraph-ops - extra-repo-sha: branch-23.12 + extra-repo-sha: branch-24.02 extra-repo-deploy-key: CUGRAPH_OPS_SSH_PRIVATE_DEPLOY_KEY node_type: cpu32 wheel-publish-pylibcugraph: needs: wheel-build-pylibcugraph secrets: inherit - uses: rapidsai/shared-workflows/.github/workflows/wheels-publish.yaml@branch-23.12 + uses: rapidsai/shared-workflows/.github/workflows/wheels-publish.yaml@branch-24.02 with: build_type: ${{ inputs.build_type || 'branch' }} branch: ${{ inputs.branch }} @@ -93,7 +93,7 @@ jobs: wheel-build-cugraph: needs: wheel-publish-pylibcugraph secrets: inherit - uses: rapidsai/shared-workflows/.github/workflows/wheels-build.yaml@branch-23.12 + uses: rapidsai/shared-workflows/.github/workflows/wheels-build.yaml@branch-24.02 with: build_type: ${{ inputs.build_type || 'branch' }} branch: ${{ inputs.branch }} @@ -101,12 +101,12 @@ jobs: date: ${{ inputs.date }} script: ci/build_wheel_cugraph.sh extra-repo: rapidsai/cugraph-ops - extra-repo-sha: branch-23.12 + extra-repo-sha: branch-24.02 extra-repo-deploy-key: CUGRAPH_OPS_SSH_PRIVATE_DEPLOY_KEY wheel-publish-cugraph: needs: wheel-build-cugraph secrets: inherit - uses: rapidsai/shared-workflows/.github/workflows/wheels-publish.yaml@branch-23.12 + uses: rapidsai/shared-workflows/.github/workflows/wheels-publish.yaml@branch-24.02 with: build_type: ${{ inputs.build_type || 'branch' }} branch: ${{ inputs.branch }} @@ -116,7 +116,7 @@ jobs: wheel-build-nx-cugraph: needs: wheel-publish-pylibcugraph secrets: inherit - uses: rapidsai/shared-workflows/.github/workflows/wheels-build.yaml@branch-23.12 + uses: rapidsai/shared-workflows/.github/workflows/wheels-build.yaml@branch-24.02 with: build_type: ${{ inputs.build_type || 'branch' }} branch: ${{ inputs.branch }} @@ -126,7 +126,7 @@ jobs: wheel-publish-nx-cugraph: needs: wheel-build-nx-cugraph secrets: inherit - uses: rapidsai/shared-workflows/.github/workflows/wheels-publish.yaml@branch-23.12 + uses: rapidsai/shared-workflows/.github/workflows/wheels-publish.yaml@branch-24.02 with: build_type: ${{ inputs.build_type || 'branch' }} branch: ${{ inputs.branch }} diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml index 9d20074381e..82c71efffdb 100644 --- a/.github/workflows/pr.yaml +++ b/.github/workflows/pr.yaml @@ -27,41 +27,41 @@ jobs: - wheel-tests-nx-cugraph - devcontainer secrets: inherit - uses: rapidsai/shared-workflows/.github/workflows/pr-builder.yaml@branch-23.12 + uses: rapidsai/shared-workflows/.github/workflows/pr-builder.yaml@branch-24.02 checks: secrets: inherit - uses: rapidsai/shared-workflows/.github/workflows/checks.yaml@branch-23.12 + uses: rapidsai/shared-workflows/.github/workflows/checks.yaml@branch-24.02 with: enable_check_generated_files: false conda-cpp-build: needs: checks secrets: inherit - uses: rapidsai/shared-workflows/.github/workflows/conda-cpp-build.yaml@branch-23.12 + uses: rapidsai/shared-workflows/.github/workflows/conda-cpp-build.yaml@branch-24.02 with: build_type: pull-request node_type: cpu32 conda-cpp-tests: needs: conda-cpp-build secrets: inherit - uses: rapidsai/shared-workflows/.github/workflows/conda-cpp-tests.yaml@branch-23.12 + uses: rapidsai/shared-workflows/.github/workflows/conda-cpp-tests.yaml@branch-24.02 with: build_type: pull-request conda-python-build: needs: conda-cpp-build secrets: inherit - uses: rapidsai/shared-workflows/.github/workflows/conda-python-build.yaml@branch-23.12 + uses: rapidsai/shared-workflows/.github/workflows/conda-python-build.yaml@branch-24.02 with: build_type: pull-request conda-python-tests: needs: conda-python-build secrets: inherit - uses: rapidsai/shared-workflows/.github/workflows/conda-python-tests.yaml@branch-23.12 + uses: rapidsai/shared-workflows/.github/workflows/conda-python-tests.yaml@branch-24.02 with: build_type: pull-request conda-notebook-tests: needs: conda-python-build secrets: inherit - uses: rapidsai/shared-workflows/.github/workflows/custom-job.yaml@branch-23.12 + uses: rapidsai/shared-workflows/.github/workflows/custom-job.yaml@branch-24.02 with: build_type: pull-request node_type: "gpu-v100-latest-1" @@ -71,7 +71,7 @@ jobs: docs-build: needs: conda-python-build secrets: inherit - uses: rapidsai/shared-workflows/.github/workflows/custom-job.yaml@branch-23.12 + uses: rapidsai/shared-workflows/.github/workflows/custom-job.yaml@branch-24.02 with: build_type: pull-request node_type: "gpu-v100-latest-1" @@ -81,55 +81,55 @@ jobs: wheel-build-pylibcugraph: needs: checks secrets: inherit - uses: rapidsai/shared-workflows/.github/workflows/wheels-build.yaml@branch-23.12 + uses: rapidsai/shared-workflows/.github/workflows/wheels-build.yaml@branch-24.02 with: build_type: pull-request script: ci/build_wheel_pylibcugraph.sh extra-repo: rapidsai/cugraph-ops - extra-repo-sha: branch-23.12 + extra-repo-sha: branch-24.02 extra-repo-deploy-key: CUGRAPH_OPS_SSH_PRIVATE_DEPLOY_KEY node_type: cpu32 wheel-tests-pylibcugraph: needs: wheel-build-pylibcugraph secrets: inherit - uses: rapidsai/shared-workflows/.github/workflows/wheels-test.yaml@branch-23.12 + uses: rapidsai/shared-workflows/.github/workflows/wheels-test.yaml@branch-24.02 with: build_type: pull-request script: ci/test_wheel_pylibcugraph.sh wheel-build-cugraph: needs: wheel-tests-pylibcugraph secrets: inherit - uses: rapidsai/shared-workflows/.github/workflows/wheels-build.yaml@branch-23.12 + uses: rapidsai/shared-workflows/.github/workflows/wheels-build.yaml@branch-24.02 with: build_type: pull-request script: ci/build_wheel_cugraph.sh extra-repo: rapidsai/cugraph-ops - extra-repo-sha: branch-23.12 + extra-repo-sha: branch-24.02 extra-repo-deploy-key: CUGRAPH_OPS_SSH_PRIVATE_DEPLOY_KEY wheel-tests-cugraph: needs: wheel-build-cugraph secrets: inherit - uses: rapidsai/shared-workflows/.github/workflows/wheels-test.yaml@branch-23.12 + uses: rapidsai/shared-workflows/.github/workflows/wheels-test.yaml@branch-24.02 with: build_type: pull-request script: ci/test_wheel_cugraph.sh wheel-build-nx-cugraph: needs: wheel-tests-pylibcugraph secrets: inherit - uses: rapidsai/shared-workflows/.github/workflows/wheels-build.yaml@branch-23.12 + uses: rapidsai/shared-workflows/.github/workflows/wheels-build.yaml@branch-24.02 with: build_type: pull-request script: ci/build_wheel_nx-cugraph.sh wheel-tests-nx-cugraph: needs: wheel-build-nx-cugraph secrets: inherit - uses: rapidsai/shared-workflows/.github/workflows/wheels-test.yaml@branch-23.12 + uses: rapidsai/shared-workflows/.github/workflows/wheels-test.yaml@branch-24.02 with: build_type: pull-request script: ci/test_wheel_nx-cugraph.sh devcontainer: secrets: inherit - uses: rapidsai/shared-workflows/.github/workflows/build-in-devcontainer.yaml@branch-23.12 + uses: rapidsai/shared-workflows/.github/workflows/build-in-devcontainer.yaml@branch-24.02 with: node_type: cpu32 extra-repo-deploy-key: CUGRAPH_OPS_SSH_PRIVATE_DEPLOY_KEY diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index a0ecb67712c..0d9f4d291c3 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -16,7 +16,7 @@ on: jobs: conda-cpp-tests: secrets: inherit - uses: rapidsai/shared-workflows/.github/workflows/conda-cpp-tests.yaml@branch-23.12 + uses: rapidsai/shared-workflows/.github/workflows/conda-cpp-tests.yaml@branch-24.02 with: build_type: nightly branch: ${{ inputs.branch }} @@ -24,7 +24,7 @@ jobs: sha: ${{ inputs.sha }} conda-python-tests: secrets: inherit - uses: rapidsai/shared-workflows/.github/workflows/conda-python-tests.yaml@branch-23.12 + uses: rapidsai/shared-workflows/.github/workflows/conda-python-tests.yaml@branch-24.02 with: build_type: nightly branch: ${{ inputs.branch }} @@ -32,7 +32,7 @@ jobs: sha: ${{ inputs.sha }} wheel-tests-pylibcugraph: secrets: inherit - uses: rapidsai/shared-workflows/.github/workflows/wheels-test.yaml@branch-23.12 + uses: rapidsai/shared-workflows/.github/workflows/wheels-test.yaml@branch-24.02 with: build_type: nightly branch: ${{ inputs.branch }} @@ -41,7 +41,7 @@ jobs: script: ci/test_wheel_pylibcugraph.sh wheel-tests-cugraph: secrets: inherit - uses: rapidsai/shared-workflows/.github/workflows/wheels-test.yaml@branch-23.12 + uses: rapidsai/shared-workflows/.github/workflows/wheels-test.yaml@branch-24.02 with: build_type: nightly branch: ${{ inputs.branch }} @@ -50,7 +50,7 @@ jobs: script: ci/test_wheel_cugraph.sh wheel-tests-nx-cugraph: secrets: inherit - uses: rapidsai/shared-workflows/.github/workflows/wheels-test.yaml@branch-23.12 + uses: rapidsai/shared-workflows/.github/workflows/wheels-test.yaml@branch-24.02 with: build_type: nightly branch: ${{ inputs.branch }} diff --git a/VERSION b/VERSION index a193fff41e8..3c6c5e2b706 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -23.12.00 +24.02.00 diff --git a/ci/build_docs.sh b/ci/build_docs.sh index 3f97f652d41..e3b136b9662 100755 --- a/ci/build_docs.sh +++ b/ci/build_docs.sh @@ -37,7 +37,7 @@ rapids-mamba-retry install \ rapids-logger "Install cugraph-dgl" rapids-mamba-retry install "${PYTHON_CHANNEL}/linux-64/cugraph-dgl-*.tar.bz2" -export RAPIDS_VERSION_NUMBER="23.12" +export RAPIDS_VERSION_NUMBER="24.02" export RAPIDS_DOCS_DIR="$(mktemp -d)" for PROJECT in libcugraphops libwholegraph; do diff --git a/conda/environments/all_cuda-118_arch-x86_64.yaml b/conda/environments/all_cuda-118_arch-x86_64.yaml index 2f3a9c988cf..fc3154d36ee 100644 --- a/conda/environments/all_cuda-118_arch-x86_64.yaml +++ b/conda/environments/all_cuda-118_arch-x86_64.yaml @@ -16,13 +16,13 @@ dependencies: - cmake>=3.26.4 - cuda-version=11.8 - cudatoolkit -- cudf==23.12.* +- cudf==24.2.* - cupy>=12.0.0 - cxx-compiler - cython>=3.0.0 - dask-core>=2023.9.2 -- dask-cuda==23.12.* -- dask-cudf==23.12.* +- dask-cuda==24.2.* +- dask-cudf==24.2.* - dask>=2023.7.1 - distributed>=2023.7.1 - doxygen @@ -32,11 +32,11 @@ dependencies: - graphviz - gtest>=1.13.0 - ipython -- libcudf==23.12.* -- libcugraphops==23.12.* -- libraft-headers==23.12.* -- libraft==23.12.* -- librmm==23.12.* +- libcudf==24.2.* +- libcugraphops==24.2.* +- libraft-headers==24.2.* +- libraft==24.2.* +- librmm==24.2.* - nbsphinx - nccl>=2.9.9 - networkx>=2.5.1 @@ -52,19 +52,19 @@ dependencies: - pandas - pre-commit - pydata-sphinx-theme -- pylibcugraphops==23.12.* -- pylibraft==23.12.* -- pylibwholegraph==23.12.* +- pylibcugraphops==24.2.* +- pylibraft==24.2.* +- pylibwholegraph==24.2.* - pytest - pytest-benchmark - pytest-cov - pytest-mpl - pytest-xdist - python-louvain -- raft-dask==23.12.* +- raft-dask==24.2.* - recommonmark - requests -- rmm==23.12.* +- rmm==24.2.* - scikit-build>=0.13.1 - scikit-learn>=0.23.1 - scipy @@ -74,7 +74,7 @@ dependencies: - sphinx<6 - sphinxcontrib-websupport - ucx-proc=*=gpu -- ucx-py==0.35.* +- ucx-py==0.36.* - wget - wheel name: all_cuda-118_arch-x86_64 diff --git a/conda/environments/all_cuda-120_arch-x86_64.yaml b/conda/environments/all_cuda-120_arch-x86_64.yaml index 31ff503e682..1fa88754835 100644 --- a/conda/environments/all_cuda-120_arch-x86_64.yaml +++ b/conda/environments/all_cuda-120_arch-x86_64.yaml @@ -16,13 +16,13 @@ dependencies: - cmake>=3.26.4 - cuda-nvcc - cuda-version=12.0 -- cudf==23.12.* +- cudf==24.2.* - cupy>=12.0.0 - cxx-compiler - cython>=3.0.0 - dask-core>=2023.9.2 -- dask-cuda==23.12.* -- dask-cudf==23.12.* +- dask-cuda==24.2.* +- dask-cudf==24.2.* - dask>=2023.7.1 - distributed>=2023.7.1 - doxygen @@ -32,11 +32,11 @@ dependencies: - graphviz - gtest>=1.13.0 - ipython -- libcudf==23.12.* -- libcugraphops==23.12.* -- libraft-headers==23.12.* -- libraft==23.12.* -- librmm==23.12.* +- libcudf==24.2.* +- libcugraphops==24.2.* +- libraft-headers==24.2.* +- libraft==24.2.* +- librmm==24.2.* - nbsphinx - nccl>=2.9.9 - networkx>=2.5.1 @@ -51,19 +51,19 @@ dependencies: - pandas - pre-commit - pydata-sphinx-theme -- pylibcugraphops==23.12.* -- pylibraft==23.12.* -- pylibwholegraph==23.12.* +- pylibcugraphops==24.2.* +- pylibraft==24.2.* +- pylibwholegraph==24.2.* - pytest - pytest-benchmark - pytest-cov - pytest-mpl - pytest-xdist - python-louvain -- raft-dask==23.12.* +- raft-dask==24.2.* - recommonmark - requests -- rmm==23.12.* +- rmm==24.2.* - scikit-build>=0.13.1 - scikit-learn>=0.23.1 - scipy @@ -73,7 +73,7 @@ dependencies: - sphinx<6 - sphinxcontrib-websupport - ucx-proc=*=gpu -- ucx-py==0.35.* +- ucx-py==0.36.* - wget - wheel name: all_cuda-120_arch-x86_64 diff --git a/conda/recipes/cugraph-service/conda_build_config.yaml b/conda/recipes/cugraph-service/conda_build_config.yaml index b971a73fd39..6a0124983fd 100644 --- a/conda/recipes/cugraph-service/conda_build_config.yaml +++ b/conda/recipes/cugraph-service/conda_build_config.yaml @@ -1,2 +1,2 @@ ucx_py_version: - - "0.35.*" + - "0.36.*" diff --git a/conda/recipes/cugraph/conda_build_config.yaml b/conda/recipes/cugraph/conda_build_config.yaml index c03d515b9f6..387f3451d8d 100644 --- a/conda/recipes/cugraph/conda_build_config.yaml +++ b/conda/recipes/cugraph/conda_build_config.yaml @@ -17,4 +17,4 @@ sysroot_version: - "2.17" ucx_py_version: - - "0.35.*" + - "0.36.*" diff --git a/conda/recipes/pylibcugraph/conda_build_config.yaml b/conda/recipes/pylibcugraph/conda_build_config.yaml index c03d515b9f6..387f3451d8d 100644 --- a/conda/recipes/pylibcugraph/conda_build_config.yaml +++ b/conda/recipes/pylibcugraph/conda_build_config.yaml @@ -17,4 +17,4 @@ sysroot_version: - "2.17" ucx_py_version: - - "0.35.*" + - "0.36.*" diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index 41870cbc92b..c19241f3e67 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -25,7 +25,7 @@ include(rapids-find) rapids_cuda_init_architectures(CUGRAPH) -project(CUGRAPH VERSION 23.12.00 LANGUAGES C CXX CUDA) +project(CUGRAPH VERSION 24.02.00 LANGUAGES C CXX CUDA) if(CMAKE_CUDA_COMPILER_ID STREQUAL "NVIDIA" AND CMAKE_CUDA_COMPILER_VERSION VERSION_LESS 11.0) diff --git a/cpp/doxygen/Doxyfile b/cpp/doxygen/Doxyfile index 482ff988098..a5ac2baa694 100644 --- a/cpp/doxygen/Doxyfile +++ b/cpp/doxygen/Doxyfile @@ -38,7 +38,7 @@ PROJECT_NAME = "libcugraph" # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER=23.12 +PROJECT_NUMBER=24.02 # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a diff --git a/cpp/libcugraph_etl/CMakeLists.txt b/cpp/libcugraph_etl/CMakeLists.txt index ac0cb6959e8..8874c75896c 100644 --- a/cpp/libcugraph_etl/CMakeLists.txt +++ b/cpp/libcugraph_etl/CMakeLists.txt @@ -25,7 +25,7 @@ include(rapids-find) rapids_cuda_init_architectures(CUGRAPH_ETL) -project(CUGRAPH_ETL VERSION 23.12.00 LANGUAGES C CXX CUDA) +project(CUGRAPH_ETL VERSION 24.02.00 LANGUAGES C CXX CUDA) if(CMAKE_CUDA_COMPILER_ID STREQUAL "NVIDIA" AND CMAKE_CUDA_COMPILER_VERSION VERSION_LESS 11.0) diff --git a/dependencies.yaml b/dependencies.yaml index b127d9bd29e..a78f6ea5858 100644 --- a/dependencies.yaml +++ b/dependencies.yaml @@ -297,10 +297,10 @@ dependencies: - cxx-compiler - gmock>=1.13.0 - gtest>=1.13.0 - - libcugraphops==23.12.* - - libraft-headers==23.12.* - - libraft==23.12.* - - librmm==23.12.* + - libcugraphops==24.2.* + - libraft-headers==24.2.* + - libraft==24.2.* + - librmm==24.2.* - openmpi # Required for building cpp-mgtests (multi-GPU tests) specific: - output_types: [conda] @@ -379,15 +379,15 @@ dependencies: packages: - &dask dask>=2023.7.1 - &distributed distributed>=2023.7.1 - - &dask_cuda dask-cuda==23.12.* + - &dask_cuda dask-cuda==24.2.* - &numba numba>=0.57 - - &ucx_py ucx-py==0.35.* + - &ucx_py ucx-py==0.36.* - output_types: conda packages: - aiohttp - &dask-core_conda dask-core>=2023.9.2 - fsspec>=0.6.0 - - libcudf==23.12.* + - libcudf==24.2.* - requests - nccl>=2.9.9 - ucx-proc=*=gpu @@ -410,7 +410,7 @@ dependencies: - *numpy - output_types: [pyproject] packages: - - &cugraph cugraph==23.12.* + - &cugraph cugraph==24.2.* python_run_cugraph_pyg: common: - output_types: [conda, pyproject] @@ -442,7 +442,7 @@ dependencies: - output_types: pyproject packages: - *cugraph - - cugraph-service-client==23.12.* + - cugraph-service-client==24.2.* test_cpp: common: - output_types: conda @@ -477,7 +477,7 @@ dependencies: - scikit-learn>=0.23.1 - output_types: [conda] packages: - - pylibwholegraph==23.12.* + - pylibwholegraph==24.2.* test_python_pylibcugraph: common: - output_types: [conda, pyproject] @@ -494,7 +494,7 @@ dependencies: common: - output_types: [conda] packages: - - cugraph==23.12.* + - cugraph==24.2.* - pytorch>=2.0 - pytorch-cuda==11.8 - dgl>=1.1.0.cu* @@ -502,7 +502,7 @@ dependencies: common: - output_types: [conda] packages: - - cugraph==23.12.* + - cugraph==24.2.* - pytorch==2.0 - pytorch-cuda==11.8 - pyg=2.3.1=*torch_2.0.0*cu118* @@ -511,7 +511,7 @@ dependencies: common: - output_types: conda packages: - - &rmm_conda rmm==23.12.* + - &rmm_conda rmm==24.2.* - output_types: requirements packages: # pip recognizes the index as a global option for the requirements.txt file @@ -521,12 +521,12 @@ dependencies: matrices: - matrix: {cuda: "12.2"} packages: &rmm_packages_pip_cu12 - - rmm-cu12==23.12.* + - rmm-cu12==24.2.* - {matrix: {cuda: "12.1"}, packages: *rmm_packages_pip_cu12} - {matrix: {cuda: "12.0"}, packages: *rmm_packages_pip_cu12} - matrix: {cuda: "11.8"} packages: &rmm_packages_pip_cu11 - - rmm-cu11==23.12.* + - rmm-cu11==24.2.* - {matrix: {cuda: "11.5"}, packages: *rmm_packages_pip_cu11} - {matrix: {cuda: "11.4"}, packages: *rmm_packages_pip_cu11} - {matrix: {cuda: "11.2"}, packages: *rmm_packages_pip_cu11} @@ -536,7 +536,7 @@ dependencies: common: - output_types: conda packages: - - &cudf_conda cudf==23.12.* + - &cudf_conda cudf==24.2.* - output_types: requirements packages: # pip recognizes the index as a global option for the requirements.txt file @@ -546,12 +546,12 @@ dependencies: matrices: - matrix: {cuda: "12.2"} packages: &cudf_packages_pip_cu12 - - cudf-cu12==23.12.* + - cudf-cu12==24.2.* - {matrix: {cuda: "12.1"}, packages: *cudf_packages_pip_cu12} - {matrix: {cuda: "12.0"}, packages: *cudf_packages_pip_cu12} - matrix: {cuda: "11.8"} packages: &cudf_packages_pip_cu11 - - cudf-cu11==23.12.* + - cudf-cu11==24.2.* - {matrix: {cuda: "11.5"}, packages: *cudf_packages_pip_cu11} - {matrix: {cuda: "11.4"}, packages: *cudf_packages_pip_cu11} - {matrix: {cuda: "11.2"}, packages: *cudf_packages_pip_cu11} @@ -561,7 +561,7 @@ dependencies: common: - output_types: conda packages: - - &dask_cudf_conda dask-cudf==23.12.* + - &dask_cudf_conda dask-cudf==24.2.* - output_types: requirements packages: # pip recognizes the index as a global option for the requirements.txt file @@ -571,12 +571,12 @@ dependencies: matrices: - matrix: {cuda: "12.2"} packages: &dask_cudf_packages_pip_cu12 - - dask-cudf-cu12==23.12.* + - dask-cudf-cu12==24.2.* - {matrix: {cuda: "12.1"}, packages: *dask_cudf_packages_pip_cu12} - {matrix: {cuda: "12.0"}, packages: *dask_cudf_packages_pip_cu12} - matrix: {cuda: "11.8"} packages: &dask_cudf_packages_pip_cu11 - - dask-cudf-cu11==23.12.* + - dask-cudf-cu11==24.2.* - {matrix: {cuda: "11.5"}, packages: *dask_cudf_packages_pip_cu11} - {matrix: {cuda: "11.4"}, packages: *dask_cudf_packages_pip_cu11} - {matrix: {cuda: "11.2"}, packages: *dask_cudf_packages_pip_cu11} @@ -586,7 +586,7 @@ dependencies: common: - output_types: conda packages: - - &pylibraft_conda pylibraft==23.12.* + - &pylibraft_conda pylibraft==24.2.* - output_types: requirements packages: # pip recognizes the index as a global option for the requirements.txt file @@ -596,12 +596,12 @@ dependencies: matrices: - matrix: {cuda: "12.2"} packages: &pylibraft_packages_pip_cu12 - - pylibraft-cu12==23.12.* + - pylibraft-cu12==24.2.* - {matrix: {cuda: "12.1"}, packages: *pylibraft_packages_pip_cu12} - {matrix: {cuda: "12.0"}, packages: *pylibraft_packages_pip_cu12} - matrix: {cuda: "11.8"} packages: &pylibraft_packages_pip_cu11 - - pylibraft-cu11==23.12.* + - pylibraft-cu11==24.2.* - {matrix: {cuda: "11.5"}, packages: *pylibraft_packages_pip_cu11} - {matrix: {cuda: "11.4"}, packages: *pylibraft_packages_pip_cu11} - {matrix: {cuda: "11.2"}, packages: *pylibraft_packages_pip_cu11} @@ -611,7 +611,7 @@ dependencies: common: - output_types: conda packages: - - &raft_dask_conda raft-dask==23.12.* + - &raft_dask_conda raft-dask==24.2.* - output_types: requirements packages: # pip recognizes the index as a global option for the requirements.txt file @@ -621,12 +621,12 @@ dependencies: matrices: - matrix: {cuda: "12.2"} packages: &raft_dask_packages_pip_cu12 - - raft-dask-cu12==23.12.* + - raft-dask-cu12==24.2.* - {matrix: {cuda: "12.1"}, packages: *raft_dask_packages_pip_cu12} - {matrix: {cuda: "12.0"}, packages: *raft_dask_packages_pip_cu12} - matrix: {cuda: "11.8"} packages: &raft_dask_packages_pip_cu11 - - raft-dask-cu11==23.12.* + - raft-dask-cu11==24.2.* - {matrix: {cuda: "11.5"}, packages: *raft_dask_packages_pip_cu11} - {matrix: {cuda: "11.4"}, packages: *raft_dask_packages_pip_cu11} - {matrix: {cuda: "11.2"}, packages: *raft_dask_packages_pip_cu11} @@ -636,7 +636,7 @@ dependencies: common: - output_types: conda packages: - - &pylibcugraph_conda pylibcugraph==23.12.* + - &pylibcugraph_conda pylibcugraph==24.2.* - output_types: requirements packages: # pip recognizes the index as a global option for the requirements.txt file @@ -646,12 +646,12 @@ dependencies: matrices: - matrix: {cuda: "12.2"} packages: &pylibcugraph_packages_pip_cu12 - - pylibcugraph-cu12==23.12.* + - pylibcugraph-cu12==24.2.* - {matrix: {cuda: "12.1"}, packages: *pylibcugraph_packages_pip_cu12} - {matrix: {cuda: "12.0"}, packages: *pylibcugraph_packages_pip_cu12} - matrix: {cuda: "11.8"} packages: &pylibcugraph_packages_pip_cu11 - - pylibcugraph-cu11==23.12.* + - pylibcugraph-cu11==24.2.* - {matrix: {cuda: "11.5"}, packages: *pylibcugraph_packages_pip_cu11} - {matrix: {cuda: "11.4"}, packages: *pylibcugraph_packages_pip_cu11} - {matrix: {cuda: "11.2"}, packages: *pylibcugraph_packages_pip_cu11} @@ -661,7 +661,7 @@ dependencies: common: - output_types: conda packages: - - &pylibcugraphops_conda pylibcugraphops==23.12.* + - &pylibcugraphops_conda pylibcugraphops==24.2.* - output_types: requirements packages: # pip recognizes the index as a global option for the requirements.txt file @@ -671,12 +671,12 @@ dependencies: matrices: - matrix: {cuda: "12.2"} packages: &pylibcugraphops_packages_pip_cu12 - - pylibcugraphops-cu12==23.12.* + - pylibcugraphops-cu12==24.2.* - {matrix: {cuda: "12.1"}, packages: *pylibcugraphops_packages_pip_cu12} - {matrix: {cuda: "12.0"}, packages: *pylibcugraphops_packages_pip_cu12} - matrix: {cuda: "11.8"} packages: &pylibcugraphops_packages_pip_cu11 - - pylibcugraphops-cu11==23.12.* + - pylibcugraphops-cu11==24.2.* - {matrix: {cuda: "11.5"}, packages: *pylibcugraphops_packages_pip_cu11} - {matrix: {cuda: "11.4"}, packages: *pylibcugraphops_packages_pip_cu11} - {matrix: {cuda: "11.2"}, packages: *pylibcugraphops_packages_pip_cu11} diff --git a/docs/cugraph/source/conf.py b/docs/cugraph/source/conf.py index 470086b4faa..940d2144273 100644 --- a/docs/cugraph/source/conf.py +++ b/docs/cugraph/source/conf.py @@ -77,9 +77,9 @@ # built documents. # # The short X.Y version. -version = '23.12' +version = '24.02' # The full version, including alpha/beta/rc tags. -release = '23.12.00' +release = '24.02.00' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/fetch_rapids.cmake b/fetch_rapids.cmake index 2c1dd855cb5..1f099e8f85f 100644 --- a/fetch_rapids.cmake +++ b/fetch_rapids.cmake @@ -12,7 +12,7 @@ # the License. # ============================================================================= if(NOT EXISTS ${CMAKE_CURRENT_BINARY_DIR}/CUGRAPH_RAPIDS.cmake) - file(DOWNLOAD https://raw.githubusercontent.com/rapidsai/rapids-cmake/branch-23.12/RAPIDS.cmake + file(DOWNLOAD https://raw.githubusercontent.com/rapidsai/rapids-cmake/branch-24.02/RAPIDS.cmake ${CMAKE_CURRENT_BINARY_DIR}/CUGRAPH_RAPIDS.cmake ) endif() diff --git a/python/cugraph-dgl/conda/cugraph_dgl_dev_cuda-118.yaml b/python/cugraph-dgl/conda/cugraph_dgl_dev_cuda-118.yaml index b73ccb0cf9a..c6df338ab9a 100644 --- a/python/cugraph-dgl/conda/cugraph_dgl_dev_cuda-118.yaml +++ b/python/cugraph-dgl/conda/cugraph_dgl_dev_cuda-118.yaml @@ -10,11 +10,11 @@ channels: - conda-forge - nvidia dependencies: -- cugraph==23.12.* +- cugraph==24.2.* - dgl>=1.1.0.cu* - pandas - pre-commit -- pylibcugraphops==23.12.* +- pylibcugraphops==24.2.* - pytest - pytest-benchmark - pytest-cov diff --git a/python/cugraph-dgl/pyproject.toml b/python/cugraph-dgl/pyproject.toml index eff7a20f0aa..62fa8ab6368 100644 --- a/python/cugraph-dgl/pyproject.toml +++ b/python/cugraph-dgl/pyproject.toml @@ -19,7 +19,7 @@ authors = [ license = { text = "Apache 2.0" } requires-python = ">=3.9" dependencies = [ - "cugraph==23.12.*", + "cugraph==24.2.*", "numba>=0.57", "numpy>=1.21", ] # This list was generated by `rapids-dependency-file-generator`. To make changes, edit ../../dependencies.yaml and run `rapids-dependency-file-generator`. diff --git a/python/cugraph-pyg/conda/cugraph_pyg_dev_cuda-118.yaml b/python/cugraph-pyg/conda/cugraph_pyg_dev_cuda-118.yaml index f98eab430ba..ec75846e81e 100644 --- a/python/cugraph-pyg/conda/cugraph_pyg_dev_cuda-118.yaml +++ b/python/cugraph-pyg/conda/cugraph_pyg_dev_cuda-118.yaml @@ -10,11 +10,11 @@ channels: - conda-forge - nvidia dependencies: -- cugraph==23.12.* +- cugraph==24.2.* - pandas - pre-commit - pyg=2.3.1=*torch_2.0.0*cu118* -- pylibcugraphops==23.12.* +- pylibcugraphops==24.2.* - pytest - pytest-benchmark - pytest-cov diff --git a/python/cugraph-pyg/pyproject.toml b/python/cugraph-pyg/pyproject.toml index 95b1fa27402..b0671644982 100644 --- a/python/cugraph-pyg/pyproject.toml +++ b/python/cugraph-pyg/pyproject.toml @@ -26,7 +26,7 @@ classifiers = [ "Programming Language :: Python :: 3.10", ] dependencies = [ - "cugraph==23.12.*", + "cugraph==24.2.*", "numba>=0.57", "numpy>=1.21", ] # This list was generated by `rapids-dependency-file-generator`. To make changes, edit ../../dependencies.yaml and run `rapids-dependency-file-generator`. diff --git a/python/cugraph-service/server/pyproject.toml b/python/cugraph-service/server/pyproject.toml index f50b33b3f15..c0e1e6d0bb9 100644 --- a/python/cugraph-service/server/pyproject.toml +++ b/python/cugraph-service/server/pyproject.toml @@ -19,19 +19,19 @@ authors = [ license = { text = "Apache 2.0" } requires-python = ">=3.9" dependencies = [ - "cudf==23.12.*", - "cugraph-service-client==23.12.*", - "cugraph==23.12.*", + "cudf==24.2.*", + "cugraph-service-client==24.2.*", + "cugraph==24.2.*", "cupy-cuda11x>=12.0.0", - "dask-cuda==23.12.*", - "dask-cudf==23.12.*", + "dask-cuda==24.2.*", + "dask-cudf==24.2.*", "dask>=2023.7.1", "distributed>=2023.7.1", "numba>=0.57", "numpy>=1.21", - "rmm==23.12.*", + "rmm==24.2.*", "thriftpy2", - "ucx-py==0.35.*", + "ucx-py==0.36.*", ] # This list was generated by `rapids-dependency-file-generator`. To make changes, edit ../../../dependencies.yaml and run `rapids-dependency-file-generator`. classifiers = [ "Intended Audience :: Developers", diff --git a/python/cugraph/CMakeLists.txt b/python/cugraph/CMakeLists.txt index 8693c0e9e1f..a1ec12c6e07 100644 --- a/python/cugraph/CMakeLists.txt +++ b/python/cugraph/CMakeLists.txt @@ -14,7 +14,7 @@ cmake_minimum_required(VERSION 3.26.4 FATAL_ERROR) -set(cugraph_version 23.12.00) +set(cugraph_version 24.02.00) include(../../fetch_rapids.cmake) diff --git a/python/cugraph/pyproject.toml b/python/cugraph/pyproject.toml index aaa301fa05f..da898583d1b 100644 --- a/python/cugraph/pyproject.toml +++ b/python/cugraph/pyproject.toml @@ -6,9 +6,9 @@ requires = [ "cmake>=3.26.4", "cython>=3.0.0", "ninja", - "pylibcugraph==23.12.*", - "pylibraft==23.12.*", - "rmm==23.12.*", + "pylibcugraph==24.2.*", + "pylibraft==24.2.*", + "rmm==24.2.*", "scikit-build>=0.13.1", "setuptools>=61.0.0", "wheel", @@ -29,18 +29,18 @@ authors = [ license = { text = "Apache 2.0" } requires-python = ">=3.9" dependencies = [ - "cudf==23.12.*", + "cudf==24.2.*", "cupy-cuda11x>=12.0.0", - "dask-cuda==23.12.*", - "dask-cudf==23.12.*", + "dask-cuda==24.2.*", + "dask-cudf==24.2.*", "dask>=2023.7.1", "distributed>=2023.7.1", "fsspec[http]>=0.6.0", "numba>=0.57", - "pylibcugraph==23.12.*", - "raft-dask==23.12.*", - "rmm==23.12.*", - "ucx-py==0.35.*", + "pylibcugraph==24.2.*", + "raft-dask==24.2.*", + "rmm==24.2.*", + "ucx-py==0.36.*", ] # This list was generated by `rapids-dependency-file-generator`. To make changes, edit ../../dependencies.yaml and run `rapids-dependency-file-generator`. classifiers = [ "Intended Audience :: Developers", diff --git a/python/nx-cugraph/_nx_cugraph/__init__.py b/python/nx-cugraph/_nx_cugraph/__init__.py index 26638d1e735..2e5a16aa445 100644 --- a/python/nx-cugraph/_nx_cugraph/__init__.py +++ b/python/nx-cugraph/_nx_cugraph/__init__.py @@ -24,7 +24,7 @@ "backend_name": "cugraph", "project": "nx-cugraph", "package": "nx_cugraph", - "url": "https://github.com/rapidsai/cugraph/tree/branch-23.12/python/nx-cugraph", + "url": "https://github.com/rapidsai/cugraph/tree/branch-24.02/python/nx-cugraph", "short_summary": "GPU-accelerated backend.", # "description": "TODO", "functions": { @@ -137,7 +137,7 @@ def get_info(): # FIXME: can this use the standard VERSION file and update mechanism? -__version__ = "23.12.00" +__version__ = "24.02.00" if __name__ == "__main__": from pathlib import Path diff --git a/python/nx-cugraph/pyproject.toml b/python/nx-cugraph/pyproject.toml index f309f4797a7..b29578b036f 100644 --- a/python/nx-cugraph/pyproject.toml +++ b/python/nx-cugraph/pyproject.toml @@ -33,7 +33,7 @@ dependencies = [ "cupy-cuda11x>=12.0.0", "networkx>=3.0", "numpy>=1.21", - "pylibcugraph==23.12.*", + "pylibcugraph==24.2.*", ] # This list was generated by `rapids-dependency-file-generator`. To make changes, edit ../../dependencies.yaml and run `rapids-dependency-file-generator`. [project.optional-dependencies] diff --git a/python/pylibcugraph/CMakeLists.txt b/python/pylibcugraph/CMakeLists.txt index 057f30ef3ad..7d5dc790ad0 100644 --- a/python/pylibcugraph/CMakeLists.txt +++ b/python/pylibcugraph/CMakeLists.txt @@ -14,7 +14,7 @@ cmake_minimum_required(VERSION 3.26.4 FATAL_ERROR) -set(pylibcugraph_version 23.12.00) +set(pylibcugraph_version 24.02.00) include(../../fetch_rapids.cmake) diff --git a/python/pylibcugraph/pyproject.toml b/python/pylibcugraph/pyproject.toml index 96f5ec84efb..0f2d742e7c5 100644 --- a/python/pylibcugraph/pyproject.toml +++ b/python/pylibcugraph/pyproject.toml @@ -6,8 +6,8 @@ requires = [ "cmake>=3.26.4", "cython>=3.0.0", "ninja", - "pylibraft==23.12.*", - "rmm==23.12.*", + "pylibraft==24.2.*", + "rmm==24.2.*", "scikit-build>=0.13.1", "setuptools>=61.0.0", "wheel", @@ -28,8 +28,8 @@ authors = [ license = { text = "Apache 2.0" } requires-python = ">=3.9" dependencies = [ - "pylibraft==23.12.*", - "rmm==23.12.*", + "pylibraft==24.2.*", + "rmm==24.2.*", ] # This list was generated by `rapids-dependency-file-generator`. To make changes, edit ../../dependencies.yaml and run `rapids-dependency-file-generator`. classifiers = [ "Intended Audience :: Developers", @@ -40,7 +40,7 @@ classifiers = [ [project.optional-dependencies] test = [ - "cudf==23.12.*", + "cudf==24.2.*", "numpy>=1.21", "pandas", "pytest", From 96a7a6eaf12e4550256c5835a0f0486c78db539e Mon Sep 17 00:00:00 2001 From: Naim <110031745+naimnv@users.noreply.github.com> Date: Tue, 14 Nov 2023 18:25:39 +0100 Subject: [PATCH 081/111] Move MTMG_TEST to MG tests block (#3993) * Move MTMG_TEST to MG tests block * Remove extra newline * remove reference to get_ucp.cmake * make building MTMG a separate build flag * Updates PropertyGraph for latest cudf changes. * style fixes. --------- Co-authored-by: Naim Co-authored-by: Charles Hastings Co-authored-by: Rick Ratzel --- build.sh | 8 ++++ cpp/CMakeLists.txt | 5 ++- cpp/tests/CMakeLists.txt | 37 +++++++++++-------- .../cugraph/structure/property_graph.py | 24 +++--------- 4 files changed, 39 insertions(+), 35 deletions(-) diff --git a/build.sh b/build.sh index 99082fa96fb..1723e750978 100755 --- a/build.sh +++ b/build.sh @@ -31,6 +31,7 @@ VALIDARGS=" cugraph-dgl nx-cugraph cpp-mgtests + cpp-mtmgtests docs all -v @@ -59,6 +60,7 @@ HELP="$0 [ ...] [ ...] cugraph-dgl - build the cugraph-dgl extensions for DGL nx-cugraph - build the nx-cugraph Python package cpp-mgtests - build libcugraph and libcugraph_etl MG tests. Builds MPI communicator, adding MPI as a dependency. + cpp-mtmgtests - build libcugraph MTMG tests. Adds UCX as a dependency (temporary). docs - build the docs all - build everything and is: @@ -105,6 +107,7 @@ BUILD_TYPE=Release INSTALL_TARGET="--target install" BUILD_CPP_TESTS=ON BUILD_CPP_MG_TESTS=OFF +BUILD_CPP_MTMG_TESTS=OFF BUILD_ALL_GPU_ARCH=0 BUILD_WITH_CUGRAPHOPS=ON CMAKE_GENERATOR_OPTION="-G Ninja" @@ -172,6 +175,9 @@ fi if hasArg --without_cugraphops; then BUILD_WITH_CUGRAPHOPS=OFF fi +if hasArg cpp-mtmgtests; then + BUILD_CPP_MTMG_TESTS=ON +fi if hasArg cpp-mgtests || hasArg all; then BUILD_CPP_MG_TESTS=ON fi @@ -264,6 +270,7 @@ if buildDefault || hasArg libcugraph || hasArg all; then -DCMAKE_BUILD_TYPE=${BUILD_TYPE} \ -DBUILD_TESTS=${BUILD_CPP_TESTS} \ -DBUILD_CUGRAPH_MG_TESTS=${BUILD_CPP_MG_TESTS} \ + -DBUILD_CUGRAPH_MTMG_TESTS=${BUILD_CPP_MTMG_TESTS} \ -DUSE_CUGRAPH_OPS=${BUILD_WITH_CUGRAPHOPS} \ ${CMAKE_GENERATOR_OPTION} \ ${CMAKE_VERBOSE_OPTION} @@ -294,6 +301,7 @@ if buildDefault || hasArg libcugraph_etl || hasArg all; then -DCMAKE_BUILD_TYPE=${BUILD_TYPE} \ -DBUILD_TESTS=${BUILD_CPP_TESTS} \ -DBUILD_CUGRAPH_MG_TESTS=${BUILD_CPP_MG_TESTS} \ + -DBUILD_CUGRAPH_MTMG_TESTS=${BUILD_CPP_MTMG_TESTS} \ -DCMAKE_PREFIX_PATH=${LIBCUGRAPH_BUILD_DIR} \ ${CMAKE_GENERATOR_OPTION} \ ${CMAKE_VERBOSE_OPTION} \ diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index 41870cbc92b..360165e688d 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -166,7 +166,10 @@ endif() include(cmake/thirdparty/get_nccl.cmake) include(cmake/thirdparty/get_cuhornet.cmake) -include(cmake/thirdparty/get_ucp.cmake) + +if (BUILD_CUGRAPH_MTMG_TESTS) + include(cmake/thirdparty/get_ucp.cmake) +endif() if(BUILD_TESTS) include(cmake/thirdparty/get_gtest.cmake) diff --git a/cpp/tests/CMakeLists.txt b/cpp/tests/CMakeLists.txt index 2f69cf9cb0d..6530a25d178 100644 --- a/cpp/tests/CMakeLists.txt +++ b/cpp/tests/CMakeLists.txt @@ -415,13 +415,6 @@ ConfigureTest(K_HOP_NBRS_TEST traversal/k_hop_nbrs_test.cpp) # - install tests --------------------------------------------------------------------------------- rapids_test_install_relocatable(INSTALL_COMPONENT_SET testing DESTINATION bin/gtests/libcugraph) -################################################################################################### -# - MTMG tests ------------------------------------------------------------------------- -ConfigureTest(MTMG_TEST mtmg/threaded_test.cu) -target_link_libraries(MTMG_TEST - PRIVATE - UCP::UCP - ) ################################################################################################### # - MG tests -------------------------------------------------------------------------------------- @@ -681,15 +674,6 @@ if(BUILD_CUGRAPH_MG_TESTS) rapids_test_install_relocatable(INSTALL_COMPONENT_SET testing_mg DESTINATION bin/gtests/libcugraph_mg) - ############################################################################################### - # - Multi-node MTMG tests --------------------------------------------------------------------- - ConfigureTest(MTMG_MULTINODE_TEST mtmg/multi_node_threaded_test.cu utilities/mg_utilities.cpp) - target_link_libraries(MTMG_MULTINODE_TEST - PRIVATE - cugraphmgtestutil - UCP::UCP - ) - endif() ################################################################################################### @@ -749,4 +733,25 @@ ConfigureCTest(CAPI_EGONET_TEST c_api/egonet_test.c) ConfigureCTest(CAPI_TWO_HOP_NEIGHBORS_TEST c_api/two_hop_neighbors_test.c) ConfigureCTest(CAPI_LEGACY_K_TRUSS_TEST c_api/legacy_k_truss_test.c) +if (BUILD_CUGRAPH_MTMG_TESTS) + ################################################################################################### + # - MTMG tests ------------------------------------------------------------------------- + ConfigureTest(MTMG_TEST mtmg/threaded_test.cu) + target_link_libraries(MTMG_TEST + PRIVATE + UCP::UCP + ) + + if(BUILD_CUGRAPH_MG_TESTS) + ############################################################################################### + # - Multi-node MTMG tests --------------------------------------------------------------------- + ConfigureTest(MTMG_MULTINODE_TEST mtmg/multi_node_threaded_test.cu utilities/mg_utilities.cpp) + target_link_libraries(MTMG_MULTINODE_TEST + PRIVATE + cugraphmgtestutil + UCP::UCP + ) + endif(BUILD_CUGRAPH_MG_TESTS) +endif(BUILD_CUGRAPH_MTMG_TESTS) + rapids_test_install_relocatable(INSTALL_COMPONENT_SET testing_c DESTINATION bin/gtests/libcugraph_c) diff --git a/python/cugraph/cugraph/structure/property_graph.py b/python/cugraph/cugraph/structure/property_graph.py index 36ce5baa212..513798f35f9 100644 --- a/python/cugraph/cugraph/structure/property_graph.py +++ b/python/cugraph/cugraph/structure/property_graph.py @@ -800,15 +800,9 @@ def add_vertex_data( tmp_df.index = tmp_df.index.rename(self.vertex_col_name) # FIXME: handle case of a type_name column already being in tmp_df - if self.__series_type is cudf.Series: - # cudf does not yet support initialization with a scalar - tmp_df[TCN] = cudf.Series( - cudf.Series([type_name], dtype=cat_dtype).repeat(len(tmp_df)), - index=tmp_df.index, - ) - else: - # pandas is oddly slow if dtype is passed to the constructor here - tmp_df[TCN] = pd.Series(type_name, index=tmp_df.index).astype(cat_dtype) + tmp_df[TCN] = self.__series_type(type_name, index=tmp_df.index).astype( + cat_dtype + ) if property_columns: # all columns @@ -1207,15 +1201,9 @@ def add_edge_data( tmp_df[self.src_col_name] = tmp_df[vertex_col_names[0]] tmp_df[self.dst_col_name] = tmp_df[vertex_col_names[1]] - if self.__series_type is cudf.Series: - # cudf does not yet support initialization with a scalar - tmp_df[TCN] = cudf.Series( - cudf.Series([type_name], dtype=cat_dtype).repeat(len(tmp_df)), - index=tmp_df.index, - ) - else: - # pandas is oddly slow if dtype is passed to the constructor here - tmp_df[TCN] = pd.Series(type_name, index=tmp_df.index).astype(cat_dtype) + tmp_df[TCN] = self.__series_type(type_name, index=tmp_df.index).astype( + cat_dtype + ) # Add unique edge IDs to the new rows. This is just a count for each # row starting from the last edge ID value, with initial edge ID 0. From b90e1a53e4c2ae6fdde73258c2d366dde596a154 Mon Sep 17 00:00:00 2001 From: GALI PREM SAGAR Date: Tue, 14 Nov 2023 14:35:20 -0600 Subject: [PATCH 082/111] Use new `rapids-dask-dependency` metapackage for managing `dask` versions (#3991) Currently dask versions are pinned as part of every release cycle and then unpinned for the next development cycle across all of RAPIDS. This introduces a great deal of churn. To centralize the dependency, we have created a metapackage to manage the required dask version and this PR introduces that metapackage as a dependency of cugraph. xref: https://github.com/rapidsai/cudf/pull/14364 Authors: - Chuck Hastings (https://github.com/ChuckHastings) - Rick Ratzel (https://github.com/rlratzel) - Vyas Ramasubramani (https://github.com/vyasr) - GALI PREM SAGAR (https://github.com/galipremsagar) - Naim (naim@uib.no) Approvers: - Jake Awe (https://github.com/AyodeAwe) --- ci/build_wheel.sh | 7 +++++-- ci/release/update-version.sh | 1 + ci/test_wheel_cugraph.sh | 3 --- conda/environments/all_cuda-118_arch-x86_64.yaml | 4 +--- conda/environments/all_cuda-120_arch-x86_64.yaml | 4 +--- conda/recipes/cugraph-pyg/meta.yaml | 2 +- conda/recipes/cugraph-service/meta.yaml | 2 +- conda/recipes/cugraph/meta.yaml | 4 +--- dependencies.yaml | 8 +------- python/cugraph-service/server/pyproject.toml | 3 +-- python/cugraph/pyproject.toml | 3 +-- 11 files changed, 14 insertions(+), 27 deletions(-) diff --git a/ci/build_wheel.sh b/ci/build_wheel.sh index c888c908056..163520ea1da 100755 --- a/ci/build_wheel.sh +++ b/ci/build_wheel.sh @@ -40,8 +40,11 @@ for dep in rmm cudf raft-dask pylibcugraph pylibraft ucx-py; do sed -r -i "s/${dep}==(.*)\"/${dep}${PACKAGE_CUDA_SUFFIX}==\1${alpha_spec}\"/g" ${pyproject_file} done -# dask-cuda doesn't get a suffix, but it does get an alpha spec. -sed -r -i "s/dask-cuda==(.*)\"/dask-cuda==\1${alpha_spec}\"/g" ${pyproject_file} +# dask-cuda & rapids-dask-dependency doesn't get a suffix, but it does get an alpha spec. +for dep in dask-cuda rapids-dask-dependency; do + sed -r -i "s/${dep}==(.*)\"/${dep}==\1${alpha_spec}\"/g" ${pyproject_file} +done + if [[ $PACKAGE_CUDA_SUFFIX == "-cu12" ]]; then sed -i "s/cupy-cuda11x/cupy-cuda12x/g" ${pyproject_file} diff --git a/ci/release/update-version.sh b/ci/release/update-version.sh index 0f2d4a3b914..69eb085e7ed 100755 --- a/ci/release/update-version.sh +++ b/ci/release/update-version.sh @@ -88,6 +88,7 @@ DEPENDENCIES=( raft-dask rmm ucx-py + rapids-dask-dependency ) for DEP in "${DEPENDENCIES[@]}"; do for FILE in dependencies.yaml conda/environments/*.yaml python/cugraph-{pyg,dgl}/conda/*.yaml; do diff --git a/ci/test_wheel_cugraph.sh b/ci/test_wheel_cugraph.sh index f9e2aa6d8da..d351ea21624 100755 --- a/ci/test_wheel_cugraph.sh +++ b/ci/test_wheel_cugraph.sh @@ -8,7 +8,4 @@ RAPIDS_PY_CUDA_SUFFIX="$(rapids-wheel-ctk-name-gen ${RAPIDS_CUDA_VERSION})" RAPIDS_PY_WHEEL_NAME="pylibcugraph_${RAPIDS_PY_CUDA_SUFFIX}" rapids-download-wheels-from-s3 ./local-pylibcugraph-dep python -m pip install --no-deps ./local-pylibcugraph-dep/pylibcugraph*.whl -# Always install latest dask for testing -python -m pip install git+https://github.com/dask/dask.git@main git+https://github.com/dask/distributed.git@main - ./ci/test_wheel.sh cugraph python/cugraph diff --git a/conda/environments/all_cuda-118_arch-x86_64.yaml b/conda/environments/all_cuda-118_arch-x86_64.yaml index 2f3a9c988cf..aa38defcd7c 100644 --- a/conda/environments/all_cuda-118_arch-x86_64.yaml +++ b/conda/environments/all_cuda-118_arch-x86_64.yaml @@ -20,11 +20,8 @@ dependencies: - cupy>=12.0.0 - cxx-compiler - cython>=3.0.0 -- dask-core>=2023.9.2 - dask-cuda==23.12.* - dask-cudf==23.12.* -- dask>=2023.7.1 -- distributed>=2023.7.1 - doxygen - fsspec>=0.6.0 - gcc_linux-64=11.* @@ -62,6 +59,7 @@ dependencies: - pytest-xdist - python-louvain - raft-dask==23.12.* +- rapids-dask-dependency==23.12.* - recommonmark - requests - rmm==23.12.* diff --git a/conda/environments/all_cuda-120_arch-x86_64.yaml b/conda/environments/all_cuda-120_arch-x86_64.yaml index 31ff503e682..a9f793b15f5 100644 --- a/conda/environments/all_cuda-120_arch-x86_64.yaml +++ b/conda/environments/all_cuda-120_arch-x86_64.yaml @@ -20,11 +20,8 @@ dependencies: - cupy>=12.0.0 - cxx-compiler - cython>=3.0.0 -- dask-core>=2023.9.2 - dask-cuda==23.12.* - dask-cudf==23.12.* -- dask>=2023.7.1 -- distributed>=2023.7.1 - doxygen - fsspec>=0.6.0 - gcc_linux-64=11.* @@ -61,6 +58,7 @@ dependencies: - pytest-xdist - python-louvain - raft-dask==23.12.* +- rapids-dask-dependency==23.12.* - recommonmark - requests - rmm==23.12.* diff --git a/conda/recipes/cugraph-pyg/meta.yaml b/conda/recipes/cugraph-pyg/meta.yaml index 2714dcfa55a..a6744a9f340 100644 --- a/conda/recipes/cugraph-pyg/meta.yaml +++ b/conda/recipes/cugraph-pyg/meta.yaml @@ -26,7 +26,7 @@ requirements: - python - scikit-build >=0.13.1 run: - - distributed >=2023.9.2 + - rapids-dask-dependency ={{ version }} - numba >=0.57 - numpy >=1.21 - python diff --git a/conda/recipes/cugraph-service/meta.yaml b/conda/recipes/cugraph-service/meta.yaml index ae8074ba7d3..d52a004db05 100644 --- a/conda/recipes/cugraph-service/meta.yaml +++ b/conda/recipes/cugraph-service/meta.yaml @@ -59,10 +59,10 @@ outputs: - cupy >=12.0.0 - dask-cuda ={{ minor_version }} - dask-cudf ={{ minor_version }} - - distributed >=2023.9.2 - numba >=0.57 - numpy >=1.21 - python + - rapids-dask-dependency ={{ minor_version }} - thriftpy2 >=0.4.15 - ucx-py {{ ucx_py_version }} diff --git a/conda/recipes/cugraph/meta.yaml b/conda/recipes/cugraph/meta.yaml index 65403bc8d73..58b9ea220d4 100644 --- a/conda/recipes/cugraph/meta.yaml +++ b/conda/recipes/cugraph/meta.yaml @@ -76,15 +76,13 @@ requirements: - cupy >=12.0.0 - dask-cuda ={{ minor_version }} - dask-cudf ={{ minor_version }} - - dask >=2023.9.2 - - dask-core >=2023.9.2 - - distributed >=2023.9.2 - fsspec>=0.6.0 - libcugraph ={{ version }} - pylibcugraph ={{ version }} - pylibraft ={{ minor_version }} - python - raft-dask ={{ minor_version }} + - rapids-dask-dependency ={{ minor_version }} - requests - ucx-proc=*=gpu - ucx-py {{ ucx_py_version }} diff --git a/dependencies.yaml b/dependencies.yaml index b127d9bd29e..13f100610cf 100644 --- a/dependencies.yaml +++ b/dependencies.yaml @@ -377,15 +377,13 @@ dependencies: common: - output_types: [conda, pyproject] packages: - - &dask dask>=2023.7.1 - - &distributed distributed>=2023.7.1 + - &dask rapids-dask-dependency==23.12.* - &dask_cuda dask-cuda==23.12.* - &numba numba>=0.57 - &ucx_py ucx-py==0.35.* - output_types: conda packages: - aiohttp - - &dask-core_conda dask-core>=2023.9.2 - fsspec>=0.6.0 - libcudf==23.12.* - requests @@ -431,14 +429,10 @@ dependencies: packages: - *dask - *dask_cuda - - *distributed - *numba - *numpy - *thrift - *ucx_py - - output_types: conda - packages: - - *dask-core_conda - output_types: pyproject packages: - *cugraph diff --git a/python/cugraph-service/server/pyproject.toml b/python/cugraph-service/server/pyproject.toml index f50b33b3f15..d68f8055ded 100644 --- a/python/cugraph-service/server/pyproject.toml +++ b/python/cugraph-service/server/pyproject.toml @@ -25,10 +25,9 @@ dependencies = [ "cupy-cuda11x>=12.0.0", "dask-cuda==23.12.*", "dask-cudf==23.12.*", - "dask>=2023.7.1", - "distributed>=2023.7.1", "numba>=0.57", "numpy>=1.21", + "rapids-dask-dependency==23.12.*", "rmm==23.12.*", "thriftpy2", "ucx-py==0.35.*", diff --git a/python/cugraph/pyproject.toml b/python/cugraph/pyproject.toml index aaa301fa05f..319900b3de3 100644 --- a/python/cugraph/pyproject.toml +++ b/python/cugraph/pyproject.toml @@ -33,12 +33,11 @@ dependencies = [ "cupy-cuda11x>=12.0.0", "dask-cuda==23.12.*", "dask-cudf==23.12.*", - "dask>=2023.7.1", - "distributed>=2023.7.1", "fsspec[http]>=0.6.0", "numba>=0.57", "pylibcugraph==23.12.*", "raft-dask==23.12.*", + "rapids-dask-dependency==23.12.*", "rmm==23.12.*", "ucx-py==0.35.*", ] # This list was generated by `rapids-dependency-file-generator`. To make changes, edit ../../dependencies.yaml and run `rapids-dependency-file-generator`. From 8c104a52bbece783f91001c64cc6bf0732f10594 Mon Sep 17 00:00:00 2001 From: Bradley Dice Date: Wed, 15 Nov 2023 17:01:59 -0600 Subject: [PATCH 083/111] Pin to minor versions of packages outside the cuGraph repository. (#4004) This PR fixes some pinnings in cuGraph conda recipes. The problem is similar to that handled in https://github.com/rapidsai/cudf/pull/14420. The `{{ version }}` variable can only be used to constrain conda packages built by CI workflows in the _same repository_ because `{{ version }}` includes information about the git commit. We must use `{{ minor_version }}` to constrain other RAPIDS packages. In cuGraph, that means that `pylibcugraphops` (which is built by the cugraph-ops repository) and `rapids-dask-dependency` must pin with `={{ minor_version }}` instead of `={{ version }}`. Authors: - Bradley Dice (https://github.com/bdice) Approvers: - GALI PREM SAGAR (https://github.com/galipremsagar) - Ray Douglass (https://github.com/raydouglass) --- conda/recipes/cugraph-dgl/meta.yaml | 2 +- conda/recipes/cugraph-pyg/meta.yaml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/conda/recipes/cugraph-dgl/meta.yaml b/conda/recipes/cugraph-dgl/meta.yaml index bb85734098a..aaa1cd8a936 100644 --- a/conda/recipes/cugraph-dgl/meta.yaml +++ b/conda/recipes/cugraph-dgl/meta.yaml @@ -26,7 +26,7 @@ requirements: - dgl >=1.1.0.cu* - numba >=0.57 - numpy >=1.21 - - pylibcugraphops ={{ version }} + - pylibcugraphops ={{ minor_version }} - python - pytorch diff --git a/conda/recipes/cugraph-pyg/meta.yaml b/conda/recipes/cugraph-pyg/meta.yaml index a6744a9f340..07caf07daab 100644 --- a/conda/recipes/cugraph-pyg/meta.yaml +++ b/conda/recipes/cugraph-pyg/meta.yaml @@ -26,14 +26,14 @@ requirements: - python - scikit-build >=0.13.1 run: - - rapids-dask-dependency ={{ version }} + - rapids-dask-dependency ={{ minor_version }} - numba >=0.57 - numpy >=1.21 - python - pytorch >=2.0 - cupy >=12.0.0 - cugraph ={{ version }} - - pylibcugraphops ={{ version }} + - pylibcugraphops ={{ minor_version }} - pyg >=2.3,<2.4 tests: From 119816cfe04be421bf96baa8075d342f72cbbfc4 Mon Sep 17 00:00:00 2001 From: Alex Barghi <105237337+alexbarghi-nv@users.noreply.github.com> Date: Fri, 17 Nov 2023 09:16:31 -0500 Subject: [PATCH 084/111] [BUG] Fix Incorrect Edge Index, Directory Selection in cuGraph-PyG Loader (#3978) Fixes three major bugs: 1. Edge index is set to [dst, dst] instead of [dst, src] in some cases 2. The sample directory is always set to a new temporary directory rather than the path given 3. The version of `pylibcugraphops` in `meta.yaml` is wrong and causes the wrong packages to be resolved This PR also simplifies `ci/test_python.sh` by doing only a single conda install when creating the `test_cugraph_pyg` environment. Closes #3959 Authors: - Alex Barghi (https://github.com/alexbarghi-nv) - Naim (https://github.com/naimnv) Approvers: - Brad Rees (https://github.com/BradReesWork) - Ray Douglass (https://github.com/raydouglass) URL: https://github.com/rapidsai/cugraph/pull/3978 --- ci/test_python.sh | 31 ++++++----- conda/recipes/cugraph-pyg/meta.yaml | 2 +- dependencies.yaml | 4 +- .../conda/cugraph_pyg_dev_cuda-118.yaml | 4 +- .../cugraph_pyg/data/cugraph_store.py | 12 +++-- .../cugraph_pyg/loader/cugraph_node_loader.py | 48 ++++++++++------- .../tests/mg/test_mg_cugraph_store.py | 2 +- .../cugraph_pyg/tests/test_cugraph_loader.py | 54 +++++++++++++++++-- .../cugraph_pyg/tests/test_cugraph_store.py | 2 +- 9 files changed, 112 insertions(+), 47 deletions(-) diff --git a/ci/test_python.sh b/ci/test_python.sh index 1690ce2f15b..273d3c93482 100755 --- a/ci/test_python.sh +++ b/ci/test_python.sh @@ -197,27 +197,26 @@ if [[ "${RAPIDS_CUDA_VERSION}" == "11.8.0" ]]; then conda activate test_cugraph_pyg set -u - # Install pytorch + # Will automatically install built dependencies of cuGraph-PyG rapids-mamba-retry install \ - --force-reinstall \ - --channel pyg \ + --channel "${CPP_CHANNEL}" \ + --channel "${PYTHON_CHANNEL}" \ --channel pytorch \ --channel nvidia \ - 'pyg=2.3' \ - 'pytorch=2.0.0' \ - 'pytorch-cuda=11.8' + --channel pyg \ + --channel rapidsai-nightly \ + "cugraph-pyg" \ + "pytorch>=2.0,<2.1" \ + "pytorch-cuda=11.8" # Install pyg dependencies (which requires pip) - pip install pyg_lib torch_scatter torch_sparse torch_cluster torch_spline_conv -f https://data.pyg.org/whl/torch-2.0.0+cu118.html - - rapids-mamba-retry install \ - --channel "${CPP_CHANNEL}" \ - --channel "${PYTHON_CHANNEL}" \ - libcugraph \ - pylibcugraph \ - pylibcugraphops \ - cugraph \ - cugraph-pyg + pip install \ + pyg_lib \ + torch_scatter \ + torch_sparse \ + torch_cluster \ + torch_spline_conv \ + -f https://data.pyg.org/whl/torch-2.0.0+cu118.html rapids-print-env diff --git a/conda/recipes/cugraph-pyg/meta.yaml b/conda/recipes/cugraph-pyg/meta.yaml index 07caf07daab..a2a02a1d9f6 100644 --- a/conda/recipes/cugraph-pyg/meta.yaml +++ b/conda/recipes/cugraph-pyg/meta.yaml @@ -34,7 +34,7 @@ requirements: - cupy >=12.0.0 - cugraph ={{ version }} - pylibcugraphops ={{ minor_version }} - - pyg >=2.3,<2.4 + - pyg >=2.3,<2.5 tests: imports: diff --git a/dependencies.yaml b/dependencies.yaml index 13f100610cf..a89acd9288b 100644 --- a/dependencies.yaml +++ b/dependencies.yaml @@ -497,9 +497,9 @@ dependencies: - output_types: [conda] packages: - cugraph==23.12.* - - pytorch==2.0 + - pytorch>=2.0 - pytorch-cuda==11.8 - - pyg=2.3.1=*torch_2.0.0*cu118* + - pyg>=2.4.0 depends_on_rmm: common: diff --git a/python/cugraph-pyg/conda/cugraph_pyg_dev_cuda-118.yaml b/python/cugraph-pyg/conda/cugraph_pyg_dev_cuda-118.yaml index f98eab430ba..71d1c7e389c 100644 --- a/python/cugraph-pyg/conda/cugraph_pyg_dev_cuda-118.yaml +++ b/python/cugraph-pyg/conda/cugraph_pyg_dev_cuda-118.yaml @@ -13,13 +13,13 @@ dependencies: - cugraph==23.12.* - pandas - pre-commit -- pyg=2.3.1=*torch_2.0.0*cu118* +- pyg>=2.4.0 - pylibcugraphops==23.12.* - pytest - pytest-benchmark - pytest-cov - pytest-xdist - pytorch-cuda==11.8 -- pytorch==2.0 +- pytorch>=2.0 - scipy name: cugraph_pyg_dev_cuda-118 diff --git a/python/cugraph-pyg/cugraph_pyg/data/cugraph_store.py b/python/cugraph-pyg/cugraph_pyg/data/cugraph_store.py index d1b24543956..edeeface4c4 100644 --- a/python/cugraph-pyg/cugraph_pyg/data/cugraph_store.py +++ b/python/cugraph-pyg/cugraph_pyg/data/cugraph_store.py @@ -210,7 +210,10 @@ class EXPERIMENTAL__CuGraphStore: def __init__( self, F: cugraph.gnn.FeatureStore, - G: Union[Dict[str, Tuple[TensorType]], Dict[str, int]], + G: Union[ + Dict[Tuple[str, str, str], Tuple[TensorType]], + Dict[Tuple[str, str, str], int], + ], num_nodes_dict: Dict[str, int], *, multi_gpu: bool = False, @@ -744,7 +747,7 @@ def _subgraph(self, edge_types: List[tuple] = None) -> cugraph.MultiGraph: def _get_vertex_groups_from_sample( self, nodes_of_interest: TensorType, is_sorted: bool = False - ) -> dict: + ) -> Dict[str, torch.Tensor]: """ Given a tensor of nodes of interest, this method a single dictionary, noi_index. @@ -808,7 +811,10 @@ def _get_sample_from_vertex_groups( def _get_renumbered_edge_groups_from_sample( self, sampling_results: cudf.DataFrame, noi_index: dict - ) -> Tuple[dict, dict]: + ) -> Tuple[ + Dict[Tuple[str, str, str], torch.Tensor], + Tuple[Dict[Tuple[str, str, str], torch.Tensor]], + ]: """ Given a cudf (NOT dask_cudf) DataFrame of sampling results and a dictionary of non-renumbered vertex ids grouped by vertex type, this method diff --git a/python/cugraph-pyg/cugraph_pyg/loader/cugraph_node_loader.py b/python/cugraph-pyg/cugraph_pyg/loader/cugraph_node_loader.py index 8552e7412e0..ad8d22e255e 100644 --- a/python/cugraph-pyg/cugraph_pyg/loader/cugraph_node_loader.py +++ b/python/cugraph-pyg/cugraph_pyg/loader/cugraph_node_loader.py @@ -15,6 +15,7 @@ import os import re +import warnings import cupy import cudf @@ -159,23 +160,34 @@ def __init__( if batch_size is None or batch_size < 1: raise ValueError("Batch size must be >= 1") - self.__directory = tempfile.TemporaryDirectory(dir=directory) + self.__directory = ( + tempfile.TemporaryDirectory() if directory is None else directory + ) if isinstance(num_neighbors, dict): raise ValueError("num_neighbors dict is currently unsupported!") - renumber = ( - True - if ( - (len(self.__graph_store.node_types) == 1) - and (len(self.__graph_store.edge_types) == 1) + if "renumber" in kwargs: + warnings.warn( + "Setting renumbering manually could result in invalid output," + " please ensure you intended to do this." + ) + renumber = kwargs.pop("renumber") + else: + renumber = ( + True + if ( + (len(self.__graph_store.node_types) == 1) + and (len(self.__graph_store.edge_types) == 1) + ) + else False ) - else False - ) bulk_sampler = BulkSampler( batch_size, - self.__directory.name, + self.__directory + if isinstance(self.__directory, str) + else self.__directory.name, self.__graph_store._subgraph(edge_types), fanout_vals=num_neighbors, with_replacement=replace, @@ -219,7 +231,13 @@ def __init__( ) bulk_sampler.flush() - self.__input_files = iter(os.listdir(self.__directory.name)) + self.__input_files = iter( + os.listdir( + self.__directory + if isinstance(self.__directory, str) + else self.__directory.name + ) + ) def __next__(self): from time import perf_counter @@ -423,9 +441,6 @@ def __next__(self): sampler_output.edge, ) else: - if self.__graph_store.order == "CSR": - raise ValueError("CSR format incompatible with CSC output") - out = filter_cugraph_store_csc( self.__feature_store, self.__graph_store, @@ -437,11 +452,8 @@ def __next__(self): # Account for CSR format in cuGraph vs. CSC format in PyG if self.__coo and self.__graph_store.order == "CSC": - for node_type in out.edge_index_dict: - out[node_type].edge_index[0], out[node_type].edge_index[1] = ( - out[node_type].edge_index[1], - out[node_type].edge_index[0], - ) + for edge_type in out.edge_index_dict: + out[edge_type].edge_index = out[edge_type].edge_index.flip(dims=[0]) out.set_value_dict("num_sampled_nodes", sampler_output.num_sampled_nodes) out.set_value_dict("num_sampled_edges", sampler_output.num_sampled_edges) diff --git a/python/cugraph-pyg/cugraph_pyg/tests/mg/test_mg_cugraph_store.py b/python/cugraph-pyg/cugraph_pyg/tests/mg/test_mg_cugraph_store.py index ed7f70034e2..13c9c90c7c2 100644 --- a/python/cugraph-pyg/cugraph_pyg/tests/mg/test_mg_cugraph_store.py +++ b/python/cugraph-pyg/cugraph_pyg/tests/mg/test_mg_cugraph_store.py @@ -120,7 +120,7 @@ def test_get_edge_index(graph, edge_index_type, dask_client): G[et][0] = dask_cudf.from_cudf(cudf.Series(G[et][0]), npartitions=1) G[et][1] = dask_cudf.from_cudf(cudf.Series(G[et][1]), npartitions=1) - cugraph_store = CuGraphStore(F, G, N, multi_gpu=True) + cugraph_store = CuGraphStore(F, G, N, order="CSC", multi_gpu=True) for pyg_can_edge_type in G: src, dst = cugraph_store.get_edge_index( diff --git a/python/cugraph-pyg/cugraph_pyg/tests/test_cugraph_loader.py b/python/cugraph-pyg/cugraph_pyg/tests/test_cugraph_loader.py index 853836dc2a6..27b73bf7d35 100644 --- a/python/cugraph-pyg/cugraph_pyg/tests/test_cugraph_loader.py +++ b/python/cugraph-pyg/cugraph_pyg/tests/test_cugraph_loader.py @@ -18,6 +18,7 @@ import cudf import cupy +import numpy as np from cugraph_pyg.loader import CuGraphNeighborLoader from cugraph_pyg.loader import BulkSampleLoader @@ -27,6 +28,8 @@ from cugraph.gnn import FeatureStore from cugraph.utilities.utils import import_optional, MissingModule +from typing import Dict, Tuple + torch = import_optional("torch") torch_geometric = import_optional("torch_geometric") trim_to_layer = import_optional("torch_geometric.utils.trim_to_layer") @@ -40,7 +43,11 @@ @pytest.mark.skipif(isinstance(torch, MissingModule), reason="torch not available") -def test_cugraph_loader_basic(karate_gnn): +def test_cugraph_loader_basic( + karate_gnn: Tuple[ + FeatureStore, Dict[Tuple[str, str, str], np.ndarray], Dict[str, int] + ] +): F, G, N = karate_gnn cugraph_store = CuGraphStore(F, G, N, order="CSR") loader = CuGraphNeighborLoader( @@ -66,7 +73,11 @@ def test_cugraph_loader_basic(karate_gnn): @pytest.mark.skipif(isinstance(torch, MissingModule), reason="torch not available") -def test_cugraph_loader_hetero(karate_gnn): +def test_cugraph_loader_hetero( + karate_gnn: Tuple[ + FeatureStore, Dict[Tuple[str, str, str], np.ndarray], Dict[str, int] + ] +): F, G, N = karate_gnn cugraph_store = CuGraphStore(F, G, N, order="CSR") loader = CuGraphNeighborLoader( @@ -342,7 +353,7 @@ def test_cugraph_loader_e2e_coo(): @pytest.mark.skipif(isinstance(torch, MissingModule), reason="torch not available") @pytest.mark.skipif(not HAS_TORCH_SPARSE, reason="torch-sparse not available") @pytest.mark.parametrize("framework", ["pyg", "cugraph-ops"]) -def test_cugraph_loader_e2e_csc(framework): +def test_cugraph_loader_e2e_csc(framework: str): m = [2, 9, 99, 82, 9, 3, 18, 1, 12] x = torch.randint(3000, (256, 256)).to(torch.float32) F = FeatureStore() @@ -442,3 +453,40 @@ def test_cugraph_loader_e2e_csc(framework): x = x.narrow(dim=0, start=0, length=s - num_sampled_nodes[1]) assert list(x.shape) == [1, 1] + + +@pytest.mark.skipif(isinstance(torch, MissingModule), reason="torch not available") +@pytest.mark.parametrize("directory", ["local", "temp"]) +def test_load_directory( + karate_gnn: Tuple[ + FeatureStore, Dict[Tuple[str, str, str], np.ndarray], Dict[str, int] + ], + directory: str, +): + if directory == "local": + local_dir = tempfile.TemporaryDirectory(dir=".") + + cugraph_store = CuGraphStore(*karate_gnn) + cugraph_loader = CuGraphNeighborLoader( + (cugraph_store, cugraph_store), + torch.arange(8, dtype=torch.int64), + 2, + num_neighbors=[8, 4, 2], + random_state=62, + replace=False, + directory=None if directory == "temp" else local_dir.name, + batches_per_partition=1, + ) + + it = iter(cugraph_loader) + next_batch = next(it) + assert next_batch is not None + + if directory == "local": + assert len(os.listdir(local_dir.name)) == 4 + + count = 1 + while next(it, None) is not None: + count += 1 + + assert count == 4 diff --git a/python/cugraph-pyg/cugraph_pyg/tests/test_cugraph_store.py b/python/cugraph-pyg/cugraph_pyg/tests/test_cugraph_store.py index da3043760d4..b39ebad8254 100644 --- a/python/cugraph-pyg/cugraph_pyg/tests/test_cugraph_store.py +++ b/python/cugraph-pyg/cugraph_pyg/tests/test_cugraph_store.py @@ -113,7 +113,7 @@ def test_get_edge_index(graph, edge_index_type): G[et][0] = cudf.Series(G[et][0]) G[et][1] = cudf.Series(G[et][1]) - cugraph_store = CuGraphStore(F, G, N) + cugraph_store = CuGraphStore(F, G, N, order="CSC") for pyg_can_edge_type in G: src, dst = cugraph_store.get_edge_index( From 06f082b43aab84408d63a907327b9524f1cd0229 Mon Sep 17 00:00:00 2001 From: Bradley Dice Date: Fri, 17 Nov 2023 10:36:35 -0600 Subject: [PATCH 085/111] Enable build concurrency for nightly and merge triggers. (#4009) --- .github/workflows/build.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index ccfdb826812..0f490283795 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -22,7 +22,7 @@ on: default: nightly concurrency: - group: ${{ github.workflow }}-${{ github.ref }} + group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event_name }} cancel-in-progress: true jobs: From 5d43f1463c1829322d2a92684f2bb11269730af3 Mon Sep 17 00:00:00 2001 From: Vyas Ramasubramani Date: Fri, 17 Nov 2023 18:17:47 -0800 Subject: [PATCH 086/111] Find rmm before cuco (#4011) RAPIDS currently relies on copies of CCCL headers bundled into rmm. This dependency is centralized by virtue of rmm installing these into the package and everything else finding those installed packages. To do this, however, rmm must be loaded first so that the libcudacxx install location is patched into CMake's search paths. cugraph also uses cuco, which requires libcudacxx but does not bundle its own, so rmm must be found first so that cuco can find libcudacxx where rmm installed it. Authors: - Vyas Ramasubramani (https://github.com/vyasr) Approvers: - Bradley Dice (https://github.com/bdice) - Rick Ratzel (https://github.com/rlratzel) URL: https://github.com/rapidsai/cugraph/pull/4011 --- cpp/CMakeLists.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index 360165e688d..3e867643041 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -153,6 +153,11 @@ rapids_cpm_init() # lags behind. ### +# Need to make sure rmm is found before cuco so that rmm patches the libcudacxx +# directory to be found by cuco. +include(${rapids-cmake-dir}/cpm/rmm.cmake) +rapids_cpm_rmm(BUILD_EXPORT_SET cugraph-exports + INSTALL_EXPORT_SET cugraph-exports) # Putting this before raft to override RAFT from pulling them in. include(cmake/thirdparty/get_libcudacxx.cmake) include(${rapids-cmake-dir}/cpm/cuco.cmake) From 0684f9de2015c4b7d45f28605f2bb2f9bf523359 Mon Sep 17 00:00:00 2001 From: Ralph Liu <137829296+nv-rliu@users.noreply.github.com> Date: Sun, 19 Nov 2023 07:09:32 -0500 Subject: [PATCH 087/111] `Resultset` and `Dataset` Refactors (#3957) This PR replaces and is a continuation of #3857 (by @betochimas) > This PR primarily adds testing for the `Resultset` class, introduced earlier in 23.10. The tests take a similar approach to test_dataset, creating a temporary directory to test downloading all result files. To align `Resultset` and `Dataset`, the setter and getter for each download directory is moved into `DefaultDownloadDir`, so that each class shares an instance of `DefaultDownloadDir` and can be configured independently, although their default locations are still both dependent on the RAPIDS_DATASET_ROOT_DIR_PATH environment variable. The old patterns are present but commented-out, so this change would be breaking. This PR also removes the deprecated `experimental.datasets` package due to it being promoted to stable for >=1 release. Authors: - Ralph Liu (https://github.com/nv-rliu) - Dylan Chima-Sanchez (https://github.com/betochimas) - Rick Ratzel (https://github.com/rlratzel) - Brad Rees (https://github.com/BradReesWork) Approvers: - Rick Ratzel (https://github.com/rlratzel) URL: https://github.com/rapidsai/cugraph/pull/3957 --- datasets/README.md | 4 + .../cugraph-pyg/cugraph_pyg/tests/conftest.py | 2 +- .../testing/benchmark_server_extension.py | 2 +- .../cugraph/cugraph/dask/community/leiden.py | 2 +- .../cugraph/cugraph/dask/community/louvain.py | 2 +- python/cugraph/cugraph/datasets/__init__.py | 10 + python/cugraph/cugraph/datasets/dataset.py | 86 +++-- .../datasets/metadata/cit-patents.yaml | 22 ++ .../cugraph/datasets/metadata/europe_osm.yaml | 21 ++ .../cugraph/datasets/metadata/hollywood.yaml | 26 ++ .../datasets/metadata/soc-livejournal1.yaml | 22 ++ .../datasets/metadata/soc-twitter-2010.yaml | 22 ++ .../cugraph/experimental/datasets/__init__.py | 79 ----- .../cugraph/experimental/datasets/dataset.py | 312 ------------------ .../datasets/datasets_config.yaml | 5 - .../datasets/metadata/__init__.py | 13 - .../experimental/datasets/metadata/cyber.yaml | 22 -- .../datasets/metadata/dolphins.yaml | 25 -- .../datasets/metadata/email-Eu-core.yaml | 22 -- .../datasets/metadata/karate-disjoint.yaml | 22 -- .../datasets/metadata/karate.yaml | 24 -- .../datasets/metadata/karate_asymmetric.yaml | 24 -- .../datasets/metadata/karate_data.yaml | 22 -- .../datasets/metadata/karate_undirected.yaml | 22 -- .../datasets/metadata/ktruss_polbooks.yaml | 23 -- .../datasets/metadata/netscience.yaml | 22 -- .../datasets/metadata/polbooks.yaml | 22 -- .../datasets/metadata/small_line.yaml | 22 -- .../datasets/metadata/small_tree.yaml | 22 -- .../datasets/metadata/toy_graph.yaml | 22 -- .../metadata/toy_graph_undirected.yaml | 22 -- python/cugraph/cugraph/testing/__init__.py | 8 +- .../cugraph/testing/generate_resultsets.py | 9 +- python/cugraph/cugraph/testing/resultset.py | 90 ++--- .../test_edge_betweenness_centrality_mg.py | 4 +- .../cugraph/tests/nx/test_compat_pr.py | 2 +- .../cugraph/tests/utils/test_dataset.py | 109 +++--- .../cugraph/tests/utils/test_resultset.py | 71 ++++ 38 files changed, 379 insertions(+), 882 deletions(-) create mode 100644 python/cugraph/cugraph/datasets/metadata/cit-patents.yaml create mode 100644 python/cugraph/cugraph/datasets/metadata/europe_osm.yaml create mode 100644 python/cugraph/cugraph/datasets/metadata/hollywood.yaml create mode 100644 python/cugraph/cugraph/datasets/metadata/soc-livejournal1.yaml create mode 100644 python/cugraph/cugraph/datasets/metadata/soc-twitter-2010.yaml delete mode 100644 python/cugraph/cugraph/experimental/datasets/__init__.py delete mode 100644 python/cugraph/cugraph/experimental/datasets/dataset.py delete mode 100644 python/cugraph/cugraph/experimental/datasets/datasets_config.yaml delete mode 100644 python/cugraph/cugraph/experimental/datasets/metadata/__init__.py delete mode 100644 python/cugraph/cugraph/experimental/datasets/metadata/cyber.yaml delete mode 100644 python/cugraph/cugraph/experimental/datasets/metadata/dolphins.yaml delete mode 100644 python/cugraph/cugraph/experimental/datasets/metadata/email-Eu-core.yaml delete mode 100644 python/cugraph/cugraph/experimental/datasets/metadata/karate-disjoint.yaml delete mode 100644 python/cugraph/cugraph/experimental/datasets/metadata/karate.yaml delete mode 100644 python/cugraph/cugraph/experimental/datasets/metadata/karate_asymmetric.yaml delete mode 100644 python/cugraph/cugraph/experimental/datasets/metadata/karate_data.yaml delete mode 100644 python/cugraph/cugraph/experimental/datasets/metadata/karate_undirected.yaml delete mode 100644 python/cugraph/cugraph/experimental/datasets/metadata/ktruss_polbooks.yaml delete mode 100644 python/cugraph/cugraph/experimental/datasets/metadata/netscience.yaml delete mode 100644 python/cugraph/cugraph/experimental/datasets/metadata/polbooks.yaml delete mode 100644 python/cugraph/cugraph/experimental/datasets/metadata/small_line.yaml delete mode 100644 python/cugraph/cugraph/experimental/datasets/metadata/small_tree.yaml delete mode 100644 python/cugraph/cugraph/experimental/datasets/metadata/toy_graph.yaml delete mode 100644 python/cugraph/cugraph/experimental/datasets/metadata/toy_graph_undirected.yaml create mode 100644 python/cugraph/cugraph/tests/utils/test_resultset.py diff --git a/datasets/README.md b/datasets/README.md index e42413fc996..a23dc644081 100644 --- a/datasets/README.md +++ b/datasets/README.md @@ -120,9 +120,13 @@ The benchmark datasets are described below: | soc-twitter-2010 | 21,297,772 | 265,025,809 | No | No | **cit-Patents** : A citation graph that includes all citations made by patents granted between 1975 and 1999, totaling 16,522,438 citations. + **soc-LiveJournal** : A graph of the LiveJournal social network. + **europe_osm** : A graph of OpenStreetMap data for Europe. + **hollywood** : A graph of movie actors where vertices are actors, and two actors are joined by an edge whenever they appeared in a movie together. + **soc-twitter-2010** : A network of follower relationships from a snapshot of Twitter in 2010, where an edge from i to j indicates that j is a follower of i. _NOTE: the benchmark datasets were converted to a CSV format from their original format described in the reference URL below, and in doing so had edge weights and isolated vertices discarded._ diff --git a/python/cugraph-pyg/cugraph_pyg/tests/conftest.py b/python/cugraph-pyg/cugraph_pyg/tests/conftest.py index 083c4a2b37b..1512901822a 100644 --- a/python/cugraph-pyg/cugraph_pyg/tests/conftest.py +++ b/python/cugraph-pyg/cugraph_pyg/tests/conftest.py @@ -24,7 +24,7 @@ import torch import numpy as np from cugraph.gnn import FeatureStore -from cugraph.experimental.datasets import karate +from cugraph.datasets import karate import tempfile diff --git a/python/cugraph-service/server/cugraph_service_server/testing/benchmark_server_extension.py b/python/cugraph-service/server/cugraph_service_server/testing/benchmark_server_extension.py index 5f9eac6b2a3..361226c8071 100644 --- a/python/cugraph-service/server/cugraph_service_server/testing/benchmark_server_extension.py +++ b/python/cugraph-service/server/cugraph_service_server/testing/benchmark_server_extension.py @@ -17,7 +17,7 @@ import cugraph from cugraph.experimental import PropertyGraph, MGPropertyGraph -from cugraph.experimental import datasets +from cugraph import datasets from cugraph.generators import rmat diff --git a/python/cugraph/cugraph/dask/community/leiden.py b/python/cugraph/cugraph/dask/community/leiden.py index 75582fa48f7..67bd0876ce6 100644 --- a/python/cugraph/cugraph/dask/community/leiden.py +++ b/python/cugraph/cugraph/dask/community/leiden.py @@ -125,7 +125,7 @@ def leiden( Examples -------- - >>> from cugraph.experimental.datasets import karate + >>> from cugraph.datasets import karate >>> G = karate.get_graph(fetch=True) >>> parts, modularity_score = cugraph.leiden(G) diff --git a/python/cugraph/cugraph/dask/community/louvain.py b/python/cugraph/cugraph/dask/community/louvain.py index 8efbbafaf7b..1b091817a1a 100644 --- a/python/cugraph/cugraph/dask/community/louvain.py +++ b/python/cugraph/cugraph/dask/community/louvain.py @@ -129,7 +129,7 @@ def louvain( Examples -------- - >>> from cugraph.experimental.datasets import karate + >>> from cugraph.datasets import karate >>> G = karate.get_graph(fetch=True) >>> parts = cugraph.louvain(G) diff --git a/python/cugraph/cugraph/datasets/__init__.py b/python/cugraph/cugraph/datasets/__init__.py index 65a820f108b..ac18274d354 100644 --- a/python/cugraph/cugraph/datasets/__init__.py +++ b/python/cugraph/cugraph/datasets/__init__.py @@ -39,3 +39,13 @@ small_tree = Dataset(meta_path / "small_tree.yaml") toy_graph = Dataset(meta_path / "toy_graph.yaml") toy_graph_undirected = Dataset(meta_path / "toy_graph_undirected.yaml") + +# Benchmarking datasets: be mindful of memory usage +# 250 MB +soc_livejournal = Dataset(meta_path / "soc-livejournal1.yaml") +# 965 MB +cit_patents = Dataset(meta_path / "cit-patents.yaml") +# 1.8 GB +europe_osm = Dataset(meta_path / "europe_osm.yaml") +# 1.5 GB +hollywood = Dataset(meta_path / "hollywood.yaml") diff --git a/python/cugraph/cugraph/datasets/dataset.py b/python/cugraph/cugraph/datasets/dataset.py index 877eade7708..dd7aa0df00a 100644 --- a/python/cugraph/cugraph/datasets/dataset.py +++ b/python/cugraph/cugraph/datasets/dataset.py @@ -14,44 +14,45 @@ import cudf import yaml import os +import pandas as pd from pathlib import Path from cugraph.structure.graph_classes import Graph class DefaultDownloadDir: """ - Maintains the path to the download directory used by Dataset instances. + Maintains a path to be used as a default download directory. + + All DefaultDownloadDir instances are based on RAPIDS_DATASET_ROOT_DIR if + set, or _default_base_dir if not set. + Instances of this class are typically shared by several Dataset instances in order to allow for the download directory to be defined and updated by a single object. """ - def __init__(self): - self._path = Path( - os.environ.get("RAPIDS_DATASET_ROOT_DIR", Path.home() / ".cugraph/datasets") - ) + _default_base_dir = Path.home() / ".cugraph/datasets" - @property - def path(self): + def __init__(self, *, subdir=""): """ - If `path` is not set, set it to the environment variable - RAPIDS_DATASET_ROOT_DIR. If the variable is not set, default to the - user's home directory. + subdir can be specified to provide a specialized dir under the base dir. """ - if self._path is None: - self._path = Path( - os.environ.get( - "RAPIDS_DATASET_ROOT_DIR", Path.home() / ".cugraph/datasets" - ) - ) - return self._path + self._subdir = Path(subdir) + self.reset() + + @property + def path(self): + return self._path.absolute() @path.setter def path(self, new): self._path = Path(new) - def clear(self): - self._path = None + def reset(self): + self._basedir = Path( + os.environ.get("RAPIDS_DATASET_ROOT_DIR", self._default_base_dir) + ) + self._path = self._basedir / self._subdir default_download_dir = DefaultDownloadDir() @@ -159,7 +160,7 @@ def unload(self): """ self._edgelist = None - def get_edgelist(self, download=False): + def get_edgelist(self, download=False, reader="cudf"): """ Return an Edgelist @@ -168,6 +169,9 @@ def get_edgelist(self, download=False): download : Boolean (default=False) Automatically download the dataset from the 'url' location within the YAML file. + + reader : 'cudf' or 'pandas' (default='cudf') + The library used to read a CSV and return an edgelist DataFrame. """ if self._edgelist is None: full_path = self.get_path() @@ -180,14 +184,29 @@ def get_edgelist(self, download=False): " exist. Try setting download=True" " to download the datafile" ) + header = None if isinstance(self.metadata["header"], int): header = self.metadata["header"] - self._edgelist = cudf.read_csv( - full_path, + + if reader == "cudf": + self.__reader = cudf.read_csv + elif reader == "pandas": + self.__reader = pd.read_csv + else: + raise ValueError( + "reader must be a module with a read_csv function compatible with \ + cudf.read_csv" + ) + + self._edgelist = self.__reader( + filepath_or_buffer=full_path, delimiter=self.metadata["delim"], names=self.metadata["col_names"], - dtype=self.metadata["col_types"], + dtype={ + self.metadata["col_names"][i]: self.metadata["col_types"][i] + for i in range(len(self.metadata["col_types"])) + }, header=header, ) @@ -219,6 +238,10 @@ def get_graph( dataset -if present- will be applied to the Graph. If the dataset does not contain weights, the Graph returned will be unweighted regardless of ignore_weights. + + store_transposed: Boolean (default=False) + If True, stores the transpose of the adjacency matrix. Required + for certain algorithms, such as pagerank. """ if self._edgelist is None: self.get_edgelist(download) @@ -237,20 +260,19 @@ def get_graph( "(or subclass) type or instance, got: " f"{type(create_using)}" ) - if len(self.metadata["col_names"]) > 2 and not (ignore_weights): G.from_cudf_edgelist( self._edgelist, - source="src", - destination="dst", - edge_attr="wgt", + source=self.metadata["col_names"][0], + destination=self.metadata["col_names"][1], + edge_attr=self.metadata["col_names"][2], store_transposed=store_transposed, ) else: G.from_cudf_edgelist( self._edgelist, - source="src", - destination="dst", + source=self.metadata["col_names"][0], + destination=self.metadata["col_names"][1], store_transposed=store_transposed, ) return G @@ -331,7 +353,7 @@ def download_all(force=False): def set_download_dir(path): """ - Set the download location fors datasets + Set the download location for datasets Parameters ---------- @@ -339,10 +361,10 @@ def set_download_dir(path): Location used to store datafiles """ if path is None: - default_download_dir.clear() + default_download_dir.reset() else: default_download_dir.path = path def get_download_dir(): - return default_download_dir.path.absolute() + return default_download_dir.path diff --git a/python/cugraph/cugraph/datasets/metadata/cit-patents.yaml b/python/cugraph/cugraph/datasets/metadata/cit-patents.yaml new file mode 100644 index 00000000000..d5c4cf195bd --- /dev/null +++ b/python/cugraph/cugraph/datasets/metadata/cit-patents.yaml @@ -0,0 +1,22 @@ +name: cit-Patents +file_type: .csv +description: A citation graph that includes all citations made by patents granted between 1975 and 1999, totaling 16,522,438 citations. +author: NBER +refs: + J. Leskovec, J. Kleinberg and C. Faloutsos. Graphs over Time Densification Laws, Shrinking Diameters and Possible Explanations. + ACM SIGKDD International Conference on Knowledge Discovery and Data Mining (KDD), 2005. +delim: " " +header: None +col_names: + - src + - dst +col_types: + - int32 + - int32 +has_loop: true +is_directed: true +is_multigraph: false +is_symmetric: false +number_of_edges: 16518948 +number_of_nodes: 3774768 +url: https://data.rapids.ai/cugraph/datasets/cit-Patents.csv \ No newline at end of file diff --git a/python/cugraph/cugraph/datasets/metadata/europe_osm.yaml b/python/cugraph/cugraph/datasets/metadata/europe_osm.yaml new file mode 100644 index 00000000000..fe0e42a4b86 --- /dev/null +++ b/python/cugraph/cugraph/datasets/metadata/europe_osm.yaml @@ -0,0 +1,21 @@ +name: europe_osm +file_type: .csv +description: A graph of OpenStreetMap data for Europe. +author: M. Kobitzsh / Geofabrik GmbH +refs: + Rossi, Ryan. Ahmed, Nesreen. The Network Data Respoistory with Interactive Graph Analytics and Visualization. +delim: " " +header: None +col_names: + - src + - dst +col_types: + - int32 + - int32 +has_loop: false +is_directed: false +is_multigraph: false +is_symmetric: true +number_of_edges: 54054660 +number_of_nodes: 50912018 +url: https://data.rapids.ai/cugraph/datasets/europe_osm.csv \ No newline at end of file diff --git a/python/cugraph/cugraph/datasets/metadata/hollywood.yaml b/python/cugraph/cugraph/datasets/metadata/hollywood.yaml new file mode 100644 index 00000000000..2f09cf7679b --- /dev/null +++ b/python/cugraph/cugraph/datasets/metadata/hollywood.yaml @@ -0,0 +1,26 @@ +name: hollywood +file_type: .csv +description: + A graph of movie actors where vertices are actors, and two actors are + joined by an edge whenever they appeared in a movie together. +author: Laboratory for Web Algorithmics (LAW) +refs: + The WebGraph Framework I Compression Techniques, Paolo Boldi + and Sebastiano Vigna, Proc. of the Thirteenth International + World Wide Web Conference (WWW 2004), 2004, Manhattan, USA, + pp. 595--601, ACM Press. +delim: " " +header: None +col_names: + - src + - dst +col_types: + - int32 + - int32 +has_loop: false +is_directed: false +is_multigraph: false +is_symmetric: true +number_of_edges: 57515616 +number_of_nodes: 1139905 +url: https://data.rapids.ai/cugraph/datasets/hollywood.csv \ No newline at end of file diff --git a/python/cugraph/cugraph/datasets/metadata/soc-livejournal1.yaml b/python/cugraph/cugraph/datasets/metadata/soc-livejournal1.yaml new file mode 100644 index 00000000000..fafc68acb9b --- /dev/null +++ b/python/cugraph/cugraph/datasets/metadata/soc-livejournal1.yaml @@ -0,0 +1,22 @@ +name: soc-LiveJournal1 +file_type: .csv +description: A graph of the LiveJournal social network. +author: L. Backstrom, D. Huttenlocher, J. Kleinberg, X. Lan +refs: + L. Backstrom, D. Huttenlocher, J. Kleinberg, X. Lan. Group Formation in + Large Social Networks Membership, Growth, and Evolution. KDD, 2006. +delim: " " +header: None +col_names: + - src + - dst +col_types: + - int32 + - int32 +has_loop: true +is_directed: true +is_multigraph: false +is_symmetric: false +number_of_edges: 68993773 +number_of_nodes: 4847571 +url: https://data.rapids.ai/cugraph/datasets/soc-LiveJournal1.csv \ No newline at end of file diff --git a/python/cugraph/cugraph/datasets/metadata/soc-twitter-2010.yaml b/python/cugraph/cugraph/datasets/metadata/soc-twitter-2010.yaml new file mode 100644 index 00000000000..df5df5735af --- /dev/null +++ b/python/cugraph/cugraph/datasets/metadata/soc-twitter-2010.yaml @@ -0,0 +1,22 @@ +name: soc-twitter-2010 +file_type: .csv +description: A network of follower relationships from a snapshot of Twitter in 2010, where an edge from i to j indicates that j is a follower of i. +author: H. Kwak, C. Lee, H. Park, S. Moon +refs: + J. Yang, J. Leskovec. Temporal Variation in Online Media. ACM Intl. + Conf. on Web Search and Data Mining (WSDM '11), 2011. +delim: " " +header: None +col_names: + - src + - dst +col_types: + - int32 + - int32 +has_loop: false +is_directed: false +is_multigraph: false +is_symmetric: false +number_of_edges: 530051354 +number_of_nodes: 21297772 +url: https://data.rapids.ai/cugraph/datasets/soc-twitter-2010.csv \ No newline at end of file diff --git a/python/cugraph/cugraph/experimental/datasets/__init__.py b/python/cugraph/cugraph/experimental/datasets/__init__.py deleted file mode 100644 index 18220243df1..00000000000 --- a/python/cugraph/cugraph/experimental/datasets/__init__.py +++ /dev/null @@ -1,79 +0,0 @@ -# Copyright (c) 2022-2023, NVIDIA CORPORATION. -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -from cugraph.experimental.datasets.dataset import ( - Dataset, - load_all, - set_download_dir, - get_download_dir, - default_download_dir, -) -from cugraph.experimental.datasets import metadata -from pathlib import Path - -from cugraph.utilities.api_tools import promoted_experimental_warning_wrapper - - -Dataset = promoted_experimental_warning_wrapper(Dataset) -load_all = promoted_experimental_warning_wrapper(load_all) -set_download_dir = promoted_experimental_warning_wrapper(set_download_dir) -get_download_dir = promoted_experimental_warning_wrapper(get_download_dir) - -meta_path = Path(__file__).parent / "metadata" - - -# individual dataset objects -karate = Dataset(meta_path / "karate.yaml") -karate_data = Dataset(meta_path / "karate_data.yaml") -karate_undirected = Dataset(meta_path / "karate_undirected.yaml") -karate_asymmetric = Dataset(meta_path / "karate_asymmetric.yaml") -karate_disjoint = Dataset(meta_path / "karate-disjoint.yaml") -dolphins = Dataset(meta_path / "dolphins.yaml") -polbooks = Dataset(meta_path / "polbooks.yaml") -netscience = Dataset(meta_path / "netscience.yaml") -cyber = Dataset(meta_path / "cyber.yaml") -small_line = Dataset(meta_path / "small_line.yaml") -small_tree = Dataset(meta_path / "small_tree.yaml") -toy_graph = Dataset(meta_path / "toy_graph.yaml") -toy_graph_undirected = Dataset(meta_path / "toy_graph_undirected.yaml") -email_Eu_core = Dataset(meta_path / "email-Eu-core.yaml") -ktruss_polbooks = Dataset(meta_path / "ktruss_polbooks.yaml") - - -# batches of datasets -DATASETS_UNDIRECTED = [karate, dolphins] - -DATASETS_UNDIRECTED_WEIGHTS = [netscience] - -DATASETS_UNRENUMBERED = [karate_disjoint] - -DATASETS = [dolphins, netscience, karate_disjoint] - -DATASETS_SMALL = [karate, dolphins, polbooks] - -STRONGDATASETS = [dolphins, netscience, email_Eu_core] - -DATASETS_KTRUSS = [(polbooks, ktruss_polbooks)] - -MEDIUM_DATASETS = [polbooks] - -SMALL_DATASETS = [karate, dolphins, netscience] - -RLY_SMALL_DATASETS = [small_line, small_tree] - -ALL_DATASETS = [karate, dolphins, netscience, polbooks, small_line, small_tree] - -ALL_DATASETS_WGT = [karate, dolphins, netscience, polbooks, small_line, small_tree] - -TEST_GROUP = [dolphins, netscience] diff --git a/python/cugraph/cugraph/experimental/datasets/dataset.py b/python/cugraph/cugraph/experimental/datasets/dataset.py deleted file mode 100644 index 6b395d50fef..00000000000 --- a/python/cugraph/cugraph/experimental/datasets/dataset.py +++ /dev/null @@ -1,312 +0,0 @@ -# Copyright (c) 2022-2023, NVIDIA CORPORATION. -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import cudf -import yaml -import os -from pathlib import Path -from cugraph.structure.graph_classes import Graph - - -class DefaultDownloadDir: - """ - Maintains the path to the download directory used by Dataset instances. - Instances of this class are typically shared by several Dataset instances - in order to allow for the download directory to be defined and updated by - a single object. - """ - - def __init__(self): - self._path = Path( - os.environ.get("RAPIDS_DATASET_ROOT_DIR", Path.home() / ".cugraph/datasets") - ) - - @property - def path(self): - """ - If `path` is not set, set it to the environment variable - RAPIDS_DATASET_ROOT_DIR. If the variable is not set, default to the - user's home directory. - """ - if self._path is None: - self._path = Path( - os.environ.get( - "RAPIDS_DATASET_ROOT_DIR", Path.home() / ".cugraph/datasets" - ) - ) - return self._path - - @path.setter - def path(self, new): - self._path = Path(new) - - def clear(self): - self._path = None - - -default_download_dir = DefaultDownloadDir() - - -class Dataset: - """ - A Dataset Object, used to easily import edgelist data and cuGraph.Graph - instances. - - Parameters - ---------- - meta_data_file_name : yaml file - The metadata file for the specific graph dataset, which includes - information on the name, type, url link, data loading format, graph - properties - """ - - def __init__( - self, - metadata_yaml_file=None, - csv_file=None, - csv_header=None, - csv_delim=" ", - csv_col_names=None, - csv_col_dtypes=None, - ): - self._metadata_file = None - self._dl_path = default_download_dir - self._edgelist = None - self._path = None - - if metadata_yaml_file is not None and csv_file is not None: - raise ValueError("cannot specify both metadata_yaml_file and csv_file") - - elif metadata_yaml_file is not None: - with open(metadata_yaml_file, "r") as file: - self.metadata = yaml.safe_load(file) - self._metadata_file = Path(metadata_yaml_file) - - elif csv_file is not None: - if csv_col_names is None or csv_col_dtypes is None: - raise ValueError( - "csv_col_names and csv_col_dtypes must both be " - "not None when csv_file is specified." - ) - self._path = Path(csv_file) - if self._path.exists() is False: - raise FileNotFoundError(csv_file) - self.metadata = { - "name": self._path.with_suffix("").name, - "file_type": ".csv", - "url": None, - "header": csv_header, - "delim": csv_delim, - "col_names": csv_col_names, - "col_types": csv_col_dtypes, - } - - else: - raise ValueError("must specify either metadata_yaml_file or csv_file") - - def __str__(self): - """ - Use the basename of the meta_data_file the instance was constructed with, - without any extension, as the string repr. - """ - # The metadata file is likely to have a more descriptive file name, so - # use that one first if present. - # FIXME: this may need to provide a more unique or descriptive string repr - if self._metadata_file is not None: - return self._metadata_file.with_suffix("").name - else: - return self.get_path().with_suffix("").name - - def __download_csv(self, url): - """ - Downloads the .csv file from url to the current download path - (self._dl_path), updates self._path with the full path to the - downloaded file, and returns the latest value of self._path. - """ - self._dl_path.path.mkdir(parents=True, exist_ok=True) - - filename = self.metadata["name"] + self.metadata["file_type"] - if self._dl_path.path.is_dir(): - df = cudf.read_csv(url) - self._path = self._dl_path.path / filename - df.to_csv(self._path, index=False) - - else: - raise RuntimeError( - f"The directory {self._dl_path.path.absolute()}" "does not exist" - ) - return self._path - - def unload(self): - - """ - Remove all saved internal objects, forcing them to be re-created when - accessed. - - NOTE: This will cause calls to get_*() to re-read the dataset file from - disk. The caller should ensure the file on disk has not moved/been - deleted/changed. - """ - self._edgelist = None - - def get_edgelist(self, fetch=False): - """ - Return an Edgelist - - Parameters - ---------- - fetch : Boolean (default=False) - Automatically fetch for the dataset from the 'url' location within - the YAML file. - """ - if self._edgelist is None: - full_path = self.get_path() - if not full_path.is_file(): - if fetch: - full_path = self.__download_csv(self.metadata["url"]) - else: - raise RuntimeError( - f"The datafile {full_path} does not" - " exist. Try get_edgelist(fetch=True)" - " to download the datafile" - ) - header = None - if isinstance(self.metadata["header"], int): - header = self.metadata["header"] - self._edgelist = cudf.read_csv( - full_path, - delimiter=self.metadata["delim"], - names=self.metadata["col_names"], - dtype=self.metadata["col_types"], - header=header, - ) - - return self._edgelist - - def get_graph( - self, - fetch=False, - create_using=Graph, - ignore_weights=False, - store_transposed=False, - ): - """ - Return a Graph object. - - Parameters - ---------- - fetch : Boolean (default=False) - Downloads the dataset from the web. - - create_using: cugraph.Graph (instance or class), optional - (default=Graph) - Specify the type of Graph to create. Can pass in an instance to - create a Graph instance with specified 'directed' attribute. - - ignore_weights : Boolean (default=False) - Ignores weights in the dataset if True, resulting in an - unweighted Graph. If False (the default), weights from the - dataset -if present- will be applied to the Graph. If the - dataset does not contain weights, the Graph returned will - be unweighted regardless of ignore_weights. - """ - if self._edgelist is None: - self.get_edgelist(fetch) - - if create_using is None: - G = Graph() - elif isinstance(create_using, Graph): - # what about BFS if trnaposed is True - attrs = {"directed": create_using.is_directed()} - G = type(create_using)(**attrs) - elif type(create_using) is type: - G = create_using() - else: - raise TypeError( - "create_using must be a cugraph.Graph " - "(or subclass) type or instance, got: " - f"{type(create_using)}" - ) - - if len(self.metadata["col_names"]) > 2 and not (ignore_weights): - G.from_cudf_edgelist( - self._edgelist, - source="src", - destination="dst", - edge_attr="wgt", - store_transposed=store_transposed, - ) - else: - G.from_cudf_edgelist( - self._edgelist, - source="src", - destination="dst", - store_transposed=store_transposed, - ) - return G - - def get_path(self): - """ - Returns the location of the stored dataset file - """ - if self._path is None: - self._path = self._dl_path.path / ( - self.metadata["name"] + self.metadata["file_type"] - ) - - return self._path.absolute() - - -def load_all(force=False): - """ - Looks in `metadata` directory and fetches all datafiles from the the URLs - provided in each YAML file. - - Parameters - force : Boolean (default=False) - Overwrite any existing copies of datafiles. - """ - default_download_dir.path.mkdir(parents=True, exist_ok=True) - - meta_path = Path(__file__).parent.absolute() / "metadata" - for file in meta_path.iterdir(): - meta = None - if file.suffix == ".yaml": - with open(meta_path / file, "r") as metafile: - meta = yaml.safe_load(metafile) - - if "url" in meta: - filename = meta["name"] + meta["file_type"] - save_to = default_download_dir.path / filename - if not save_to.is_file() or force: - df = cudf.read_csv(meta["url"]) - df.to_csv(save_to, index=False) - - -def set_download_dir(path): - """ - Set the download directory for fetching datasets - - Parameters - ---------- - path : String - Location used to store datafiles - """ - if path is None: - default_download_dir.clear() - else: - default_download_dir.path = path - - -def get_download_dir(): - return default_download_dir.path.absolute() diff --git a/python/cugraph/cugraph/experimental/datasets/datasets_config.yaml b/python/cugraph/cugraph/experimental/datasets/datasets_config.yaml deleted file mode 100644 index 69a79db9cd9..00000000000 --- a/python/cugraph/cugraph/experimental/datasets/datasets_config.yaml +++ /dev/null @@ -1,5 +0,0 @@ ---- -fetch: "False" -force: "False" -# path where datasets will be downloaded to and stored -download_dir: "datasets" diff --git a/python/cugraph/cugraph/experimental/datasets/metadata/__init__.py b/python/cugraph/cugraph/experimental/datasets/metadata/__init__.py deleted file mode 100644 index 081b2ae8260..00000000000 --- a/python/cugraph/cugraph/experimental/datasets/metadata/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2022-2023, NVIDIA CORPORATION. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/python/cugraph/cugraph/experimental/datasets/metadata/cyber.yaml b/python/cugraph/cugraph/experimental/datasets/metadata/cyber.yaml deleted file mode 100644 index 93ab5345442..00000000000 --- a/python/cugraph/cugraph/experimental/datasets/metadata/cyber.yaml +++ /dev/null @@ -1,22 +0,0 @@ -name: cyber -file_type: .csv -author: N/A -url: https://raw.githubusercontent.com/rapidsai/cugraph/branch-22.08/datasets/cyber.csv -refs: N/A -col_names: - - idx - - srcip - - dstip -col_types: - - int32 - - str - - str -delim: "," -header: 0 -has_loop: true -is_directed: true -is_multigraph: false -is_symmetric: false -number_of_edges: 2546575 -number_of_nodes: 706529 -number_of_lines: 2546576 diff --git a/python/cugraph/cugraph/experimental/datasets/metadata/dolphins.yaml b/python/cugraph/cugraph/experimental/datasets/metadata/dolphins.yaml deleted file mode 100644 index e4951375321..00000000000 --- a/python/cugraph/cugraph/experimental/datasets/metadata/dolphins.yaml +++ /dev/null @@ -1,25 +0,0 @@ -name: dolphins -file_type: .csv -author: D. Lusseau -url: https://raw.githubusercontent.com/rapidsai/cugraph/branch-22.08/datasets/dolphins.csv -refs: - D. Lusseau, K. Schneider, O. J. Boisseau, P. Haase, E. Slooten, and S. M. Dawson, - The bottlenose dolphin community of Doubtful Sound features a large proportion of - long-lasting associations, Behavioral Ecology and Sociobiology 54, 396-405 (2003). -col_names: - - src - - dst - - wgt -col_types: - - int32 - - int32 - - float32 -delim: " " -header: None -has_loop: false -is_directed: true -is_multigraph: false -is_symmetric: false -number_of_edges: 318 -number_of_nodes: 62 -number_of_lines: 318 diff --git a/python/cugraph/cugraph/experimental/datasets/metadata/email-Eu-core.yaml b/python/cugraph/cugraph/experimental/datasets/metadata/email-Eu-core.yaml deleted file mode 100644 index 97d0dc82ee3..00000000000 --- a/python/cugraph/cugraph/experimental/datasets/metadata/email-Eu-core.yaml +++ /dev/null @@ -1,22 +0,0 @@ -name: email-Eu-core -file_type: .csv -author: null -url: https://raw.githubusercontent.com/rapidsai/cugraph/branch-22.08/datasets/email-Eu-core.csv -refs: null -delim: " " -header: None -col_names: - - src - - dst - - wgt -col_types: - - int32 - - int32 - - float32 -has_loop: false -is_directed: false -is_multigraph: false -is_symmetric: true -number_of_edges: 25571 -number_of_nodes: 1005 -number_of_lines: 25571 diff --git a/python/cugraph/cugraph/experimental/datasets/metadata/karate-disjoint.yaml b/python/cugraph/cugraph/experimental/datasets/metadata/karate-disjoint.yaml deleted file mode 100644 index 0c0eaf78b63..00000000000 --- a/python/cugraph/cugraph/experimental/datasets/metadata/karate-disjoint.yaml +++ /dev/null @@ -1,22 +0,0 @@ -name: karate-disjoint -file_type: .csv -author: null -url: https://raw.githubusercontent.com/rapidsai/cugraph/branch-22.08/datasets/karate-disjoint.csv -refs: null -delim: " " -header: None -col_names: - - src - - dst - - wgt -col_types: - - int32 - - int32 - - float32 -has_loop: false -is_directed: True -is_multigraph: false -is_symmetric: true -number_of_edges: 312 -number_of_nodes: 68 -number_of_lines: 312 diff --git a/python/cugraph/cugraph/experimental/datasets/metadata/karate.yaml b/python/cugraph/cugraph/experimental/datasets/metadata/karate.yaml deleted file mode 100644 index 273381ed368..00000000000 --- a/python/cugraph/cugraph/experimental/datasets/metadata/karate.yaml +++ /dev/null @@ -1,24 +0,0 @@ -name: karate -file_type: .csv -author: Zachary W. -url: https://raw.githubusercontent.com/rapidsai/cugraph/branch-22.08/datasets/karate.csv -refs: - W. W. Zachary, An information flow model for conflict and fission in small groups, - Journal of Anthropological Research 33, 452-473 (1977). -delim: " " -header: None -col_names: - - src - - dst - - wgt -col_types: - - int32 - - int32 - - float32 -has_loop: true -is_directed: true -is_multigraph: false -is_symmetric: true -number_of_edges: 156 -number_of_nodes: 34 -number_of_lines: 156 diff --git a/python/cugraph/cugraph/experimental/datasets/metadata/karate_asymmetric.yaml b/python/cugraph/cugraph/experimental/datasets/metadata/karate_asymmetric.yaml deleted file mode 100644 index 3616b8fb3a5..00000000000 --- a/python/cugraph/cugraph/experimental/datasets/metadata/karate_asymmetric.yaml +++ /dev/null @@ -1,24 +0,0 @@ -name: karate-asymmetric -file_type: .csv -author: Zachary W. -url: https://raw.githubusercontent.com/rapidsai/cugraph/branch-22.08/datasets/karate-asymmetric.csv -delim: " " -header: None -refs: - W. W. Zachary, An information flow model for conflict and fission in small groups, - Journal of Anthropological Research 33, 452-473 (1977). -col_names: - - src - - dst - - wgt -col_types: - - int32 - - int32 - - float32 -has_loop: true -is_directed: false -is_multigraph: false -is_symmetric: false -number_of_edges: 78 -number_of_nodes: 34 -number_of_lines: 78 diff --git a/python/cugraph/cugraph/experimental/datasets/metadata/karate_data.yaml b/python/cugraph/cugraph/experimental/datasets/metadata/karate_data.yaml deleted file mode 100644 index 9a8b27f21ae..00000000000 --- a/python/cugraph/cugraph/experimental/datasets/metadata/karate_data.yaml +++ /dev/null @@ -1,22 +0,0 @@ -name: karate-data -file_type: .csv -author: Zachary W. -url: https://raw.githubusercontent.com/rapidsai/cugraph/branch-22.08/datasets/karate-data.csv -refs: - W. W. Zachary, An information flow model for conflict and fission in small groups, - Journal of Anthropological Research 33, 452-473 (1977). -delim: "\t" -header: None -col_names: - - src - - dst -col_types: - - int32 - - int32 -has_loop: true -is_directed: true -is_multigraph: false -is_symmetric: true -number_of_edges: 156 -number_of_nodes: 34 -number_of_lines: 156 diff --git a/python/cugraph/cugraph/experimental/datasets/metadata/karate_undirected.yaml b/python/cugraph/cugraph/experimental/datasets/metadata/karate_undirected.yaml deleted file mode 100644 index 1b45f86caee..00000000000 --- a/python/cugraph/cugraph/experimental/datasets/metadata/karate_undirected.yaml +++ /dev/null @@ -1,22 +0,0 @@ -name: karate_undirected -file_type: .csv -author: Zachary W. -url: https://raw.githubusercontent.com/rapidsai/cugraph/branch-22.08/datasets/karate_undirected.csv -refs: - W. W. Zachary, An information flow model for conflict and fission in small groups, - Journal of Anthropological Research 33, 452-473 (1977). -delim: "\t" -header: None -col_names: - - src - - dst -col_types: - - int32 - - int32 -has_loop: true -is_directed: false -is_multigraph: false -is_symmetric: true -number_of_edges: 78 -number_of_nodes: 34 -number_of_lines: 78 diff --git a/python/cugraph/cugraph/experimental/datasets/metadata/ktruss_polbooks.yaml b/python/cugraph/cugraph/experimental/datasets/metadata/ktruss_polbooks.yaml deleted file mode 100644 index 1ef29b3917e..00000000000 --- a/python/cugraph/cugraph/experimental/datasets/metadata/ktruss_polbooks.yaml +++ /dev/null @@ -1,23 +0,0 @@ -name: ktruss_polbooks -file_type: .csv -author: null -url: https://raw.githubusercontent.com/rapidsai/cugraph/branch-22.08/datasets/ref/ktruss/polbooks.csv -refs: null -delim: " " -header: None -col_names: - - src - - dst - - wgt -col_types: - - int32 - - int32 - - float32 -has_loop: false -is_directed: true -is_multigraph: false -is_symmetric: false -number_of_edges: 233 -number_of_nodes: 58 -number_of_lines: 233 - diff --git a/python/cugraph/cugraph/experimental/datasets/metadata/netscience.yaml b/python/cugraph/cugraph/experimental/datasets/metadata/netscience.yaml deleted file mode 100644 index 2dca702df3d..00000000000 --- a/python/cugraph/cugraph/experimental/datasets/metadata/netscience.yaml +++ /dev/null @@ -1,22 +0,0 @@ -name: netscience -file_type: .csv -author: Newman, Mark EJ -url: https://raw.githubusercontent.com/rapidsai/cugraph/branch-22.08/datasets/netscience.csv -refs: Finding community structure in networks using the eigenvectors of matrices. -delim: " " -header: None -col_names: - - src - - dst - - wgt -col_types: - - int32 - - int32 - - float32 -has_loop: false -is_directed: true -is_multigraph: false -is_symmetric: true -number_of_edges: 2742 -number_of_nodes: 1461 -number_of_lines: 5484 diff --git a/python/cugraph/cugraph/experimental/datasets/metadata/polbooks.yaml b/python/cugraph/cugraph/experimental/datasets/metadata/polbooks.yaml deleted file mode 100644 index 5816e5672fd..00000000000 --- a/python/cugraph/cugraph/experimental/datasets/metadata/polbooks.yaml +++ /dev/null @@ -1,22 +0,0 @@ -name: polbooks -file_type: .csv -author: V. Krebs -url: https://raw.githubusercontent.com/rapidsai/cugraph/branch-22.08/datasets/polbooks.csv -refs: null -delim: " " -header: None -col_names: - - src - - dst - - wgt -col_types: - - int32 - - int32 - - float32 -is_directed: true -has_loop: null -is_multigraph: null -is_symmetric: true -number_of_edges: 882 -number_of_nodes: 105 -number_of_lines: 882 diff --git a/python/cugraph/cugraph/experimental/datasets/metadata/small_line.yaml b/python/cugraph/cugraph/experimental/datasets/metadata/small_line.yaml deleted file mode 100644 index 5b724ac99fd..00000000000 --- a/python/cugraph/cugraph/experimental/datasets/metadata/small_line.yaml +++ /dev/null @@ -1,22 +0,0 @@ -name: small_line -file_type: .csv -author: null -url: https://raw.githubusercontent.com/rapidsai/cugraph/branch-22.08/datasets/small_line.csv -refs: null -delim: " " -header: None -col_names: - - src - - dst - - wgt -col_types: - - int32 - - int32 - - float32 -has_loop: false -is_directed: false -is_multigraph: false -is_symmetric: true -number_of_edges: 9 -number_of_nodes: 10 -number_of_lines: 8 diff --git a/python/cugraph/cugraph/experimental/datasets/metadata/small_tree.yaml b/python/cugraph/cugraph/experimental/datasets/metadata/small_tree.yaml deleted file mode 100644 index 8eeac346d2a..00000000000 --- a/python/cugraph/cugraph/experimental/datasets/metadata/small_tree.yaml +++ /dev/null @@ -1,22 +0,0 @@ -name: small_tree -file_type: .csv -author: null -url: https://raw.githubusercontent.com/rapidsai/cugraph/branch-22.08/datasets/small_tree.csv -refs: null -delim: " " -header: None -col_names: - - src - - dst - - wgt -col_types: - - int32 - - int32 - - float32 -has_loop: false -is_directed: true -is_multigraph: false -is_symmetric: true -number_of_edges: 11 -number_of_nodes: 9 -number_of_lines: 11 diff --git a/python/cugraph/cugraph/experimental/datasets/metadata/toy_graph.yaml b/python/cugraph/cugraph/experimental/datasets/metadata/toy_graph.yaml deleted file mode 100644 index 819aad06f6a..00000000000 --- a/python/cugraph/cugraph/experimental/datasets/metadata/toy_graph.yaml +++ /dev/null @@ -1,22 +0,0 @@ -name: toy_graph -file_type: .csv -author: null -url: https://raw.githubusercontent.com/rapidsai/cugraph/branch-22.08/datasets/toy_graph.csv -refs: null -delim: " " -header: None -col_names: - - src - - dst - - wgt -col_types: - - int32 - - int32 - - float32 -has_loop: false -is_directed: false -is_multigraph: false -is_symmetric: true -number_of_edges: 16 -number_of_nodes: 6 -number_of_lines: 16 diff --git a/python/cugraph/cugraph/experimental/datasets/metadata/toy_graph_undirected.yaml b/python/cugraph/cugraph/experimental/datasets/metadata/toy_graph_undirected.yaml deleted file mode 100644 index c6e86bdf334..00000000000 --- a/python/cugraph/cugraph/experimental/datasets/metadata/toy_graph_undirected.yaml +++ /dev/null @@ -1,22 +0,0 @@ -name: toy_graph_undirected -file_type: .csv -author: null -url: https://raw.githubusercontent.com/rapidsai/cugraph/branch-22.08/datasets/toy_graph_undirected.csv -refs: null -delim: " " -header: None -col_names: - - src - - dst - - wgt -col_types: - - int32 - - int32 - - float32 -has_loop: false -is_directed: false -is_multigraph: false -is_symmetric: true -number_of_edges: 8 -number_of_nodes: 6 -number_of_lines: 8 diff --git a/python/cugraph/cugraph/testing/__init__.py b/python/cugraph/cugraph/testing/__init__.py index f5f0bcb06eb..2b4a4fd3ebf 100644 --- a/python/cugraph/cugraph/testing/__init__.py +++ b/python/cugraph/cugraph/testing/__init__.py @@ -19,7 +19,7 @@ Resultset, load_resultset, get_resultset, - results_dir_path, + default_resultset_download_dir, ) from cugraph.datasets import ( cyber, @@ -34,6 +34,11 @@ email_Eu_core, toy_graph, toy_graph_undirected, + soc_livejournal, + cit_patents, + europe_osm, + hollywood, + # twitter, ) # @@ -66,3 +71,4 @@ toy_graph_undirected, ] DEFAULT_DATASETS = [dolphins, netscience, karate_disjoint] +BENCHMARKING_DATASETS = [soc_livejournal, cit_patents, europe_osm, hollywood] diff --git a/python/cugraph/cugraph/testing/generate_resultsets.py b/python/cugraph/cugraph/testing/generate_resultsets.py index 9724aca32dc..2ae0f52d88b 100644 --- a/python/cugraph/cugraph/testing/generate_resultsets.py +++ b/python/cugraph/cugraph/testing/generate_resultsets.py @@ -20,8 +20,14 @@ import cudf import cugraph from cugraph.datasets import dolphins, netscience, karate_disjoint, karate -from cugraph.testing import utils, Resultset, SMALL_DATASETS, results_dir_path +# from cugraph.testing import utils, Resultset, SMALL_DATASETS, results_dir_path +from cugraph.testing import ( + utils, + Resultset, + SMALL_DATASETS, + default_resultset_download_dir, +) _resultsets = {} @@ -224,6 +230,7 @@ def add_resultset(result_data_dictionary, **kwargs): ] ) # Generating ALL results files + results_dir_path = default_resultset_download_dir.path if not results_dir_path.exists(): results_dir_path.mkdir(parents=True, exist_ok=True) diff --git a/python/cugraph/cugraph/testing/resultset.py b/python/cugraph/cugraph/testing/resultset.py index 490e3a7c4ff..9570d7f3e04 100644 --- a/python/cugraph/cugraph/testing/resultset.py +++ b/python/cugraph/cugraph/testing/resultset.py @@ -16,10 +16,12 @@ import urllib.request import cudf -from cugraph.testing import utils +from cugraph.datasets.dataset import ( + DefaultDownloadDir, + default_download_dir, +) - -results_dir_path = utils.RAPIDS_DATASET_ROOT_DIR_PATH / "tests" / "resultsets" +# results_dir_path = utils.RAPIDS_DATASET_ROOT_DIR_PATH / "tests" / "resultsets" class Resultset: @@ -48,6 +50,42 @@ def get_cudf_dataframe(self): _resultsets = {} +def get_resultset(resultset_name, **kwargs): + """ + Returns the golden results for a specific test. + + Parameters + ---------- + resultset_name : String + Name of the test's module (currently just 'traversal' is supported) + + kwargs : + All distinct test details regarding the choice of algorithm, dataset, + and graph + """ + arg_dict = dict(kwargs) + arg_dict["resultset_name"] = resultset_name + # Example: + # {'a': 1, 'z': 9, 'c': 5, 'b': 2} becomes 'a-1-b-2-c-5-z-9' + resultset_key = "-".join( + [ + str(val) + for arg_dict_pair in sorted(arg_dict.items()) + for val in arg_dict_pair + ] + ) + uuid = _resultsets.get(resultset_key) + if uuid is None: + raise KeyError(f"results for {arg_dict} not found") + + results_dir_path = default_resultset_download_dir.path + results_filename = results_dir_path / (uuid + ".csv") + return cudf.read_csv(results_filename) + + +default_resultset_download_dir = DefaultDownloadDir(subdir="tests/resultsets") + + def load_resultset(resultset_name, resultset_download_url): """ Read a mapping file (.csv) in the _results_dir and save the @@ -56,17 +94,21 @@ def load_resultset(resultset_name, resultset_download_url): _results_dir, use resultset_download_url to download a file to install/unpack/etc. to _results_dir first. """ - mapping_file_path = results_dir_path / (resultset_name + "_mappings.csv") + # curr_resultset_download_dir = get_resultset_download_dir() + curr_resultset_download_dir = default_resultset_download_dir.path + # curr_download_dir = path + curr_download_dir = default_download_dir.path + mapping_file_path = curr_resultset_download_dir / (resultset_name + "_mappings.csv") if not mapping_file_path.exists(): # Downloads a tar gz from s3 bucket, then unpacks the results files - compressed_file_dir = utils.RAPIDS_DATASET_ROOT_DIR_PATH / "tests" + compressed_file_dir = curr_download_dir / "tests" compressed_file_path = compressed_file_dir / "resultsets.tar.gz" - if not results_dir_path.exists(): - results_dir_path.mkdir(parents=True, exist_ok=True) + if not curr_resultset_download_dir.exists(): + curr_resultset_download_dir.mkdir(parents=True, exist_ok=True) if not compressed_file_path.exists(): urllib.request.urlretrieve(resultset_download_url, compressed_file_path) tar = tarfile.open(str(compressed_file_path), "r:gz") - tar.extractall(str(results_dir_path)) + tar.extractall(str(curr_resultset_download_dir)) tar.close() # FIXME: This assumes separator is " ", but should this be configurable? @@ -102,35 +144,3 @@ def load_resultset(resultset_name, resultset_download_url): ) _resultsets[resultset_key] = uuid - - -def get_resultset(resultset_name, **kwargs): - """ - Returns the golden results for a specific test. - - Parameters - ---------- - resultset_name : String - Name of the test's module (currently just 'traversal' is supported) - - kwargs : - All distinct test details regarding the choice of algorithm, dataset, - and graph - """ - arg_dict = dict(kwargs) - arg_dict["resultset_name"] = resultset_name - # Example: - # {'a': 1, 'z': 9, 'c': 5, 'b': 2} becomes 'a-1-b-2-c-5-z-9' - resultset_key = "-".join( - [ - str(val) - for arg_dict_pair in sorted(arg_dict.items()) - for val in arg_dict_pair - ] - ) - uuid = _resultsets.get(resultset_key) - if uuid is None: - raise KeyError(f"results for {arg_dict} not found") - - results_filename = results_dir_path / (uuid + ".csv") - return cudf.read_csv(results_filename) diff --git a/python/cugraph/cugraph/tests/centrality/test_edge_betweenness_centrality_mg.py b/python/cugraph/cugraph/tests/centrality/test_edge_betweenness_centrality_mg.py index 4277f94a396..478b7e655d5 100644 --- a/python/cugraph/cugraph/tests/centrality/test_edge_betweenness_centrality_mg.py +++ b/python/cugraph/cugraph/tests/centrality/test_edge_betweenness_centrality_mg.py @@ -16,7 +16,7 @@ import dask_cudf from pylibcugraph.testing.utils import gen_fixture_params_product -from cugraph.experimental.datasets import DATASETS_UNDIRECTED +from cugraph.datasets import karate, dolphins import cugraph import cugraph.dask as dcg @@ -41,7 +41,7 @@ def setup_function(): # email_Eu_core is too expensive to test -datasets = DATASETS_UNDIRECTED +datasets = [karate, dolphins] # ============================================================================= diff --git a/python/cugraph/cugraph/tests/nx/test_compat_pr.py b/python/cugraph/cugraph/tests/nx/test_compat_pr.py index 9be3912a33f..45cab7a5674 100644 --- a/python/cugraph/cugraph/tests/nx/test_compat_pr.py +++ b/python/cugraph/cugraph/tests/nx/test_compat_pr.py @@ -24,7 +24,7 @@ import numpy as np from cugraph.testing import utils -from cugraph.experimental.datasets import karate +from cugraph.datasets import karate from pylibcugraph.testing.utils import gen_fixture_params_product diff --git a/python/cugraph/cugraph/tests/utils/test_dataset.py b/python/cugraph/cugraph/tests/utils/test_dataset.py index c2a4f7c6072..60bc6dbb45a 100644 --- a/python/cugraph/cugraph/tests/utils/test_dataset.py +++ b/python/cugraph/cugraph/tests/utils/test_dataset.py @@ -13,11 +13,10 @@ import os import gc -import sys -import warnings from pathlib import Path from tempfile import TemporaryDirectory +import pandas import pytest import cudf @@ -27,6 +26,7 @@ ALL_DATASETS, WEIGHTED_DATASETS, SMALL_DATASETS, + BENCHMARKING_DATASETS, ) from cugraph import datasets @@ -74,27 +74,14 @@ def setup(tmpdir): gc.collect() -@pytest.fixture() -def setup_deprecation_warning_tests(): - """ - Fixture used to set warning filters to 'default' and reload - experimental.datasets module if it has been previously - imported. Tests that import this fixture are expected to - import cugraph.experimental.datasets - """ - warnings.filterwarnings("default") - - if "cugraph.experimental.datasets" in sys.modules: - del sys.modules["cugraph.experimental.datasets"] - - yield - - ############################################################################### # Helpers # check if there is a row where src == dst -def has_loop(df): +def has_selfloop(dataset): + if not dataset.metadata["is_directed"]: + return False + df = dataset.get_edgelist(download=True) df.rename(columns={df.columns[0]: "src", df.columns[1]: "dst"}, inplace=True) res = df.where(df["src"] == df["dst"]) @@ -109,7 +96,13 @@ def is_symmetric(dataset): else: df = dataset.get_edgelist(download=True) df_a = df.sort_values("src") - df_b = df_a[["dst", "src", "wgt"]] + + # create df with swapped src/dst columns + df_b = None + if "wgt" in df_a.columns: + df_b = df_a[["dst", "src", "wgt"]] + else: + df_b = df_a[["dst", "src"]] df_b.rename(columns={"dst": "src", "src": "dst"}, inplace=True) # created a df by appending the two res = cudf.concat([df_a, df_b]) @@ -157,6 +150,27 @@ def test_download(dataset): assert dataset.get_path().is_file() +@pytest.mark.parametrize("dataset", SMALL_DATASETS) +def test_reader(dataset): + # defaults to using cudf.read_csv + E = dataset.get_edgelist(download=True) + + assert E is not None + assert isinstance(E, cudf.core.dataframe.DataFrame) + dataset.unload() + + # using pandas + E_pd = dataset.get_edgelist(download=True, reader="pandas") + + assert E_pd is not None + assert isinstance(E_pd, pandas.core.frame.DataFrame) + dataset.unload() + + with pytest.raises(ValueError): + dataset.get_edgelist(reader="fail") + dataset.get_edgelist(reader=None) + + @pytest.mark.parametrize("dataset", ALL_DATASETS) def test_get_edgelist(dataset): E = dataset.get_edgelist(download=True) @@ -172,7 +186,6 @@ def test_get_graph(dataset): @pytest.mark.parametrize("dataset", ALL_DATASETS) def test_metadata(dataset): M = dataset.metadata - assert M is not None @@ -310,10 +323,8 @@ def test_is_directed(dataset): @pytest.mark.parametrize("dataset", ALL_DATASETS) -def test_has_loop(dataset): - df = dataset.get_edgelist(download=True) - - assert has_loop(df) == dataset.metadata["has_loop"] +def test_has_selfloop(dataset): + assert has_selfloop(dataset) == dataset.metadata["has_loop"] @pytest.mark.parametrize("dataset", ALL_DATASETS) @@ -328,6 +339,25 @@ def test_is_multigraph(dataset): assert G.is_multigraph() == dataset.metadata["is_multigraph"] +# The datasets used for benchmarks are in their own test, since downloading them +# repeatedly would increase testing overhead significantly +@pytest.mark.parametrize("dataset", BENCHMARKING_DATASETS) +def test_benchmarking_datasets(dataset): + dataset_is_directed = dataset.metadata["is_directed"] + G = dataset.get_graph( + download=True, create_using=Graph(directed=dataset_is_directed) + ) + + assert G.is_directed() == dataset.metadata["is_directed"] + assert G.number_of_nodes() == dataset.metadata["number_of_nodes"] + assert G.number_of_edges() == dataset.metadata["number_of_edges"] + assert has_selfloop(dataset) == dataset.metadata["has_loop"] + assert is_symmetric(dataset) == dataset.metadata["is_symmetric"] + assert G.is_multigraph() == dataset.metadata["is_multigraph"] + + dataset.unload() + + @pytest.mark.parametrize("dataset", ALL_DATASETS) def test_object_getters(dataset): assert dataset.is_directed() == dataset.metadata["is_directed"] @@ -336,32 +366,3 @@ def test_object_getters(dataset): assert dataset.number_of_nodes() == dataset.metadata["number_of_nodes"] assert dataset.number_of_vertices() == dataset.metadata["number_of_nodes"] assert dataset.number_of_edges() == dataset.metadata["number_of_edges"] - - -# -# Test experimental for DeprecationWarnings -# -def test_experimental_dataset_import(setup_deprecation_warning_tests): - with pytest.deprecated_call(): - from cugraph.experimental.datasets import karate - - # unload() is called to pass flake8 - karate.unload() - - -def test_experimental_method_warnings(setup_deprecation_warning_tests): - from cugraph.experimental.datasets import ( - load_all, - set_download_dir, - get_download_dir, - ) - - warnings.filterwarnings("default") - tmpd = TemporaryDirectory() - - with pytest.deprecated_call(): - set_download_dir(tmpd.name) - get_download_dir() - load_all() - - tmpd.cleanup() diff --git a/python/cugraph/cugraph/tests/utils/test_resultset.py b/python/cugraph/cugraph/tests/utils/test_resultset.py new file mode 100644 index 00000000000..5c2298bedb7 --- /dev/null +++ b/python/cugraph/cugraph/tests/utils/test_resultset.py @@ -0,0 +1,71 @@ +# Copyright (c) 2023, NVIDIA CORPORATION. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os + +from pathlib import Path +from tempfile import TemporaryDirectory + +import cudf +from cugraph.datasets.dataset import ( + set_download_dir, + get_download_dir, +) +from cugraph.testing.resultset import load_resultset, default_resultset_download_dir + +############################################################################### + + +def test_load_resultset(): + with TemporaryDirectory() as tmpd: + + set_download_dir(Path(tmpd)) + default_resultset_download_dir.path = Path(tmpd) / "tests" / "resultsets" + default_resultset_download_dir.path.mkdir(parents=True, exist_ok=True) + + datasets_download_dir = get_download_dir() + resultsets_download_dir = default_resultset_download_dir.path + assert "tests" in os.listdir(datasets_download_dir) + assert "resultsets.tar.gz" not in os.listdir(datasets_download_dir / "tests") + assert "traversal_mappings.csv" not in os.listdir(resultsets_download_dir) + + load_resultset( + "traversal", "https://data.rapids.ai/cugraph/results/resultsets.tar.gz" + ) + + assert "resultsets.tar.gz" in os.listdir(datasets_download_dir / "tests") + assert "traversal_mappings.csv" in os.listdir(resultsets_download_dir) + + +def test_verify_resultset_load(): + # This test is more detailed than test_load_resultset, where for each module, + # we check that every single resultset file is included along with the + # corresponding mapping file. + with TemporaryDirectory() as tmpd: + set_download_dir(Path(tmpd)) + default_resultset_download_dir.path = Path(tmpd) / "tests" / "resultsets" + default_resultset_download_dir.path.mkdir(parents=True, exist_ok=True) + + resultsets_download_dir = default_resultset_download_dir.path + + load_resultset( + "traversal", "https://data.rapids.ai/cugraph/results/resultsets.tar.gz" + ) + + resultsets = os.listdir(resultsets_download_dir) + downloaded_results = cudf.read_csv( + resultsets_download_dir / "traversal_mappings.csv", sep=" " + ) + downloaded_uuids = downloaded_results["#UUID"].values + for resultset_uuid in downloaded_uuids: + assert str(resultset_uuid) + ".csv" in resultsets From 0f28b2ee45130486ca891b757574780ac58dd720 Mon Sep 17 00:00:00 2001 From: Alex Barghi <105237337+alexbarghi-nv@users.noreply.github.com> Date: Mon, 20 Nov 2023 13:04:06 -0500 Subject: [PATCH 088/111] [BUG] Fix Graph Construction From Pandas in cuGraph-PyG (#3985) The current graph construction creates a single pandas dataframe, which for larger datasets (i.e. ogbn-papers100M) cannot be serialized. This PR resolves this by breaking up the dataframe into scattered numpy arrays that are then reassembled. Merge after #3978 Authors: - Alex Barghi (https://github.com/alexbarghi-nv) - Naim (https://github.com/naimnv) Approvers: - Vibhu Jawa (https://github.com/VibhuJawa) - Brad Rees (https://github.com/BradReesWork) - Tingyu Wang (https://github.com/tingyu66) URL: https://github.com/rapidsai/cugraph/pull/3985 --- .../cugraph_pyg/data/cugraph_store.py | 75 +++++++++++++------ .../tests/mg/test_mg_cugraph_loader.py | 1 - .../tests/mg/test_mg_cugraph_store.py | 26 +++++++ 3 files changed, 80 insertions(+), 22 deletions(-) diff --git a/python/cugraph-pyg/cugraph_pyg/data/cugraph_store.py b/python/cugraph-pyg/cugraph_pyg/data/cugraph_store.py index edeeface4c4..14dc5d84f90 100644 --- a/python/cugraph-pyg/cugraph_pyg/data/cugraph_store.py +++ b/python/cugraph-pyg/cugraph_pyg/data/cugraph_store.py @@ -27,11 +27,12 @@ import cugraph import warnings -from cugraph.utilities.utils import import_optional, MissingModule +import dask.array as dar +import dask.dataframe as dd +import dask.distributed as distributed +import dask_cudf -dd = import_optional("dask.dataframe") -distributed = import_optional("dask.distributed") -dask_cudf = import_optional("dask_cudf") +from cugraph.utilities.utils import import_optional, MissingModule torch = import_optional("torch") torch_geometric = import_optional("torch_geometric") @@ -367,6 +368,13 @@ def __infer_offsets( } ) + def __dask_array_from_numpy(self, array: np.ndarray, npartitions: int): + return dar.from_array( + array, + meta=np.array([], dtype=array.dtype), + chunks=max(1, len(array) // npartitions), + ) + def __construct_graph( self, edge_info: Dict[Tuple[str, str, str], List[TensorType]], @@ -464,22 +472,32 @@ def __construct_graph( ] ) - df = pandas.DataFrame( - { - "src": pandas.Series(na_dst) - if order == "CSC" - else pandas.Series(na_src), - "dst": pandas.Series(na_src) - if order == "CSC" - else pandas.Series(na_dst), - "etp": pandas.Series(na_etp), - } - ) - vertex_dtype = df.src.dtype + vertex_dtype = na_src.dtype if multi_gpu: - nworkers = len(distributed.get_client().scheduler_info()["workers"]) - df = dd.from_pandas(df, npartitions=nworkers if len(df) > 32 else 1) + client = distributed.get_client() + nworkers = len(client.scheduler_info()["workers"]) + npartitions = nworkers * 4 + + src_dar = self.__dask_array_from_numpy(na_src, npartitions) + del na_src + + dst_dar = self.__dask_array_from_numpy(na_dst, npartitions) + del na_dst + + etp_dar = self.__dask_array_from_numpy(na_etp, npartitions) + del na_etp + + df = dd.from_dask_array(etp_dar, columns=["etp"]) + df["src"] = dst_dar if order == "CSC" else src_dar + df["dst"] = src_dar if order == "CSC" else dst_dar + + del src_dar + del dst_dar + del etp_dar + + if df.etp.dtype != "int32": + raise ValueError("Edge type must be int32!") # Ensure the dataframe is constructed on each partition # instead of adding additional synchronization head from potential @@ -487,9 +505,9 @@ def __construct_graph( def get_empty_df(): return cudf.DataFrame( { + "etp": cudf.Series([], dtype="int32"), "src": cudf.Series([], dtype=vertex_dtype), "dst": cudf.Series([], dtype=vertex_dtype), - "etp": cudf.Series([], dtype="int32"), } ) @@ -500,9 +518,23 @@ def get_empty_df(): if len(f) > 0 else get_empty_df(), meta=get_empty_df(), - ).reset_index(drop=True) + ).reset_index( + drop=True + ) # should be ok for dask else: - df = cudf.from_pandas(df).reset_index(drop=True) + df = pandas.DataFrame( + { + "src": pandas.Series(na_dst) + if order == "CSC" + else pandas.Series(na_src), + "dst": pandas.Series(na_src) + if order == "CSC" + else pandas.Series(na_dst), + "etp": pandas.Series(na_etp), + } + ) + df = cudf.from_pandas(df) + df.reset_index(drop=True, inplace=True) graph = cugraph.MultiGraph(directed=True) if multi_gpu: @@ -521,6 +553,7 @@ def get_empty_df(): edge_type="etp", ) + del df return graph @property diff --git a/python/cugraph-pyg/cugraph_pyg/tests/mg/test_mg_cugraph_loader.py b/python/cugraph-pyg/cugraph_pyg/tests/mg/test_mg_cugraph_loader.py index 55aebf305da..f5035a38621 100644 --- a/python/cugraph-pyg/cugraph_pyg/tests/mg/test_mg_cugraph_loader.py +++ b/python/cugraph-pyg/cugraph_pyg/tests/mg/test_mg_cugraph_loader.py @@ -15,7 +15,6 @@ from cugraph_pyg.loader import CuGraphNeighborLoader from cugraph_pyg.data import CuGraphStore - from cugraph.utilities.utils import import_optional, MissingModule torch = import_optional("torch") diff --git a/python/cugraph-pyg/cugraph_pyg/tests/mg/test_mg_cugraph_store.py b/python/cugraph-pyg/cugraph_pyg/tests/mg/test_mg_cugraph_store.py index 13c9c90c7c2..be8f8245807 100644 --- a/python/cugraph-pyg/cugraph_pyg/tests/mg/test_mg_cugraph_store.py +++ b/python/cugraph-pyg/cugraph_pyg/tests/mg/test_mg_cugraph_store.py @@ -386,3 +386,29 @@ def test_mg_frame_handle(graph, dask_client): F, G, N = graph cugraph_store = CuGraphStore(F, G, N, multi_gpu=True) assert isinstance(cugraph_store._EXPERIMENTAL__CuGraphStore__graph._plc_graph, dict) + + +@pytest.mark.skipif(isinstance(torch, MissingModule), reason="torch not available") +def test_cugraph_loader_large_index(dask_client): + large_index = ( + np.random.randint(0, 1_000_000, (100_000_000,)), + np.random.randint(0, 1_000_000, (100_000_000,)), + ) + + large_features = np.random.randint(0, 50, (1_000_000,)) + F = cugraph.gnn.FeatureStore(backend="torch") + F.add_data(large_features, "N", "f") + + store = CuGraphStore( + F, + {("N", "e", "N"): large_index}, + {"N": 1_000_000}, + multi_gpu=True, + ) + + graph = store._subgraph() + assert isinstance(graph, cugraph.Graph) + + el = graph.view_edge_list().compute() + assert (el["src"].values_host - large_index[0]).sum() == 0 + assert (el["dst"].values_host - large_index[1]).sum() == 0 From d34e3d6522f1f3d8e9fbea6581b7ce37de7e1005 Mon Sep 17 00:00:00 2001 From: Seunghwa Kang <45857425+seunghwak@users.noreply.github.com> Date: Mon, 20 Nov 2023 12:33:35 -0800 Subject: [PATCH 089/111] Address FIXMEs (#3988) This PR works on addressing FIXMEs (and reduce the number of outstanding FIXMEs). Authors: - Seunghwa Kang (https://github.com/seunghwak) - Naim (https://github.com/naimnv) - Ralph Liu (https://github.com/nv-rliu) Approvers: - Naim (https://github.com/naimnv) - Joseph Nke (https://github.com/jnke2016) - Chuck Hastings (https://github.com/ChuckHastings) URL: https://github.com/rapidsai/cugraph/pull/3988 --- cpp/include/cugraph/algorithms.hpp | 45 --------- cpp/include/cugraph/utilities/device_comm.hpp | 8 +- .../cugraph/utilities/host_scalar_comm.hpp | 98 ++++++++++++++----- .../cugraph/utilities/shuffle_comm.cuh | 5 - cpp/src/centrality/katz_centrality_impl.cuh | 2 - .../weakly_connected_components_impl.cuh | 40 ++------ 6 files changed, 83 insertions(+), 115 deletions(-) diff --git a/cpp/include/cugraph/algorithms.hpp b/cpp/include/cugraph/algorithms.hpp index 78846bc5766..8501eedce5c 100644 --- a/cpp/include/cugraph/algorithms.hpp +++ b/cpp/include/cugraph/algorithms.hpp @@ -464,51 +464,6 @@ k_truss_subgraph(raft::handle_t const& handle, size_t number_of_vertices, int k); -// FIXME: Internally distances is of int (signed 32-bit) data type, but current -// template uses data from VT, ET, WT from the legacy::GraphCSR View even if weights -// are not considered -/** - * @Synopsis Performs a breadth first search traversal of a graph starting from a vertex. - * - * @throws cugraph::logic_error with a custom message when an error occurs. - * - * @tparam VT Type of vertex identifiers. Supported value : int (signed, - * 32-bit) - * @tparam ET Type of edge identifiers. Supported value : int (signed, - * 32-bit) - * @tparam WT Type of edge weights. Supported values : int (signed, 32-bit) - * - * @param[in] handle Library handle (RAFT). If a communicator is set in the handle, - the multi GPU version will be selected. - * @param[in] graph cuGraph graph descriptor, should contain the connectivity - * information as a CSR - * - * @param[out] distances If set to a valid pointer, this is populated by distance of - * every vertex in the graph from the starting vertex - * - * @param[out] predecessors If set to a valid pointer, this is populated by bfs traversal - * predecessor of every vertex - * - * @param[out] sp_counters If set to a valid pointer, this is populated by bfs traversal - * shortest_path counter of every vertex - * - * @param[in] start_vertex The starting vertex for breadth first search traversal - * - * @param[in] directed Treat the input graph as directed - * - * @param[in] mg_batch If set to true use SG BFS path when comms are initialized. - * - */ -template -void bfs(raft::handle_t const& handle, - legacy::GraphCSRView const& graph, - VT* distances, - VT* predecessors, - double* sp_counters, - const VT start_vertex, - bool directed = true, - bool mg_batch = false); - /** * @brief Compute Hungarian algorithm on a weighted bipartite graph * diff --git a/cpp/include/cugraph/utilities/device_comm.hpp b/cpp/include/cugraph/utilities/device_comm.hpp index 7087724921a..990074e781b 100644 --- a/cpp/include/cugraph/utilities/device_comm.hpp +++ b/cpp/include/cugraph/utilities/device_comm.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2022, NVIDIA CORPORATION. + * Copyright (c) 2020-2023, NVIDIA CORPORATION. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -806,9 +806,6 @@ device_sendrecv(raft::comms::comms_t const& comm, size_t constexpr tuple_size = thrust::tuple_size::value_type>::value; - // FIXME: NCCL 2.7 supports only one ncclSend and one ncclRecv for a source rank and destination - // rank inside ncclGroupStart/ncclGroupEnd, so we cannot place this inside - // ncclGroupStart/ncclGroupEnd, this restriction will be lifted in NCCL 2.8 detail::device_sendrecv_tuple_iterator_element_impl::value_type>::value; - // FIXME: NCCL 2.7 supports only one ncclSend and one ncclRecv for a source rank and destination - // rank inside ncclGroupStart/ncclGroupEnd, so we cannot place this inside - // ncclGroupStart/ncclGroupEnd, this restriction will be lifted in NCCL 2.8 detail::device_multicast_sendrecv_tuple_iterator_element_impl std::enable_if_t::value, std::vector> host_scalar_allgather( raft::comms::comms_t const& comm, T input, cudaStream_t stream) { - std::vector rx_counts(comm.get_size(), size_t{1}); - std::vector displacements(rx_counts.size(), size_t{0}); - std::iota(displacements.begin(), displacements.end(), size_t{0}); - rmm::device_uvector d_outputs(rx_counts.size(), stream); + rmm::device_uvector d_outputs(comm.get_size(), stream); raft::update_device(d_outputs.data() + comm.get_rank(), &input, 1, stream); - // FIXME: better use allgather - comm.allgatherv(d_outputs.data() + comm.get_rank(), - d_outputs.data(), - rx_counts.data(), - displacements.data(), - stream); - std::vector h_outputs(rx_counts.size()); - raft::update_host(h_outputs.data(), d_outputs.data(), rx_counts.size(), stream); + comm.allgather(d_outputs.data() + comm.get_rank(), d_outputs.data(), size_t{1}, stream); + std::vector h_outputs(d_outputs.size()); + raft::update_host(h_outputs.data(), d_outputs.data(), d_outputs.size(), stream); auto status = comm.sync_stream(stream); CUGRAPH_EXPECTS(status == raft::comms::status_t::SUCCESS, "sync_stream() failure."); return h_outputs; @@ -277,11 +269,6 @@ std::enable_if_t::value, std::vector::value; - std::vector rx_counts(comm.get_size(), tuple_size); - std::vector displacements(rx_counts.size(), size_t{0}); - for (size_t i = 0; i < displacements.size(); ++i) { - displacements[i] = i * tuple_size; - } std::vector h_tuple_scalar_elements(tuple_size); rmm::device_uvector d_allgathered_tuple_scalar_elements(comm.get_size() * tuple_size, stream); @@ -292,12 +279,10 @@ host_scalar_allgather(raft::comms::comms_t const& comm, T input, cudaStream_t st h_tuple_scalar_elements.data(), tuple_size, stream); - // FIXME: better use allgather - comm.allgatherv(d_allgathered_tuple_scalar_elements.data() + comm.get_rank() * tuple_size, - d_allgathered_tuple_scalar_elements.data(), - rx_counts.data(), - displacements.data(), - stream); + comm.allgather(d_allgathered_tuple_scalar_elements.data() + comm.get_rank() * tuple_size, + d_allgathered_tuple_scalar_elements.data(), + tuple_size, + stream); std::vector h_allgathered_tuple_scalar_elements(comm.get_size() * tuple_size); raft::update_host(h_allgathered_tuple_scalar_elements.data(), d_allgathered_tuple_scalar_elements.data(), @@ -318,6 +303,71 @@ host_scalar_allgather(raft::comms::comms_t const& comm, T input, cudaStream_t st return ret; } +template +std::enable_if_t::value, T> host_scalar_scatter( + raft::comms::comms_t const& comm, + std::vector const& inputs, // relevant only in root + int root, + cudaStream_t stream) +{ + CUGRAPH_EXPECTS( + ((comm.get_rank() == root) && (inputs.size() == static_cast(comm.get_size()))) || + ((comm.get_rank() != root) && (inputs.size() == 0)), + "inputs.size() should match with comm.get_size() in root and should be 0 otherwise."); + rmm::device_uvector d_outputs(comm.get_size(), stream); + if (comm.get_rank() == root) { + raft::update_device(d_outputs.data(), inputs.data(), inputs.size(), stream); + } + comm.bcast(d_outputs.data(), d_outputs.size(), root, stream); + T h_output{}; + raft::update_host(&h_output, d_outputs.data() + comm.get_rank(), 1, stream); + auto status = comm.sync_stream(stream); + CUGRAPH_EXPECTS(status == raft::comms::status_t::SUCCESS, "sync_stream() failure."); + return h_output; +} + +template +std::enable_if_t::value, T> host_scalar_scatter( + raft::comms::comms_t const& comm, + std::vector const& inputs, // relevant only in root + int root, + cudaStream_t stream) +{ + CUGRAPH_EXPECTS( + ((comm.get_rank() == root) && (inputs.size() == static_cast(comm.get_size()))) || + ((comm.get_rank() != root) && (inputs.size() == 0)), + "inputs.size() should match with comm.get_size() in root and should be 0 otherwise."); + size_t constexpr tuple_size = thrust::tuple_size::value; + rmm::device_uvector d_scatter_tuple_scalar_elements(comm.get_size() * tuple_size, + stream); + if (comm.get_rank() == root) { + for (int i = 0; i < comm.get_size(); ++i) { + std::vector h_tuple_scalar_elements(tuple_size); + detail::update_vector_of_tuple_scalar_elements_from_tuple_impl() + .update(h_tuple_scalar_elements, inputs[i]); + raft::update_device(d_scatter_tuple_scalar_elements.data() + i * tuple_size, + h_tuple_scalar_elements.data(), + tuple_size, + stream); + } + } + comm.bcast( + d_scatter_tuple_scalar_elements.data(), d_scatter_tuple_scalar_elements.size(), root, stream); + std::vector h_tuple_scalar_elements(tuple_size); + raft::update_host(h_tuple_scalar_elements.data(), + d_scatter_tuple_scalar_elements.data() + comm.get_rank() * tuple_size, + tuple_size, + stream); + auto status = comm.sync_stream(stream); + CUGRAPH_EXPECTS(status == raft::comms::status_t::SUCCESS, "sync_stream() failure."); + + T ret{}; + detail::update_tuple_from_vector_of_tuple_scalar_elements_impl().update( + ret, h_tuple_scalar_elements); + + return ret; +} + // Return value is valid only in root (return value may better be std::optional in C++17 or later) template std::enable_if_t::value, std::vector> host_scalar_gather( diff --git a/cpp/include/cugraph/utilities/shuffle_comm.cuh b/cpp/include/cugraph/utilities/shuffle_comm.cuh index 6a260144324..ab6a54cc1c0 100644 --- a/cpp/include/cugraph/utilities/shuffle_comm.cuh +++ b/cpp/include/cugraph/utilities/shuffle_comm.cuh @@ -80,7 +80,6 @@ compute_tx_rx_counts_offsets_ranks(raft::comms::comms_t const& comm, rmm::device_uvector d_rx_value_counts(comm_size, stream_view); - // FIXME: this needs to be replaced with AlltoAll once NCCL 2.8 is released. std::vector tx_counts(comm_size, size_t{1}); std::vector tx_offsets(comm_size); std::iota(tx_offsets.begin(), tx_offsets.end(), size_t{0}); @@ -835,7 +834,6 @@ auto shuffle_values(raft::comms::comms_t const& comm, allocate_dataframe_buffer::value_type>( rx_offsets.size() > 0 ? rx_offsets.back() + rx_counts.back() : size_t{0}, stream_view); - // FIXME: this needs to be replaced with AlltoAll once NCCL 2.8 is released // (if num_tx_dst_ranks == num_rx_src_ranks == comm_size). device_multicast_sendrecv(comm, tx_value_first, @@ -889,7 +887,6 @@ auto groupby_gpu_id_and_shuffle_values(raft::comms::comms_t const& comm, allocate_dataframe_buffer::value_type>( rx_offsets.size() > 0 ? rx_offsets.back() + rx_counts.back() : size_t{0}, stream_view); - // FIXME: this needs to be replaced with AlltoAll once NCCL 2.8 is released // (if num_tx_dst_ranks == num_rx_src_ranks == comm_size). device_multicast_sendrecv(comm, tx_value_first, @@ -946,7 +943,6 @@ auto groupby_gpu_id_and_shuffle_kv_pairs(raft::comms::comms_t const& comm, allocate_dataframe_buffer::value_type>( rx_keys.size(), stream_view); - // FIXME: this needs to be replaced with AlltoAll once NCCL 2.8 is released // (if num_tx_dst_ranks == num_rx_src_ranks == comm_size). device_multicast_sendrecv(comm, tx_key_first, @@ -959,7 +955,6 @@ auto groupby_gpu_id_and_shuffle_kv_pairs(raft::comms::comms_t const& comm, rx_src_ranks, stream_view); - // FIXME: this needs to be replaced with AlltoAll once NCCL 2.8 is released // (if num_tx_dst_ranks == num_rx_src_ranks == comm_size). device_multicast_sendrecv(comm, tx_value_first, diff --git a/cpp/src/centrality/katz_centrality_impl.cuh b/cpp/src/centrality/katz_centrality_impl.cuh index 202d00a5771..ac31043d862 100644 --- a/cpp/src/centrality/katz_centrality_impl.cuh +++ b/cpp/src/centrality/katz_centrality_impl.cuh @@ -74,8 +74,6 @@ void katz_centrality( CUGRAPH_EXPECTS(epsilon >= 0.0, "Invalid input argument: epsilon should be non-negative."); if (do_expensive_check) { - // FIXME: should I check for betas? - if (has_initial_guess) { auto num_negative_values = count_if_v(handle, pull_graph_view, katz_centralities, [] __device__(auto, auto val) { diff --git a/cpp/src/components/weakly_connected_components_impl.cuh b/cpp/src/components/weakly_connected_components_impl.cuh index 615a50ded54..b7b6e139cfa 100644 --- a/cpp/src/components/weakly_connected_components_impl.cuh +++ b/cpp/src/components/weakly_connected_components_impl.cuh @@ -236,18 +236,16 @@ struct v_op_t { auto tag = thrust::get<1>(tagged_v); auto v_offset = vertex_partition.local_vertex_partition_offset_from_vertex_nocheck(thrust::get<0>(tagged_v)); - // FIXME: better switch to atomic_ref after - // https://github.com/nvidia/libcudacxx/milestone/2 - auto old = - atomicCAS(level_components + v_offset, invalid_component_id::value, tag); - if (old != invalid_component_id::value && old != tag) { // conflict + cuda::atomic_ref v_component(*(level_components + v_offset)); + auto old = invalid_component_id::value; + bool success = v_component.compare_exchange_strong(old, tag, cuda::std::memory_order_relaxed); + if (!success && (old != tag)) { // conflict return thrust::make_tuple(thrust::optional{bucket_idx_conflict}, thrust::optional{std::byte{0}} /* dummy */); } else { - auto update = (old == invalid_component_id::value); return thrust::make_tuple( - update ? thrust::optional{bucket_idx_next} : thrust::nullopt, - update ? thrust::optional{std::byte{0}} /* dummy */ : thrust::nullopt); + success ? thrust::optional{bucket_idx_next} : thrust::nullopt, + success ? thrust::optional{std::byte{0}} /* dummy */ : thrust::nullopt); } } @@ -457,33 +455,11 @@ void weakly_connected_components_impl(raft::handle_t const& handle, std::numeric_limits::max()); } - // FIXME: we need to add host_scalar_scatter -#if 1 - rmm::device_uvector d_counts(comm_size, handle.get_stream()); - raft::update_device(d_counts.data(), - init_max_new_root_counts.data(), - init_max_new_root_counts.size(), - handle.get_stream()); - device_bcast( - comm, d_counts.data(), d_counts.data(), d_counts.size(), int{0}, handle.get_stream()); - raft::update_host( - &init_max_new_roots, d_counts.data() + comm_rank, size_t{1}, handle.get_stream()); -#else init_max_new_roots = - host_scalar_scatter(comm, init_max_new_root_counts.data(), int{0}, handle.get_stream()); -#endif + host_scalar_scatter(comm, init_max_new_root_counts, int{0}, handle.get_stream()); } else { - // FIXME: we need to add host_scalar_scatter -#if 1 - rmm::device_uvector d_counts(comm_size, handle.get_stream()); - device_bcast( - comm, d_counts.data(), d_counts.data(), d_counts.size(), int{0}, handle.get_stream()); - raft::update_host( - &init_max_new_roots, d_counts.data() + comm_rank, size_t{1}, handle.get_stream()); -#else init_max_new_roots = - host_scalar_scatter(comm, init_max_new_root_counts.data(), int{0}, handle.get_stream()); -#endif + host_scalar_scatter(comm, std::vector{}, int{0}, handle.get_stream()); } handle.sync_stream(); From 8549b546ef1a97b4c25a0f25b73700802d563d17 Mon Sep 17 00:00:00 2001 From: Naim <110031745+naimnv@users.noreply.github.com> Date: Mon, 20 Nov 2023 21:39:53 +0100 Subject: [PATCH 090/111] Fix Leiden refinement phase (#3990) - Normalization factor was missing in the equation to decide if a node and a refined community is strongly connected inside their Louvain community. This PR adds that factor. - Disable random moves in the refinement phase. We plan to expose a flag to enable/disable random moves in a future PR. - Adds new function to flatten Leiden dendrogram as dendrogram flattening process needs additional info to unroll hierarchical leiden clustering Closes #3850 Closes #3749 Authors: - Naim (https://github.com/naimnv) - Alex Barghi (https://github.com/alexbarghi-nv) Approvers: - Chuck Hastings (https://github.com/ChuckHastings) - Seunghwa Kang (https://github.com/seunghwak) - Brad Rees (https://github.com/BradReesWork) URL: https://github.com/rapidsai/cugraph/pull/3990 --- cpp/src/community/detail/common_methods.cuh | 46 +++- cpp/src/community/detail/refine_impl.cuh | 22 +- cpp/src/community/flatten_dendrogram.hpp | 29 ++- cpp/src/community/leiden_impl.cuh | 200 ++++++++++-------- cpp/tests/c_api/leiden_test.c | 4 +- cpp/tests/c_api/louvain_test.c | 39 +++- cpp/tests/community/louvain_test.cpp | 81 +------ .../cugraph/tests/community/test_leiden.py | 28 +-- 8 files changed, 242 insertions(+), 207 deletions(-) diff --git a/cpp/src/community/detail/common_methods.cuh b/cpp/src/community/detail/common_methods.cuh index b388ba53e81..f67d4d939ad 100644 --- a/cpp/src/community/detail/common_methods.cuh +++ b/cpp/src/community/detail/common_methods.cuh @@ -52,7 +52,7 @@ struct is_bitwise_comparable> : std::true_type {}; namespace cugraph { namespace detail { -// a workaround for cudaErrorInvalidDeviceFunction error when device lambda is used +// FIXME: a workaround for cudaErrorInvalidDeviceFunction error when device lambda is used template struct key_aggregated_edge_op_t { weight_t total_edge_weight{}; @@ -80,7 +80,7 @@ struct key_aggregated_edge_op_t { } }; -// a workaround for cudaErrorInvalidDeviceFunction error when device lambda is used +// FIXME: a workaround for cudaErrorInvalidDeviceFunction error when device lambda is used template struct reduce_op_t { using type = thrust::tuple; @@ -100,7 +100,28 @@ struct reduce_op_t { } }; -// a workaround for cudaErrorInvalidDeviceFunction error when device lambda is used +// FIXME: a workaround for cudaErrorInvalidDeviceFunction error when device lambda is used +template +struct count_updown_moves_op_t { + bool up_down{}; + __device__ auto operator()(thrust::tuple> p) const + { + vertex_t old_cluster = thrust::get<0>(p); + auto new_cluster_gain_pair = thrust::get<1>(p); + vertex_t new_cluster = thrust::get<0>(new_cluster_gain_pair); + weight_t delta_modularity = thrust::get<1>(new_cluster_gain_pair); + + auto result_assignment = + (delta_modularity > weight_t{0}) + ? (((new_cluster > old_cluster) != up_down) ? old_cluster : new_cluster) + : old_cluster; + + return (delta_modularity > weight_t{0}) + ? (((new_cluster > old_cluster) != up_down) ? false : true) + : false; + } +}; +// FIXME: a workaround for cudaErrorInvalidDeviceFunction error when device lambda is used template struct cluster_update_op_t { bool up_down{}; @@ -115,7 +136,7 @@ struct cluster_update_op_t { } }; -// a workaround for cudaErrorInvalidDeviceFunction error when device lambda is used +// FIXME: a workaround for cudaErrorInvalidDeviceFunction error when device lambda is used template struct return_edge_weight_t { __device__ auto operator()( @@ -125,7 +146,7 @@ struct return_edge_weight_t { } }; -// a workaround for cudaErrorInvalidDeviceFunction error when device lambda is used +// FIXME: a workaround for cudaErrorInvalidDeviceFunction error when device lambda is used template struct return_one_t { __device__ auto operator()( @@ -394,6 +415,21 @@ rmm::device_uvector update_clustering_by_delta_modularity( detail::reduce_op_t{}, cugraph::get_dataframe_buffer_begin(output_buffer)); + int nr_moves = thrust::count_if( + handle.get_thrust_policy(), + thrust::make_zip_iterator(thrust::make_tuple( + next_clusters_v.begin(), cugraph::get_dataframe_buffer_begin(output_buffer))), + thrust::make_zip_iterator( + thrust::make_tuple(next_clusters_v.end(), cugraph::get_dataframe_buffer_end(output_buffer))), + detail::count_updown_moves_op_t{up_down}); + + if (multi_gpu) { + nr_moves = host_scalar_allreduce( + handle.get_comms(), nr_moves, raft::comms::op_t::SUM, handle.get_stream()); + } + + if (nr_moves == 0) { up_down = !up_down; } + thrust::transform(handle.get_thrust_policy(), next_clusters_v.begin(), next_clusters_v.end(), diff --git a/cpp/src/community/detail/refine_impl.cuh b/cpp/src/community/detail/refine_impl.cuh index 6b6470991bb..ebaae498d04 100644 --- a/cpp/src/community/detail/refine_impl.cuh +++ b/cpp/src/community/detail/refine_impl.cuh @@ -89,8 +89,9 @@ struct leiden_key_aggregated_edge_op_t { // E(Cr, S-Cr) > ||Cr||*(||S|| -||Cr||) bool is_dst_leiden_cluster_well_connected = - dst_leiden_cut_to_louvain > - resolution * dst_leiden_volume * (louvain_cluster_volume - dst_leiden_volume); + dst_leiden_cut_to_louvain > resolution * dst_leiden_volume * + (louvain_cluster_volume - dst_leiden_volume) / + total_edge_weight; // E(v, Cr-v) - ||v||* ||Cr-v||/||V(G)|| // aggregated_weight_to_neighboring_leiden_cluster == E(v, Cr-v)? @@ -98,11 +99,11 @@ struct leiden_key_aggregated_edge_op_t { weight_t mod_gain = -1.0; if (is_src_active > 0) { if ((louvain_of_dst_leiden_cluster == src_louvain_cluster) && - is_dst_leiden_cluster_well_connected) { + (dst_leiden_cluster_id != src_leiden_cluster) && is_dst_leiden_cluster_well_connected) { mod_gain = aggregated_weight_to_neighboring_leiden_cluster - - resolution * src_weighted_deg * (dst_leiden_volume - src_weighted_deg) / - total_edge_weight; - + resolution * src_weighted_deg * dst_leiden_volume / total_edge_weight; +// FIXME: Disable random moves in refinement phase for now. +#if 0 weight_t random_number{0.0}; if (mod_gain > 0.0) { auto flat_id = uint64_t{threadIdx.x + blockIdx.x * blockDim.x}; @@ -117,6 +118,8 @@ struct leiden_key_aggregated_edge_op_t { ? __expf(static_cast((2.0 * mod_gain) / (theta * total_edge_weight))) * random_number : -1.0; +#endif + mod_gain = mod_gain > 0.0 ? mod_gain : -1.0; } } @@ -240,11 +243,12 @@ refine_clustering( wcut_deg_and_cluster_vol_triple_begin, wcut_deg_and_cluster_vol_triple_end, singleton_and_connected_flags.begin(), - [resolution] __device__(auto wcut_wdeg_and_louvain_volume) { + [resolution, total_edge_weight] __device__(auto wcut_wdeg_and_louvain_volume) { auto wcut = thrust::get<0>(wcut_wdeg_and_louvain_volume); auto wdeg = thrust::get<1>(wcut_wdeg_and_louvain_volume); auto louvain_volume = thrust::get<2>(wcut_wdeg_and_louvain_volume); - return wcut > (resolution * wdeg * (louvain_volume - wdeg)); + return wcut > + (resolution * wdeg * (louvain_volume - wdeg) / total_edge_weight); }); edge_src_property_t src_louvain_cluster_weight_cache(handle); @@ -478,7 +482,7 @@ refine_clustering( auto values_for_leiden_cluster_keys = thrust::make_zip_iterator( thrust::make_tuple(refined_community_volumes.begin(), refined_community_cuts.begin(), - leiden_keys_used_in_edge_reduction.begin(), // redundant + leiden_keys_used_in_edge_reduction.begin(), louvain_of_leiden_keys_used_in_edge_reduction.begin())); using value_t = thrust::tuple; diff --git a/cpp/src/community/flatten_dendrogram.hpp b/cpp/src/community/flatten_dendrogram.hpp index 9a0c103c01f..eac20389765 100644 --- a/cpp/src/community/flatten_dendrogram.hpp +++ b/cpp/src/community/flatten_dendrogram.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021-2022, NVIDIA CORPORATION. + * Copyright (c) 2021-2023, NVIDIA CORPORATION. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -59,4 +59,31 @@ void partition_at_level(raft::handle_t const& handle, }); } +template +void leiden_partition_at_level(raft::handle_t const& handle, + Dendrogram const& dendrogram, + vertex_t* d_partition, + size_t level) +{ + vertex_t local_num_verts = dendrogram.get_level_size_nocheck(0); + raft::copy( + d_partition, dendrogram.get_level_ptr_nocheck(0), local_num_verts, handle.get_stream()); + + rmm::device_uvector local_vertex_ids_v(local_num_verts, handle.get_stream()); + + std::for_each( + thrust::make_counting_iterator(0), + thrust::make_counting_iterator((level - 1) / 2), + [&handle, &dendrogram, &local_vertex_ids_v, &d_partition, local_num_verts](size_t l) { + cugraph::relabel( + handle, + std::tuple(dendrogram.get_level_ptr_nocheck(2 * l + 1), + dendrogram.get_level_ptr_nocheck(2 * l + 2)), + dendrogram.get_level_size_nocheck(2 * l + 1), + d_partition, + local_num_verts, + false); + }); +} + } // namespace cugraph diff --git a/cpp/src/community/leiden_impl.cuh b/cpp/src/community/leiden_impl.cuh index a9faf2f2d82..b6e20272de9 100644 --- a/cpp/src/community/leiden_impl.cuh +++ b/cpp/src/community/leiden_impl.cuh @@ -43,6 +43,34 @@ void check_clustering(graph_view_t const& gr if (graph_view.local_vertex_partition_range_size() > 0) CUGRAPH_EXPECTS(clustering != nullptr, "Invalid input argument: clustering is null"); } +template +vertex_t remove_duplicates(raft::handle_t const& handle, rmm::device_uvector& input_array) +{ + thrust::sort(handle.get_thrust_policy(), input_array.begin(), input_array.end()); + + auto nr_unique_elements = static_cast(thrust::distance( + input_array.begin(), + thrust::unique(handle.get_thrust_policy(), input_array.begin(), input_array.end()))); + + input_array.resize(nr_unique_elements, handle.get_stream()); + + if constexpr (multi_gpu) { + input_array = cugraph::detail::shuffle_ext_vertices_to_local_gpu_by_vertex_partitioning( + handle, std::move(input_array)); + + thrust::sort(handle.get_thrust_policy(), input_array.begin(), input_array.end()); + + nr_unique_elements = static_cast(thrust::distance( + input_array.begin(), + thrust::unique(handle.get_thrust_policy(), input_array.begin(), input_array.end()))); + + input_array.resize(nr_unique_elements, handle.get_stream()); + + nr_unique_elements = host_scalar_allreduce( + handle.get_comms(), nr_unique_elements, raft::comms::op_t::SUM, handle.get_stream()); + } + return nr_unique_elements; +} template >, weight_t> leiden( rmm::device_uvector louvain_of_refined_graph(0, handle.get_stream()); // #V - while (dendrogram->num_levels() < max_level) { + while (dendrogram->num_levels() < 2 * max_level + 1) { // // Initialize every cluster to reference each vertex to itself // @@ -353,40 +381,8 @@ std::pair>, weight_t> leiden( dendrogram->current_level_begin(), dendrogram->current_level_begin() + dendrogram->current_level_size(), copied_louvain_partition.begin()); - - thrust::sort( - handle.get_thrust_policy(), copied_louvain_partition.begin(), copied_louvain_partition.end()); - auto nr_unique_louvain_clusters = - static_cast(thrust::distance(copied_louvain_partition.begin(), - thrust::unique(handle.get_thrust_policy(), - copied_louvain_partition.begin(), - copied_louvain_partition.end()))); - - copied_louvain_partition.resize(nr_unique_louvain_clusters, handle.get_stream()); - - if constexpr (graph_view_t::is_multi_gpu) { - copied_louvain_partition = - cugraph::detail::shuffle_ext_vertices_to_local_gpu_by_vertex_partitioning( - handle, std::move(copied_louvain_partition)); - - thrust::sort(handle.get_thrust_policy(), - copied_louvain_partition.begin(), - copied_louvain_partition.end()); - - nr_unique_louvain_clusters = - static_cast(thrust::distance(copied_louvain_partition.begin(), - thrust::unique(handle.get_thrust_policy(), - copied_louvain_partition.begin(), - copied_louvain_partition.end()))); - - copied_louvain_partition.resize(nr_unique_louvain_clusters, handle.get_stream()); - - nr_unique_louvain_clusters = host_scalar_allreduce(handle.get_comms(), - nr_unique_louvain_clusters, - raft::comms::op_t::SUM, - handle.get_stream()); - } + remove_duplicates(handle, copied_louvain_partition); terminate = terminate || (nr_unique_louvain_clusters == current_graph_view.number_of_vertices()); @@ -481,6 +477,15 @@ std::pair>, weight_t> leiden( (*cluster_assignment).data(), (*cluster_assignment).size(), false); + // louvain assignment of aggregated graph which is necessary to flatten dendrogram + dendrogram->add_level(current_graph_view.local_vertex_partition_range_first(), + current_graph_view.local_vertex_partition_range_size(), + handle.get_stream()); + + raft::copy(dendrogram->current_level_begin(), + (*cluster_assignment).begin(), + (*cluster_assignment).size(), + handle.get_stream()); louvain_of_refined_graph.resize(current_graph_view.local_vertex_partition_range_size(), handle.get_stream()); @@ -492,47 +497,6 @@ std::pair>, weight_t> leiden( } } - // Relabel dendrogram - vertex_t local_cluster_id_first{0}; - if constexpr (multi_gpu) { - auto unique_cluster_range_lasts = cugraph::partition_manager::compute_partition_range_lasts( - handle, static_cast(copied_louvain_partition.size())); - - auto& comm = handle.get_comms(); - auto const comm_size = comm.get_size(); - auto const comm_rank = comm.get_rank(); - auto& major_comm = handle.get_subcomm(cugraph::partition_manager::major_comm_name()); - auto const major_comm_size = major_comm.get_size(); - auto const major_comm_rank = major_comm.get_rank(); - auto& minor_comm = handle.get_subcomm(cugraph::partition_manager::minor_comm_name()); - auto const minor_comm_size = minor_comm.get_size(); - auto const minor_comm_rank = minor_comm.get_rank(); - - auto vertex_partition_id = - partition_manager::compute_vertex_partition_id_from_graph_subcomm_ranks( - major_comm_size, minor_comm_size, major_comm_rank, minor_comm_rank); - - local_cluster_id_first = vertex_partition_id == 0 - ? vertex_t{0} - : unique_cluster_range_lasts[vertex_partition_id - 1]; - } - - rmm::device_uvector numbering_indices(copied_louvain_partition.size(), - handle.get_stream()); - detail::sequence_fill(handle.get_stream(), - numbering_indices.data(), - numbering_indices.size(), - local_cluster_id_first); - - relabel( - handle, - std::make_tuple(static_cast(copied_louvain_partition.begin()), - static_cast(numbering_indices.begin())), - copied_louvain_partition.size(), - dendrogram->current_level_begin(), - dendrogram->current_level_size(), - false); - copied_louvain_partition.resize(0, handle.get_stream()); copied_louvain_partition.shrink_to_fit(handle.get_stream()); @@ -550,23 +514,71 @@ std::pair>, weight_t> leiden( return std::make_pair(std::move(dendrogram), best_modularity); } -// FIXME: Can we have a common flatten_dendrogram to be used by both -// Louvain and Leiden, and possibly other clustering methods? +template +void relabel_cluster_ids(raft::handle_t const& handle, + rmm::device_uvector& unique_cluster_ids, + vertex_t* clustering, + size_t num_nodes) +{ + vertex_t local_cluster_id_first{0}; + if constexpr (multi_gpu) { + auto unique_cluster_range_lasts = cugraph::partition_manager::compute_partition_range_lasts( + handle, static_cast(unique_cluster_ids.size())); + + auto& comm = handle.get_comms(); + auto const comm_size = comm.get_size(); + auto const comm_rank = comm.get_rank(); + auto& major_comm = handle.get_subcomm(cugraph::partition_manager::major_comm_name()); + auto const major_comm_size = major_comm.get_size(); + auto const major_comm_rank = major_comm.get_rank(); + auto& minor_comm = handle.get_subcomm(cugraph::partition_manager::minor_comm_name()); + auto const minor_comm_size = minor_comm.get_size(); + auto const minor_comm_rank = minor_comm.get_rank(); + + auto vertex_partition_id = + partition_manager::compute_vertex_partition_id_from_graph_subcomm_ranks( + major_comm_size, minor_comm_size, major_comm_rank, minor_comm_rank); + + local_cluster_id_first = + vertex_partition_id == 0 ? vertex_t{0} : unique_cluster_range_lasts[vertex_partition_id - 1]; + } + + rmm::device_uvector numbering_indices(unique_cluster_ids.size(), handle.get_stream()); + detail::sequence_fill(handle.get_stream(), + numbering_indices.data(), + numbering_indices.size(), + local_cluster_id_first); + + relabel( + handle, + std::make_tuple(static_cast(unique_cluster_ids.begin()), + static_cast(numbering_indices.begin())), + unique_cluster_ids.size(), + clustering, + num_nodes, + false); +} + template -void flatten_dendrogram(raft::handle_t const& handle, - graph_view_t const& graph_view, - Dendrogram const& dendrogram, - vertex_t* clustering) +void flatten_leiden_dendrogram(raft::handle_t const& handle, + graph_view_t const& graph_view, + Dendrogram const& dendrogram, + vertex_t* clustering) { - rmm::device_uvector vertex_ids_v(graph_view.number_of_vertices(), handle.get_stream()); + leiden_partition_at_level( + handle, dendrogram, clustering, dendrogram.num_levels()); + + rmm::device_uvector unique_cluster_ids(graph_view.number_of_vertices(), + handle.get_stream()); + thrust::copy(handle.get_thrust_policy(), + clustering, + clustering + graph_view.number_of_vertices(), + unique_cluster_ids.begin()); - thrust::sequence(handle.get_thrust_policy(), - vertex_ids_v.begin(), - vertex_ids_v.end(), - graph_view.local_vertex_partition_range_first()); + remove_duplicates(handle, unique_cluster_ids); - partition_at_level( - handle, dendrogram, vertex_ids_v.data(), clustering, dendrogram.num_levels()); + relabel_cluster_ids( + handle, unique_cluster_ids, clustering, graph_view.number_of_vertices()); } } // namespace detail @@ -588,14 +600,14 @@ std::pair>, weight_t> leiden( } template -void flatten_dendrogram(raft::handle_t const& handle, - graph_view_t const& graph_view, - Dendrogram const& dendrogram, - vertex_t* clustering) +void flatten_leiden_dendrogram(raft::handle_t const& handle, + graph_view_t const& graph_view, + Dendrogram const& dendrogram, + vertex_t* clustering) { CUGRAPH_EXPECTS(!graph_view.has_edge_mask(), "unimplemented."); - detail::flatten_dendrogram(handle, graph_view, dendrogram, clustering); + detail::flatten_leiden_dendrogram(handle, graph_view, dendrogram, clustering); } template @@ -620,7 +632,7 @@ std::pair leiden( std::tie(dendrogram, modularity) = detail::leiden(handle, rng_state, graph_view, edge_weight_view, max_level, resolution, theta); - detail::flatten_dendrogram(handle, graph_view, *dendrogram, clustering); + detail::flatten_leiden_dendrogram(handle, graph_view, *dendrogram, clustering); return std::make_pair(dendrogram->num_levels(), modularity); } diff --git a/cpp/tests/c_api/leiden_test.c b/cpp/tests/c_api/leiden_test.c index 9e91adf9f89..df206ebd1ed 100644 --- a/cpp/tests/c_api/leiden_test.c +++ b/cpp/tests/c_api/leiden_test.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, NVIDIA CORPORATION. + * Copyright (c) 2022-2023, NVIDIA CORPORATION. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -161,7 +161,7 @@ int test_leiden_no_weights() vertex_t h_src[] = {0, 1, 1, 2, 2, 2, 3, 4, 1, 3, 4, 0, 1, 3, 5, 5}; vertex_t h_dst[] = {1, 3, 4, 0, 1, 3, 5, 5, 0, 1, 1, 2, 2, 2, 3, 4}; vertex_t h_result[] = {1, 1, 1, 2, 0, 0}; - weight_t expected_modularity = 0.0859375; + weight_t expected_modularity = 0.125; // Louvain wants store_transposed = FALSE return generic_leiden_test(h_src, diff --git a/cpp/tests/c_api/louvain_test.c b/cpp/tests/c_api/louvain_test.c index e9ac5c9ff06..41d777545b2 100644 --- a/cpp/tests/c_api/louvain_test.c +++ b/cpp/tests/c_api/louvain_test.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, NVIDIA CORPORATION. + * Copyright (c) 2022-2023, NVIDIA CORPORATION. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -46,22 +46,39 @@ int generic_louvain_test(vertex_t* h_src, cugraph_graph_t* p_graph = NULL; cugraph_hierarchical_clustering_result_t* p_result = NULL; - data_type_id_t vertex_tid = INT32; - data_type_id_t edge_tid = INT32; - data_type_id_t weight_tid = FLOAT32; + data_type_id_t vertex_tid = INT32; + data_type_id_t edge_tid = INT32; + data_type_id_t weight_tid = FLOAT32; data_type_id_t edge_id_tid = INT32; data_type_id_t edge_type_tid = INT32; p_handle = cugraph_create_resource_handle(NULL); TEST_ASSERT(test_ret_value, p_handle != NULL, "resource handle creation failed."); - ret_code = create_sg_test_graph(p_handle, vertex_tid, edge_tid, h_src, h_dst, weight_tid, h_wgt, edge_type_tid, NULL, edge_id_tid, NULL, num_edges, store_transposed, FALSE, FALSE, FALSE, &p_graph, &ret_error); + ret_code = create_sg_test_graph(p_handle, + vertex_tid, + edge_tid, + h_src, + h_dst, + weight_tid, + h_wgt, + edge_type_tid, + NULL, + edge_id_tid, + NULL, + num_edges, + store_transposed, + FALSE, + FALSE, + FALSE, + &p_graph, + &ret_error); TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "create_test_graph failed."); TEST_ALWAYS_ASSERT(ret_code == CUGRAPH_SUCCESS, cugraph_error_message(ret_error)); - ret_code = - cugraph_louvain(p_handle, p_graph, max_level, threshold, resolution, FALSE, &p_result, &ret_error); + ret_code = cugraph_louvain( + p_handle, p_graph, max_level, threshold, resolution, FALSE, &p_result, &ret_error); TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, cugraph_error_message(ret_error)); TEST_ALWAYS_ASSERT(ret_code == CUGRAPH_SUCCESS, "cugraph_louvain failed."); @@ -141,10 +158,10 @@ int test_louvain_no_weight() weight_t threshold = 1e-7; weight_t resolution = 1.0; - vertex_t h_src[] = {0, 1, 1, 2, 2, 2, 3, 4, 1, 3, 4, 0, 1, 3, 5, 5}; - vertex_t h_dst[] = {1, 3, 4, 0, 1, 3, 5, 5, 0, 1, 1, 2, 2, 2, 3, 4}; - vertex_t h_result[] = {1, 1, 1, 2, 0, 0}; - weight_t expected_modularity = 0.0859375; + vertex_t h_src[] = {0, 1, 1, 2, 2, 2, 3, 4, 1, 3, 4, 0, 1, 3, 5, 5}; + vertex_t h_dst[] = {1, 3, 4, 0, 1, 3, 5, 5, 0, 1, 1, 2, 2, 2, 3, 4}; + vertex_t h_result[] = {1, 1, 1, 1, 0, 0}; + weight_t expected_modularity = 0.125; // Louvain wants store_transposed = FALSE return generic_louvain_test(h_src, diff --git a/cpp/tests/community/louvain_test.cpp b/cpp/tests/community/louvain_test.cpp index 1e1fb6d4c33..284dcc94b8c 100644 --- a/cpp/tests/community/louvain_test.cpp +++ b/cpp/tests/community/louvain_test.cpp @@ -317,72 +317,6 @@ TEST(louvain_legacy, success) } } -TEST(louvain_legacy_renumbered, success) -{ - raft::handle_t handle; - - auto stream = handle.get_stream(); - - std::vector off_h = {0, 16, 25, 30, 34, 38, 42, 44, 46, 48, 50, 52, - 54, 56, 73, 85, 95, 101, 107, 112, 117, 121, 125, 129, - 132, 135, 138, 141, 144, 147, 149, 151, 153, 155, 156}; - std::vector ind_h = { - 1, 3, 7, 11, 15, 16, 17, 18, 19, 20, 21, 23, 24, 25, 30, 33, 0, 5, 11, 15, 16, 19, 21, - 25, 30, 4, 13, 14, 22, 27, 0, 9, 20, 24, 2, 13, 15, 26, 1, 13, 14, 18, 13, 15, 0, 16, - 13, 14, 3, 20, 13, 14, 0, 1, 13, 22, 2, 4, 5, 6, 8, 10, 12, 14, 17, 18, 19, 22, 25, - 28, 29, 31, 32, 2, 5, 8, 10, 13, 15, 17, 18, 22, 29, 31, 32, 0, 1, 4, 6, 14, 16, 18, - 19, 21, 28, 0, 1, 7, 15, 19, 21, 0, 13, 14, 26, 27, 28, 0, 5, 13, 14, 15, 0, 1, 13, - 16, 16, 0, 3, 9, 23, 0, 1, 15, 16, 2, 12, 13, 14, 0, 20, 24, 0, 3, 23, 0, 1, 13, - 4, 17, 27, 2, 17, 26, 13, 15, 17, 13, 14, 0, 1, 13, 14, 13, 14, 0}; - - std::vector w_h = { - 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, - 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, - 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, - 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, - 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, - 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, - 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, - 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, - 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0}; - - int num_verts = off_h.size() - 1; - int num_edges = ind_h.size(); - - rmm::device_uvector offsets_v(num_verts + 1, stream); - rmm::device_uvector indices_v(num_edges, stream); - rmm::device_uvector weights_v(num_edges, stream); - rmm::device_uvector result_v(num_verts, stream); - - raft::update_device(offsets_v.data(), off_h.data(), off_h.size(), stream); - raft::update_device(indices_v.data(), ind_h.data(), ind_h.size(), stream); - raft::update_device(weights_v.data(), w_h.data(), w_h.size(), stream); - - cugraph::legacy::GraphCSRView G( - offsets_v.data(), indices_v.data(), weights_v.data(), num_verts, num_edges); - - float modularity{0.0}; - size_t num_level = 40; - - // "FIXME": remove this check once we drop support for Pascal - // - // Calling louvain on Pascal will throw an exception, we'll check that - // this is the behavior while we still support Pascal (device_prop.major < 7) - // - if (handle.get_device_properties().major < 7) { - EXPECT_THROW(cugraph::louvain(handle, G, result_v.data()), cugraph::logic_error); - } else { - std::tie(num_level, modularity) = cugraph::louvain(handle, G, result_v.data()); - - auto cluster_id = cugraph::test::to_host(handle, result_v); - - int min = *min_element(cluster_id.begin(), cluster_id.end()); - - ASSERT_GE(min, 0); - ASSERT_FLOAT_EQ(modularity, 0.41880345); - } -} - using Tests_Louvain_File = Tests_Louvain; using Tests_Louvain_File32 = Tests_Louvain; using Tests_Louvain_File64 = Tests_Louvain; @@ -390,11 +324,15 @@ using Tests_Louvain_Rmat = Tests_Louvain; using Tests_Louvain_Rmat32 = Tests_Louvain; using Tests_Louvain_Rmat64 = Tests_Louvain; +#if 0 +// FIXME: Reenable legacy tests once threshold parameter is exposed +// by louvain legacy API. TEST_P(Tests_Louvain_File, CheckInt32Int32FloatFloatLegacy) { run_legacy_test( override_File_Usecase_with_cmd_line_arguments(GetParam())); } +#endif TEST_P(Tests_Louvain_File, CheckInt32Int32FloatFloat) { @@ -458,11 +396,12 @@ TEST_P(Tests_Louvain_Rmat64, CheckInt64Int64FloatFloat) INSTANTIATE_TEST_SUITE_P( simple_test, Tests_Louvain_File, - ::testing::Combine( - ::testing::Values(Louvain_Usecase{std::nullopt, std::nullopt, std::nullopt, true, 3, 0.408695}, - Louvain_Usecase{20, double{1e-4}, std::nullopt, true, 3, 0.408695}, - Louvain_Usecase{100, double{1e-4}, double{0.8}, true, 3, 0.48336622}), - ::testing::Values(cugraph::test::File_Usecase("test/datasets/karate.mtx")))); + ::testing::Combine(::testing::Values( + Louvain_Usecase{ + std::nullopt, std::nullopt, std::nullopt, true, 3, 0.39907956}, + Louvain_Usecase{20, double{1e-3}, std::nullopt, true, 3, 0.39907956}, + Louvain_Usecase{100, double{1e-3}, double{0.8}, true, 3, 0.47547662}), + ::testing::Values(cugraph::test::File_Usecase("test/datasets/karate.mtx")))); INSTANTIATE_TEST_SUITE_P( file_benchmark_test, /* note that the test filename can be overridden in benchmarking (with diff --git a/python/cugraph/cugraph/tests/community/test_leiden.py b/python/cugraph/cugraph/tests/community/test_leiden.py index a06b0dd22c5..71117c4210f 100644 --- a/python/cugraph/cugraph/tests/community/test_leiden.py +++ b/python/cugraph/cugraph/tests/community/test_leiden.py @@ -22,8 +22,6 @@ from cugraph.testing import utils, UNDIRECTED_DATASETS from cugraph.datasets import karate_asymmetric -from cudf.testing.testing import assert_series_equal - # ============================================================================= # Test data @@ -43,8 +41,8 @@ "resolution": 1.0, "input_type": "COO", "expected_output": { - "partition": [1, 0, 1, 2, 2, 2], - "modularity_score": 0.1757322, + "partition": [0, 0, 0, 1, 1, 1], + "modularity_score": 0.215969, }, }, "data_2": { @@ -85,10 +83,10 @@ "input_type": "CSR", "expected_output": { # fmt: off - "partition": [6, 6, 3, 3, 1, 5, 5, 3, 0, 3, 1, 6, 3, 3, 4, 4, 5, 6, 4, 6, 4, - 6, 4, 4, 2, 2, 4, 4, 2, 4, 0, 2, 4, 4], + "partition": [3, 3, 3, 3, 2, 2, 2, 3, 1, 3, 2, 3, 3, 3, 1, 1, 2, 3, 1, 3, + 1, 3, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1], # fmt: on - "modularity_score": 0.3468113, + "modularity_score": 0.41880345, }, }, } @@ -138,7 +136,7 @@ def input_and_expected_output(request): # Create graph from csr offsets = src_or_offset_array indices = dst_or_index_array - G.from_cudf_adjlist(offsets, indices, weight) + G.from_cudf_adjlist(offsets, indices, weight, renumber=False) parts, mod = cugraph.leiden(G, max_level, resolution) @@ -223,9 +221,7 @@ def test_leiden_directed_graph(): @pytest.mark.sg def test_leiden_golden_results(input_and_expected_output): - expected_partition = cudf.Series( - input_and_expected_output["expected_output"]["partition"] - ) + expected_partition = input_and_expected_output["expected_output"]["partition"] expected_mod = input_and_expected_output["expected_output"]["modularity_score"] result_partition = input_and_expected_output["result_output"]["partition"] @@ -233,6 +229,10 @@ def test_leiden_golden_results(input_and_expected_output): assert abs(expected_mod - result_mod) < 0.0001 - assert_series_equal( - expected_partition, result_partition, check_dtype=False, check_names=False - ) + expected_to_result_map = {} + for e, r in zip(expected_partition, list(result_partition.to_pandas())): + if e in expected_to_result_map.keys(): + assert r == expected_to_result_map[e] + + else: + expected_to_result_map[e] = r From 6e765bb36e7a8d2d7120a3afd034bb005b48eda9 Mon Sep 17 00:00:00 2001 From: Erik Welch Date: Mon, 20 Nov 2023 17:36:50 -0600 Subject: [PATCH 091/111] nx-cugraph: add SSSP (unweighted) (#3976) There are many more traversal algorithms to implement, but these get us started! Authors: - Erik Welch (https://github.com/eriknw) - Brad Rees (https://github.com/BradReesWork) - Rick Ratzel (https://github.com/rlratzel) Approvers: - Brad Rees (https://github.com/BradReesWork) - Rick Ratzel (https://github.com/rlratzel) URL: https://github.com/rapidsai/cugraph/pull/3976 --- python/nx-cugraph/_nx_cugraph/__init__.py | 2 + .../nx_cugraph/algorithms/__init__.py | 3 +- .../algorithms/shortest_paths/__init__.py | 13 +++++ .../algorithms/shortest_paths/unweighted.py | 53 +++++++++++++++++++ python/nx-cugraph/nx_cugraph/classes/graph.py | 9 +++- python/nx-cugraph/nx_cugraph/interface.py | 2 + 6 files changed, 79 insertions(+), 3 deletions(-) create mode 100644 python/nx-cugraph/nx_cugraph/algorithms/shortest_paths/__init__.py create mode 100644 python/nx-cugraph/nx_cugraph/algorithms/shortest_paths/unweighted.py diff --git a/python/nx-cugraph/_nx_cugraph/__init__.py b/python/nx-cugraph/_nx_cugraph/__init__.py index 26638d1e735..910db1bc379 100644 --- a/python/nx-cugraph/_nx_cugraph/__init__.py +++ b/python/nx-cugraph/_nx_cugraph/__init__.py @@ -79,6 +79,8 @@ "path_graph", "petersen_graph", "sedgewick_maze_graph", + "single_source_shortest_path_length", + "single_target_shortest_path_length", "star_graph", "tadpole_graph", "tetrahedral_graph", diff --git a/python/nx-cugraph/nx_cugraph/algorithms/__init__.py b/python/nx-cugraph/nx_cugraph/algorithms/__init__.py index 87b1967fa93..32cd6f31a47 100644 --- a/python/nx-cugraph/nx_cugraph/algorithms/__init__.py +++ b/python/nx-cugraph/nx_cugraph/algorithms/__init__.py @@ -10,9 +10,10 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -from . import bipartite, centrality, community, components +from . import bipartite, centrality, community, components, shortest_paths from .bipartite import complete_bipartite_graph from .centrality import * from .components import * from .core import * from .isolate import * +from .shortest_paths import * diff --git a/python/nx-cugraph/nx_cugraph/algorithms/shortest_paths/__init__.py b/python/nx-cugraph/nx_cugraph/algorithms/shortest_paths/__init__.py new file mode 100644 index 00000000000..b7d6b742176 --- /dev/null +++ b/python/nx-cugraph/nx_cugraph/algorithms/shortest_paths/__init__.py @@ -0,0 +1,13 @@ +# Copyright (c) 2023, NVIDIA CORPORATION. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +from .unweighted import * diff --git a/python/nx-cugraph/nx_cugraph/algorithms/shortest_paths/unweighted.py b/python/nx-cugraph/nx_cugraph/algorithms/shortest_paths/unweighted.py new file mode 100644 index 00000000000..3413a637b32 --- /dev/null +++ b/python/nx-cugraph/nx_cugraph/algorithms/shortest_paths/unweighted.py @@ -0,0 +1,53 @@ +# Copyright (c) 2023, NVIDIA CORPORATION. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import cupy as cp +import networkx as nx +import numpy as np +import pylibcugraph as plc + +from nx_cugraph.convert import _to_graph +from nx_cugraph.utils import index_dtype, networkx_algorithm + +__all__ = ["single_source_shortest_path_length", "single_target_shortest_path_length"] + + +@networkx_algorithm +def single_source_shortest_path_length(G, source, cutoff=None): + return _single_shortest_path_length(G, source, cutoff, "Source") + + +@networkx_algorithm +def single_target_shortest_path_length(G, target, cutoff=None): + return _single_shortest_path_length(G, target, cutoff, "Target") + + +def _single_shortest_path_length(G, source, cutoff, kind): + G = _to_graph(G) + if source not in G: + raise nx.NodeNotFound(f"{kind} {source} is not in G") + if G.src_indices.size == 0: + return {source: 0} + if cutoff is None: + cutoff = -1 + src_index = source if G.key_to_id is None else G.key_to_id[source] + distances, predecessors, node_ids = plc.bfs( + handle=plc.ResourceHandle(), + graph=G._get_plc_graph(switch_indices=kind == "Target"), + sources=cp.array([src_index], index_dtype), + direction_optimizing=False, # True for undirected only; what's recommended? + depth_limit=cutoff, + compute_predecessors=False, + do_expensive_check=False, + ) + mask = distances != np.iinfo(distances.dtype).max + return G._nodearrays_to_dict(node_ids[mask], distances[mask]) diff --git a/python/nx-cugraph/nx_cugraph/classes/graph.py b/python/nx-cugraph/nx_cugraph/classes/graph.py index 23004651fc5..fea318e036e 100644 --- a/python/nx-cugraph/nx_cugraph/classes/graph.py +++ b/python/nx-cugraph/nx_cugraph/classes/graph.py @@ -559,6 +559,7 @@ def _get_plc_graph( edge_dtype: Dtype | None = None, *, store_transposed: bool = False, + switch_indices: bool = False, edge_array: cp.ndarray[EdgeValue] | None = None, ): if edge_array is not None: @@ -613,14 +614,18 @@ def _get_plc_graph( elif edge_array.dtype not in self._plc_allowed_edge_types: raise TypeError(edge_array.dtype) # Should we cache PLC graph? + src_indices = self.src_indices + dst_indices = self.dst_indices + if switch_indices: + src_indices, dst_indices = dst_indices, src_indices return plc.SGGraph( resource_handle=plc.ResourceHandle(), graph_properties=plc.GraphProperties( is_multigraph=self.is_multigraph(), is_symmetric=not self.is_directed(), ), - src_or_offset_array=self.src_indices, - dst_or_index_array=self.dst_indices, + src_or_offset_array=src_indices, + dst_or_index_array=dst_indices, weight_array=edge_array, store_transposed=store_transposed, renumber=False, diff --git a/python/nx-cugraph/nx_cugraph/interface.py b/python/nx-cugraph/nx_cugraph/interface.py index 875f8621021..8903fdc541e 100644 --- a/python/nx-cugraph/nx_cugraph/interface.py +++ b/python/nx-cugraph/nx_cugraph/interface.py @@ -224,9 +224,11 @@ def key(testpath): ) too_slow = "Too slow to run" + maybe_oom = "out of memory in CI" skip = { key("test_tree_isomorphism.py:test_positive"): too_slow, key("test_tree_isomorphism.py:test_negative"): too_slow, + key("test_efficiency.py:TestEfficiency.test_using_ego_graph"): maybe_oom, } for item in items: From f3eecdad957a362b5b1aa14b4fd27afc18d322ca Mon Sep 17 00:00:00 2001 From: Alex Barghi <105237337+alexbarghi-nv@users.noreply.github.com> Date: Mon, 20 Nov 2023 18:59:30 -0500 Subject: [PATCH 092/111] Support `drop_last` Argument in cuGraph-PyG Loader (#3995) Supports the `drop_last` argument in cuGraph-PyG for better compatibility with native PyG workflows. Closes #3949 Merge after #3985 Authors: - Alex Barghi (https://github.com/alexbarghi-nv) - Naim (https://github.com/naimnv) Approvers: - Tingyu Wang (https://github.com/tingyu66) - Brad Rees (https://github.com/BradReesWork) - Vibhu Jawa (https://github.com/VibhuJawa) URL: https://github.com/rapidsai/cugraph/pull/3995 --- .../cugraph_pyg/loader/cugraph_node_loader.py | 37 ++++++++++-------- .../cugraph_pyg/tests/test_cugraph_loader.py | 38 +++++++++++++++++++ 2 files changed, 60 insertions(+), 15 deletions(-) diff --git a/python/cugraph-pyg/cugraph_pyg/loader/cugraph_node_loader.py b/python/cugraph-pyg/cugraph_pyg/loader/cugraph_node_loader.py index ad8d22e255e..200a82b460b 100644 --- a/python/cugraph-pyg/cugraph_pyg/loader/cugraph_node_loader.py +++ b/python/cugraph-pyg/cugraph_pyg/loader/cugraph_node_loader.py @@ -52,7 +52,9 @@ def __init__( graph_store: CuGraphStore, input_nodes: InputNodes = None, batch_size: int = 0, + *, shuffle: bool = False, + drop_last: bool = True, edge_types: Sequence[Tuple[str]] = None, directory: Union[str, tempfile.TemporaryDirectory] = None, input_files: List[str] = None, @@ -209,26 +211,31 @@ def __init__( # Truncate if we can't evenly divide the input array stop = (len(input_nodes) // batch_size) * batch_size - input_nodes = input_nodes[:stop] + input_nodes, remainder = cupy.array_split(input_nodes, [stop]) # Split into batches - input_nodes = cupy.split(input_nodes, len(input_nodes) // batch_size) + input_nodes = cupy.split(input_nodes, max(len(input_nodes) // batch_size, 1)) + + if not drop_last: + input_nodes.append(remainder) self.__num_batches = 0 for batch_num, batch_i in enumerate(input_nodes): - self.__num_batches += 1 - bulk_sampler.add_batches( - cudf.DataFrame( - { - "start": batch_i, - "batch": cupy.full( - batch_size, batch_num + starting_batch_id, dtype="int32" - ), - } - ), - start_col_name="start", - batch_col_name="batch", - ) + batch_len = len(batch_i) + if batch_len > 0: + self.__num_batches += 1 + bulk_sampler.add_batches( + cudf.DataFrame( + { + "start": batch_i, + "batch": cupy.full( + batch_len, batch_num + starting_batch_id, dtype="int32" + ), + } + ), + start_col_name="start", + batch_col_name="batch", + ) bulk_sampler.flush() self.__input_files = iter( diff --git a/python/cugraph-pyg/cugraph_pyg/tests/test_cugraph_loader.py b/python/cugraph-pyg/cugraph_pyg/tests/test_cugraph_loader.py index 27b73bf7d35..9813fa933ee 100644 --- a/python/cugraph-pyg/cugraph_pyg/tests/test_cugraph_loader.py +++ b/python/cugraph-pyg/cugraph_pyg/tests/test_cugraph_loader.py @@ -456,6 +456,44 @@ def test_cugraph_loader_e2e_csc(framework: str): @pytest.mark.skipif(isinstance(torch, MissingModule), reason="torch not available") +@pytest.mark.parametrize("drop_last", [True, False]) +def test_drop_last(drop_last): + N = {"N": 10} + G = { + ("N", "e", "N"): torch.stack( + [torch.tensor([0, 1, 2, 3, 4]), torch.tensor([5, 6, 7, 8, 9])] + ) + } + F = FeatureStore(backend="torch") + F.add_data(torch.arange(10), "N", "z") + + store = CuGraphStore(F, G, N) + with tempfile.TemporaryDirectory() as dir: + loader = CuGraphNeighborLoader( + (store, store), + input_nodes=torch.tensor([0, 1, 2, 3, 4]), + num_neighbors=[1], + batch_size=2, + shuffle=False, + drop_last=drop_last, + batches_per_partition=1, + directory=dir, + ) + + t = torch.tensor([]) + for batch in loader: + t = torch.concat([t, batch["N"].z]) + + t = t.tolist() + + files = os.listdir(dir) + assert len(files) == 2 if drop_last else 3 + assert "batch=0-0.parquet" in files + assert "batch=1-1.parquet" in files + if not drop_last: + assert "batch=2-2.parquet" in files + + @pytest.mark.parametrize("directory", ["local", "temp"]) def test_load_directory( karate_gnn: Tuple[ From 6b3d3e3f06044c86f26d9773b6daf7ab68857321 Mon Sep 17 00:00:00 2001 From: Joseph Nke <76006812+jnke2016@users.noreply.github.com> Date: Mon, 20 Nov 2023 19:44:48 -0600 Subject: [PATCH 093/111] Enable parallel mode (#3875) This PR enables parallel mode Closes https://github.com/rapidsai/graph_dl/issues/328 Authors: - Joseph Nke (https://github.com/jnke2016) - Rick Ratzel (https://github.com/rlratzel) Approvers: - Chuck Hastings (https://github.com/ChuckHastings) - Naim (https://github.com/naimnv) - Brad Rees (https://github.com/BradReesWork) - Rick Ratzel (https://github.com/rlratzel) - Jake Awe (https://github.com/AyodeAwe) URL: https://github.com/rapidsai/cugraph/pull/3875 --- cpp/CMakeLists.txt | 2 + .../cugraph/detail/collect_comm_wrapper.hpp | 42 +++ cpp/include/cugraph_c/graph_functions.h | 47 ++- cpp/src/c_api/allgather.cpp | 282 ++++++++++++++ cpp/src/c_api/extract_ego.cpp | 2 + cpp/src/c_api/induced_subgraph.cpp | 3 + cpp/src/c_api/induced_subgraph_result.cpp | 24 ++ cpp/src/c_api/induced_subgraph_result.hpp | 4 +- cpp/src/c_api/legacy_k_truss.cpp | 3 + cpp/src/detail/collect_comm_wrapper.cu | 57 +++ dependencies.yaml | 3 +- python/cugraph/cugraph/structure/__init__.py | 5 + .../cugraph/structure/replicate_edgelist.py | 351 ++++++++++++++++++ .../community/test_induced_subgraph_mg.py | 2 +- .../internals/test_replicate_edgelist_mg.py | 128 +++++++ python/cugraph/pyproject.toml | 1 + .../pylibcugraph/pylibcugraph/CMakeLists.txt | 1 + python/pylibcugraph/pylibcugraph/__init__.py | 2 + .../_cugraph_c/graph_functions.pxd | 26 +- .../pylibcugraph/replicate_edgelist.pyx | 202 ++++++++++ 20 files changed, 1182 insertions(+), 5 deletions(-) create mode 100644 cpp/include/cugraph/detail/collect_comm_wrapper.hpp create mode 100644 cpp/src/c_api/allgather.cpp create mode 100644 cpp/src/detail/collect_comm_wrapper.cu create mode 100644 python/cugraph/cugraph/structure/replicate_edgelist.py create mode 100644 python/cugraph/cugraph/tests/internals/test_replicate_edgelist_mg.py create mode 100644 python/pylibcugraph/pylibcugraph/replicate_edgelist.pyx diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index 3e867643041..626d62cffa5 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -192,6 +192,7 @@ set(CUGRAPH_SOURCES src/detail/shuffle_vertex_pairs.cu src/detail/collect_local_vertex_values.cu src/detail/groupby_and_count.cu + src/detail/collect_comm_wrapper.cu src/sampling/random_walks_mg.cu src/community/detail/common_methods_mg.cu src/community/detail/common_methods_sg.cu @@ -443,6 +444,7 @@ add_library(cugraph_c src/c_api/labeling_result.cpp src/c_api/weakly_connected_components.cpp src/c_api/strongly_connected_components.cpp + src/c_api/allgather.cpp src/c_api/legacy_k_truss.cpp ) add_library(cugraph::cugraph_c ALIAS cugraph_c) diff --git a/cpp/include/cugraph/detail/collect_comm_wrapper.hpp b/cpp/include/cugraph/detail/collect_comm_wrapper.hpp new file mode 100644 index 00000000000..b791c593f41 --- /dev/null +++ b/cpp/include/cugraph/detail/collect_comm_wrapper.hpp @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2023, NVIDIA CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include +#include + +#include + +namespace cugraph { +namespace detail { + +/** + * @brief Gather the span of data from all ranks and broadcast the combined data to all ranks. + * + * @param[in] handle RAFT handle object to encapsulate resources (e.g. CUDA stream, communicator, + * and handles to various CUDA libraries) to run graph algorithms. + * @param[in] comm Raft comms that manages underlying NCCL comms handles across the ranks. + * @param[in] d_input The span of data to perform the 'allgatherv'. + * + * @return A vector containing the combined data of all ranks. + */ +template +rmm::device_uvector device_allgatherv(raft::handle_t const& handle, + raft::comms::comms_t const& comm, + raft::device_span d_input); + +} // namespace detail +} // namespace cugraph diff --git a/cpp/include/cugraph_c/graph_functions.h b/cpp/include/cugraph_c/graph_functions.h index 655324df284..19b69922fa5 100644 --- a/cpp/include/cugraph_c/graph_functions.h +++ b/cpp/include/cugraph_c/graph_functions.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, NVIDIA CORPORATION. + * Copyright (c) 2022-2023, NVIDIA CORPORATION. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -136,6 +136,24 @@ cugraph_type_erased_device_array_view_t* cugraph_induced_subgraph_get_destinatio cugraph_type_erased_device_array_view_t* cugraph_induced_subgraph_get_edge_weights( cugraph_induced_subgraph_result_t* induced_subgraph); +/** + * @brief Get the edge ids + * + * @param [in] induced_subgraph Opaque pointer to induced subgraph + * @return type erased array view of edge ids + */ +cugraph_type_erased_device_array_view_t* cugraph_induced_subgraph_get_edge_ids( + cugraph_induced_subgraph_result_t* induced_subgraph); + +/** + * @brief Get the edge types + * + * @param [in] induced_subgraph Opaque pointer to induced subgraph + * @return type erased array view of edge types + */ +cugraph_type_erased_device_array_view_t* cugraph_induced_subgraph_get_edge_type_ids( + cugraph_induced_subgraph_result_t* induced_subgraph); + /** * @brief Get the subgraph offsets * @@ -184,6 +202,33 @@ cugraph_error_code_t cugraph_extract_induced_subgraph( cugraph_induced_subgraph_result_t** result, cugraph_error_t** error); +// FIXME: Rename the return type +/** + * @brief Gather edgelist + * + * This function collects the edgelist from all ranks and stores the combine edgelist + * in each rank + * + * @param [in] handle Handle for accessing resources. + * @param [in] src Device array containing the source vertex ids. + * @param [in] dst Device array containing the destination vertex ids + * @param [in] weights Optional device array containing the edge weights + * @param [in] edge_ids Optional device array containing the edge ids for each edge. + * @param [in] edge_type_ids Optional device array containing the edge types for each edge + * @param [out] result Opaque pointer to gathered edgelist result + * @param [out] error Pointer to an error object storing details of any error. Will + * be populated if error code is not CUGRAPH_SUCCESS + * @return error code + */ +cugraph_error_code_t cugraph_allgather(const cugraph_resource_handle_t* handle, + const cugraph_type_erased_device_array_view_t* src, + const cugraph_type_erased_device_array_view_t* dst, + const cugraph_type_erased_device_array_view_t* weights, + const cugraph_type_erased_device_array_view_t* edge_ids, + const cugraph_type_erased_device_array_view_t* edge_type_ids, + cugraph_induced_subgraph_result_t** result, + cugraph_error_t** error); + #ifdef __cplusplus } #endif diff --git a/cpp/src/c_api/allgather.cpp b/cpp/src/c_api/allgather.cpp new file mode 100644 index 00000000000..7ef401aa6b7 --- /dev/null +++ b/cpp/src/c_api/allgather.cpp @@ -0,0 +1,282 @@ +/* + * Copyright (c) 2023, NVIDIA CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace { + +struct create_allgather_functor : public cugraph::c_api::abstract_functor { + raft::handle_t const& handle_; + cugraph::c_api::cugraph_type_erased_device_array_view_t const* src_; + cugraph::c_api::cugraph_type_erased_device_array_view_t const* dst_; + cugraph::c_api::cugraph_type_erased_device_array_view_t const* weights_; + cugraph::c_api::cugraph_type_erased_device_array_view_t const* edge_ids_; + cugraph::c_api::cugraph_type_erased_device_array_view_t const* edge_type_ids_; + cugraph::c_api::cugraph_induced_subgraph_result_t* result_{}; + + create_allgather_functor( + raft::handle_t const& handle, + cugraph::c_api::cugraph_type_erased_device_array_view_t const* src, + cugraph::c_api::cugraph_type_erased_device_array_view_t const* dst, + cugraph::c_api::cugraph_type_erased_device_array_view_t const* weights, + cugraph::c_api::cugraph_type_erased_device_array_view_t const* edge_ids, + cugraph::c_api::cugraph_type_erased_device_array_view_t const* edge_type_ids) + : abstract_functor(), + handle_(handle), + src_(src), + dst_(dst), + weights_(weights), + edge_ids_(edge_ids), + edge_type_ids_(edge_type_ids) + { + } + + template + void operator()() + { + std::optional> edgelist_srcs{std::nullopt}; + if (src_) { + edgelist_srcs = rmm::device_uvector(src_->size_, handle_.get_stream()); + raft::copy( + edgelist_srcs->data(), src_->as_type(), src_->size_, handle_.get_stream()); + } + + std::optional> edgelist_dsts{std::nullopt}; + if (dst_) { + edgelist_dsts = rmm::device_uvector(dst_->size_, handle_.get_stream()); + raft::copy( + edgelist_dsts->data(), dst_->as_type(), dst_->size_, handle_.get_stream()); + } + + std::optional> edgelist_weights{std::nullopt}; + if (weights_) { + edgelist_weights = rmm::device_uvector(weights_->size_, handle_.get_stream()); + raft::copy(edgelist_weights->data(), + weights_->as_type(), + weights_->size_, + handle_.get_stream()); + } + + std::optional> edgelist_ids{std::nullopt}; + if (edge_ids_) { + edgelist_ids = rmm::device_uvector(edge_ids_->size_, handle_.get_stream()); + raft::copy( + edgelist_ids->data(), edge_ids_->as_type(), edge_ids_->size_, handle_.get_stream()); + } + + std::optional> edgelist_type_ids{std::nullopt}; + if (edge_type_ids_) { + edgelist_type_ids = + rmm::device_uvector(edge_type_ids_->size_, handle_.get_stream()); + raft::copy(edgelist_type_ids->data(), + edge_type_ids_->as_type(), + edge_type_ids_->size_, + handle_.get_stream()); + } + + auto& comm = handle_.get_comms(); + + if (edgelist_srcs) { + edgelist_srcs = cugraph::detail::device_allgatherv( + handle_, + comm, + raft::device_span(edgelist_srcs->data(), edgelist_srcs->size())); + } + + if (edgelist_dsts) { + edgelist_dsts = cugraph::detail::device_allgatherv( + handle_, + comm, + raft::device_span(edgelist_dsts->data(), edgelist_dsts->size())); + } + + rmm::device_uvector edge_offsets(2, handle_.get_stream()); + + std::vector h_edge_offsets{ + {0, edgelist_srcs ? edgelist_srcs->size() : edgelist_weights->size()}}; + raft::update_device( + edge_offsets.data(), h_edge_offsets.data(), h_edge_offsets.size(), handle_.get_stream()); + + cugraph::c_api::cugraph_induced_subgraph_result_t* result = NULL; + + if (edgelist_weights) { + edgelist_weights = cugraph::detail::device_allgatherv( + handle_, + comm, + raft::device_span(edgelist_weights->data(), edgelist_weights->size())); + } + + if (edgelist_ids) { + edgelist_ids = cugraph::detail::device_allgatherv( + handle_, comm, raft::device_span(edgelist_ids->data(), edgelist_ids->size())); + } + + if (edgelist_type_ids) { + edgelist_type_ids = + cugraph::detail::device_allgatherv(handle_, + comm, + raft::device_span( + edgelist_type_ids->data(), edgelist_type_ids->size())); + } + + result = new cugraph::c_api::cugraph_induced_subgraph_result_t{ + edgelist_srcs + ? new cugraph::c_api::cugraph_type_erased_device_array_t(*edgelist_srcs, src_->type_) + : NULL, + edgelist_dsts + ? new cugraph::c_api::cugraph_type_erased_device_array_t(*edgelist_dsts, dst_->type_) + : NULL, + edgelist_weights + ? new cugraph::c_api::cugraph_type_erased_device_array_t(*edgelist_weights, weights_->type_) + : NULL, + edgelist_ids + ? new cugraph::c_api::cugraph_type_erased_device_array_t(*edgelist_ids, edge_ids_->type_) + : NULL, + edgelist_type_ids ? new cugraph::c_api::cugraph_type_erased_device_array_t( + *edgelist_type_ids, edge_type_ids_->type_) + : NULL, + new cugraph::c_api::cugraph_type_erased_device_array_t(edge_offsets, + cugraph_data_type_id_t::SIZE_T)}; + + result_ = reinterpret_cast(result); + } +}; + +} // namespace + +extern "C" cugraph_error_code_t cugraph_allgather( + const cugraph_resource_handle_t* handle, + const cugraph_type_erased_device_array_view_t* src, + const cugraph_type_erased_device_array_view_t* dst, + const cugraph_type_erased_device_array_view_t* weights, + const cugraph_type_erased_device_array_view_t* edge_ids, + const cugraph_type_erased_device_array_view_t* edge_type_ids, + cugraph_induced_subgraph_result_t** edgelist, + cugraph_error_t** error) +{ + *edgelist = nullptr; + *error = nullptr; + + auto p_handle = reinterpret_cast(handle); + auto p_src = + reinterpret_cast(src); + auto p_dst = + reinterpret_cast(dst); + auto p_weights = + reinterpret_cast(weights); + + auto p_edge_ids = + reinterpret_cast(edge_ids); + + auto p_edge_type_ids = + reinterpret_cast(edge_type_ids); + + CAPI_EXPECTS((dst == nullptr) || (src == nullptr) || p_src->size_ == p_dst->size_, + CUGRAPH_INVALID_INPUT, + "Invalid input arguments: src size != dst size.", + *error); + CAPI_EXPECTS((dst == nullptr) || (src == nullptr) || p_src->type_ == p_dst->type_, + CUGRAPH_INVALID_INPUT, + "Invalid input arguments: src type != dst type.", + *error); + + CAPI_EXPECTS((weights == nullptr) || (src == nullptr) || (p_weights->size_ == p_src->size_), + CUGRAPH_INVALID_INPUT, + "Invalid input arguments: src size != weights size.", + *error); + + cugraph_data_type_id_t vertex_type; + cugraph_data_type_id_t edge_type; + cugraph_data_type_id_t weight_type; + cugraph_data_type_id_t edge_type_id_type; + + if (src != nullptr) { + vertex_type = p_src->type_; + } else { + vertex_type = cugraph_data_type_id_t::INT32; + } + + if (weights != nullptr) { + weight_type = p_weights->type_; + } else { + weight_type = cugraph_data_type_id_t::FLOAT32; + } + + if (edge_ids != nullptr) { + edge_type = p_edge_ids->type_; + } else { + edge_type = cugraph_data_type_id_t::INT32; + } + + if (edge_type_ids != nullptr) { + edge_type_id_type = p_edge_type_ids->type_; + } else { + edge_type_id_type = cugraph_data_type_id_t::INT32; + } + + if (src != nullptr) { + CAPI_EXPECTS((edge_ids == nullptr) || (p_edge_ids->size_ == p_src->size_), + CUGRAPH_INVALID_INPUT, + "Invalid input arguments: src size != edge id prop size", + *error); + + CAPI_EXPECTS((edge_type_ids == nullptr) || (p_edge_type_ids->size_ == p_src->size_), + CUGRAPH_INVALID_INPUT, + "Invalid input arguments: src size != edge type prop size", + *error); + } + + constexpr bool multi_gpu = false; + constexpr bool store_transposed = false; + + ::create_allgather_functor functor( + *p_handle->handle_, p_src, p_dst, p_weights, p_edge_ids, p_edge_type_ids); + + try { + cugraph::c_api::vertex_dispatcher( + vertex_type, edge_type, weight_type, edge_type_id_type, store_transposed, multi_gpu, functor); + + if (functor.error_code_ != CUGRAPH_SUCCESS) { + *error = reinterpret_cast(functor.error_.release()); + return functor.error_code_; + } + + *edgelist = reinterpret_cast(functor.result_); + } catch (std::exception const& ex) { + *error = reinterpret_cast(new cugraph::c_api::cugraph_error_t{ex.what()}); + return CUGRAPH_UNKNOWN_ERROR; + } + + return CUGRAPH_SUCCESS; +} diff --git a/cpp/src/c_api/extract_ego.cpp b/cpp/src/c_api/extract_ego.cpp index 8f510b79023..931d58b5185 100644 --- a/cpp/src/c_api/extract_ego.cpp +++ b/cpp/src/c_api/extract_ego.cpp @@ -135,6 +135,8 @@ struct extract_ego_functor : public cugraph::c_api::abstract_functor { new cugraph::c_api::cugraph_type_erased_device_array_t(dst, graph_->vertex_type_), wgt ? new cugraph::c_api::cugraph_type_erased_device_array_t(*wgt, graph_->weight_type_) : NULL, + NULL, + NULL, new cugraph::c_api::cugraph_type_erased_device_array_t(edge_offsets, cugraph_data_type_id_t::SIZE_T)}; } diff --git a/cpp/src/c_api/induced_subgraph.cpp b/cpp/src/c_api/induced_subgraph.cpp index a1bbcb60825..ac56301e231 100644 --- a/cpp/src/c_api/induced_subgraph.cpp +++ b/cpp/src/c_api/induced_subgraph.cpp @@ -147,11 +147,14 @@ struct induced_subgraph_functor : public cugraph::c_api::abstract_functor { graph_view.vertex_partition_range_lasts(), do_expensive_check_); + // FIXME: Add support for edge_id and edge_type_id. result_ = new cugraph::c_api::cugraph_induced_subgraph_result_t{ new cugraph::c_api::cugraph_type_erased_device_array_t(src, graph_->vertex_type_), new cugraph::c_api::cugraph_type_erased_device_array_t(dst, graph_->vertex_type_), wgt ? new cugraph::c_api::cugraph_type_erased_device_array_t(*wgt, graph_->weight_type_) : NULL, + NULL, + NULL, new cugraph::c_api::cugraph_type_erased_device_array_t(graph_offsets, SIZE_T)}; } } diff --git a/cpp/src/c_api/induced_subgraph_result.cpp b/cpp/src/c_api/induced_subgraph_result.cpp index b9ad0e0d66f..5226872d404 100644 --- a/cpp/src/c_api/induced_subgraph_result.cpp +++ b/cpp/src/c_api/induced_subgraph_result.cpp @@ -45,6 +45,28 @@ extern "C" cugraph_type_erased_device_array_view_t* cugraph_induced_subgraph_get internal_pointer->wgt_->view()); } +extern "C" cugraph_type_erased_device_array_view_t* cugraph_induced_subgraph_get_edge_ids( + cugraph_induced_subgraph_result_t* induced_subgraph) +{ + auto internal_pointer = + reinterpret_cast(induced_subgraph); + return (internal_pointer->edge_ids_ == nullptr) + ? NULL + : reinterpret_cast( + internal_pointer->edge_ids_->view()); +} + +extern "C" cugraph_type_erased_device_array_view_t* cugraph_induced_subgraph_get_edge_type_ids( + cugraph_induced_subgraph_result_t* induced_subgraph) +{ + auto internal_pointer = + reinterpret_cast(induced_subgraph); + return (internal_pointer->edge_type_ids_ == nullptr) + ? NULL + : reinterpret_cast( + internal_pointer->edge_type_ids_->view()); +} + extern "C" cugraph_type_erased_device_array_view_t* cugraph_induced_subgraph_get_subgraph_offsets( cugraph_induced_subgraph_result_t* induced_subgraph) { @@ -62,6 +84,8 @@ extern "C" void cugraph_induced_subgraph_result_free( delete internal_pointer->src_; delete internal_pointer->dst_; delete internal_pointer->wgt_; + delete internal_pointer->edge_ids_; + delete internal_pointer->edge_type_ids_; delete internal_pointer->subgraph_offsets_; delete internal_pointer; } diff --git a/cpp/src/c_api/induced_subgraph_result.hpp b/cpp/src/c_api/induced_subgraph_result.hpp index acc99b617f4..6f02a699605 100644 --- a/cpp/src/c_api/induced_subgraph_result.hpp +++ b/cpp/src/c_api/induced_subgraph_result.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, NVIDIA CORPORATION. + * Copyright (c) 2023, NVIDIA CORPORATION. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,6 +25,8 @@ struct cugraph_induced_subgraph_result_t { cugraph_type_erased_device_array_t* src_{}; cugraph_type_erased_device_array_t* dst_{}; cugraph_type_erased_device_array_t* wgt_{}; + cugraph_type_erased_device_array_t* edge_ids_{}; + cugraph_type_erased_device_array_t* edge_type_ids_{}; cugraph_type_erased_device_array_t* subgraph_offsets_{}; }; diff --git a/cpp/src/c_api/legacy_k_truss.cpp b/cpp/src/c_api/legacy_k_truss.cpp index 90e0894783a..90db9fc133c 100644 --- a/cpp/src/c_api/legacy_k_truss.cpp +++ b/cpp/src/c_api/legacy_k_truss.cpp @@ -123,12 +123,15 @@ struct k_truss_functor : public cugraph::c_api::abstract_functor { raft::update_device( edge_offsets.data(), h_edge_offsets.data(), h_edge_offsets.size(), handle_.get_stream()); + // FIXME: Add support for edge_id and edge_type_id. result_ = new cugraph::c_api::cugraph_induced_subgraph_result_t{ new cugraph::c_api::cugraph_type_erased_device_array_t(result_src, graph_->vertex_type_), new cugraph::c_api::cugraph_type_erased_device_array_t(result_dst, graph_->vertex_type_), wgt ? new cugraph::c_api::cugraph_type_erased_device_array_t(*result_wgt, graph_->weight_type_) : NULL, + NULL, + NULL, new cugraph::c_api::cugraph_type_erased_device_array_t(edge_offsets, cugraph_data_type_id_t::SIZE_T)}; } diff --git a/cpp/src/detail/collect_comm_wrapper.cu b/cpp/src/detail/collect_comm_wrapper.cu new file mode 100644 index 00000000000..7ce2241c677 --- /dev/null +++ b/cpp/src/detail/collect_comm_wrapper.cu @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2023, NVIDIA CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include +#include +#include + +#include +#include + +namespace cugraph { +namespace detail { + +template +rmm::device_uvector device_allgatherv(raft::handle_t const& handle, + raft::comms::comms_t const& comm, + raft::device_span d_input) +{ + auto gathered_v = cugraph::device_allgatherv(handle, comm, d_input); + + return gathered_v; +} + +template rmm::device_uvector device_allgatherv(raft::handle_t const& handle, + raft::comms::comms_t const& comm, + raft::device_span d_input); + +template rmm::device_uvector device_allgatherv(raft::handle_t const& handle, + raft::comms::comms_t const& comm, + raft::device_span d_input); + +template rmm::device_uvector device_allgatherv(raft::handle_t const& handle, + raft::comms::comms_t const& comm, + raft::device_span d_input); + +template rmm::device_uvector device_allgatherv(raft::handle_t const& handle, + raft::comms::comms_t const& comm, + raft::device_span d_input); + +} // namespace detail +} // namespace cugraph diff --git a/dependencies.yaml b/dependencies.yaml index a89acd9288b..2c0918ad117 100644 --- a/dependencies.yaml +++ b/dependencies.yaml @@ -380,6 +380,7 @@ dependencies: - &dask rapids-dask-dependency==23.12.* - &dask_cuda dask-cuda==23.12.* - &numba numba>=0.57 + - &numpy numpy>=1.21 - &ucx_py ucx-py==0.35.* - output_types: conda packages: @@ -399,7 +400,7 @@ dependencies: - output_types: [conda, pyproject] packages: - networkx>=3.0 - - &numpy numpy>=1.21 + - *numpy python_run_cugraph_dgl: common: - output_types: [conda, pyproject] diff --git a/python/cugraph/cugraph/structure/__init__.py b/python/cugraph/cugraph/structure/__init__.py index d7e0ff62358..94f34fd23f3 100644 --- a/python/cugraph/cugraph/structure/__init__.py +++ b/python/cugraph/cugraph/structure/__init__.py @@ -25,6 +25,11 @@ ) from cugraph.structure.number_map import NumberMap from cugraph.structure.symmetrize import symmetrize, symmetrize_df, symmetrize_ddf +from cugraph.structure.replicate_edgelist import ( + replicate_edgelist, + replicate_cudf_dataframe, + replicate_cudf_series, +) from cugraph.structure.convert_matrix import ( from_edgelist, from_cudf_edgelist, diff --git a/python/cugraph/cugraph/structure/replicate_edgelist.py b/python/cugraph/cugraph/structure/replicate_edgelist.py new file mode 100644 index 00000000000..d413e50e485 --- /dev/null +++ b/python/cugraph/cugraph/structure/replicate_edgelist.py @@ -0,0 +1,351 @@ +# Copyright (c) 2023, NVIDIA CORPORATION. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import dask_cudf +import cudf +from dask.distributed import wait, default_client +import numpy as np +from pylibcugraph import ( + ResourceHandle, + replicate_edgelist as pylibcugraph_replicate_edgelist, +) + +from cugraph.dask.common.part_utils import ( + get_persisted_df_worker_map, + persist_dask_df_equal_parts_per_worker, +) + +import dask +import cupy as cp +import cugraph.dask.comms.comms as Comms +from typing import Union, Tuple + + +# FIXME: Convert it to a general-purpose util function +def _convert_to_cudf(cp_arrays: Tuple[cp.ndarray], col_names: list) -> cudf.DataFrame: + """ + Creates a cudf Dataframe from cupy arrays + """ + src, dst, wgt, edge_id, edge_type_id, _ = cp_arrays + gathered_edgelist_df = cudf.DataFrame() + gathered_edgelist_df[col_names[0]] = src + gathered_edgelist_df[col_names[1]] = dst + if wgt is not None: + gathered_edgelist_df[col_names[2]] = wgt + if edge_id is not None: + gathered_edgelist_df[col_names[3]] = edge_id + if edge_type_id is not None: + gathered_edgelist_df[col_names[4]] = edge_type_id + + return gathered_edgelist_df + + +def _call_plc_replicate_edgelist( + sID: bytes, edgelist_df: cudf.DataFrame, col_names: list +) -> cudf.DataFrame: + edgelist_df = edgelist_df[0] + cp_arrays = pylibcugraph_replicate_edgelist( + resource_handle=ResourceHandle(Comms.get_handle(sID).getHandle()), + src_array=edgelist_df[col_names[0]], + dst_array=edgelist_df[col_names[1]], + weight_array=edgelist_df[col_names[2]] if len(col_names) > 2 else None, + edge_id_array=edgelist_df[col_names[3]] if len(col_names) > 3 else None, + edge_type_id_array=edgelist_df[col_names[4]] if len(col_names) > 4 else None, + ) + return _convert_to_cudf(cp_arrays, col_names) + + +def _call_plc_replicate_dataframe(sID: bytes, df: cudf.DataFrame) -> cudf.DataFrame: + df = df[0] + df_replicated = cudf.DataFrame() + for col_name in df.columns: + cp_array = pylibcugraph_replicate_edgelist( + resource_handle=ResourceHandle(Comms.get_handle(sID).getHandle()), + src_array=df[col_name] + if df[col_name].dtype in [np.int32, np.int64] + else None, + dst_array=None, + weight_array=df[col_name] + if df[col_name].dtype in [np.float32, np.float64] + else None, + edge_id_array=None, + edge_type_id_array=None, + ) + src, _, wgt, _, _, _ = cp_array + if src is not None: + df_replicated[col_name] = src + elif wgt is not None: + df_replicated[col_name] = wgt + + return df_replicated + + +def _call_plc_replicate_series(sID: bytes, series: cudf.Series) -> cudf.Series: + series = series[0] + series_replicated = cudf.Series() + cp_array = pylibcugraph_replicate_edgelist( + resource_handle=ResourceHandle(Comms.get_handle(sID).getHandle()), + src_array=series if series.dtype in [np.int32, np.int64] else None, + dst_array=None, + weight_array=series if series.dtype in [np.float32, np.float64] else None, + edge_id_array=None, + edge_type_id_array=None, + ) + src, _, wgt, _, _, _ = cp_array + if src is not None: + series_replicated = cudf.Series(src) + elif wgt is not None: + series_replicated = cudf.Series(wgt) + + return series_replicated + + +def _mg_call_plc_replicate( + client: dask.distributed.client.Client, + sID: bytes, + dask_object: dict, + input_type: str, + col_names: list, +) -> Union[dask_cudf.DataFrame, dask_cudf.Series]: + + if input_type == "dataframe": + result = [ + client.submit( + _call_plc_replicate_dataframe, + sID, + edata, + workers=[w], + allow_other_workers=False, + pure=False, + ) + for w, edata in dask_object.items() + ] + elif input_type == "dataframe": + result = [ + client.submit( + _call_plc_replicate_series, + sID, + edata, + workers=[w], + allow_other_workers=False, + pure=False, + ) + for w, edata in dask_object.items() + ] + elif input_type == "edgelist": + result = [ + client.submit( + _call_plc_replicate_edgelist, + sID, + edata, + col_names, + workers=[w], + allow_other_workers=False, + pure=False, + ) + for w, edata in dask_object.items() + ] + + ddf = dask_cudf.from_delayed(result, verify_meta=False).persist() + wait(ddf) + wait([r.release() for r in result]) + return ddf + + +def replicate_edgelist( + edgelist_ddf: Union[dask_cudf.DataFrame, cudf.DataFrame] = None, + source="src", + destination="dst", + weight=None, + edge_id=None, + edge_type=None, +) -> dask_cudf.DataFrame: + """ + Replicate edges across all GPUs + + Parameters + ---------- + + edgelist_ddf: cudf.DataFrame or dask_cudf.DataFrame + A DataFrame that contains edge information. + + source : str or array-like + source column name or array of column names + + destination : str or array-like + destination column name or array of column names + + weight : str, optional (default=None) + Name of the weight column in the input dataframe. + + edge_id : str, optional (default=None) + Name of the edge id column in the input dataframe. + + edge_type : str, optional (default=None) + Name of the edge type column in the input dataframe. + + Returns + ------- + df : dask_cudf.DataFrame + A distributed dataframe where each partition contains the + combined edgelist from all GPUs. If a cudf.DataFrame was passed + as input, the edgelist will be replicated across all the other + GPUs in the cluster. If as dask_cudf.DataFrame was passed as input, + each partition will be filled with the edges of all partitions + in the dask_cudf.DataFrame. + + """ + + _client = default_client() + + if isinstance(edgelist_ddf, cudf.DataFrame): + edgelist_ddf = dask_cudf.from_cudf( + edgelist_ddf, npartitions=len(Comms.get_workers()) + ) + col_names = [source, destination] + + if weight is not None: + col_names.append(weight) + if edge_id is not None: + col_names.append(edge_id) + if edge_type is not None: + col_names.append(edge_type) + + if not (set(col_names).issubset(set(edgelist_ddf.columns))): + raise ValueError( + "Invalid column names were provided: valid columns names are " + f"{edgelist_ddf.columns}" + ) + + edgelist_ddf = persist_dask_df_equal_parts_per_worker(edgelist_ddf, _client) + edgelist_ddf = get_persisted_df_worker_map(edgelist_ddf, _client) + + ddf = _mg_call_plc_replicate( + _client, + Comms.get_session_id(), + edgelist_ddf, + "edgelist", + col_names, + ) + + return ddf + + +def replicate_cudf_dataframe(cudf_dataframe): + """ + Replicate dataframe across all GPUs + + Parameters + ---------- + + cudf_dataframe: cudf.DataFrame or dask_cudf.DataFrame + + Returns + ------- + df : dask_cudf.DataFrame + A distributed dataframe where each partition contains the + combined dataframe from all GPUs. If a cudf.DataFrame was passed + as input, the dataframe will be replicated across all the other + GPUs in the cluster. If as dask_cudf.DataFrame was passed as input, + each partition will be filled with the datafame of all partitions + in the dask_cudf.DataFrame. + + """ + + supported_types = [np.int32, np.int64, np.float32, np.float64] + if not all(dtype in supported_types for dtype in cudf_dataframe.dtypes): + raise TypeError( + "The supported types are 'int32', 'int64', 'float32', 'float64'" + ) + + _client = default_client() + + if not isinstance(cudf_dataframe, dask_cudf.DataFrame): + if isinstance(cudf_dataframe, cudf.DataFrame): + df = dask_cudf.from_cudf( + cudf_dataframe, npartitions=len(Comms.get_workers()) + ) + elif not isinstance(cudf_dataframe, dask_cudf.DataFrame): + raise TypeError( + "The variable 'cudf_dataframe' must be of type " + f"'cudf/dask_cudf.dataframe', got type {type(cudf_dataframe)}" + ) + else: + df = cudf_dataframe + + df = persist_dask_df_equal_parts_per_worker(df, _client) + df = get_persisted_df_worker_map(df, _client) + + ddf = _mg_call_plc_replicate( + _client, + Comms.get_session_id(), + df, + "dataframe", + ) + + return ddf + + +def replicate_cudf_series(cudf_series): + """ + Replicate series across all GPUs + + Parameters + ---------- + + cudf_series: cudf.Series or dask_cudf.Series + + Returns + ------- + series : dask_cudf.Series + A distributed series where each partition contains the + combined series from all GPUs. If a cudf.Series was passed + as input, the Series will be replicated across all the other + GPUs in the cluster. If as dask_cudf.Series was passed as input, + each partition will be filled with the series of all partitions + in the dask_cudf.Series. + + """ + + supported_types = [np.int32, np.int64, np.float32, np.float64] + if cudf_series.dtype not in supported_types: + raise TypeError( + "The supported types are 'int32', 'int64', 'float32', 'float64'" + ) + + _client = default_client() + + if not isinstance(cudf_series, dask_cudf.Series): + if isinstance(cudf_series, cudf.Series): + series = dask_cudf.from_cudf( + cudf_series, npartitions=len(Comms.get_workers()) + ) + elif not isinstance(cudf_series, dask_cudf.Series): + raise TypeError( + "The variable 'cudf_series' must be of type " + f"'cudf/dask_cudf.series', got type {type(cudf_series)}" + ) + else: + series = cudf_series + + series = persist_dask_df_equal_parts_per_worker(series, _client) + series = get_persisted_df_worker_map(series, _client) + + series = _mg_call_plc_replicate( + _client, + Comms.get_session_id(), + series, + "series", + ) + + return series diff --git a/python/cugraph/cugraph/tests/community/test_induced_subgraph_mg.py b/python/cugraph/cugraph/tests/community/test_induced_subgraph_mg.py index d93fa3b547d..8d80611a54c 100644 --- a/python/cugraph/cugraph/tests/community/test_induced_subgraph_mg.py +++ b/python/cugraph/cugraph/tests/community/test_induced_subgraph_mg.py @@ -93,7 +93,7 @@ def input_expected_output(input_combo): srcs = G.view_edge_list()["0"] dsts = G.view_edge_list()["1"] vertices = cudf.concat([srcs, dsts]).drop_duplicates() - vertices = vertices.sample(num_seeds).astype("int32") + vertices = vertices.sample(num_seeds, replace=True).astype("int32") # print randomly sample n seeds from the graph print("\nvertices: \n", vertices) diff --git a/python/cugraph/cugraph/tests/internals/test_replicate_edgelist_mg.py b/python/cugraph/cugraph/tests/internals/test_replicate_edgelist_mg.py new file mode 100644 index 00000000000..3bdb5c079ef --- /dev/null +++ b/python/cugraph/cugraph/tests/internals/test_replicate_edgelist_mg.py @@ -0,0 +1,128 @@ +# Copyright (c) 2022-2023, NVIDIA CORPORATION. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import gc + +import pytest + +import dask_cudf +import numpy as np +from cugraph.testing import UNDIRECTED_DATASETS, karate_disjoint + +from cugraph.structure.replicate_edgelist import replicate_edgelist +from cudf.testing.testing import assert_frame_equal +from pylibcugraph.testing.utils import gen_fixture_params_product + + +# ============================================================================= +# Pytest Setup / Teardown - called for each test function +# ============================================================================= +def setup_function(): + gc.collect() + + +edgeWeightCol = "weights" +edgeIdCol = "edge_id" +edgeTypeCol = "edge_type" +srcCol = "src" +dstCol = "dst" + + +input_data = UNDIRECTED_DATASETS + [karate_disjoint] +datasets = [pytest.param(d) for d in input_data] + +fixture_params = gen_fixture_params_product( + (datasets, "graph_file"), + ([True, False], "distributed"), + ([True, False], "use_weights"), + ([True, False], "use_edge_ids"), + ([True, False], "use_edge_type_ids"), +) + + +@pytest.fixture(scope="module", params=fixture_params) +def input_combo(request): + """ + Simply return the current combination of params as a dictionary for use in + tests or other parameterized fixtures. + """ + return dict( + zip( + ( + "graph_file", + "use_weights", + "use_edge_ids", + "use_edge_type_ids", + "distributed", + ), + request.param, + ) + ) + + +# ============================================================================= +# Tests +# ============================================================================= +# @pytest.mark.skipif( +# is_single_gpu(), reason="skipping MG testing on Single GPU system" +# ) +@pytest.mark.mg +def test_mg_replicate_edgelist(dask_client, input_combo): + df = input_combo["graph_file"].get_edgelist() + distributed = input_combo["distributed"] + + use_weights = input_combo["use_weights"] + use_edge_ids = input_combo["use_edge_ids"] + use_edge_type_ids = input_combo["use_edge_type_ids"] + + columns = [srcCol, dstCol] + weight = None + edge_id = None + edge_type = None + + if use_weights: + df = df.rename(columns={"wgt": edgeWeightCol}) + columns.append(edgeWeightCol) + weight = edgeWeightCol + if use_edge_ids: + df = df.reset_index().rename(columns={"index": edgeIdCol}) + df[edgeIdCol] = df[edgeIdCol].astype(df[srcCol].dtype) + columns.append(edgeIdCol) + edge_id = edgeIdCol + if use_edge_type_ids: + df[edgeTypeCol] = np.random.randint(0, 10, size=len(df)) + df[edgeTypeCol] = df[edgeTypeCol].astype(df[srcCol].dtype) + columns.append(edgeTypeCol) + edge_type = edgeTypeCol + + if distributed: + # Distribute the edges across all ranks + num_workers = len(dask_client.scheduler_info()["workers"]) + df = dask_cudf.from_cudf(df, npartitions=num_workers) + ddf = replicate_edgelist( + df[columns], weight=weight, edge_id=edge_id, edge_type=edge_type + ) + + if distributed: + df = df.compute() + + for i in range(ddf.npartitions): + result_df = ( + ddf.get_partition(i) + .compute() + .sort_values([srcCol, dstCol]) + .reset_index(drop=True) + ) + expected_df = df[columns].sort_values([srcCol, dstCol]).reset_index(drop=True) + + assert_frame_equal(expected_df, result_df, check_dtype=False, check_like=True) diff --git a/python/cugraph/pyproject.toml b/python/cugraph/pyproject.toml index 319900b3de3..bd426291c8d 100644 --- a/python/cugraph/pyproject.toml +++ b/python/cugraph/pyproject.toml @@ -35,6 +35,7 @@ dependencies = [ "dask-cudf==23.12.*", "fsspec[http]>=0.6.0", "numba>=0.57", + "numpy>=1.21", "pylibcugraph==23.12.*", "raft-dask==23.12.*", "rapids-dask-dependency==23.12.*", diff --git a/python/pylibcugraph/pylibcugraph/CMakeLists.txt b/python/pylibcugraph/pylibcugraph/CMakeLists.txt index 6618c50122c..c2e22fc1ff7 100644 --- a/python/pylibcugraph/pylibcugraph/CMakeLists.txt +++ b/python/pylibcugraph/pylibcugraph/CMakeLists.txt @@ -56,6 +56,7 @@ set(cython_sources uniform_random_walks.pyx utils.pyx weakly_connected_components.pyx + replicate_edgelist.pyx ) set(linked_libraries cugraph::cugraph;cugraph::cugraph_c) diff --git a/python/pylibcugraph/pylibcugraph/__init__.py b/python/pylibcugraph/pylibcugraph/__init__.py index 30f1c2d0fb1..1d02498ea30 100644 --- a/python/pylibcugraph/pylibcugraph/__init__.py +++ b/python/pylibcugraph/pylibcugraph/__init__.py @@ -87,6 +87,8 @@ from pylibcugraph.generate_rmat_edgelists import generate_rmat_edgelists +from pylibcugraph.replicate_edgelist import replicate_edgelist + from pylibcugraph.k_truss_subgraph import k_truss_subgraph from pylibcugraph.jaccard_coefficients import jaccard_coefficients diff --git a/python/pylibcugraph/pylibcugraph/_cugraph_c/graph_functions.pxd b/python/pylibcugraph/pylibcugraph/_cugraph_c/graph_functions.pxd index f18e9848182..8b3a629956c 100644 --- a/python/pylibcugraph/pylibcugraph/_cugraph_c/graph_functions.pxd +++ b/python/pylibcugraph/pylibcugraph/_cugraph_c/graph_functions.pxd @@ -1,4 +1,4 @@ -# Copyright (c) 2022, NVIDIA CORPORATION. +# Copyright (c) 2022-2023, NVIDIA CORPORATION. # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at @@ -138,6 +138,16 @@ cdef extern from "cugraph_c/graph_functions.h": cugraph_induced_subgraph_result_t* induced_subgraph ) + cdef cugraph_type_erased_device_array_view_t* \ + cugraph_induced_subgraph_get_edge_ids( + cugraph_induced_subgraph_result_t* induced_subgraph + ) + + cdef cugraph_type_erased_device_array_view_t* \ + cugraph_induced_subgraph_get_edge_type_ids( + cugraph_induced_subgraph_result_t* induced_subgraph + ) + cdef cugraph_type_erased_device_array_view_t* \ cugraph_induced_subgraph_get_subgraph_offsets( cugraph_induced_subgraph_result_t* induced_subgraph @@ -158,3 +168,17 @@ cdef extern from "cugraph_c/graph_functions.h": cugraph_induced_subgraph_result_t** result, cugraph_error_t** error ) + + ########################################################################### + # allgather + cdef cugraph_error_code_t \ + cugraph_allgather( + const cugraph_resource_handle_t* handle, + const cugraph_type_erased_device_array_view_t* src, + const cugraph_type_erased_device_array_view_t* dst, + const cugraph_type_erased_device_array_view_t* weights, + const cugraph_type_erased_device_array_view_t* edge_ids, + const cugraph_type_erased_device_array_view_t* edge_type_ids, + cugraph_induced_subgraph_result_t** result, + cugraph_error_t** error + ) diff --git a/python/pylibcugraph/pylibcugraph/replicate_edgelist.pyx b/python/pylibcugraph/pylibcugraph/replicate_edgelist.pyx new file mode 100644 index 00000000000..3763d4bc69d --- /dev/null +++ b/python/pylibcugraph/pylibcugraph/replicate_edgelist.pyx @@ -0,0 +1,202 @@ +# Copyright (c) 2022-2023, NVIDIA CORPORATION. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Have cython use python 3 syntax +# cython: language_level = 3 + + +from pylibcugraph._cugraph_c.resource_handle cimport ( + cugraph_resource_handle_t, +) +from pylibcugraph._cugraph_c.error cimport ( + cugraph_error_code_t, + cugraph_error_t, +) +from pylibcugraph._cugraph_c.array cimport ( + cugraph_type_erased_device_array_view_t, + cugraph_type_erased_device_array_view_free, +) +from pylibcugraph._cugraph_c.graph_functions cimport ( + cugraph_allgather, + cugraph_induced_subgraph_result_t, + cugraph_induced_subgraph_get_sources, + cugraph_induced_subgraph_get_destinations, + cugraph_induced_subgraph_get_edge_weights, + cugraph_induced_subgraph_get_edge_ids, + cugraph_induced_subgraph_get_edge_type_ids, + cugraph_induced_subgraph_get_subgraph_offsets, + cugraph_induced_subgraph_result_free, +) +from pylibcugraph.resource_handle cimport ( + ResourceHandle, +) +from pylibcugraph.utils cimport ( + assert_success, + assert_CAI_type, + copy_to_cupy_array, + create_cugraph_type_erased_device_array_view_from_py_obj +) + + +def replicate_edgelist(ResourceHandle resource_handle, + src_array, + dst_array, + weight_array, + edge_id_array, + edge_type_id_array): + """ + Replicate edges across all GPUs + + Parameters + ---------- + resource_handle : ResourceHandle + Handle to the underlying device resources needed for referencing data + and running algorithms. + + src_array : device array type, optional + Device array containing the vertex identifiers of the source of each + directed edge. The order of the array corresponds to the ordering of the + dst_array, where the ith item in src_array and the ith item in dst_array + define the ith edge of the graph. + + dst_array : device array type, optional + Device array containing the vertex identifiers of the destination of + each directed edge. The order of the array corresponds to the ordering + of the src_array, where the ith item in src_array and the ith item in + dst_array define the ith edge of the graph. + + weight_array : device array type, optional + Device array containing the weight values of each directed edge. The + order of the array corresponds to the ordering of the src_array and + dst_array arrays, where the ith item in weight_array is the weight value + of the ith edge of the graph. + + edge_id_array : device array type, optional + Device array containing the edge id values of each directed edge. The + order of the array corresponds to the ordering of the src_array and + dst_array arrays, where the ith item in edge_id_array is the id value + of the ith edge of the graph. + + edge_type_id_array : device array type, optional + Device array containing the edge type id values of each directed edge. The + order of the array corresponds to the ordering of the src_array and + dst_array arrays, where the ith item in edge_type_id_array is the type id + value of the ith edge of the graph. + + Returns + ------- + return cupy arrays of 'src' and/or 'dst' and/or 'weight'and/or 'edge_id' + and/or 'edge_type_id'. + """ + assert_CAI_type(src_array, "src_array", True) + assert_CAI_type(dst_array, "dst_array", True) + assert_CAI_type(weight_array, "weight_array", True) + assert_CAI_type(edge_id_array, "edge_id_array", True) + assert_CAI_type(edge_type_id_array, "edge_type_id_array", True) + cdef cugraph_resource_handle_t* c_resource_handle_ptr = \ + resource_handle.c_resource_handle_ptr + + cdef cugraph_induced_subgraph_result_t* result_ptr + cdef cugraph_error_code_t error_code + cdef cugraph_error_t* error_ptr + + cdef cugraph_type_erased_device_array_view_t* srcs_view_ptr = \ + create_cugraph_type_erased_device_array_view_from_py_obj(src_array) + + cdef cugraph_type_erased_device_array_view_t* dsts_view_ptr = \ + create_cugraph_type_erased_device_array_view_from_py_obj(dst_array) + + + cdef cugraph_type_erased_device_array_view_t* weights_view_ptr = \ + create_cugraph_type_erased_device_array_view_from_py_obj(weight_array) + + cdef cugraph_type_erased_device_array_view_t* edge_ids_view_ptr = \ + create_cugraph_type_erased_device_array_view_from_py_obj(edge_id_array) + + cdef cugraph_type_erased_device_array_view_t* edge_type_ids_view_ptr = \ + create_cugraph_type_erased_device_array_view_from_py_obj(edge_type_id_array) + + error_code = cugraph_allgather(c_resource_handle_ptr, + srcs_view_ptr, + dsts_view_ptr, + weights_view_ptr, + edge_ids_view_ptr, + edge_type_ids_view_ptr, + &result_ptr, + &error_ptr) + assert_success(error_code, error_ptr, "replicate_edgelist") + # Extract individual device array pointers from result and copy to cupy + # arrays for returning. + cdef cugraph_type_erased_device_array_view_t* sources_ptr + if src_array is not None: + sources_ptr = cugraph_induced_subgraph_get_sources(result_ptr) + cdef cugraph_type_erased_device_array_view_t* destinations_ptr + if dst_array is not None: + destinations_ptr = cugraph_induced_subgraph_get_destinations(result_ptr) + cdef cugraph_type_erased_device_array_view_t* edge_weights_ptr = \ + cugraph_induced_subgraph_get_edge_weights(result_ptr) + cdef cugraph_type_erased_device_array_view_t* edge_ids_ptr = \ + cugraph_induced_subgraph_get_edge_ids(result_ptr) + cdef cugraph_type_erased_device_array_view_t* edge_type_ids_ptr = \ + cugraph_induced_subgraph_get_edge_type_ids(result_ptr) + cdef cugraph_type_erased_device_array_view_t* subgraph_offsets_ptr = \ + cugraph_induced_subgraph_get_subgraph_offsets(result_ptr) + + # FIXME: Get ownership of the result data instead of performing a copy + # for perfomance improvement + + cupy_sources = None + cupy_destinations = None + cupy_edge_weights = None + cupy_edge_ids = None + cupy_edge_type_ids = None + + if src_array is not None: + cupy_sources = copy_to_cupy_array( + c_resource_handle_ptr, sources_ptr) + + if dst_array is not None: + cupy_destinations = copy_to_cupy_array( + c_resource_handle_ptr, destinations_ptr) + + if weight_array is not None: + cupy_edge_weights = copy_to_cupy_array( + c_resource_handle_ptr, edge_weights_ptr) + + if edge_id_array is not None: + cupy_edge_ids = copy_to_cupy_array( + c_resource_handle_ptr, edge_ids_ptr) + + if edge_type_id_array is not None: + cupy_edge_type_ids = copy_to_cupy_array( + c_resource_handle_ptr, edge_type_ids_ptr) + + cupy_subgraph_offsets = copy_to_cupy_array( + c_resource_handle_ptr, subgraph_offsets_ptr) + + # Free pointer + cugraph_induced_subgraph_result_free(result_ptr) + if src_array is not None: + cugraph_type_erased_device_array_view_free(srcs_view_ptr) + if dst_array is not None: + cugraph_type_erased_device_array_view_free(dsts_view_ptr) + if weight_array is not None: + cugraph_type_erased_device_array_view_free(weights_view_ptr) + if edge_id_array is not None: + cugraph_type_erased_device_array_view_free(edge_ids_view_ptr) + if edge_type_id_array is not None: + cugraph_type_erased_device_array_view_free(edge_type_ids_view_ptr) + + return (cupy_sources, cupy_destinations, + cupy_edge_weights, cupy_edge_ids, + cupy_edge_type_ids, cupy_subgraph_offsets) From 3f1c7b5ef8cd296aa5ad012cf855abdd2dfeb84b Mon Sep 17 00:00:00 2001 From: Chuck Hastings <45364586+ChuckHastings@users.noreply.github.com> Date: Mon, 20 Nov 2023 21:17:24 -0500 Subject: [PATCH 094/111] Update C API graph creation function signatures (#3982) Updating the C API graph creation functions to support the following: * Add support for isolated vertices * Add MG optimization to support multiple device arrays per rank as input and concatenate them internally * Add MG optimization to internally compute the number of edges via allreduce rather than requiring it as an input parameter (this can be expensive to compute in python) This PR implements these features. Some simple tests exist to check for isolate vertices (by running pagerank which generates a different result if the graph has isolated vertices). A simple test for multiple input arrays exists for the MG case. Closes #3947 Closes #3974 Authors: - Chuck Hastings (https://github.com/ChuckHastings) - Naim (https://github.com/naimnv) Approvers: - Naim (https://github.com/naimnv) - Joseph Nke (https://github.com/jnke2016) - Seunghwa Kang (https://github.com/seunghwak) URL: https://github.com/rapidsai/cugraph/pull/3982 --- cpp/CMakeLists.txt | 2 + cpp/include/cugraph/graph_functions.hpp | 67 +++ cpp/include/cugraph_c/graph.h | 193 ++++++- cpp/include/cugraph_c/resource_handle.h | 12 + cpp/src/c_api/graph_mg.cpp | 511 +++++++++++------- cpp/src/c_api/graph_sg.cpp | 170 +++++- cpp/src/c_api/resource_handle.cpp | 9 +- cpp/src/structure/detail/structure_utils.cuh | 61 ++- cpp/src/structure/remove_multi_edges.cu | 92 ++++ cpp/src/structure/remove_multi_edges_impl.cuh | 310 +++++++++++ cpp/src/structure/remove_self_loops.cu | 92 ++++ cpp/src/structure/remove_self_loops_impl.cuh | 94 ++++ cpp/tests/c_api/create_graph_test.c | 498 ++++++++++++++++- cpp/tests/c_api/mg_create_graph_test.c | 400 +++++++++++++- 14 files changed, 2265 insertions(+), 246 deletions(-) create mode 100644 cpp/src/structure/remove_multi_edges.cu create mode 100644 cpp/src/structure/remove_multi_edges_impl.cuh create mode 100644 cpp/src/structure/remove_self_loops.cu create mode 100644 cpp/src/structure/remove_self_loops_impl.cuh diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index 626d62cffa5..836d5569ef7 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -202,6 +202,8 @@ set(CUGRAPH_SOURCES src/community/detail/mis_mg.cu src/detail/utility_wrappers.cu src/structure/graph_view_mg.cu + src/structure/remove_self_loops.cu + src/structure/remove_multi_edges.cu src/utilities/path_retrieval.cu src/structure/legacy/graph.cu src/linear_assignment/legacy/hungarian.cu diff --git a/cpp/include/cugraph/graph_functions.hpp b/cpp/include/cugraph/graph_functions.hpp index 5c1e9d5311f..6a75a420bf8 100644 --- a/cpp/include/cugraph/graph_functions.hpp +++ b/cpp/include/cugraph/graph_functions.hpp @@ -973,4 +973,71 @@ renumber_sampled_edgelist( label_offsets, bool do_expensive_check = false); +/** + * @brief Remove self loops from an edge list + * + * @tparam vertex_t Type of vertex identifiers. Needs to be an integral type. + * @tparam edge_t Type of edge identifiers. Needs to be an integral type. + * @tparam weight_t Type of edge weight. Currently float and double are supported. + * @tparam edge_type_t Type of edge type. Needs to be an integral type. + * + * @param handle RAFT handle object to encapsulate resources (e.g. CUDA stream, communicator, and + * handles to various CUDA libraries) to run graph algorithms. + * @param edgelist_srcs List of source vertex ids + * @param edgelist_dsts List of destination vertex ids + * @param edgelist_weights Optional list of edge weights + * @param edgelist_edge_ids Optional list of edge ids + * @param edgelist_edge_types Optional list of edge types + * @return Tuple of vectors storing edge sources, destinations, optional weights, + * optional edge ids, optional edge types. + */ +template +std::tuple, + rmm::device_uvector, + std::optional>, + std::optional>, + std::optional>> +remove_self_loops(raft::handle_t const& handle, + rmm::device_uvector&& edgelist_srcs, + rmm::device_uvector&& edgelist_dsts, + std::optional>&& edgelist_weights, + std::optional>&& edgelist_edge_ids, + std::optional>&& edgelist_edge_types); + +/** + * @brief Remove all but one edge when a multi-edge exists. Note that this function does not use + * stable methods. When a multi-edge exists, one of the edges will remain, there is no + * guarantee on which one will remain. + * + * In an MG context it is assumed that edges have been shuffled to the proper GPU, + * in which case any multi-edges will be on the same GPU. + * + * @tparam vertex_t Type of vertex identifiers. Needs to be an integral type. + * @tparam edge_t Type of edge identifiers. Needs to be an integral type. + * @tparam weight_t Type of edge weight. Currently float and double are supported. + * @tparam edge_type_t Type of edge type. Needs to be an integral type. + * + * @param handle RAFT handle object to encapsulate resources (e.g. CUDA stream, communicator, and + * handles to various CUDA libraries) to run graph algorithms. + * @param edgelist_srcs List of source vertex ids + * @param edgelist_dsts List of destination vertex ids + * @param edgelist_weights Optional list of edge weights + * @param edgelist_edge_ids Optional list of edge ids + * @param edgelist_edge_types Optional list of edge types + * @return Tuple of vectors storing edge sources, destinations, optional weights, + * optional edge ids, optional edge types. + */ +template +std::tuple, + rmm::device_uvector, + std::optional>, + std::optional>, + std::optional>> +remove_multi_edges(raft::handle_t const& handle, + rmm::device_uvector&& edgelist_srcs, + rmm::device_uvector&& edgelist_dsts, + std::optional>&& edgelist_weights, + std::optional>&& edgelist_edge_ids, + std::optional>&& edgelist_edge_types); + } // namespace cugraph diff --git a/cpp/include/cugraph_c/graph.h b/cpp/include/cugraph_c/graph.h index e910d8b1244..88176a9c1b6 100644 --- a/cpp/include/cugraph_c/graph.h +++ b/cpp/include/cugraph_c/graph.h @@ -35,10 +35,11 @@ typedef struct { bool_t is_multigraph; } cugraph_graph_properties_t; -// FIXME: Add support for specifying isolated vertices /** * @brief Construct an SG graph * + * @deprecated This API will be deleted, use cugraph_graph_create_sg instead + * * @param [in] handle Handle for accessing resources * @param [in] properties Properties of the constructed graph * @param [in] src Device array containing the source vertex ids. @@ -51,11 +52,11 @@ typedef struct { argument that can be NULL if edge types are not used. * @param [in] store_transposed If true create the graph initially in transposed format * @param [in] renumber If true, renumber vertices to make an efficient data structure. - * If false, do not renumber. Renumbering is required if the vertices are not sequential - * integer values from 0 to num_vertices. + * If false, do not renumber. Renumbering enables some significant optimizations within + * the graph primitives library, so it is strongly encouraged. Renumbering is required if + * the vertices are not sequential integer values from 0 to num_vertices. * @param [in] do_expensive_check If true, do expensive checks to validate the input data * is consistent with software assumptions. If false bypass these checks. - * @param [in] properties Properties of the graph * @param [out] graph A pointer to the graph object * @param [out] error Pointer to an error object storing details of any error. Will * be populated if error code is not CUGRAPH_SUCCESS @@ -76,9 +77,63 @@ cugraph_error_code_t cugraph_sg_graph_create( cugraph_graph_t** graph, cugraph_error_t** error); +/** + * @brief Construct an SG graph + * + * @param [in] handle Handle for accessing resources + * @param [in] properties Properties of the constructed graph + * @param [in] vertices Optional device array containing a list of vertex ids + * (specify NULL if we should create vertex ids from the + * unique contents of @p src and @p dst) + * @param [in] src Device array containing the source vertex ids. + * @param [in] dst Device array containing the destination vertex ids + * @param [in] weights Device array containing the edge weights. Note that an unweighted + * graph can be created by passing weights == NULL. + * @param [in] edge_ids Device array containing the edge ids for each edge. Optional + argument that can be NULL if edge ids are not used. + * @param [in] edge_type_ids Device array containing the edge types for each edge. Optional + argument that can be NULL if edge types are not used. + * @param [in] store_transposed If true create the graph initially in transposed format + * @param [in] renumber If true, renumber vertices to make an efficient data structure. + * If false, do not renumber. Renumbering enables some significant optimizations within + * the graph primitives library, so it is strongly encouraged. Renumbering is required if + * the vertices are not sequential integer values from 0 to num_vertices. + * @param [in] drop_self_loops If true, drop any self loops that exist in the provided edge list. + * @param [in] drop_multi_edges If true, drop any multi edges that exist in the provided edge list. + * Note that setting this flag will arbitrarily select one instance of a multi edge to be the + * edge that survives. If the edges have properties that should be honored (e.g. sum the + weights, + * or take the maximum weight), the caller should do that on not rely on this flag. + * @param [in] do_expensive_check If true, do expensive checks to validate the input data + * is consistent with software assumptions. If false bypass these checks. + * @param [out] graph A pointer to the graph object + * @param [out] error Pointer to an error object storing details of any error. Will + * be populated if error code is not CUGRAPH_SUCCESS + * + * @return error code + */ +cugraph_error_code_t cugraph_graph_create_sg( + const cugraph_resource_handle_t* handle, + const cugraph_graph_properties_t* properties, + const cugraph_type_erased_device_array_view_t* vertices, + const cugraph_type_erased_device_array_view_t* src, + const cugraph_type_erased_device_array_view_t* dst, + const cugraph_type_erased_device_array_view_t* weights, + const cugraph_type_erased_device_array_view_t* edge_ids, + const cugraph_type_erased_device_array_view_t* edge_type_ids, + bool_t store_transposed, + bool_t renumber, + bool_t drop_self_loops, + bool_t drop_multi_edges, + bool_t do_expensive_check, + cugraph_graph_t** graph, + cugraph_error_t** error); + /** * @brief Construct an SG graph from a CSR input * + * @deprecated This API will be deleted, use cugraph_graph_create_sg_from_csr instead + * * @param [in] handle Handle for accessing resources * @param [in] properties Properties of the constructed graph * @param [in] offsets Device array containing the CSR offsets array @@ -91,11 +146,11 @@ cugraph_error_code_t cugraph_sg_graph_create( argument that can be NULL if edge types are not used. * @param [in] store_transposed If true create the graph initially in transposed format * @param [in] renumber If true, renumber vertices to make an efficient data structure. - * If false, do not renumber. Renumbering is required if the vertices are not sequential - * integer values from 0 to num_vertices. + * If false, do not renumber. Renumbering enables some significant optimizations within + * the graph primitives library, so it is strongly encouraged. Renumbering is required if + * the vertices are not sequential integer values from 0 to num_vertices. * @param [in] do_expensive_check If true, do expensive checks to validate the input data * is consistent with software assumptions. If false bypass these checks. - * @param [in] properties Properties of the graph * @param [out] graph A pointer to the graph object * @param [out] error Pointer to an error object storing details of any error. Will * be populated if error code is not CUGRAPH_SUCCESS @@ -117,18 +172,50 @@ cugraph_error_code_t cugraph_sg_graph_create_from_csr( cugraph_error_t** error); /** - * @brief Destroy an SG graph + * @brief Construct an SG graph from a CSR input * - * @param [in] graph A pointer to the graph object to destroy + * @param [in] handle Handle for accessing resources + * @param [in] properties Properties of the constructed graph + * @param [in] offsets Device array containing the CSR offsets array + * @param [in] indices Device array containing the destination vertex ids + * @param [in] weights Device array containing the edge weights. Note that an unweighted + * graph can be created by passing weights == NULL. + * @param [in] edge_ids Device array containing the edge ids for each edge. Optional + argument that can be NULL if edge ids are not used. + * @param [in] edge_type_ids Device array containing the edge types for each edge. Optional + argument that can be NULL if edge types are not used. + * @param [in] store_transposed If true create the graph initially in transposed format + * @param [in] renumber If true, renumber vertices to make an efficient data structure. + * If false, do not renumber. Renumbering enables some significant optimizations within + * the graph primitives library, so it is strongly encouraged. Renumbering is required if + * the vertices are not sequential integer values from 0 to num_vertices. + * @param [in] do_expensive_check If true, do expensive checks to validate the input data + * is consistent with software assumptions. If false bypass these checks. + * @param [out] graph A pointer to the graph object + * @param [out] error Pointer to an error object storing details of any error. Will + * be populated if error code is not CUGRAPH_SUCCESS + * + * @return error code */ -// FIXME: This should probably just be cugraph_graph_free -// but didn't want to confuse with original cugraph_free_graph -void cugraph_sg_graph_free(cugraph_graph_t* graph); +cugraph_error_code_t cugraph_graph_create_sg_from_csr( + const cugraph_resource_handle_t* handle, + const cugraph_graph_properties_t* properties, + const cugraph_type_erased_device_array_view_t* offsets, + const cugraph_type_erased_device_array_view_t* indices, + const cugraph_type_erased_device_array_view_t* weights, + const cugraph_type_erased_device_array_view_t* edge_ids, + const cugraph_type_erased_device_array_view_t* edge_type_ids, + bool_t store_transposed, + bool_t renumber, + bool_t do_expensive_check, + cugraph_graph_t** graph, + cugraph_error_t** error); -// FIXME: Add support for specifying isolated vertices /** * @brief Construct an MG graph * + * @deprecated This API will be deleted, use cugraph_graph_create_mg instead + * * @param [in] handle Handle for accessing resources * @param [in] properties Properties of the constructed graph * @param [in] src Device array containing the source vertex ids @@ -165,13 +252,89 @@ cugraph_error_code_t cugraph_mg_graph_create( cugraph_graph_t** graph, cugraph_error_t** error); +/** + * @brief Construct an MG graph + * + * @param [in] handle Handle for accessing resources + * @param [in] properties Properties of the constructed graph + * @param [in] vertices List of device arrays containing the unique vertex ids. + * If NULL we will construct this internally using the unique + * entries specified in src and dst + * All entries in this list will be concatenated on this GPU + * into a single array. + * @param [in] src List of device array containing the source vertex ids + * All entries in this list will be concatenated on this GPU + * into a single array. + * @param [in] dst List of device array containing the destination vertex ids + * All entries in this list will be concatenated on this GPU + * into a single array. + * @param [in] weights List of device array containing the edge weights. Note that an + * unweighted graph can be created by passing weights == NULL. If a weighted graph is to be + * created, the weights device array should be created on each rank, but the pointer can be NULL and + * the size 0 if there are no inputs provided by this rank All entries in this list will be + * concatenated on this GPU into a single array. + * @param [in] edge_ids List of device array containing the edge ids for each edge. Optional + * argument that can be NULL if edge ids are not used. + * All entries in this list will be concatenated on this GPU + * into a single array. + * @param [in] edge_type_ids List of device array containing the edge types for each edge. + * Optional argument that can be NULL if edge types are not used. All entries in this list will be + * concatenated on this GPU into a single array. + * @param [in] store_transposed If true create the graph initially in transposed format + * @param [in] num_arrays The number of arrays specified in @p vertices, @p src, @p dst, @p + * weights, @p edge_ids and @p edge_type_ids + * @param [in] drop_self_loops If true, drop any self loops that exist in the provided edge list. + * @param [in] drop_multi_edges If true, drop any multi edges that exist in the provided edge list. + * Note that setting this flag will arbitrarily select one instance of a multi edge to be the + * edge that survives. If the edges have properties that should be honored (e.g. sum the + * weights, or take the maximum weight), the caller should do that on not rely on this flag. + * @param [in] do_expensive_check If true, do expensive checks to validate the input data + * is consistent with software assumptions. If false bypass these checks. + * @param [out] graph A pointer to the graph object + * @param [out] error Pointer to an error object storing details of any error. Will + * be populated if error code is not CUGRAPH_SUCCESS + * @return error code + */ +cugraph_error_code_t cugraph_graph_create_mg( + cugraph_resource_handle_t const* handle, + cugraph_graph_properties_t const* properties, + cugraph_type_erased_device_array_view_t const* const* vertices, + cugraph_type_erased_device_array_view_t const* const* src, + cugraph_type_erased_device_array_view_t const* const* dst, + cugraph_type_erased_device_array_view_t const* const* weights, + cugraph_type_erased_device_array_view_t const* const* edge_ids, + cugraph_type_erased_device_array_view_t const* const* edge_type_ids, + bool_t store_transposed, + size_t num_arrays, + bool_t drop_self_loops, + bool_t drop_multi_edges, + bool_t do_expensive_check, + cugraph_graph_t** graph, + cugraph_error_t** error); + +/** + * @brief Destroy an graph + * + * @param [in] graph A pointer to the graph object to destroy + */ +void cugraph_graph_free(cugraph_graph_t* graph); + +/** + * @brief Destroy an SG graph + * + * @deprecated This API will be deleted, use cugraph_graph_free instead + * + * @param [in] graph A pointer to the graph object to destroy + */ +void cugraph_sg_graph_free(cugraph_graph_t* graph); + /** * @brief Destroy an MG graph * + * @deprecated This API will be deleted, use cugraph_graph_free instead + * * @param [in] graph A pointer to the graph object to destroy */ -// FIXME: This should probably just be cugraph_graph_free -// but didn't want to confuse with original cugraph_free_graph void cugraph_mg_graph_free(cugraph_graph_t* graph); /** diff --git a/cpp/include/cugraph_c/resource_handle.h b/cpp/include/cugraph_c/resource_handle.h index a239c24afe9..0e45102aae2 100644 --- a/cpp/include/cugraph_c/resource_handle.h +++ b/cpp/include/cugraph_c/resource_handle.h @@ -57,6 +57,18 @@ typedef struct cugraph_resource_handle_ { */ cugraph_resource_handle_t* cugraph_create_resource_handle(void* raft_handle); +/** + * @brief get comm_size from resource handle + * + * If the resource handle has been configured for multi-gpu, this will return + * the comm_size for this cluster. If the resource handle has not been configured for + * multi-gpu this will always return 1. + * + * @param [in] handle Handle for accessing resources + * @return comm_size + */ +int cugraph_resource_handle_get_comm_size(const cugraph_resource_handle_t* handle); + /** * @brief get rank from resource handle * diff --git a/cpp/src/c_api/graph_mg.cpp b/cpp/src/c_api/graph_mg.cpp index f50c7c08fb6..5413949e3a3 100644 --- a/cpp/src/c_api/graph_mg.cpp +++ b/cpp/src/c_api/graph_mg.cpp @@ -31,40 +31,85 @@ namespace { +template +rmm::device_uvector concatenate( + raft::handle_t const& handle, + cugraph::c_api::cugraph_type_erased_device_array_view_t const* const* values, + size_t num_arrays) +{ + size_t num_values = std::transform_reduce( + values, values + num_arrays, size_t{0}, std::plus{}, [](auto p) { return p->size_; }); + + rmm::device_uvector results(num_values, handle.get_stream()); + size_t concat_pos{0}; + + for (size_t i = 0; i < num_arrays; ++i) { + raft::copy(results.data() + concat_pos, + values[i]->as_type(), + values[i]->size_, + handle.get_stream()); + concat_pos += values[i]->size_; + } + + return results; +} + struct create_graph_functor : public cugraph::c_api::abstract_functor { raft::handle_t const& handle_; cugraph_graph_properties_t const* properties_; - cugraph::c_api::cugraph_type_erased_device_array_view_t const* src_; - cugraph::c_api::cugraph_type_erased_device_array_view_t const* dst_; - cugraph::c_api::cugraph_type_erased_device_array_view_t const* weights_; - cugraph::c_api::cugraph_type_erased_device_array_view_t const* edge_ids_; - cugraph::c_api::cugraph_type_erased_device_array_view_t const* edge_type_ids_; - bool_t renumber_; - bool_t check_; + cugraph_data_type_id_t vertex_type_; cugraph_data_type_id_t edge_type_; + cugraph_data_type_id_t weight_type_; + cugraph_data_type_id_t edge_type_id_type_; + cugraph::c_api::cugraph_type_erased_device_array_view_t const* const* vertices_; + cugraph::c_api::cugraph_type_erased_device_array_view_t const* const* src_; + cugraph::c_api::cugraph_type_erased_device_array_view_t const* const* dst_; + cugraph::c_api::cugraph_type_erased_device_array_view_t const* const* weights_; + cugraph::c_api::cugraph_type_erased_device_array_view_t const* const* edge_ids_; + cugraph::c_api::cugraph_type_erased_device_array_view_t const* const* edge_type_ids_; + size_t num_arrays_; + bool_t renumber_; + bool_t drop_self_loops_; + bool_t drop_multi_edges_; + bool_t do_expensive_check_; cugraph::c_api::cugraph_graph_t* result_{}; - create_graph_functor(raft::handle_t const& handle, - cugraph_graph_properties_t const* properties, - cugraph::c_api::cugraph_type_erased_device_array_view_t const* src, - cugraph::c_api::cugraph_type_erased_device_array_view_t const* dst, - cugraph::c_api::cugraph_type_erased_device_array_view_t const* weights, - cugraph::c_api::cugraph_type_erased_device_array_view_t const* edge_ids, - cugraph::c_api::cugraph_type_erased_device_array_view_t const* edge_type_ids, - bool_t renumber, - bool_t check, - cugraph_data_type_id_t edge_type) + create_graph_functor( + raft::handle_t const& handle, + cugraph_graph_properties_t const* properties, + cugraph_data_type_id_t vertex_type, + cugraph_data_type_id_t edge_type, + cugraph_data_type_id_t weight_type, + cugraph_data_type_id_t edge_type_id_type, + cugraph::c_api::cugraph_type_erased_device_array_view_t const* const* vertices, + cugraph::c_api::cugraph_type_erased_device_array_view_t const* const* src, + cugraph::c_api::cugraph_type_erased_device_array_view_t const* const* dst, + cugraph::c_api::cugraph_type_erased_device_array_view_t const* const* weights, + cugraph::c_api::cugraph_type_erased_device_array_view_t const* const* edge_ids, + cugraph::c_api::cugraph_type_erased_device_array_view_t const* const* edge_type_ids, + size_t num_arrays, + bool_t renumber, + bool_t drop_self_loops, + bool_t drop_multi_edges, + bool_t do_expensive_check) : abstract_functor(), properties_(properties), + vertex_type_(vertex_type), + edge_type_(edge_type), + weight_type_(weight_type), + edge_type_id_type_(edge_type_id_type), handle_(handle), + vertices_(vertices), src_(src), dst_(dst), weights_(weights), edge_ids_(edge_ids), edge_type_ids_(edge_type_ids), + num_arrays_(num_arrays), renumber_(renumber), - check_(check), - edge_type_(edge_type) + drop_self_loops_(drop_self_loops), + drop_multi_edges_(drop_multi_edges), + do_expensive_check_(do_expensive_check) { } @@ -96,49 +141,27 @@ struct create_graph_functor : public cugraph::c_api::abstract_functor { edge_type_id_t>> new_edge_types{std::nullopt}; - rmm::device_uvector edgelist_srcs(src_->size_, handle_.get_stream()); - rmm::device_uvector edgelist_dsts(dst_->size_, handle_.get_stream()); + std::optional> vertex_list = + vertices_ ? std::make_optional(concatenate(handle_, vertices_, num_arrays_)) + : std::nullopt; - raft::copy( - edgelist_srcs.data(), src_->as_type(), src_->size_, handle_.get_stream()); - raft::copy( - edgelist_dsts.data(), dst_->as_type(), dst_->size_, handle_.get_stream()); + rmm::device_uvector edgelist_srcs = + concatenate(handle_, src_, num_arrays_); + rmm::device_uvector edgelist_dsts = + concatenate(handle_, dst_, num_arrays_); std::optional> edgelist_weights = - weights_ - ? std::make_optional(rmm::device_uvector(weights_->size_, handle_.get_stream())) - : std::nullopt; - - if (edgelist_weights) { - raft::copy(edgelist_weights->data(), - weights_->as_type(), - weights_->size_, - handle_.get_stream()); - } + weights_ ? std::make_optional(concatenate(handle_, weights_, num_arrays_)) + : std::nullopt; std::optional> edgelist_edge_ids = - edge_ids_ - ? std::make_optional(rmm::device_uvector(edge_ids_->size_, handle_.get_stream())) - : std::nullopt; - - if (edgelist_edge_ids) { - raft::copy(edgelist_edge_ids->data(), - edge_ids_->as_type(), - edge_ids_->size_, - handle_.get_stream()); - } + edge_ids_ ? std::make_optional(concatenate(handle_, edge_ids_, num_arrays_)) + : std::nullopt; std::optional> edgelist_edge_types = - edge_type_ids_ ? std::make_optional(rmm::device_uvector( - edge_type_ids_->size_, handle_.get_stream())) - : std::nullopt; - - if (edgelist_edge_types) { - raft::copy(edgelist_edge_types->data(), - edge_type_ids_->as_type(), - edge_type_ids_->size_, - handle_.get_stream()); - } + edge_type_ids_ + ? std::make_optional(concatenate(handle_, edge_type_ids_, num_arrays_)) + : std::nullopt; std::tie(store_transposed ? edgelist_dsts : edgelist_srcs, store_transposed ? edgelist_srcs : edgelist_dsts, @@ -153,6 +176,11 @@ struct create_graph_functor : public cugraph::c_api::abstract_functor { std::move(edgelist_edge_ids), std::move(edgelist_edge_types)); + if (vertex_list) { + vertex_list = cugraph::detail::shuffle_ext_vertices_to_local_gpu_by_vertex_partitioning( + handle_, std::move(*vertex_list)); + } + auto graph = new cugraph::graph_t(handle_); rmm::device_uvector* number_map = @@ -170,6 +198,28 @@ struct create_graph_functor : public cugraph::c_api::abstract_functor { cugraph::graph_view_t, edge_type_id_t>(handle_); + if (drop_self_loops_) { + std::tie( + edgelist_srcs, edgelist_dsts, edgelist_weights, edgelist_edge_ids, edgelist_edge_types) = + cugraph::remove_self_loops(handle_, + std::move(edgelist_srcs), + std::move(edgelist_dsts), + std::move(edgelist_weights), + std::move(edgelist_edge_ids), + std::move(edgelist_edge_types)); + } + + if (drop_multi_edges_) { + std::tie( + edgelist_srcs, edgelist_dsts, edgelist_weights, edgelist_edge_ids, edgelist_edge_types) = + cugraph::remove_multi_edges(handle_, + std::move(edgelist_srcs), + std::move(edgelist_dsts), + std::move(edgelist_weights), + std::move(edgelist_edge_ids), + std::move(edgelist_edge_types)); + } + std::tie(*graph, new_edge_weights, new_edge_ids, new_edge_types, new_number_map) = cugraph::create_graph_from_edgelist( handle_, - std::nullopt, + std::move(vertex_list), std::move(edgelist_srcs), std::move(edgelist_dsts), std::move(edgelist_weights), @@ -187,7 +237,7 @@ struct create_graph_functor : public cugraph::c_api::abstract_functor { std::move(edgelist_edge_types), cugraph::graph_properties_t{properties_->is_symmetric, properties_->is_multigraph}, renumber_, - check_); + do_expensive_check_); if (renumber_) { *number_map = std::move(new_number_map.value()); @@ -204,90 +254,39 @@ struct create_graph_functor : public cugraph::c_api::abstract_functor { if (new_edge_types) { *edge_types = std::move(new_edge_types.value()); } // Set up return - auto result = new cugraph::c_api::cugraph_graph_t{ - src_->type_, - edge_type_, - weights_ ? weights_->type_ : cugraph_data_type_id_t::FLOAT32, - edge_type_ids_ ? edge_type_ids_->type_ : cugraph_data_type_id_t::INT32, - store_transposed, - multi_gpu, - graph, - number_map, - new_edge_weights ? edge_weights : nullptr, - new_edge_ids ? edge_ids : nullptr, - new_edge_types ? edge_types : nullptr}; + auto result = new cugraph::c_api::cugraph_graph_t{vertex_type_, + edge_type_, + weight_type_, + edge_type_id_type_, + store_transposed, + multi_gpu, + graph, + number_map, + new_edge_weights ? edge_weights : nullptr, + new_edge_ids ? edge_ids : nullptr, + new_edge_types ? edge_types : nullptr}; result_ = reinterpret_cast(result); } } }; -struct destroy_graph_functor : public cugraph::c_api::abstract_functor { - void* graph_; - void* number_map_; - void* edge_weights_; - void* edge_ids_; - void* edge_types_; - - destroy_graph_functor( - void* graph, void* number_map, void* edge_weights, void* edge_ids, void* edge_types) - : abstract_functor(), - graph_(graph), - number_map_(number_map), - edge_weights_(edge_weights), - edge_ids_(edge_ids), - edge_types_(edge_types) - { - } - - template - void operator()() - { - auto internal_graph_pointer = - reinterpret_cast*>(graph_); - - delete internal_graph_pointer; - - auto internal_number_map_pointer = - reinterpret_cast*>(number_map_); - - delete internal_number_map_pointer; - - auto internal_edge_weight_pointer = reinterpret_cast< - cugraph::edge_property_t, - weight_t>*>(edge_weights_); - if (internal_edge_weight_pointer) { delete internal_edge_weight_pointer; } - - auto internal_edge_id_pointer = reinterpret_cast< - cugraph::edge_property_t, - edge_t>*>(edge_ids_); - if (internal_edge_id_pointer) { delete internal_edge_id_pointer; } - - auto internal_edge_type_pointer = reinterpret_cast< - cugraph::edge_property_t, - edge_type_id_t>*>(edge_types_); - if (internal_edge_type_pointer) { delete internal_edge_type_pointer; } - } -}; - } // namespace -extern "C" cugraph_error_code_t cugraph_mg_graph_create( - const cugraph_resource_handle_t* handle, - const cugraph_graph_properties_t* properties, - const cugraph_type_erased_device_array_view_t* src, - const cugraph_type_erased_device_array_view_t* dst, - const cugraph_type_erased_device_array_view_t* weights, - const cugraph_type_erased_device_array_view_t* edge_ids, - const cugraph_type_erased_device_array_view_t* edge_type_ids, +extern "C" cugraph_error_code_t cugraph_graph_create_mg( + cugraph_resource_handle_t const* handle, + cugraph_graph_properties_t const* properties, + cugraph_type_erased_device_array_view_t const* const* vertices, + cugraph_type_erased_device_array_view_t const* const* src, + cugraph_type_erased_device_array_view_t const* const* dst, + cugraph_type_erased_device_array_view_t const* const* weights, + cugraph_type_erased_device_array_view_t const* const* edge_ids, + cugraph_type_erased_device_array_view_t const* const* edge_type_ids, bool_t store_transposed, - size_t num_edges, - bool_t check, + size_t num_arrays, + bool_t drop_self_loops, + bool_t drop_multi_edges, + bool_t do_expensive_check, cugraph_graph_t** graph, cugraph_error_t** error) { @@ -298,87 +297,198 @@ extern "C" cugraph_error_code_t cugraph_mg_graph_create( *error = nullptr; auto p_handle = reinterpret_cast(handle); + auto p_vertices = + reinterpret_cast( + vertices); auto p_src = - reinterpret_cast(src); + reinterpret_cast(src); auto p_dst = - reinterpret_cast(dst); + reinterpret_cast(dst); auto p_weights = - reinterpret_cast(weights); + reinterpret_cast( + weights); auto p_edge_ids = - reinterpret_cast(edge_ids); + reinterpret_cast( + edge_ids); auto p_edge_type_ids = - reinterpret_cast(edge_type_ids); + reinterpret_cast( + edge_type_ids); + + size_t local_num_edges{0}; + + // + // Determine the type of vertex, weight, edge_type_id across + // multiple input arrays and acros multiple GPUs. Also compute + // the number of edges so we can determine what type to use for + // edge_t + // + cugraph_data_type_id_t vertex_type{cugraph_data_type_id_t::NTYPES}; + cugraph_data_type_id_t weight_type{cugraph_data_type_id_t::NTYPES}; + + for (size_t i = 0; i < num_arrays; ++i) { + CAPI_EXPECTS(p_src[i]->size_ == p_dst[i]->size_, + CUGRAPH_INVALID_INPUT, + "Invalid input arguments: src size != dst size.", + *error); + + CAPI_EXPECTS(p_src[i]->type_ == p_dst[i]->type_, + CUGRAPH_INVALID_INPUT, + "Invalid input arguments: src type != dst type.", + *error); + + CAPI_EXPECTS((p_vertices == nullptr) || (p_src[i]->type_ == p_vertices[i]->type_), + CUGRAPH_INVALID_INPUT, + "Invalid input arguments: src type != vertices type.", + *error); + + CAPI_EXPECTS((weights == nullptr) || (p_weights[i]->size_ == p_src[i]->size_), + CUGRAPH_INVALID_INPUT, + "Invalid input arguments: src size != weights size.", + *error); + + local_num_edges += p_src[i]->size_; + + if (vertex_type == cugraph_data_type_id_t::NTYPES) vertex_type = p_src[i]->type_; + + if (weights != nullptr) { + if (weight_type == cugraph_data_type_id_t::NTYPES) weight_type = p_weights[i]->type_; + } - CAPI_EXPECTS(p_src->size_ == p_dst->size_, - CUGRAPH_INVALID_INPUT, - "Invalid input arguments: src size != dst size.", - *error); - CAPI_EXPECTS(p_src->type_ == p_dst->type_, + CAPI_EXPECTS(p_src[i]->type_ == vertex_type, + CUGRAPH_INVALID_INPUT, + "Invalid input arguments: all vertex types must match", + *error); + + CAPI_EXPECTS((weights == nullptr) || (p_weights[i]->type_ == weight_type), + CUGRAPH_INVALID_INPUT, + "Invalid input arguments: all weight types must match", + *error); + } + + size_t num_edges = cugraph::host_scalar_allreduce(p_handle->handle_->get_comms(), + local_num_edges, + raft::comms::op_t::SUM, + p_handle->handle_->get_stream()); + + auto vertex_types = cugraph::host_scalar_allgather( + p_handle->handle_->get_comms(), static_cast(vertex_type), p_handle->handle_->get_stream()); + + auto weight_types = cugraph::host_scalar_allgather( + p_handle->handle_->get_comms(), static_cast(weight_type), p_handle->handle_->get_stream()); + + if (vertex_type == cugraph_data_type_id_t::NTYPES) { + // Only true if this GPU had no vertex arrays + vertex_type = static_cast( + *std::min_element(vertex_types.begin(), vertex_types.end())); + } + + if (weight_type == cugraph_data_type_id_t::NTYPES) { + // Only true if this GPU had no weight arrays + weight_type = static_cast( + *std::min_element(weight_types.begin(), weight_types.end())); + } + + CAPI_EXPECTS(std::all_of(vertex_types.begin(), + vertex_types.end(), + [vertex_type](auto t) { return vertex_type == static_cast(t); }), CUGRAPH_INVALID_INPUT, - "Invalid input arguments: src type != dst type.", + "different vertex type used on different GPUs", *error); - CAPI_EXPECTS((weights == nullptr) || (p_weights->size_ == p_src->size_), + CAPI_EXPECTS(std::all_of(weight_types.begin(), + weight_types.end(), + [weight_type](auto t) { return weight_type == static_cast(t); }), CUGRAPH_INVALID_INPUT, - "Invalid input arguments: src size != weights size.", + "different weight type used on different GPUs", *error); cugraph_data_type_id_t edge_type; - cugraph_data_type_id_t weight_type; if (num_edges < int32_threshold) { - edge_type = p_src->type_; + edge_type = static_cast(vertex_types[0]); } else { edge_type = cugraph_data_type_id_t::INT64; } - if (weights != nullptr) { - weight_type = p_weights->type_; - } else { + if (weight_type == cugraph_data_type_id_t::NTYPES) { weight_type = cugraph_data_type_id_t::FLOAT32; } - CAPI_EXPECTS((edge_ids == nullptr) || (p_edge_ids->type_ == edge_type), - CUGRAPH_INVALID_INPUT, - "Invalid input arguments: Edge id type must match edge type", - *error); + cugraph_data_type_id_t edge_type_id_type{cugraph_data_type_id_t::NTYPES}; - CAPI_EXPECTS((edge_ids == nullptr) || (p_edge_ids->size_ == p_src->size_), - CUGRAPH_INVALID_INPUT, - "Invalid input arguments: src size != edge id prop size", - *error); + for (size_t i = 0; i < num_arrays; ++i) { + CAPI_EXPECTS((edge_ids == nullptr) || (p_edge_ids[i]->type_ == edge_type), + CUGRAPH_INVALID_INPUT, + "Invalid input arguments: Edge id type must match edge type", + *error); - CAPI_EXPECTS((edge_type_ids == nullptr) || (p_edge_type_ids->size_ == p_src->size_), - CUGRAPH_INVALID_INPUT, - "Invalid input arguments: src size != edge type prop size", - *error); + CAPI_EXPECTS((edge_ids == nullptr) || (p_edge_ids[i]->size_ == p_src[i]->size_), + CUGRAPH_INVALID_INPUT, + "Invalid input arguments: src size != edge id prop size", + *error); + + if (edge_type_ids != nullptr) { + CAPI_EXPECTS(p_edge_type_ids[i]->size_ == p_src[i]->size_, + CUGRAPH_INVALID_INPUT, + "Invalid input arguments: src size != edge type prop size", + *error); + + if (edge_type_id_type == cugraph_data_type_id_t::NTYPES) + edge_type_id_type = p_edge_type_ids[i]->type_; + + CAPI_EXPECTS(p_edge_type_ids[i]->type_ == edge_type_id_type, + CUGRAPH_INVALID_INPUT, + "Invalid input arguments: src size != edge type prop size", + *error); + } + } + + auto edge_type_id_types = cugraph::host_scalar_allgather(p_handle->handle_->get_comms(), + static_cast(edge_type_id_type), + p_handle->handle_->get_stream()); + + if (edge_type_id_type == cugraph_data_type_id_t::NTYPES) { + // Only true if this GPU had no edge_type_id arrays + edge_type_id_type = static_cast( + *std::min_element(edge_type_id_types.begin(), edge_type_id_types.end())); + } + + CAPI_EXPECTS( + std::all_of(edge_type_id_types.begin(), + edge_type_id_types.end(), + [edge_type_id_type](auto t) { return edge_type_id_type == static_cast(t); }), + CUGRAPH_INVALID_INPUT, + "different edge_type_id type used on different GPUs", + *error); - cugraph_data_type_id_t edge_type_id_type; - if (edge_type_ids == nullptr) { + if (edge_type_id_type == cugraph_data_type_id_t::NTYPES) { edge_type_id_type = cugraph_data_type_id_t::INT32; - } else { - edge_type_id_type = p_edge_type_ids->type_; } + // + // Now we know enough to create the graph + // create_graph_functor functor(*p_handle->handle_, properties, + vertex_type, + edge_type, + weight_type, + edge_type_id_type, + p_vertices, p_src, p_dst, p_weights, p_edge_ids, p_edge_type_ids, + num_arrays, bool_t::TRUE, - check, - edge_type); + drop_self_loops, + drop_multi_edges, + do_expensive_check); try { - cugraph::c_api::vertex_dispatcher(p_src->type_, - edge_type, - weight_type, - edge_type_id_type, - store_transposed, - multi_gpu, - functor); + cugraph::c_api::vertex_dispatcher( + vertex_type, edge_type, weight_type, edge_type_id_type, store_transposed, multi_gpu, functor); if (functor.error_code_ != CUGRAPH_SUCCESS) { *error = reinterpret_cast(functor.error_.release()); @@ -394,25 +504,38 @@ extern "C" cugraph_error_code_t cugraph_mg_graph_create( return CUGRAPH_SUCCESS; } +extern "C" cugraph_error_code_t cugraph_mg_graph_create( + cugraph_resource_handle_t const* handle, + cugraph_graph_properties_t const* properties, + cugraph_type_erased_device_array_view_t const* src, + cugraph_type_erased_device_array_view_t const* dst, + cugraph_type_erased_device_array_view_t const* weights, + cugraph_type_erased_device_array_view_t const* edge_ids, + cugraph_type_erased_device_array_view_t const* edge_type_ids, + bool_t store_transposed, + size_t num_edges, + bool_t do_expensive_check, + cugraph_graph_t** graph, + cugraph_error_t** error) +{ + return cugraph_graph_create_mg(handle, + properties, + NULL, + &src, + &dst, + &weights, + &edge_ids, + &edge_type_ids, + store_transposed, + 1, + FALSE, + FALSE, + do_expensive_check, + graph, + error); +} + extern "C" void cugraph_mg_graph_free(cugraph_graph_t* ptr_graph) { - if (ptr_graph != NULL) { - auto internal_pointer = reinterpret_cast(ptr_graph); - - destroy_graph_functor functor(internal_pointer->graph_, - internal_pointer->number_map_, - internal_pointer->edge_weights_, - internal_pointer->edge_ids_, - internal_pointer->edge_types_); - - cugraph::c_api::vertex_dispatcher(internal_pointer->vertex_type_, - internal_pointer->edge_type_, - internal_pointer->weight_type_, - internal_pointer->edge_type_id_type_, - internal_pointer->store_transposed_, - internal_pointer->multi_gpu_, - functor); - - delete internal_pointer; - } + if (ptr_graph != NULL) { cugraph_graph_free(ptr_graph); } } diff --git a/cpp/src/c_api/graph_sg.cpp b/cpp/src/c_api/graph_sg.cpp index 9536869f123..7793458b53a 100644 --- a/cpp/src/c_api/graph_sg.cpp +++ b/cpp/src/c_api/graph_sg.cpp @@ -33,35 +33,44 @@ namespace { struct create_graph_functor : public cugraph::c_api::abstract_functor { raft::handle_t const& handle_; cugraph_graph_properties_t const* properties_; + cugraph::c_api::cugraph_type_erased_device_array_view_t const* vertices_; cugraph::c_api::cugraph_type_erased_device_array_view_t const* src_; cugraph::c_api::cugraph_type_erased_device_array_view_t const* dst_; cugraph::c_api::cugraph_type_erased_device_array_view_t const* weights_; cugraph::c_api::cugraph_type_erased_device_array_view_t const* edge_ids_; cugraph::c_api::cugraph_type_erased_device_array_view_t const* edge_type_ids_; bool_t renumber_; + bool_t drop_self_loops_; + bool_t drop_multi_edges_; bool_t do_expensive_check_; cugraph_data_type_id_t edge_type_; cugraph::c_api::cugraph_graph_t* result_{}; create_graph_functor(raft::handle_t const& handle, cugraph_graph_properties_t const* properties, + cugraph::c_api::cugraph_type_erased_device_array_view_t const* vertices, cugraph::c_api::cugraph_type_erased_device_array_view_t const* src, cugraph::c_api::cugraph_type_erased_device_array_view_t const* dst, cugraph::c_api::cugraph_type_erased_device_array_view_t const* weights, cugraph::c_api::cugraph_type_erased_device_array_view_t const* edge_ids, cugraph::c_api::cugraph_type_erased_device_array_view_t const* edge_type_ids, bool_t renumber, + bool_t drop_self_loops, + bool_t drop_multi_edges, bool_t do_expensive_check, cugraph_data_type_id_t edge_type) : abstract_functor(), properties_(properties), handle_(handle), + vertices_(vertices), src_(src), dst_(dst), weights_(weights), edge_ids_(edge_ids), edge_type_ids_(edge_type_ids), renumber_(renumber), + drop_self_loops_(drop_self_loops), + drop_multi_edges_(drop_multi_edges), do_expensive_check_(do_expensive_check), edge_type_(edge_type) { @@ -99,6 +108,18 @@ struct create_graph_functor : public cugraph::c_api::abstract_functor { edge_type_id_t>> new_edge_types{std::nullopt}; + std::optional> vertex_list = + vertices_ ? std::make_optional( + rmm::device_uvector(vertices_->size_, handle_.get_stream())) + : std::nullopt; + + if (vertex_list) { + raft::copy(vertex_list->data(), + vertices_->as_type(), + vertices_->size_, + handle_.get_stream()); + } + rmm::device_uvector edgelist_srcs(src_->size_, handle_.get_stream()); rmm::device_uvector edgelist_dsts(dst_->size_, handle_.get_stream()); @@ -160,6 +181,28 @@ struct create_graph_functor : public cugraph::c_api::abstract_functor { cugraph::graph_view_t, edge_type_id_t>(handle_); + if (drop_self_loops_) { + std::tie( + edgelist_srcs, edgelist_dsts, edgelist_weights, edgelist_edge_ids, edgelist_edge_types) = + cugraph::remove_self_loops(handle_, + std::move(edgelist_srcs), + std::move(edgelist_dsts), + std::move(edgelist_weights), + std::move(edgelist_edge_ids), + std::move(edgelist_edge_types)); + } + + if (drop_multi_edges_) { + std::tie( + edgelist_srcs, edgelist_dsts, edgelist_weights, edgelist_edge_ids, edgelist_edge_types) = + cugraph::remove_multi_edges(handle_, + std::move(edgelist_srcs), + std::move(edgelist_dsts), + std::move(edgelist_weights), + std::move(edgelist_edge_ids), + std::move(edgelist_edge_types)); + } + std::tie(*graph, new_edge_weights, new_edge_ids, new_edge_types, new_number_map) = cugraph::create_graph_from_edgelist( handle_, - std::nullopt, + std::move(vertex_list), std::move(edgelist_srcs), std::move(edgelist_dsts), std::move(edgelist_weights), @@ -279,6 +322,12 @@ struct create_graph_csr_functor : public cugraph::c_api::abstract_functor { edge_type_id_t>> new_edge_types{std::nullopt}; + std::optional> vertex_list = std::make_optional( + rmm::device_uvector(offsets_->size_ - 1, handle_.get_stream())); + + cugraph::detail::sequence_fill( + handle_.get_stream(), vertex_list->data(), vertex_list->size(), vertex_t{0}); + rmm::device_uvector edgelist_srcs(0, handle_.get_stream()); rmm::device_uvector edgelist_dsts(indices_->size_, handle_.get_stream()); @@ -354,7 +403,7 @@ struct create_graph_csr_functor : public cugraph::c_api::abstract_functor { store_transposed, multi_gpu>( handle_, - std::nullopt, + std::move(vertex_list), std::move(edgelist_srcs), std::move(edgelist_dsts), std::move(edgelist_weights), @@ -452,9 +501,10 @@ struct destroy_graph_functor : public cugraph::c_api::abstract_functor { } // namespace -extern "C" cugraph_error_code_t cugraph_sg_graph_create( +extern "C" cugraph_error_code_t cugraph_graph_create_sg( const cugraph_resource_handle_t* handle, const cugraph_graph_properties_t* properties, + const cugraph_type_erased_device_array_view_t* vertices, const cugraph_type_erased_device_array_view_t* src, const cugraph_type_erased_device_array_view_t* dst, const cugraph_type_erased_device_array_view_t* weights, @@ -462,6 +512,8 @@ extern "C" cugraph_error_code_t cugraph_sg_graph_create( const cugraph_type_erased_device_array_view_t* edge_type_ids, bool_t store_transposed, bool_t renumber, + bool_t drop_self_loops, + bool_t drop_multi_edges, bool_t do_expensive_check, cugraph_graph_t** graph, cugraph_error_t** error) @@ -473,6 +525,8 @@ extern "C" cugraph_error_code_t cugraph_sg_graph_create( *error = nullptr; auto p_handle = reinterpret_cast(handle); + auto p_vertices = + reinterpret_cast(vertices); auto p_src = reinterpret_cast(src); auto p_dst = @@ -488,6 +542,12 @@ extern "C" cugraph_error_code_t cugraph_sg_graph_create( CUGRAPH_INVALID_INPUT, "Invalid input arguments: src size != dst size.", *error); + + CAPI_EXPECTS((p_vertices == nullptr) || (p_src->type_ == p_vertices->type_), + CUGRAPH_INVALID_INPUT, + "Invalid input arguments: src type != vertices type.", + *error); + CAPI_EXPECTS(p_src->type_ == p_dst->type_, CUGRAPH_INVALID_INPUT, "Invalid input arguments: src type != dst type.", @@ -533,12 +593,15 @@ extern "C" cugraph_error_code_t cugraph_sg_graph_create( ::create_graph_functor functor(*p_handle->handle_, properties, + p_vertices, p_src, p_dst, p_weights, p_edge_ids, p_edge_type_ids, renumber, + drop_self_loops, + drop_multi_edges, do_expensive_check, edge_type); @@ -565,7 +628,38 @@ extern "C" cugraph_error_code_t cugraph_sg_graph_create( return CUGRAPH_SUCCESS; } -cugraph_error_code_t cugraph_sg_graph_create_from_csr( +extern "C" cugraph_error_code_t cugraph_sg_graph_create( + const cugraph_resource_handle_t* handle, + const cugraph_graph_properties_t* properties, + const cugraph_type_erased_device_array_view_t* src, + const cugraph_type_erased_device_array_view_t* dst, + const cugraph_type_erased_device_array_view_t* weights, + const cugraph_type_erased_device_array_view_t* edge_ids, + const cugraph_type_erased_device_array_view_t* edge_type_ids, + bool_t store_transposed, + bool_t renumber, + bool_t do_expensive_check, + cugraph_graph_t** graph, + cugraph_error_t** error) +{ + return cugraph_graph_create_sg(handle, + properties, + NULL, + src, + dst, + weights, + edge_ids, + edge_type_ids, + store_transposed, + renumber, + FALSE, + FALSE, + do_expensive_check, + graph, + error); +} + +cugraph_error_code_t cugraph_graph_create_sg_from_csr( const cugraph_resource_handle_t* handle, const cugraph_graph_properties_t* properties, const cugraph_type_erased_device_array_view_t* offsets, @@ -662,23 +756,55 @@ cugraph_error_code_t cugraph_sg_graph_create_from_csr( return CUGRAPH_SUCCESS; } -extern "C" void cugraph_sg_graph_free(cugraph_graph_t* ptr_graph) +cugraph_error_code_t cugraph_sg_graph_create_from_csr( + const cugraph_resource_handle_t* handle, + const cugraph_graph_properties_t* properties, + const cugraph_type_erased_device_array_view_t* offsets, + const cugraph_type_erased_device_array_view_t* indices, + const cugraph_type_erased_device_array_view_t* weights, + const cugraph_type_erased_device_array_view_t* edge_ids, + const cugraph_type_erased_device_array_view_t* edge_type_ids, + bool_t store_transposed, + bool_t renumber, + bool_t do_expensive_check, + cugraph_graph_t** graph, + cugraph_error_t** error) { - auto internal_pointer = reinterpret_cast(ptr_graph); - - destroy_graph_functor functor(internal_pointer->graph_, - internal_pointer->number_map_, - internal_pointer->edge_weights_, - internal_pointer->edge_ids_, - internal_pointer->edge_types_); - - cugraph::c_api::vertex_dispatcher(internal_pointer->vertex_type_, - internal_pointer->edge_type_, - internal_pointer->weight_type_, - internal_pointer->edge_type_id_type_, - internal_pointer->store_transposed_, - internal_pointer->multi_gpu_, - functor); - - delete internal_pointer; + return cugraph_graph_create_sg_from_csr(handle, + properties, + offsets, + indices, + weights, + edge_ids, + edge_type_ids, + store_transposed, + renumber, + do_expensive_check, + graph, + error); } + +extern "C" void cugraph_graph_free(cugraph_graph_t* ptr_graph) +{ + if (ptr_graph != NULL) { + auto internal_pointer = reinterpret_cast(ptr_graph); + + destroy_graph_functor functor(internal_pointer->graph_, + internal_pointer->number_map_, + internal_pointer->edge_weights_, + internal_pointer->edge_ids_, + internal_pointer->edge_types_); + + cugraph::c_api::vertex_dispatcher(internal_pointer->vertex_type_, + internal_pointer->edge_type_, + internal_pointer->weight_type_, + internal_pointer->edge_type_id_type_, + internal_pointer->store_transposed_, + internal_pointer->multi_gpu_, + functor); + + delete internal_pointer; + } +} + +extern "C" void cugraph_sg_graph_free(cugraph_graph_t* ptr_graph) { cugraph_graph_free(ptr_graph); } diff --git a/cpp/src/c_api/resource_handle.cpp b/cpp/src/c_api/resource_handle.cpp index 767a6f0add6..75b9537ef49 100644 --- a/cpp/src/c_api/resource_handle.cpp +++ b/cpp/src/c_api/resource_handle.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, NVIDIA CORPORATION. + * Copyright (c) 2022-2023, NVIDIA CORPORATION. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -41,3 +41,10 @@ extern "C" int cugraph_resource_handle_get_rank(const cugraph_resource_handle_t* auto& comm = internal->handle_->get_comms(); return static_cast(comm.get_rank()); } + +extern "C" int cugraph_resource_handle_get_comm_size(const cugraph_resource_handle_t* handle) +{ + auto internal = reinterpret_cast(handle); + auto& comm = internal->handle_->get_comms(); + return static_cast(comm.get_size()); +} diff --git a/cpp/src/structure/detail/structure_utils.cuh b/cpp/src/structure/detail/structure_utils.cuh index 01fbccaa53e..c49b62e4543 100644 --- a/cpp/src/structure/detail/structure_utils.cuh +++ b/cpp/src/structure/detail/structure_utils.cuh @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -33,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -496,6 +498,63 @@ void sort_adjacency_list(raft::handle_t const& handle, } } -} // namespace detail +template +std::tuple> mark_entries(raft::handle_t const& handle, + size_t num_entries, + comparison_t comparison) +{ + rmm::device_uvector marked_entries(cugraph::packed_bool_size(num_entries), + handle.get_stream()); + + thrust::tabulate(handle.get_thrust_policy(), + marked_entries.begin(), + marked_entries.end(), + [comparison, num_entries] __device__(size_t idx) { + auto word = cugraph::packed_bool_empty_mask(); + size_t start_index = idx * cugraph::packed_bools_per_word(); + size_t bits_in_this_word = + (start_index + cugraph::packed_bools_per_word() < num_entries) + ? cugraph::packed_bools_per_word() + : (num_entries - start_index); + + for (size_t bit = 0; bit < bits_in_this_word; ++bit) { + if (comparison(start_index + bit)) word |= cugraph::packed_bool_mask(bit); + } + + return word; + }); + + size_t bit_count = thrust::transform_reduce( + handle.get_thrust_policy(), + marked_entries.begin(), + marked_entries.end(), + [] __device__(auto word) { return __popc(word); }, + size_t{0}, + thrust::plus()); + + return std::make_tuple(bit_count, std::move(marked_entries)); +} +template +rmm::device_uvector remove_flagged_elements(raft::handle_t const& handle, + rmm::device_uvector&& vector, + raft::device_span remove_flags, + size_t remove_count) +{ + rmm::device_uvector result(vector.size() - remove_count, handle.get_stream()); + + thrust::copy_if( + handle.get_thrust_policy(), + thrust::make_counting_iterator(size_t{0}), + thrust::make_counting_iterator(vector.size()), + thrust::make_transform_output_iterator(result.begin(), + indirection_t{vector.data()}), + [remove_flags] __device__(size_t i) { + return !(remove_flags[cugraph::packed_bool_offset(i)] & cugraph::packed_bool_mask(i)); + }); + + return result; +} + +} // namespace detail } // namespace cugraph diff --git a/cpp/src/structure/remove_multi_edges.cu b/cpp/src/structure/remove_multi_edges.cu new file mode 100644 index 00000000000..ba07d068c0e --- /dev/null +++ b/cpp/src/structure/remove_multi_edges.cu @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2023, NVIDIA CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include + +namespace cugraph { + +template std::tuple, + rmm::device_uvector, + std::optional>, + std::optional>, + std::optional>> +remove_multi_edges(raft::handle_t const& handle, + rmm::device_uvector&& edgelist_srcs, + rmm::device_uvector&& edgelist_dsts, + std::optional>&& edgelist_weights, + std::optional>&& edgelist_edge_ids, + std::optional>&& edgelist_edge_types); + +template std::tuple, + rmm::device_uvector, + std::optional>, + std::optional>, + std::optional>> +remove_multi_edges(raft::handle_t const& handle, + rmm::device_uvector&& edgelist_srcs, + rmm::device_uvector&& edgelist_dsts, + std::optional>&& edgelist_weights, + std::optional>&& edgelist_edge_ids, + std::optional>&& edgelist_edge_types); + +template std::tuple, + rmm::device_uvector, + std::optional>, + std::optional>, + std::optional>> +remove_multi_edges(raft::handle_t const& handle, + rmm::device_uvector&& edgelist_srcs, + rmm::device_uvector&& edgelist_dsts, + std::optional>&& edgelist_weights, + std::optional>&& edgelist_edge_ids, + std::optional>&& edgelist_edge_types); + +template std::tuple, + rmm::device_uvector, + std::optional>, + std::optional>, + std::optional>> +remove_multi_edges(raft::handle_t const& handle, + rmm::device_uvector&& edgelist_srcs, + rmm::device_uvector&& edgelist_dsts, + std::optional>&& edgelist_weights, + std::optional>&& edgelist_edge_ids, + std::optional>&& edgelist_edge_types); + +template std::tuple, + rmm::device_uvector, + std::optional>, + std::optional>, + std::optional>> +remove_multi_edges(raft::handle_t const& handle, + rmm::device_uvector&& edgelist_srcs, + rmm::device_uvector&& edgelist_dsts, + std::optional>&& edgelist_weights, + std::optional>&& edgelist_edge_ids, + std::optional>&& edgelist_edge_types); + +template std::tuple, + rmm::device_uvector, + std::optional>, + std::optional>, + std::optional>> +remove_multi_edges(raft::handle_t const& handle, + rmm::device_uvector&& edgelist_srcs, + rmm::device_uvector&& edgelist_dsts, + std::optional>&& edgelist_weights, + std::optional>&& edgelist_edge_ids, + std::optional>&& edgelist_edge_types); + +} // namespace cugraph diff --git a/cpp/src/structure/remove_multi_edges_impl.cuh b/cpp/src/structure/remove_multi_edges_impl.cuh new file mode 100644 index 00000000000..ab6b1fba8eb --- /dev/null +++ b/cpp/src/structure/remove_multi_edges_impl.cuh @@ -0,0 +1,310 @@ +/* + * Copyright (c) 2023, NVIDIA CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include + +#include +// FIXME: mem_frugal_partition should probably not be in shuffle_comm.hpp +// It's used here without any notion of shuffling +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace cugraph { + +namespace detail { + +template +struct hash_src_dst_pair { + int32_t num_groups; + + int32_t __device__ operator()(thrust::tuple t) const + { + vertex_t pair[2]; + pair[0] = thrust::get<0>(t); + pair[1] = thrust::get<1>(t); + cuco::detail::MurmurHash3_32 hash_func{}; + return hash_func.compute_hash(reinterpret_cast(pair), 2 * sizeof(vertex_t)) % + num_groups; + } +}; + +template +std::tuple, rmm::device_uvector> group_multi_edges( + raft::handle_t const& handle, + rmm::device_uvector&& edgelist_srcs, + rmm::device_uvector&& edgelist_dsts, + size_t mem_frugal_threshold) +{ + auto pair_first = thrust::make_zip_iterator(edgelist_srcs.begin(), edgelist_dsts.begin()); + + if (edgelist_srcs.size() > mem_frugal_threshold) { + // FIXME: Tuning parameter to address high frequency multi-edges + // Defaulting to 2 which makes the code easier. If + // num_groups > 2 we can evaluate whether to find a good + // midpoint to do 2 sorts, or if we should do more than 2 sorts. + const size_t num_groups{2}; + + auto group_counts = groupby_and_count(pair_first, + pair_first + edgelist_srcs.size(), + hash_src_dst_pair{}, + num_groups, + mem_frugal_threshold, + handle.get_stream()); + + std::vector h_group_counts(group_counts.size()); + raft::update_host( + h_group_counts.data(), group_counts.data(), group_counts.size(), handle.get_stream()); + + thrust::sort(handle.get_thrust_policy(), pair_first, pair_first + h_group_counts[0]); + thrust::sort(handle.get_thrust_policy(), + pair_first + h_group_counts[0], + pair_first + edgelist_srcs.size()); + } else { + thrust::sort(handle.get_thrust_policy(), pair_first, pair_first + edgelist_srcs.size()); + } + + return std::make_tuple(std::move(edgelist_srcs), std::move(edgelist_dsts)); +} + +template +std::tuple, + rmm::device_uvector, + decltype(allocate_dataframe_buffer(size_t{0}, rmm::cuda_stream_view{}))> +group_multi_edges( + raft::handle_t const& handle, + rmm::device_uvector&& edgelist_srcs, + rmm::device_uvector&& edgelist_dsts, + decltype(allocate_dataframe_buffer(0, rmm::cuda_stream_view{}))&& edgelist_values, + size_t mem_frugal_threshold) +{ + auto pair_first = thrust::make_zip_iterator(edgelist_srcs.begin(), edgelist_dsts.begin()); + auto value_first = get_dataframe_buffer_begin(edgelist_values); + + if (edgelist_srcs.size() > mem_frugal_threshold) { + // FIXME: Tuning parameter to address high frequency multi-edges + // Defaulting to 2 which makes the code easier. If + // num_groups > 2 we can evaluate whether to find a good + // midpoint to do 2 sorts, or if we should do more than 2 sorts. + const size_t num_groups{2}; + + auto group_counts = groupby_and_count(pair_first, + pair_first + edgelist_srcs.size(), + value_first, + hash_src_dst_pair{}, + num_groups, + mem_frugal_threshold, + handle.get_stream()); + + std::vector h_group_counts(group_counts.size()); + raft::update_host( + h_group_counts.data(), group_counts.data(), group_counts.size(), handle.get_stream()); + + thrust::sort_by_key(handle.get_thrust_policy(), + pair_first, + pair_first + h_group_counts[0], + get_dataframe_buffer_begin(edgelist_values)); + thrust::sort_by_key(handle.get_thrust_policy(), + pair_first + h_group_counts[0], + pair_first + edgelist_srcs.size(), + get_dataframe_buffer_begin(edgelist_values) + h_group_counts[0]); + } else { + thrust::sort_by_key(handle.get_thrust_policy(), + pair_first, + pair_first + edgelist_srcs.size(), + get_dataframe_buffer_begin(edgelist_values)); + } + + return std::make_tuple( + std::move(edgelist_srcs), std::move(edgelist_dsts), std::move(edgelist_values)); +} + +} // namespace detail + +template +std::tuple, + rmm::device_uvector, + std::optional>, + std::optional>, + std::optional>> +remove_multi_edges(raft::handle_t const& handle, + rmm::device_uvector&& edgelist_srcs, + rmm::device_uvector&& edgelist_dsts, + std::optional>&& edgelist_weights, + std::optional>&& edgelist_edge_ids, + std::optional>&& edgelist_edge_types) +{ + auto total_global_mem = handle.get_device_properties().totalGlobalMem; + size_t element_size = sizeof(vertex_t) * 2; + if (edgelist_weights) { element_size += sizeof(weight_t); } + if (edgelist_edge_ids) { element_size += sizeof(edge_t); } + if (edgelist_edge_types) { element_size += sizeof(edge_type_t); } + + auto constexpr mem_frugal_ratio = + 0.25; // if the expected temporary buffer size exceeds the mem_frugal_ratio of the + // total_global_mem, switch to the memory frugal approach + auto mem_frugal_threshold = + static_cast(static_cast(total_global_mem / element_size) * mem_frugal_ratio); + + if (edgelist_weights) { + if (edgelist_edge_ids) { + if (edgelist_edge_types) { + std::forward_as_tuple(edgelist_srcs, + edgelist_dsts, + std::tie(edgelist_weights, edgelist_edge_ids, edgelist_edge_types)) = + detail::group_multi_edges>( + handle, + std::move(edgelist_srcs), + std::move(edgelist_dsts), + std::make_tuple(std::move(*edgelist_weights), + std::move(*edgelist_edge_ids), + std::move(*edgelist_edge_types)), + mem_frugal_threshold); + } else { + std::forward_as_tuple( + edgelist_srcs, edgelist_dsts, std::tie(edgelist_weights, edgelist_edge_ids)) = + detail::group_multi_edges>( + handle, + std::move(edgelist_srcs), + std::move(edgelist_dsts), + std::make_tuple(std::move(*edgelist_weights), std::move(*edgelist_edge_ids)), + mem_frugal_threshold); + } + } else { + if (edgelist_edge_types) { + std::forward_as_tuple( + edgelist_srcs, edgelist_dsts, std::tie(edgelist_weights, edgelist_edge_types)) = + detail::group_multi_edges>( + handle, + std::move(edgelist_srcs), + std::move(edgelist_dsts), + std::make_tuple(std::move(*edgelist_weights), std::move(*edgelist_edge_types)), + mem_frugal_threshold); + } else { + std::forward_as_tuple(edgelist_srcs, edgelist_dsts, std::tie(edgelist_weights)) = + detail::group_multi_edges>( + handle, + std::move(edgelist_srcs), + std::move(edgelist_dsts), + std::make_tuple(std::move(*edgelist_weights)), + mem_frugal_threshold); + } + } + } else { + if (edgelist_edge_ids) { + if (edgelist_edge_types) { + std::forward_as_tuple( + edgelist_srcs, edgelist_dsts, std::tie(edgelist_edge_ids, edgelist_edge_types)) = + detail::group_multi_edges>( + handle, + std::move(edgelist_srcs), + std::move(edgelist_dsts), + std::make_tuple(std::move(*edgelist_edge_ids), std::move(*edgelist_edge_types)), + mem_frugal_threshold); + } else { + std::forward_as_tuple(edgelist_srcs, edgelist_dsts, std::tie(edgelist_edge_ids)) = + detail::group_multi_edges>( + handle, + std::move(edgelist_srcs), + std::move(edgelist_dsts), + std::make_tuple(std::move(*edgelist_edge_ids)), + mem_frugal_threshold); + } + } else { + if (edgelist_edge_types) { + std::forward_as_tuple(edgelist_srcs, edgelist_dsts, std::tie(edgelist_edge_types)) = + detail::group_multi_edges>( + handle, + std::move(edgelist_srcs), + std::move(edgelist_dsts), + std::make_tuple(std::move(*edgelist_edge_types)), + mem_frugal_threshold); + } else { + std::tie(edgelist_srcs, edgelist_dsts) = detail::group_multi_edges( + handle, std::move(edgelist_srcs), std::move(edgelist_dsts), mem_frugal_threshold); + } + } + } + + auto [multi_edge_count, multi_edges_to_delete] = + detail::mark_entries(handle, + edgelist_srcs.size(), + [d_edgelist_srcs = edgelist_srcs.data(), + d_edgelist_dsts = edgelist_dsts.data()] __device__(auto idx) { + return (idx > 0) && (d_edgelist_srcs[idx - 1] == d_edgelist_srcs[idx]) && + (d_edgelist_dsts[idx - 1] == d_edgelist_dsts[idx]); + }); + + if (multi_edge_count > 0) { + edgelist_srcs = detail::remove_flagged_elements( + handle, + std::move(edgelist_srcs), + raft::device_span{multi_edges_to_delete.data(), multi_edges_to_delete.size()}, + multi_edge_count); + edgelist_dsts = detail::remove_flagged_elements( + handle, + std::move(edgelist_dsts), + raft::device_span{multi_edges_to_delete.data(), multi_edges_to_delete.size()}, + multi_edge_count); + + if (edgelist_weights) + edgelist_weights = detail::remove_flagged_elements( + handle, + std::move(*edgelist_weights), + raft::device_span{multi_edges_to_delete.data(), + multi_edges_to_delete.size()}, + multi_edge_count); + + if (edgelist_edge_ids) + edgelist_edge_ids = detail::remove_flagged_elements( + handle, + std::move(*edgelist_edge_ids), + raft::device_span{multi_edges_to_delete.data(), + multi_edges_to_delete.size()}, + multi_edge_count); + + if (edgelist_edge_types) + edgelist_edge_types = detail::remove_flagged_elements( + handle, + std::move(*edgelist_edge_types), + raft::device_span{multi_edges_to_delete.data(), + multi_edges_to_delete.size()}, + multi_edge_count); + } + + return std::make_tuple(std::move(edgelist_srcs), + std::move(edgelist_dsts), + std::move(edgelist_weights), + std::move(edgelist_edge_ids), + std::move(edgelist_edge_types)); +} + +} // namespace cugraph diff --git a/cpp/src/structure/remove_self_loops.cu b/cpp/src/structure/remove_self_loops.cu new file mode 100644 index 00000000000..8a66c1e05e3 --- /dev/null +++ b/cpp/src/structure/remove_self_loops.cu @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2023, NVIDIA CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include + +namespace cugraph { + +template std::tuple, + rmm::device_uvector, + std::optional>, + std::optional>, + std::optional>> +remove_self_loops(raft::handle_t const& handle, + rmm::device_uvector&& edgelist_srcs, + rmm::device_uvector&& edgelist_dsts, + std::optional>&& edgelist_weights, + std::optional>&& edgelist_edge_ids, + std::optional>&& edgelist_edge_types); + +template std::tuple, + rmm::device_uvector, + std::optional>, + std::optional>, + std::optional>> +remove_self_loops(raft::handle_t const& handle, + rmm::device_uvector&& edgelist_srcs, + rmm::device_uvector&& edgelist_dsts, + std::optional>&& edgelist_weights, + std::optional>&& edgelist_edge_ids, + std::optional>&& edgelist_edge_types); + +template std::tuple, + rmm::device_uvector, + std::optional>, + std::optional>, + std::optional>> +remove_self_loops(raft::handle_t const& handle, + rmm::device_uvector&& edgelist_srcs, + rmm::device_uvector&& edgelist_dsts, + std::optional>&& edgelist_weights, + std::optional>&& edgelist_edge_ids, + std::optional>&& edgelist_edge_types); + +template std::tuple, + rmm::device_uvector, + std::optional>, + std::optional>, + std::optional>> +remove_self_loops(raft::handle_t const& handle, + rmm::device_uvector&& edgelist_srcs, + rmm::device_uvector&& edgelist_dsts, + std::optional>&& edgelist_weights, + std::optional>&& edgelist_edge_ids, + std::optional>&& edgelist_edge_types); + +template std::tuple, + rmm::device_uvector, + std::optional>, + std::optional>, + std::optional>> +remove_self_loops(raft::handle_t const& handle, + rmm::device_uvector&& edgelist_srcs, + rmm::device_uvector&& edgelist_dsts, + std::optional>&& edgelist_weights, + std::optional>&& edgelist_edge_ids, + std::optional>&& edgelist_edge_types); + +template std::tuple, + rmm::device_uvector, + std::optional>, + std::optional>, + std::optional>> +remove_self_loops(raft::handle_t const& handle, + rmm::device_uvector&& edgelist_srcs, + rmm::device_uvector&& edgelist_dsts, + std::optional>&& edgelist_weights, + std::optional>&& edgelist_edge_ids, + std::optional>&& edgelist_edge_types); + +} // namespace cugraph diff --git a/cpp/src/structure/remove_self_loops_impl.cuh b/cpp/src/structure/remove_self_loops_impl.cuh new file mode 100644 index 00000000000..161ffeae28e --- /dev/null +++ b/cpp/src/structure/remove_self_loops_impl.cuh @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2023, NVIDIA CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +namespace cugraph { + +template +std::tuple, + rmm::device_uvector, + std::optional>, + std::optional>, + std::optional>> +remove_self_loops(raft::handle_t const& handle, + rmm::device_uvector&& edgelist_srcs, + rmm::device_uvector&& edgelist_dsts, + std::optional>&& edgelist_weights, + std::optional>&& edgelist_edge_ids, + std::optional>&& edgelist_edge_types) +{ + auto [self_loop_count, self_loops_to_delete] = + detail::mark_entries(handle, + edgelist_srcs.size(), + [d_srcs = edgelist_srcs.data(), d_dsts = edgelist_dsts.data()] __device__( + size_t i) { return d_srcs[i] == d_dsts[i]; }); + + if (self_loop_count > 0) { + edgelist_srcs = detail::remove_flagged_elements( + handle, + std::move(edgelist_srcs), + raft::device_span{self_loops_to_delete.data(), self_loops_to_delete.size()}, + self_loop_count); + edgelist_dsts = detail::remove_flagged_elements( + handle, + std::move(edgelist_dsts), + raft::device_span{self_loops_to_delete.data(), self_loops_to_delete.size()}, + self_loop_count); + + if (edgelist_weights) + edgelist_weights = detail::remove_flagged_elements( + handle, + std::move(*edgelist_weights), + raft::device_span{self_loops_to_delete.data(), self_loops_to_delete.size()}, + self_loop_count); + + if (edgelist_edge_ids) + edgelist_edge_ids = detail::remove_flagged_elements( + handle, + std::move(*edgelist_edge_ids), + raft::device_span{self_loops_to_delete.data(), self_loops_to_delete.size()}, + self_loop_count); + + if (edgelist_edge_types) + edgelist_edge_types = detail::remove_flagged_elements( + handle, + std::move(*edgelist_edge_types), + raft::device_span{self_loops_to_delete.data(), self_loops_to_delete.size()}, + self_loop_count); + } + + return std::make_tuple(std::move(edgelist_srcs), + std::move(edgelist_dsts), + std::move(edgelist_weights), + std::move(edgelist_edge_ids), + std::move(edgelist_edge_types)); +} + +} // namespace cugraph diff --git a/cpp/tests/c_api/create_graph_test.c b/cpp/tests/c_api/create_graph_test.c index 736db761ebd..11da2eb8589 100644 --- a/cpp/tests/c_api/create_graph_test.c +++ b/cpp/tests/c_api/create_graph_test.c @@ -91,8 +91,9 @@ int test_create_sg_graph_simple() handle, wgt_view, (byte_t*)h_wgt, &ret_error); TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "wgt copy_from_host failed."); - ret_code = cugraph_sg_graph_create(handle, + ret_code = cugraph_graph_create_sg(handle, &properties, + NULL, src_view, dst_view, wgt_view, @@ -101,11 +102,13 @@ int test_create_sg_graph_simple() FALSE, FALSE, FALSE, + FALSE, + FALSE, &graph, &ret_error); TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "graph creation failed."); - cugraph_sg_graph_free(graph); + cugraph_graph_free(graph); cugraph_type_erased_device_array_view_free(wgt_view); cugraph_type_erased_device_array_view_free(dst_view); @@ -300,7 +303,7 @@ int test_create_sg_graph_csr() } cugraph_sample_result_free(result); - cugraph_sg_graph_free(graph); + cugraph_graph_free(graph); cugraph_type_erased_device_array_view_free(wgt_view); cugraph_type_erased_device_array_view_free(indices_view); cugraph_type_erased_device_array_view_free(offsets_view); @@ -382,8 +385,9 @@ int test_create_sg_graph_symmetric_error() handle, wgt_view, (byte_t*)h_wgt, &ret_error); TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "wgt copy_from_host failed."); - ret_code = cugraph_sg_graph_create(handle, + ret_code = cugraph_graph_create_sg(handle, &properties, + NULL, src_view, dst_view, wgt_view, @@ -391,19 +395,500 @@ int test_create_sg_graph_symmetric_error() NULL, FALSE, FALSE, + FALSE, + FALSE, TRUE, &graph, &ret_error); TEST_ASSERT(test_ret_value, ret_code != CUGRAPH_SUCCESS, "graph creation succeeded but should have failed."); - if (ret_code == CUGRAPH_SUCCESS) cugraph_sg_graph_free(graph); + if (ret_code == CUGRAPH_SUCCESS) cugraph_graph_free(graph); + + cugraph_type_erased_device_array_view_free(wgt_view); + cugraph_type_erased_device_array_view_free(dst_view); + cugraph_type_erased_device_array_view_free(src_view); + cugraph_type_erased_device_array_free(wgt); + cugraph_type_erased_device_array_free(dst); + cugraph_type_erased_device_array_free(src); + + cugraph_free_resource_handle(handle); + cugraph_error_free(ret_error); + + return test_ret_value; +} + +int test_create_sg_graph_with_isolated_vertices() +{ + int test_ret_value = 0; + + typedef int32_t vertex_t; + typedef int32_t edge_t; + typedef float weight_t; + + cugraph_error_code_t ret_code = CUGRAPH_SUCCESS; + cugraph_error_t* ret_error; + size_t num_edges = 8; + size_t num_vertices = 7; + double alpha = 0.95; + double epsilon = 0.0001; + size_t max_iterations = 20; + + vertex_t h_vertices[] = { 0, 1, 2, 3, 4, 5, 6 }; + vertex_t h_src[] = {0, 1, 1, 2, 2, 2, 3, 4}; + vertex_t h_dst[] = {1, 3, 4, 0, 1, 3, 5, 5}; + weight_t h_wgt[] = {0.1f, 2.1f, 1.1f, 5.1f, 3.1f, 4.1f, 7.2f, 3.2f}; + weight_t h_result[] = { 0.0859168, 0.158029, 0.0616337, 0.179675, 0.113239, 0.339873, 0.0616337 }; + + cugraph_resource_handle_t* handle = NULL; + cugraph_graph_t* graph = NULL; + cugraph_graph_properties_t properties; + + properties.is_symmetric = FALSE; + properties.is_multigraph = FALSE; + + data_type_id_t vertex_tid = INT32; + data_type_id_t edge_tid = INT32; + data_type_id_t weight_tid = FLOAT32; + + handle = cugraph_create_resource_handle(NULL); + TEST_ASSERT(test_ret_value, handle != NULL, "resource handle creation failed."); + + cugraph_type_erased_device_array_t* vertices; + cugraph_type_erased_device_array_t* src; + cugraph_type_erased_device_array_t* dst; + cugraph_type_erased_device_array_t* wgt; + cugraph_type_erased_device_array_view_t* vertices_view; + cugraph_type_erased_device_array_view_t* src_view; + cugraph_type_erased_device_array_view_t* dst_view; + cugraph_type_erased_device_array_view_t* wgt_view; + + ret_code = + cugraph_type_erased_device_array_create(handle, num_vertices, vertex_tid, &vertices, &ret_error); + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "vertices create failed."); + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, cugraph_error_message(ret_error)); + + ret_code = + cugraph_type_erased_device_array_create(handle, num_edges, vertex_tid, &src, &ret_error); + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "src create failed."); + + ret_code = + cugraph_type_erased_device_array_create(handle, num_edges, vertex_tid, &dst, &ret_error); + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "dst create failed."); + + ret_code = + cugraph_type_erased_device_array_create(handle, num_edges, weight_tid, &wgt, &ret_error); + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "wgt create failed."); + + vertices_view = cugraph_type_erased_device_array_view(vertices); + src_view = cugraph_type_erased_device_array_view(src); + dst_view = cugraph_type_erased_device_array_view(dst); + wgt_view = cugraph_type_erased_device_array_view(wgt); + + ret_code = cugraph_type_erased_device_array_view_copy_from_host( + handle, vertices_view, (byte_t*)h_vertices, &ret_error); + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "vertices copy_from_host failed."); + + ret_code = cugraph_type_erased_device_array_view_copy_from_host( + handle, src_view, (byte_t*)h_src, &ret_error); + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "src copy_from_host failed."); + + ret_code = cugraph_type_erased_device_array_view_copy_from_host( + handle, dst_view, (byte_t*)h_dst, &ret_error); + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "dst copy_from_host failed."); + + ret_code = cugraph_type_erased_device_array_view_copy_from_host( + handle, wgt_view, (byte_t*)h_wgt, &ret_error); + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "wgt copy_from_host failed."); + + ret_code = cugraph_graph_create_sg(handle, + &properties, + vertices_view, + src_view, + dst_view, + wgt_view, + NULL, + NULL, + FALSE, + FALSE, + FALSE, + FALSE, + FALSE, + &graph, + &ret_error); + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "graph creation failed."); + + cugraph_centrality_result_t* result = NULL; + + // To verify we will call pagerank + ret_code = cugraph_pagerank(handle, + graph, + NULL, + NULL, + NULL, + NULL, + alpha, + epsilon, + max_iterations, + FALSE, + &result, + &ret_error); + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "cugraph_pagerank failed."); + TEST_ALWAYS_ASSERT(ret_code == CUGRAPH_SUCCESS, cugraph_error_message(ret_error)); + + cugraph_type_erased_device_array_view_t* result_vertices; + cugraph_type_erased_device_array_view_t* pageranks; + + result_vertices = cugraph_centrality_result_get_vertices(result); + pageranks = cugraph_centrality_result_get_values(result); + + vertex_t h_result_vertices[num_vertices]; + weight_t h_pageranks[num_vertices]; + + ret_code = cugraph_type_erased_device_array_view_copy_to_host( + handle, (byte_t*)h_result_vertices, result_vertices, &ret_error); + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "copy_to_host failed."); + + ret_code = cugraph_type_erased_device_array_view_copy_to_host( + handle, (byte_t*)h_pageranks, pageranks, &ret_error); + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "copy_to_host failed."); + + for (int i = 0; (i < num_vertices) && (test_ret_value == 0); ++i) { + TEST_ASSERT(test_ret_value, + nearlyEqual(h_result[h_result_vertices[i]], h_pageranks[i], 0.001), + "pagerank results don't match"); + } + + cugraph_centrality_result_free(result); + cugraph_graph_free(graph); + + cugraph_type_erased_device_array_view_free(wgt_view); + cugraph_type_erased_device_array_view_free(dst_view); + cugraph_type_erased_device_array_view_free(src_view); + cugraph_type_erased_device_array_view_free(vertices_view); + cugraph_type_erased_device_array_free(wgt); + cugraph_type_erased_device_array_free(dst); + cugraph_type_erased_device_array_free(src); + cugraph_type_erased_device_array_free(vertices); + + cugraph_free_resource_handle(handle); + cugraph_error_free(ret_error); + + return test_ret_value; +} + +int test_create_sg_graph_csr_with_isolated() +{ + int test_ret_value = 0; + + typedef int32_t vertex_t; + typedef int32_t edge_t; + typedef float weight_t; + + cugraph_error_code_t ret_code = CUGRAPH_SUCCESS; + cugraph_error_t* ret_error; + size_t num_edges = 8; + size_t num_vertices = 7; + double alpha = 0.95; + double epsilon = 0.0001; + size_t max_iterations = 20; + + /* + vertex_t h_src[] = {0, 1, 1, 2, 2, 2, 3, 4}; + vertex_t h_dst[] = {1, 3, 4, 0, 1, 3, 5, 5}; + */ + edge_t h_offsets[] = {0, 1, 3, 6, 7, 8, 8, 8}; + vertex_t h_indices[] = {1, 3, 4, 0, 1, 3, 5, 5}; + vertex_t h_start[] = {0, 1, 2, 3, 4, 5}; + weight_t h_wgt[] = {0.1f, 2.1f, 1.1f, 5.1f, 3.1f, 4.1f, 7.2f, 3.2f}; + weight_t h_result[] = { 0.0859168, 0.158029, 0.0616337, 0.179675, 0.113239, 0.339873, 0.0616337 }; + + cugraph_resource_handle_t* handle = NULL; + cugraph_graph_t* graph = NULL; + cugraph_graph_properties_t properties; + + properties.is_symmetric = FALSE; + properties.is_multigraph = FALSE; + + data_type_id_t vertex_tid = INT32; + data_type_id_t edge_tid = INT32; + data_type_id_t weight_tid = FLOAT32; + + handle = cugraph_create_resource_handle(NULL); + TEST_ASSERT(test_ret_value, handle != NULL, "resource handle creation failed."); + + cugraph_type_erased_device_array_t* offsets; + cugraph_type_erased_device_array_t* indices; + cugraph_type_erased_device_array_t* wgt; + cugraph_type_erased_device_array_view_t* offsets_view; + cugraph_type_erased_device_array_view_t* indices_view; + cugraph_type_erased_device_array_view_t* wgt_view; + + ret_code = cugraph_type_erased_device_array_create( + handle, num_vertices + 1, vertex_tid, &offsets, &ret_error); + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "offsets create failed."); + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, cugraph_error_message(ret_error)); + + ret_code = + cugraph_type_erased_device_array_create(handle, num_edges, vertex_tid, &indices, &ret_error); + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "indices create failed."); + + ret_code = + cugraph_type_erased_device_array_create(handle, num_edges, weight_tid, &wgt, &ret_error); + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "wgt create failed."); + + offsets_view = cugraph_type_erased_device_array_view(offsets); + indices_view = cugraph_type_erased_device_array_view(indices); + wgt_view = cugraph_type_erased_device_array_view(wgt); + + ret_code = cugraph_type_erased_device_array_view_copy_from_host( + handle, offsets_view, (byte_t*)h_offsets, &ret_error); + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "offsets copy_from_host failed."); + + ret_code = cugraph_type_erased_device_array_view_copy_from_host( + handle, indices_view, (byte_t*)h_indices, &ret_error); + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "indices copy_from_host failed."); + + ret_code = cugraph_type_erased_device_array_view_copy_from_host( + handle, wgt_view, (byte_t*)h_wgt, &ret_error); + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "wgt copy_from_host failed."); + + ret_code = cugraph_sg_graph_create_from_csr(handle, + &properties, + offsets_view, + indices_view, + wgt_view, + NULL, + NULL, + FALSE, + FALSE, + FALSE, + &graph, + &ret_error); + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "graph creation failed."); + + cugraph_centrality_result_t* result = NULL; + + // To verify we will call pagerank + ret_code = cugraph_pagerank(handle, + graph, + NULL, + NULL, + NULL, + NULL, + alpha, + epsilon, + max_iterations, + FALSE, + &result, + &ret_error); + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "cugraph_pagerank failed."); + TEST_ALWAYS_ASSERT(ret_code == CUGRAPH_SUCCESS, cugraph_error_message(ret_error)); + + cugraph_type_erased_device_array_view_t* result_vertices; + cugraph_type_erased_device_array_view_t* pageranks; + + result_vertices = cugraph_centrality_result_get_vertices(result); + pageranks = cugraph_centrality_result_get_values(result); + + vertex_t h_result_vertices[num_vertices]; + weight_t h_pageranks[num_vertices]; + + ret_code = cugraph_type_erased_device_array_view_copy_to_host( + handle, (byte_t*)h_result_vertices, result_vertices, &ret_error); + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "copy_to_host failed."); + + ret_code = cugraph_type_erased_device_array_view_copy_to_host( + handle, (byte_t*)h_pageranks, pageranks, &ret_error); + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "copy_to_host failed."); + + for (int i = 0; (i < num_vertices) && (test_ret_value == 0); ++i) { + TEST_ASSERT(test_ret_value, + nearlyEqual(h_result[h_result_vertices[i]], h_pageranks[i], 0.001), + "pagerank results don't match"); + } + + cugraph_centrality_result_free(result); + cugraph_graph_free(graph); + cugraph_type_erased_device_array_view_free(wgt_view); + cugraph_type_erased_device_array_view_free(indices_view); + cugraph_type_erased_device_array_view_free(offsets_view); + cugraph_type_erased_device_array_free(wgt); + cugraph_type_erased_device_array_free(indices); + cugraph_type_erased_device_array_free(offsets); + + cugraph_free_resource_handle(handle); + cugraph_error_free(ret_error); + + return test_ret_value; +} + +int test_create_sg_graph_with_isolated_vertices_multi_input() +{ + int test_ret_value = 0; + + typedef int32_t vertex_t; + typedef int32_t edge_t; + typedef float weight_t; + + cugraph_error_code_t ret_code = CUGRAPH_SUCCESS; + cugraph_error_t* ret_error; + size_t num_edges = 66; + size_t num_vertices = 7; + double alpha = 0.95; + double epsilon = 0.0001; + size_t max_iterations = 20; + + vertex_t h_vertices[] = { 0, 1, 2, 3, 4, 5, 6 }; + vertex_t h_src[] = {0, 1, 1, 2, 2, 2, 3, 4, 4, 4, 5, + 0, 1, 1, 2, 2, 2, 3, 4, 4, 4, 5, + 0, 1, 1, 2, 2, 2, 3, 4, 4, 4, 5, + 0, 1, 1, 2, 2, 2, 3, 4, 4, 4, 5, + 0, 1, 1, 2, 2, 2, 3, 4, 4, 4, 5, + 0, 1, 1, 2, 2, 2, 3, 4, 4, 4, 5}; + vertex_t h_dst[] = {1, 3, 4, 0, 1, 3, 5, 5, 5, 5, 5, + 1, 3, 4, 0, 1, 3, 5, 5, 5, 5, 5, + 1, 3, 4, 0, 1, 3, 5, 5, 5, 5, 5, + 1, 3, 4, 0, 1, 3, 5, 5, 5, 5, 5, + 1, 3, 4, 0, 1, 3, 5, 5, 5, 5, 5, + 1, 3, 4, 0, 1, 3, 5, 5, 5, 5, 5}; + weight_t h_wgt[] = {0.1f, 2.1f, 1.1f, 5.1f, 3.1f, 4.1f, 7.2f, 3.2f, 3.2f, 3.2f, 1.7f, + 0.1f, 2.1f, 1.1f, 5.1f, 3.1f, 4.1f, 7.2f, 3.2f, 3.2f, 3.2f, 1.7f, + 0.1f, 2.1f, 1.1f, 5.1f, 3.1f, 4.1f, 7.2f, 3.2f, 3.2f, 3.2f, 1.7f, + 0.1f, 2.1f, 1.1f, 5.1f, 3.1f, 4.1f, 7.2f, 3.2f, 3.2f, 3.2f, 1.7f, + 0.1f, 2.1f, 1.1f, 5.1f, 3.1f, 4.1f, 7.2f, 3.2f, 3.2f, 3.2f, 1.7f, + 0.1f, 2.1f, 1.1f, 5.1f, 3.1f, 4.1f, 7.2f, 3.2f, 3.2f, 3.2f, 1.7f}; + weight_t h_result[] = { 0.0859168, 0.158029, 0.0616337, 0.179675, 0.113239, 0.339873, 0.0616337 }; + + cugraph_resource_handle_t* handle = NULL; + cugraph_graph_t* graph = NULL; + cugraph_graph_properties_t properties; + + properties.is_symmetric = FALSE; + properties.is_multigraph = FALSE; + + data_type_id_t vertex_tid = INT32; + data_type_id_t edge_tid = INT32; + data_type_id_t weight_tid = FLOAT32; + + handle = cugraph_create_resource_handle(NULL); + TEST_ASSERT(test_ret_value, handle != NULL, "resource handle creation failed."); + + cugraph_type_erased_device_array_t* vertices; + cugraph_type_erased_device_array_t* src; + cugraph_type_erased_device_array_t* dst; + cugraph_type_erased_device_array_t* wgt; + cugraph_type_erased_device_array_view_t* vertices_view; + cugraph_type_erased_device_array_view_t* src_view; + cugraph_type_erased_device_array_view_t* dst_view; + cugraph_type_erased_device_array_view_t* wgt_view; + + ret_code = + cugraph_type_erased_device_array_create(handle, num_vertices, vertex_tid, &vertices, &ret_error); + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "vertices create failed."); + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, cugraph_error_message(ret_error)); + + ret_code = + cugraph_type_erased_device_array_create(handle, num_edges, vertex_tid, &src, &ret_error); + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "src create failed."); + + ret_code = + cugraph_type_erased_device_array_create(handle, num_edges, vertex_tid, &dst, &ret_error); + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "dst create failed."); + + ret_code = + cugraph_type_erased_device_array_create(handle, num_edges, weight_tid, &wgt, &ret_error); + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "wgt create failed."); + + vertices_view = cugraph_type_erased_device_array_view(vertices); + src_view = cugraph_type_erased_device_array_view(src); + dst_view = cugraph_type_erased_device_array_view(dst); + wgt_view = cugraph_type_erased_device_array_view(wgt); + + ret_code = cugraph_type_erased_device_array_view_copy_from_host( + handle, vertices_view, (byte_t*)h_vertices, &ret_error); + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "vertices copy_from_host failed."); + + ret_code = cugraph_type_erased_device_array_view_copy_from_host( + handle, src_view, (byte_t*)h_src, &ret_error); + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "src copy_from_host failed."); + + ret_code = cugraph_type_erased_device_array_view_copy_from_host( + handle, dst_view, (byte_t*)h_dst, &ret_error); + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "dst copy_from_host failed."); + + ret_code = cugraph_type_erased_device_array_view_copy_from_host( + handle, wgt_view, (byte_t*)h_wgt, &ret_error); + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "wgt copy_from_host failed."); + + ret_code = cugraph_graph_create_sg(handle, + &properties, + vertices_view, + src_view, + dst_view, + wgt_view, + NULL, + NULL, + FALSE, + FALSE, + TRUE, + TRUE, + FALSE, + &graph, + &ret_error); + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "graph creation failed."); + + cugraph_centrality_result_t* result = NULL; + + // To verify we will call pagerank + ret_code = cugraph_pagerank(handle, + graph, + NULL, + NULL, + NULL, + NULL, + alpha, + epsilon, + max_iterations, + FALSE, + &result, + &ret_error); + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "cugraph_pagerank failed."); + TEST_ALWAYS_ASSERT(ret_code == CUGRAPH_SUCCESS, cugraph_error_message(ret_error)); + + cugraph_type_erased_device_array_view_t* result_vertices; + cugraph_type_erased_device_array_view_t* pageranks; + + result_vertices = cugraph_centrality_result_get_vertices(result); + pageranks = cugraph_centrality_result_get_values(result); + + vertex_t h_result_vertices[num_vertices]; + weight_t h_pageranks[num_vertices]; + + ret_code = cugraph_type_erased_device_array_view_copy_to_host( + handle, (byte_t*)h_result_vertices, result_vertices, &ret_error); + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "copy_to_host failed."); + + ret_code = cugraph_type_erased_device_array_view_copy_to_host( + handle, (byte_t*)h_pageranks, pageranks, &ret_error); + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "copy_to_host failed."); + + for (int i = 0; (i < num_vertices) && (test_ret_value == 0); ++i) { + TEST_ASSERT(test_ret_value, + nearlyEqual(h_result[h_result_vertices[i]], h_pageranks[i], 0.001), + "pagerank results don't match"); + } + + cugraph_centrality_result_free(result); + cugraph_graph_free(graph); cugraph_type_erased_device_array_view_free(wgt_view); cugraph_type_erased_device_array_view_free(dst_view); cugraph_type_erased_device_array_view_free(src_view); + cugraph_type_erased_device_array_view_free(vertices_view); cugraph_type_erased_device_array_free(wgt); cugraph_type_erased_device_array_free(dst); cugraph_type_erased_device_array_free(src); + cugraph_type_erased_device_array_free(vertices); cugraph_free_resource_handle(handle); cugraph_error_free(ret_error); @@ -419,5 +904,8 @@ int main(int argc, char** argv) result |= RUN_TEST(test_create_sg_graph_simple); result |= RUN_TEST(test_create_sg_graph_csr); result |= RUN_TEST(test_create_sg_graph_symmetric_error); + result |= RUN_TEST(test_create_sg_graph_with_isolated_vertices); + result |= RUN_TEST(test_create_sg_graph_csr_with_isolated); + result |= RUN_TEST(test_create_sg_graph_with_isolated_vertices_multi_input); return result; } diff --git a/cpp/tests/c_api/mg_create_graph_test.c b/cpp/tests/c_api/mg_create_graph_test.c index 4c8f2f22982..fec319d1881 100644 --- a/cpp/tests/c_api/mg_create_graph_test.c +++ b/cpp/tests/c_api/mg_create_graph_test.c @@ -17,6 +17,8 @@ #include "c_test_utils.h" /* RUN_TEST */ #include "mg_test_utils.h" /* RUN_TEST */ +#include + #include #include #include @@ -41,7 +43,7 @@ int test_create_mg_graph_simple(const cugraph_resource_handle_t* handle) vertex_t h_dst[] = {1, 3, 4, 0, 1, 3, 5, 5}; weight_t h_wgt[] = {0.1f, 2.1f, 1.1f, 5.1f, 3.1f, 4.1f, 7.2f, 3.2f}; - cugraph_graph_t* p_graph = NULL; + cugraph_graph_t* graph = NULL; cugraph_graph_properties_t properties; properties.is_symmetric = FALSE; @@ -94,21 +96,25 @@ int test_create_mg_graph_simple(const cugraph_resource_handle_t* handle) handle, wgt_view, (byte_t*)h_wgt, &ret_error); TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "wgt copy_from_host failed."); - ret_code = cugraph_mg_graph_create(handle, + ret_code = cugraph_graph_create_mg(handle, &properties, - src_view, - dst_view, - wgt_view, + NULL, + (cugraph_type_erased_device_array_view_t const* const*) &src_view, + (cugraph_type_erased_device_array_view_t const* const*) &dst_view, + (cugraph_type_erased_device_array_view_t const* const*) &wgt_view, NULL, NULL, FALSE, - num_edges, + 1, + FALSE, + FALSE, TRUE, - &p_graph, + &graph, &ret_error); TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "graph creation failed."); + TEST_ALWAYS_ASSERT(ret_code == CUGRAPH_SUCCESS, cugraph_error_message(ret_error)); - cugraph_mg_graph_free(p_graph); + cugraph_graph_free(graph); cugraph_type_erased_device_array_view_free(wgt_view); cugraph_type_erased_device_array_view_free(dst_view); @@ -122,6 +128,382 @@ int test_create_mg_graph_simple(const cugraph_resource_handle_t* handle) return test_ret_value; } +int test_create_mg_graph_multiple_edge_lists(const cugraph_resource_handle_t* handle) +{ + int test_ret_value = 0; + + typedef int32_t vertex_t; + typedef int32_t edge_t; + typedef float weight_t; + + cugraph_error_code_t ret_code = CUGRAPH_SUCCESS; + cugraph_error_t* ret_error; + size_t num_edges = 8; + size_t num_vertices = 7; + + double alpha = 0.95; + double epsilon = 0.0001; + size_t max_iterations = 20; + + vertex_t h_vertices[] = { 0, 1, 2, 3, 4, 5, 6 }; + vertex_t h_src[] = {0, 1, 1, 2, 2, 2, 3, 4}; + vertex_t h_dst[] = {1, 3, 4, 0, 1, 3, 5, 5}; + weight_t h_wgt[] = {0.1f, 2.1f, 1.1f, 5.1f, 3.1f, 4.1f, 7.2f, 3.2f}; + weight_t h_result[] = { 0.0859168, 0.158029, 0.0616337, 0.179675, 0.113239, 0.339873, 0.0616337 }; + + cugraph_graph_t* graph = NULL; + cugraph_graph_properties_t properties; + + properties.is_symmetric = FALSE; + properties.is_multigraph = FALSE; + + data_type_id_t vertex_tid = INT32; + data_type_id_t edge_tid = INT32; + data_type_id_t weight_tid = FLOAT32; + + const size_t num_local_arrays = 2; + + cugraph_type_erased_device_array_t* vertices[num_local_arrays]; + cugraph_type_erased_device_array_t* src[num_local_arrays]; + cugraph_type_erased_device_array_t* dst[num_local_arrays]; + cugraph_type_erased_device_array_t* wgt[num_local_arrays]; + cugraph_type_erased_device_array_view_t* vertices_view[num_local_arrays]; + cugraph_type_erased_device_array_view_t* src_view[num_local_arrays]; + cugraph_type_erased_device_array_view_t* dst_view[num_local_arrays]; + cugraph_type_erased_device_array_view_t* wgt_view[num_local_arrays]; + + int my_rank = cugraph_resource_handle_get_rank(handle); + int comm_size = cugraph_resource_handle_get_comm_size(handle); + + size_t local_num_vertices = (num_vertices + comm_size - 1) / comm_size; + size_t local_start_vertex = my_rank * local_num_vertices; + size_t local_num_edges = (num_edges + comm_size - 1) / comm_size; + size_t local_start_edge = my_rank * local_num_edges; + + local_num_edges = (local_num_edges < (num_edges - local_start_edge)) ? local_num_edges : (num_edges - local_start_edge); + local_num_vertices = (local_num_vertices < (num_vertices - local_start_vertex)) ? local_num_vertices : (num_vertices - local_start_vertex); + + for (size_t i = 0 ; i < num_local_arrays ; ++i) { + size_t vertex_count = (local_num_vertices + num_local_arrays - 1) / num_local_arrays; + size_t vertex_start = i * vertex_count; + vertex_count = (vertex_count < (local_num_vertices - vertex_start)) ? vertex_count : (local_num_vertices - vertex_start); + + ret_code = + cugraph_type_erased_device_array_create(handle, vertex_count, vertex_tid, vertices + i, &ret_error); + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "vertices create failed."); + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, cugraph_error_message(ret_error)); + + size_t edge_count = (local_num_edges + num_local_arrays - 1) / num_local_arrays; + size_t edge_start = i * edge_count; + edge_count = (edge_count < (local_num_edges - edge_start)) ? edge_count : (local_num_edges - edge_start); + + ret_code = + cugraph_type_erased_device_array_create(handle, edge_count, vertex_tid, src + i, &ret_error); + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "src create failed."); + + ret_code = + cugraph_type_erased_device_array_create(handle, edge_count, vertex_tid, dst + i, &ret_error); + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "dst create failed."); + + ret_code = + cugraph_type_erased_device_array_create(handle, edge_count, weight_tid, wgt + i, &ret_error); + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "wgt create failed."); + + vertices_view[i] = cugraph_type_erased_device_array_view(vertices[i]); + src_view[i] = cugraph_type_erased_device_array_view(src[i]); + dst_view[i] = cugraph_type_erased_device_array_view(dst[i]); + wgt_view[i] = cugraph_type_erased_device_array_view(wgt[i]); + + ret_code = cugraph_type_erased_device_array_view_copy_from_host( + handle, vertices_view[i], (byte_t*)(h_vertices + local_start_vertex + vertex_start), &ret_error); + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "src copy_from_host failed."); + + ret_code = cugraph_type_erased_device_array_view_copy_from_host( + handle, src_view[i], (byte_t*)(h_src + local_start_edge + edge_start), &ret_error); + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "src copy_from_host failed."); + + ret_code = cugraph_type_erased_device_array_view_copy_from_host( + handle, dst_view[i], (byte_t*)(h_dst + local_start_edge + edge_start), &ret_error); + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "dst copy_from_host failed."); + + ret_code = cugraph_type_erased_device_array_view_copy_from_host( + handle, wgt_view[i], (byte_t*)(h_wgt + local_start_edge + edge_start), &ret_error); + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "wgt copy_from_host failed."); + } + + ret_code = cugraph_graph_create_mg(handle, + &properties, + (cugraph_type_erased_device_array_view_t const* const*) vertices_view, + (cugraph_type_erased_device_array_view_t const* const*) src_view, + (cugraph_type_erased_device_array_view_t const* const*) dst_view, + (cugraph_type_erased_device_array_view_t const* const*) wgt_view, + NULL, + NULL, + FALSE, + num_local_arrays, + FALSE, + FALSE, + TRUE, + &graph, + &ret_error); + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "graph creation failed."); + TEST_ALWAYS_ASSERT(ret_code == CUGRAPH_SUCCESS, cugraph_error_message(ret_error)); + + // + // Now call pagerank and check results... + // + cugraph_centrality_result_t* result = NULL; + + ret_code = cugraph_pagerank(handle, + graph, + NULL, + NULL, + NULL, + NULL, + alpha, + epsilon, + max_iterations, + FALSE, + &result, + &ret_error); + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "cugraph_pagerank failed."); + + // NOTE: Because we get back vertex ids and pageranks, we can simply compare + // the returned values with the expected results for the entire + // graph. Each GPU will have a subset of the total vertices, so + // they will do a subset of the comparisons. + cugraph_type_erased_device_array_view_t* result_vertices; + cugraph_type_erased_device_array_view_t* pageranks; + + result_vertices = cugraph_centrality_result_get_vertices(result); + pageranks = cugraph_centrality_result_get_values(result); + + size_t num_local_vertices = cugraph_type_erased_device_array_view_size(result_vertices); + + vertex_t h_result_vertices[num_local_vertices]; + weight_t h_pageranks[num_local_vertices]; + + ret_code = cugraph_type_erased_device_array_view_copy_to_host( + handle, (byte_t*)h_result_vertices, result_vertices, &ret_error); + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "copy_to_host failed."); + + ret_code = cugraph_type_erased_device_array_view_copy_to_host( + handle, (byte_t*)h_pageranks, pageranks, &ret_error); + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "copy_to_host failed."); + + for (int i = 0; (i < num_local_vertices) && (test_ret_value == 0); ++i) { + TEST_ASSERT(test_ret_value, + nearlyEqual(h_result[h_result_vertices[i]], h_pageranks[i], 0.001), + "pagerank results don't match"); + } + + cugraph_centrality_result_free(result); + cugraph_graph_free(graph); + + for (size_t i = 0 ; i < num_local_arrays ; ++i) { + cugraph_type_erased_device_array_view_free(wgt_view[i]); + cugraph_type_erased_device_array_view_free(dst_view[i]); + cugraph_type_erased_device_array_view_free(src_view[i]); + cugraph_type_erased_device_array_view_free(vertices_view[i]); + cugraph_type_erased_device_array_free(wgt[i]); + cugraph_type_erased_device_array_free(dst[i]); + cugraph_type_erased_device_array_free(src[i]); + cugraph_type_erased_device_array_free(vertices[i]); + } + + cugraph_error_free(ret_error); + + return test_ret_value; +} + +int test_create_mg_graph_multiple_edge_lists_multi_edge(const cugraph_resource_handle_t* handle) +{ + int test_ret_value = 0; + + typedef int32_t vertex_t; + typedef int32_t edge_t; + typedef float weight_t; + + cugraph_error_code_t ret_code = CUGRAPH_SUCCESS; + cugraph_error_t* ret_error; + size_t num_edges = 11; + size_t num_vertices = 7; + + double alpha = 0.95; + double epsilon = 0.0001; + size_t max_iterations = 20; + + vertex_t h_vertices[] = { 0, 1, 2, 3, 4, 5, 6 }; + vertex_t h_src[] = {0, 1, 1, 2, 2, 2, 3, 4, 4, 4, 5}; + vertex_t h_dst[] = {1, 3, 4, 0, 1, 3, 5, 5, 5, 5, 5}; + weight_t h_wgt[] = {0.1f, 2.1f, 1.1f, 5.1f, 3.1f, 4.1f, 7.2f, 3.2f, 3.2f, 3.2f, 1.1f}; + weight_t h_result[] = { 0.0859168, 0.158029, 0.0616337, 0.179675, 0.113239, 0.339873, 0.0616337 }; + + cugraph_graph_t* graph = NULL; + cugraph_graph_properties_t properties; + + properties.is_symmetric = FALSE; + properties.is_multigraph = FALSE; + + data_type_id_t vertex_tid = INT32; + data_type_id_t edge_tid = INT32; + data_type_id_t weight_tid = FLOAT32; + + const size_t num_local_arrays = 2; + + cugraph_type_erased_device_array_t* vertices[num_local_arrays]; + cugraph_type_erased_device_array_t* src[num_local_arrays]; + cugraph_type_erased_device_array_t* dst[num_local_arrays]; + cugraph_type_erased_device_array_t* wgt[num_local_arrays]; + cugraph_type_erased_device_array_view_t* vertices_view[num_local_arrays]; + cugraph_type_erased_device_array_view_t* src_view[num_local_arrays]; + cugraph_type_erased_device_array_view_t* dst_view[num_local_arrays]; + cugraph_type_erased_device_array_view_t* wgt_view[num_local_arrays]; + + int my_rank = cugraph_resource_handle_get_rank(handle); + int comm_size = cugraph_resource_handle_get_comm_size(handle); + + size_t local_num_vertices = (num_vertices + comm_size - 1) / comm_size; + size_t local_start_vertex = my_rank * local_num_vertices; + size_t local_num_edges = (num_edges + comm_size - 1) / comm_size; + size_t local_start_edge = my_rank * local_num_edges; + + local_num_edges = (local_num_edges < (num_edges - local_start_edge)) ? local_num_edges : (num_edges - local_start_edge); + local_num_vertices = (local_num_vertices < (num_vertices - local_start_vertex)) ? local_num_vertices : (num_vertices - local_start_vertex); + + for (size_t i = 0 ; i < num_local_arrays ; ++i) { + size_t vertex_count = (local_num_vertices + num_local_arrays - 1) / num_local_arrays; + size_t vertex_start = i * vertex_count; + vertex_count = (vertex_count < (local_num_vertices - vertex_start)) ? vertex_count : (local_num_vertices - vertex_start); + + ret_code = + cugraph_type_erased_device_array_create(handle, vertex_count, vertex_tid, vertices + i, &ret_error); + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "vertices create failed."); + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, cugraph_error_message(ret_error)); + + size_t edge_count = (local_num_edges + num_local_arrays - 1) / num_local_arrays; + size_t edge_start = i * edge_count; + edge_count = (edge_count < (local_num_edges - edge_start)) ? edge_count : (local_num_edges - edge_start); + + ret_code = + cugraph_type_erased_device_array_create(handle, edge_count, vertex_tid, src + i, &ret_error); + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "src create failed."); + + ret_code = + cugraph_type_erased_device_array_create(handle, edge_count, vertex_tid, dst + i, &ret_error); + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "dst create failed."); + + ret_code = + cugraph_type_erased_device_array_create(handle, edge_count, weight_tid, wgt + i, &ret_error); + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "wgt create failed."); + + vertices_view[i] = cugraph_type_erased_device_array_view(vertices[i]); + src_view[i] = cugraph_type_erased_device_array_view(src[i]); + dst_view[i] = cugraph_type_erased_device_array_view(dst[i]); + wgt_view[i] = cugraph_type_erased_device_array_view(wgt[i]); + + ret_code = cugraph_type_erased_device_array_view_copy_from_host( + handle, vertices_view[i], (byte_t*)(h_vertices + local_start_vertex + vertex_start), &ret_error); + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "src copy_from_host failed."); + + ret_code = cugraph_type_erased_device_array_view_copy_from_host( + handle, src_view[i], (byte_t*)(h_src + local_start_edge + edge_start), &ret_error); + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "src copy_from_host failed."); + + ret_code = cugraph_type_erased_device_array_view_copy_from_host( + handle, dst_view[i], (byte_t*)(h_dst + local_start_edge + edge_start), &ret_error); + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "dst copy_from_host failed."); + + ret_code = cugraph_type_erased_device_array_view_copy_from_host( + handle, wgt_view[i], (byte_t*)(h_wgt + local_start_edge + edge_start), &ret_error); + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "wgt copy_from_host failed."); + } + + ret_code = cugraph_graph_create_mg(handle, + &properties, + (cugraph_type_erased_device_array_view_t const* const*) vertices_view, + (cugraph_type_erased_device_array_view_t const* const*) src_view, + (cugraph_type_erased_device_array_view_t const* const*) dst_view, + (cugraph_type_erased_device_array_view_t const* const*) wgt_view, + NULL, + NULL, + FALSE, + num_local_arrays, + TRUE, + TRUE, + TRUE, + &graph, + &ret_error); + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "graph creation failed."); + TEST_ALWAYS_ASSERT(ret_code == CUGRAPH_SUCCESS, cugraph_error_message(ret_error)); + + // + // Now call pagerank and check results... + // + cugraph_centrality_result_t* result = NULL; + + ret_code = cugraph_pagerank(handle, + graph, + NULL, + NULL, + NULL, + NULL, + alpha, + epsilon, + max_iterations, + FALSE, + &result, + &ret_error); + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "cugraph_pagerank failed."); + + // NOTE: Because we get back vertex ids and pageranks, we can simply compare + // the returned values with the expected results for the entire + // graph. Each GPU will have a subset of the total vertices, so + // they will do a subset of the comparisons. + cugraph_type_erased_device_array_view_t* result_vertices; + cugraph_type_erased_device_array_view_t* pageranks; + + result_vertices = cugraph_centrality_result_get_vertices(result); + pageranks = cugraph_centrality_result_get_values(result); + + size_t num_local_vertices = cugraph_type_erased_device_array_view_size(result_vertices); + + vertex_t h_result_vertices[num_local_vertices]; + weight_t h_pageranks[num_local_vertices]; + + ret_code = cugraph_type_erased_device_array_view_copy_to_host( + handle, (byte_t*)h_result_vertices, result_vertices, &ret_error); + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "copy_to_host failed."); + + ret_code = cugraph_type_erased_device_array_view_copy_to_host( + handle, (byte_t*)h_pageranks, pageranks, &ret_error); + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "copy_to_host failed."); + + for (int i = 0; (i < num_local_vertices) && (test_ret_value == 0); ++i) { + TEST_ASSERT(test_ret_value, + nearlyEqual(h_result[h_result_vertices[i]], h_pageranks[i], 0.001), + "pagerank results don't match"); + } + + cugraph_centrality_result_free(result); + cugraph_graph_free(graph); + + for (size_t i = 0 ; i < num_local_arrays ; ++i) { + cugraph_type_erased_device_array_view_free(wgt_view[i]); + cugraph_type_erased_device_array_view_free(dst_view[i]); + cugraph_type_erased_device_array_view_free(src_view[i]); + cugraph_type_erased_device_array_view_free(vertices_view[i]); + cugraph_type_erased_device_array_free(wgt[i]); + cugraph_type_erased_device_array_free(dst[i]); + cugraph_type_erased_device_array_free(src[i]); + cugraph_type_erased_device_array_free(vertices[i]); + } + + cugraph_error_free(ret_error); + + return test_ret_value; +} + /******************************************************************************/ int main(int argc, char** argv) @@ -131,6 +513,8 @@ int main(int argc, char** argv) int result = 0; result |= RUN_MG_TEST(test_create_mg_graph_simple, handle); + result |= RUN_MG_TEST(test_create_mg_graph_multiple_edge_lists, handle); + result |= RUN_MG_TEST(test_create_mg_graph_multiple_edge_lists_multi_edge, handle); cugraph_free_resource_handle(handle); free_mg_raft_handle(raft_handle); From fceb6b7a4b34bc821700ba5729423a0cf76fbbdf Mon Sep 17 00:00:00 2001 From: Brad Rees <34135411+BradReesWork@users.noreply.github.com> Date: Tue, 21 Nov 2023 09:48:41 -0500 Subject: [PATCH 095/111] adding C/C++ API docs (#3938) adding C and C++ API Adding WholeGraph APIs Adding cugraph-ops APIs closes #3406 Authors: - Brad Rees (https://github.com/BradReesWork) - GALI PREM SAGAR (https://github.com/galipremsagar) - Ralph Liu (https://github.com/nv-rliu) - Don Acosta (https://github.com/acostadon) Approvers: - Seunghwa Kang (https://github.com/seunghwak) - Don Acosta (https://github.com/acostadon) - Rick Ratzel (https://github.com/rlratzel) - AJ Schmidt (https://github.com/ajschmidt8) URL: https://github.com/rapidsai/cugraph/pull/3938 --- .gitignore | 4 +- build.sh | 22 + ci/build_docs.sh | 7 +- ci/release/update-version.sh | 4 + cpp/doxygen/Doxyfile | 765 ++++++++++++------ cpp/include/cugraph_c/centrality_algorithms.h | 26 +- cpp/include/cugraph_c/community_algorithms.h | 1 - cpp/include/cugraph_c/core_algorithms.h | 14 +- cpp/include/cugraph_c/labeling_algorithms.h | 11 +- cpp/include/cugraph_c/sampling_algorithms.h | 3 +- cpp/include/cugraph_c/similarity_algorithms.h | 7 +- docs/cugraph/Makefile | 2 +- .../cugraph-ops/bipartite_operators.rst | 16 - .../api_docs/cugraph-ops/c_cpp/index.rst | 3 + .../api_docs/cugraph-ops/fg_operators.rst | 83 -- .../api_docs/cugraph-ops/graph_types.rst | 33 - .../source/api_docs/cugraph-ops/index.rst | 11 +- .../api_docs/cugraph-ops/mfg_operators.rst | 31 - .../cugraph-ops/{ => python}/dimenet.rst | 4 +- .../cugraph-ops/python/graph_types.rst | 34 + .../api_docs/cugraph-ops/python/index.rst | 13 + .../api_docs/cugraph-ops/python/operators.rst | 93 +++ .../cugraph-ops/{ => python}/pytorch.rst | 22 +- .../api_docs/cugraph-ops/static_operators.rst | 16 - .../api_docs/cugraph-pyg/cugraph_pyg.rst | 4 +- .../source/api_docs/cugraph_c/c_and_cpp.rst | 4 - .../source/api_docs/cugraph_c/centrality.rst | 51 ++ .../source/api_docs/cugraph_c/community.rst | 63 ++ .../source/api_docs/cugraph_c/core.rst | 21 + .../source/api_docs/cugraph_c/index.rst | 16 + .../source/api_docs/cugraph_c/labeling.rst | 20 + .../source/api_docs/cugraph_c/sampling.rst | 37 + .../source/api_docs/cugraph_c/similarity.rst | 25 + .../source/api_docs/cugraph_c/traversal.rst | 30 + docs/cugraph/source/api_docs/index.rst | 33 +- .../service/cugraph_service_client.rst | 2 +- .../service/cugraph_service_server.rst | 2 +- .../source/api_docs/wholegraph/index.rst | 11 + .../wholegraph/libwholegraph/index.rst | 228 ++++++ .../wholegraph/pylibwholegraph/index.rst | 38 + docs/cugraph/source/basics/cugraph_intro.md | 9 +- docs/cugraph/source/conf.py | 5 +- .../source/graph_support/algorithms.md | 12 +- .../graph_support/algorithms/Centrality.md | 12 +- .../graph_support/algorithms/Similarity.md | 6 +- docs/cugraph/source/index.rst | 10 +- .../source/wholegraph/basics/index.rst | 11 + .../wholegraph/basics/wholegraph_intro.md | 135 ++++ .../wholememory_implementation_details.md | 58 ++ .../wholegraph/basics/wholememory_intro.md | 123 +++ .../imgs/device_chunked_wholememory_step1.png | Bin 0 -> 23136 bytes .../imgs/device_chunked_wholememory_step2.png | Bin 0 -> 28201 bytes .../device_continuous_wholememory_step1.png | Bin 0 -> 21107 bytes .../device_continuous_wholememory_step2.png | Bin 0 -> 26434 bytes .../imgs/distributed_wholememory.png | Bin 0 -> 23138 bytes .../wholegraph/imgs/general_wholememory.png | Bin 0 -> 14073 bytes .../imgs/host_mapped_wholememory_step1.png | Bin 0 -> 21733 bytes .../imgs/host_mapped_wholememory_step2.png | Bin 0 -> 32110 bytes .../wholegraph/imgs/wholememory_tensor.png | Bin 0 -> 40367 bytes docs/cugraph/source/wholegraph/index.rst | 14 + .../wholegraph/installation/container.md | 29 + .../installation/getting_wholegraph.md | 48 ++ .../source/wholegraph/installation/index.rst | 9 + .../wholegraph/installation/source_build.md | 187 +++++ 64 files changed, 1951 insertions(+), 527 deletions(-) delete mode 100644 docs/cugraph/source/api_docs/cugraph-ops/bipartite_operators.rst create mode 100644 docs/cugraph/source/api_docs/cugraph-ops/c_cpp/index.rst delete mode 100644 docs/cugraph/source/api_docs/cugraph-ops/fg_operators.rst delete mode 100644 docs/cugraph/source/api_docs/cugraph-ops/graph_types.rst delete mode 100644 docs/cugraph/source/api_docs/cugraph-ops/mfg_operators.rst rename docs/cugraph/source/api_docs/cugraph-ops/{ => python}/dimenet.rst (89%) create mode 100644 docs/cugraph/source/api_docs/cugraph-ops/python/graph_types.rst create mode 100644 docs/cugraph/source/api_docs/cugraph-ops/python/index.rst create mode 100644 docs/cugraph/source/api_docs/cugraph-ops/python/operators.rst rename docs/cugraph/source/api_docs/cugraph-ops/{ => python}/pytorch.rst (59%) delete mode 100644 docs/cugraph/source/api_docs/cugraph-ops/static_operators.rst delete mode 100644 docs/cugraph/source/api_docs/cugraph_c/c_and_cpp.rst create mode 100644 docs/cugraph/source/api_docs/cugraph_c/centrality.rst create mode 100644 docs/cugraph/source/api_docs/cugraph_c/community.rst create mode 100644 docs/cugraph/source/api_docs/cugraph_c/core.rst create mode 100644 docs/cugraph/source/api_docs/cugraph_c/index.rst create mode 100644 docs/cugraph/source/api_docs/cugraph_c/labeling.rst create mode 100644 docs/cugraph/source/api_docs/cugraph_c/sampling.rst create mode 100644 docs/cugraph/source/api_docs/cugraph_c/similarity.rst create mode 100644 docs/cugraph/source/api_docs/cugraph_c/traversal.rst create mode 100644 docs/cugraph/source/api_docs/wholegraph/index.rst create mode 100644 docs/cugraph/source/api_docs/wholegraph/libwholegraph/index.rst create mode 100644 docs/cugraph/source/api_docs/wholegraph/pylibwholegraph/index.rst create mode 100644 docs/cugraph/source/wholegraph/basics/index.rst create mode 100644 docs/cugraph/source/wholegraph/basics/wholegraph_intro.md create mode 100644 docs/cugraph/source/wholegraph/basics/wholememory_implementation_details.md create mode 100644 docs/cugraph/source/wholegraph/basics/wholememory_intro.md create mode 100644 docs/cugraph/source/wholegraph/imgs/device_chunked_wholememory_step1.png create mode 100644 docs/cugraph/source/wholegraph/imgs/device_chunked_wholememory_step2.png create mode 100644 docs/cugraph/source/wholegraph/imgs/device_continuous_wholememory_step1.png create mode 100644 docs/cugraph/source/wholegraph/imgs/device_continuous_wholememory_step2.png create mode 100644 docs/cugraph/source/wholegraph/imgs/distributed_wholememory.png create mode 100644 docs/cugraph/source/wholegraph/imgs/general_wholememory.png create mode 100644 docs/cugraph/source/wholegraph/imgs/host_mapped_wholememory_step1.png create mode 100644 docs/cugraph/source/wholegraph/imgs/host_mapped_wholememory_step2.png create mode 100644 docs/cugraph/source/wholegraph/imgs/wholememory_tensor.png create mode 100644 docs/cugraph/source/wholegraph/index.rst create mode 100644 docs/cugraph/source/wholegraph/installation/container.md create mode 100644 docs/cugraph/source/wholegraph/installation/getting_wholegraph.md create mode 100644 docs/cugraph/source/wholegraph/installation/index.rst create mode 100644 docs/cugraph/source/wholegraph/installation/source_build.md diff --git a/.gitignore b/.gitignore index c6bcf6965d7..358650cfc5a 100644 --- a/.gitignore +++ b/.gitignore @@ -84,8 +84,10 @@ datasets/* # Jupyter Notebooks .ipynb_checkpoints -## Doxygen +## Doxygen and Docs cpp/doxygen/html +docs/cugraph/lib* +docs/cugraph/api/* # created by Dask tests python/dask-worker-space diff --git a/build.sh b/build.sh index 1723e750978..eef19046d85 100755 --- a/build.sh +++ b/build.sh @@ -18,6 +18,8 @@ ARGS=$* # script, and that this script resides in the repo dir! REPODIR=$(cd $(dirname $0); pwd) +RAPIDS_VERSION=23.12 + # Valid args to this script (all possible targets and options) - only one per line VALIDARGS=" clean @@ -412,8 +414,28 @@ if hasArg docs || hasArg all; then ${CMAKE_GENERATOR_OPTION} \ ${CMAKE_VERBOSE_OPTION} fi + + for PROJECT in libcugraphops libwholegraph; do + XML_DIR="${REPODIR}/docs/cugraph/${PROJECT}" + rm -rf "${XML_DIR}" + mkdir -p "${XML_DIR}" + export XML_DIR_${PROJECT^^}="$XML_DIR" + + echo "downloading xml for ${PROJECT} into ${XML_DIR}. Environment variable XML_DIR_${PROJECT^^} is set to ${XML_DIR}" + curl -O "https://d1664dvumjb44w.cloudfront.net/${PROJECT}/xml_tar/${RAPIDS_VERSION}/xml.tar.gz" + tar -xzf xml.tar.gz -C "${XML_DIR}" + rm "./xml.tar.gz" + done + cd ${LIBCUGRAPH_BUILD_DIR} cmake --build "${LIBCUGRAPH_BUILD_DIR}" -j${PARALLEL_LEVEL} --target docs_cugraph ${VERBOSE_FLAG} + + echo "making libcugraph doc dir" + rm -rf ${REPODIR}/docs/cugraph/libcugraph + mkdir -p ${REPODIR}/docs/cugraph/libcugraph + + export XML_DIR_LIBCUGRAPH="${REPODIR}/cpp/doxygen/xml" + cd ${REPODIR}/docs/cugraph make html fi diff --git a/ci/build_docs.sh b/ci/build_docs.sh index 3f97f652d41..3f765704bdb 100755 --- a/ci/build_docs.sh +++ b/ci/build_docs.sh @@ -29,7 +29,9 @@ rapids-mamba-retry install \ cugraph-pyg \ cugraph-service-server \ cugraph-service-client \ - libcugraph_etl + libcugraph_etl \ + pylibcugraphops \ + pylibwholegraph # This command installs `cugraph-dgl` without its dependencies # since this package can currently only run in `11.6` CTK environments @@ -50,8 +52,7 @@ done rapids-logger "Build CPP docs" pushd cpp/doxygen doxygen Doxyfile -mkdir -p "${RAPIDS_DOCS_DIR}/libcugraph/html" -mv html/* "${RAPIDS_DOCS_DIR}/libcugraph/html" +export XML_DIR_LIBCUGRAPH="$(pwd)/xml" popd rapids-logger "Build Python docs" diff --git a/ci/release/update-version.sh b/ci/release/update-version.sh index 69eb085e7ed..c091bd1ed33 100755 --- a/ci/release/update-version.sh +++ b/ci/release/update-version.sh @@ -54,6 +54,10 @@ sed_runner "s/set(cugraph_version .*)/set(cugraph_version ${NEXT_FULL_TAG})/g" p sed_runner 's/version = .*/version = '"'${NEXT_SHORT_TAG}'"'/g' docs/cugraph/source/conf.py sed_runner 's/release = .*/release = '"'${NEXT_FULL_TAG}'"'/g' docs/cugraph/source/conf.py + +# build.sh script +sed_runner 's/RAPIDS_VERSION=.*/RAPIDS_VERSION='${NEXT_SHORT_TAG}'/g' build.sh + # Centralized version file update # NOTE: Any script that runs in CI will need to use gha-tool `rapids-generate-version` # and echo it to `VERSION` file to get an alpha spec of the current version diff --git a/cpp/doxygen/Doxyfile b/cpp/doxygen/Doxyfile index 482ff988098..6946bd38bfe 100644 --- a/cpp/doxygen/Doxyfile +++ b/cpp/doxygen/Doxyfile @@ -1,4 +1,4 @@ -# Doxyfile 1.8.20 +# Doxyfile 1.9.8 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project. @@ -12,6 +12,16 @@ # For lists, items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (\" \"). +# +# Note: +# +# Use doxygen to compare the used configuration file with the template +# configuration file: +# doxygen -x [configFile] +# Use doxygen to compare the used configuration file with the template +# configuration file without replacing the environment variables or CMake type +# replacement variables: +# doxygen -x_noenv [configFile] #--------------------------------------------------------------------------- # Project related configuration options @@ -32,19 +42,19 @@ DOXYFILE_ENCODING = UTF-8 # title of most generated pages and in a few other places. # The default value is: My Project. -PROJECT_NAME = "libcugraph" +PROJECT_NAME = libcugraph # The PROJECT_NUMBER tag can be used to enter a project or revision number. This # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER=23.12 +PROJECT_NUMBER = 23.12 # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a # quick idea about the purpose of the project. Keep the description short. -PROJECT_BRIEF = GPU accelerated graph analytics +PROJECT_BRIEF = "GPU accelerated graph analytics" # With the PROJECT_LOGO tag one can specify a logo or an icon that is included # in the documentation. The maximum height of the logo should not exceed 55 @@ -60,16 +70,28 @@ PROJECT_LOGO = OUTPUT_DIRECTORY = -# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub- -# directories (in 2 levels) under the output directory of each output format and -# will distribute the generated files over these directories. Enabling this +# If the CREATE_SUBDIRS tag is set to YES then doxygen will create up to 4096 +# sub-directories (in 2 levels) under the output directory of each output format +# and will distribute the generated files over these directories. Enabling this # option can be useful when feeding doxygen a huge amount of source files, where # putting all generated files in the same directory would otherwise causes -# performance problems for the file system. +# performance problems for the file system. Adapt CREATE_SUBDIRS_LEVEL to +# control the number of sub-directories. # The default value is: NO. CREATE_SUBDIRS = NO +# Controls the number of sub-directories that will be created when +# CREATE_SUBDIRS tag is set to YES. Level 0 represents 16 directories, and every +# level increment doubles the number of directories, resulting in 4096 +# directories at level 8 which is the default and also the maximum value. The +# sub-directories are organized in 2 levels, the first level always has a fixed +# number of 16 directories. +# Minimum value: 0, maximum value: 8, default value: 8. +# This tag requires that the tag CREATE_SUBDIRS is set to YES. + +CREATE_SUBDIRS_LEVEL = 8 + # If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII # characters to appear in the names of generated files. If set to NO, non-ASCII # characters will be escaped, for example _xE3_x81_x84 will be used for Unicode @@ -81,26 +103,18 @@ ALLOW_UNICODE_NAMES = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. -# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, -# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), -# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, -# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), -# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, -# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, -# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, -# Ukrainian and Vietnamese. +# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Bulgarian, +# Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, Dutch, English +# (United States), Esperanto, Farsi (Persian), Finnish, French, German, Greek, +# Hindi, Hungarian, Indonesian, Italian, Japanese, Japanese-en (Japanese with +# English messages), Korean, Korean-en (Korean with English messages), Latvian, +# Lithuanian, Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, +# Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, +# Swedish, Turkish, Ukrainian and Vietnamese. # The default value is: English. OUTPUT_LANGUAGE = English -# The OUTPUT_TEXT_DIRECTION tag is used to specify the direction in which all -# documentation generated by doxygen is written. Doxygen will use this -# information to generate all generated output in the proper direction. -# Possible values are: None, LTR, RTL and Context. -# The default value is: None. - -OUTPUT_TEXT_DIRECTION = None - # If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member # descriptions after the members that are listed in the file and class # documentation (similar to Javadoc). Set to NO to disable this. @@ -248,16 +262,16 @@ TAB_SIZE = 4 # the documentation. An alias has the form: # name=value # For example adding -# "sideeffect=@par Side Effects:\n" +# "sideeffect=@par Side Effects:^^" # will allow you to put the command \sideeffect (or @sideeffect) in the # documentation, which will result in a user-defined paragraph with heading -# "Side Effects:". You can put \n's in the value part of an alias to insert -# newlines (in the resulting output). You can put ^^ in the value part of an -# alias to insert a newline as if a physical newline was in the original file. -# When you need a literal { or } or , in the value part of an alias you have to -# escape them by means of a backslash (\), this can lead to conflicts with the -# commands \{ and \} for these it is advised to use the version @{ and @} or use -# a double escape (\\{ and \\}) +# "Side Effects:". Note that you cannot put \n's in the value part of an alias +# to insert newlines (in the resulting output). You can put ^^ in the value part +# of an alias to insert a newline as if a physical newline was in the original +# file. When you need a literal { or } or , in the value part of an alias you +# have to escape them by means of a backslash (\), this can lead to conflicts +# with the commands \{ and \} for these it is advised to use the version @{ and +# @} or use a double escape (\\{ and \\}) ALIASES = @@ -302,8 +316,8 @@ OPTIMIZE_OUTPUT_SLICE = NO # extension. Doxygen has a built-in mapping, but you can override or extend it # using this tag. The format is ext=language, where ext is a file extension, and # language is one of the parsers supported by doxygen: IDL, Java, JavaScript, -# Csharp (C#), C, C++, D, PHP, md (Markdown), Objective-C, Python, Slice, VHDL, -# Fortran (fixed format Fortran: FortranFixed, free formatted Fortran: +# Csharp (C#), C, C++, Lex, D, PHP, md (Markdown), Objective-C, Python, Slice, +# VHDL, Fortran (fixed format Fortran: FortranFixed, free formatted Fortran: # FortranFree, unknown formatted Fortran: Fortran. In the later case the parser # tries to guess whether the code is fixed or free formatted code, this is the # default for Fortran type files). For instance to make doxygen treat .inc files @@ -313,7 +327,10 @@ OPTIMIZE_OUTPUT_SLICE = NO # Note: For files without extension you can use no_extension as a placeholder. # # Note that for custom extensions you also need to set FILE_PATTERNS otherwise -# the files are not read by doxygen. +# the files are not read by doxygen. When specifying no_extension you should add +# * to the FILE_PATTERNS. +# +# Note see also the list of default file extension mappings. EXTENSION_MAPPING = cu=C++ \ cuh=C++ @@ -337,6 +354,17 @@ MARKDOWN_SUPPORT = YES TOC_INCLUDE_HEADINGS = 5 +# The MARKDOWN_ID_STYLE tag can be used to specify the algorithm used to +# generate identifiers for the Markdown headings. Note: Every identifier is +# unique. +# Possible values are: DOXYGEN use a fixed 'autotoc_md' string followed by a +# sequence number starting at 0 and GITHUB use the lower case version of title +# with any whitespace replaced by '-' and punctuation characters removed. +# The default value is: DOXYGEN. +# This tag requires that the tag MARKDOWN_SUPPORT is set to YES. + +MARKDOWN_ID_STYLE = DOXYGEN + # When enabled doxygen tries to link words that correspond to documented # classes, or namespaces to their corresponding documentation. Such a link can # be prevented in individual cases by putting a % sign in front of the word or @@ -448,19 +476,27 @@ TYPEDEF_HIDES_STRUCT = NO LOOKUP_CACHE_SIZE = 0 -# The NUM_PROC_THREADS specifies the number threads doxygen is allowed to use +# The NUM_PROC_THREADS specifies the number of threads doxygen is allowed to use # during processing. When set to 0 doxygen will based this on the number of # cores available in the system. You can set it explicitly to a value larger # than 0 to get more control over the balance between CPU load and processing # speed. At this moment only the input processing can be done using multiple # threads. Since this is still an experimental feature the default is set to 1, -# which efficively disables parallel processing. Please report any issues you +# which effectively disables parallel processing. Please report any issues you # encounter. Generating dot graphs in parallel is controlled by the # DOT_NUM_THREADS setting. # Minimum value: 0, maximum value: 32, default value: 1. NUM_PROC_THREADS = 1 +# If the TIMESTAMP tag is set different from NO then each generated page will +# contain the date or date and time when the page was generated. Setting this to +# NO can help when comparing the output of multiple runs. +# Possible values are: YES, NO, DATETIME and DATE. +# The default value is: NO. + +TIMESTAMP = NO + #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- @@ -524,6 +560,13 @@ EXTRACT_LOCAL_METHODS = NO EXTRACT_ANON_NSPACES = NO +# If this flag is set to YES, the name of an unnamed parameter in a declaration +# will be determined by the corresponding definition. By default unnamed +# parameters remain unnamed in the output. +# The default value is: YES. + +RESOLVE_UNNAMED_PARAMS = YES + # If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all # undocumented members inside documented classes or files. If set to NO these # members will be included in the various overviews, but no documentation @@ -535,7 +578,8 @@ HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. If set # to NO, these classes will be included in the various overviews. This option -# has no effect if EXTRACT_ALL is enabled. +# will also hide undocumented C++ concepts if enabled. This option has no effect +# if EXTRACT_ALL is enabled. # The default value is: NO. HIDE_UNDOC_CLASSES = NO @@ -561,12 +605,20 @@ HIDE_IN_BODY_DOCS = NO INTERNAL_DOCS = NO -# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file -# names in lower-case letters. If set to YES, upper-case letters are also -# allowed. This is useful if you have classes or files whose names only differ -# in case and if your file system supports case sensitive file names. Windows -# (including Cygwin) and Mac users are advised to set this option to NO. -# The default value is: system dependent. +# With the correct setting of option CASE_SENSE_NAMES doxygen will better be +# able to match the capabilities of the underlying filesystem. In case the +# filesystem is case sensitive (i.e. it supports files in the same directory +# whose names only differ in casing), the option must be set to YES to properly +# deal with such files in case they appear in the input. For filesystems that +# are not case sensitive the option should be set to NO to properly deal with +# output files written for symbols that only differ in casing, such as for two +# classes, one named CLASS and the other named Class, and to also support +# references to files without having to specify the exact matching casing. On +# Windows (including Cygwin) and MacOS, users should typically set this option +# to NO, whereas on Linux or other Unix flavors it should typically be set to +# YES. +# Possible values are: SYSTEM, NO and YES. +# The default value is: SYSTEM. CASE_SENSE_NAMES = YES @@ -584,6 +636,12 @@ HIDE_SCOPE_NAMES = NO HIDE_COMPOUND_REFERENCE= NO +# If the SHOW_HEADERFILE tag is set to YES then the documentation for a class +# will show which file needs to be included to use the class. +# The default value is: YES. + +SHOW_HEADERFILE = YES + # If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of # the files that are included by a file in the documentation of that file. # The default value is: YES. @@ -741,7 +799,8 @@ FILE_VERSION_FILTER = # output files in an output format independent way. To create the layout file # that represents doxygen's defaults, run doxygen with the -l option. You can # optionally specify a file name after the option, if omitted DoxygenLayout.xml -# will be used as the name of the layout file. +# will be used as the name of the layout file. See also section "Changing the +# layout of pages" for information. # # Note that if you run doxygen from a directory containing a file called # DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE @@ -787,24 +846,50 @@ WARNINGS = YES WARN_IF_UNDOCUMENTED = YES # If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for -# potential errors in the documentation, such as not documenting some parameters -# in a documented function, or documenting parameters that don't exist or using -# markup commands wrongly. +# potential errors in the documentation, such as documenting some parameters in +# a documented function twice, or documenting parameters that don't exist or +# using markup commands wrongly. # The default value is: YES. WARN_IF_DOC_ERROR = YES +# If WARN_IF_INCOMPLETE_DOC is set to YES, doxygen will warn about incomplete +# function parameter documentation. If set to NO, doxygen will accept that some +# parameters have no documentation without warning. +# The default value is: YES. + +WARN_IF_INCOMPLETE_DOC = YES + # This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that # are documented, but have no documentation for their parameters or return -# value. If set to NO, doxygen will only warn about wrong or incomplete -# parameter documentation, but not about the absence of documentation. If -# EXTRACT_ALL is set to YES then this flag will automatically be disabled. +# value. If set to NO, doxygen will only warn about wrong parameter +# documentation, but not about the absence of documentation. If EXTRACT_ALL is +# set to YES then this flag will automatically be disabled. See also +# WARN_IF_INCOMPLETE_DOC # The default value is: NO. WARN_NO_PARAMDOC = YES +# If WARN_IF_UNDOC_ENUM_VAL option is set to YES, doxygen will warn about +# undocumented enumeration values. If set to NO, doxygen will accept +# undocumented enumeration values. If EXTRACT_ALL is set to YES then this flag +# will automatically be disabled. +# The default value is: NO. + +WARN_IF_UNDOC_ENUM_VAL = NO + # If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when -# a warning is encountered. +# a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS +# then doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but +# at the end of the doxygen process doxygen will return with a non-zero status. +# If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS_PRINT then doxygen behaves +# like FAIL_ON_WARNINGS but in case no WARN_LOGFILE is defined doxygen will not +# write the warning messages in between other messages but write them at the end +# of a run, in case a WARN_LOGFILE is defined the warning messages will be +# besides being in the defined file also be shown at the end of a run, unless +# the WARN_LOGFILE is defined as - i.e. standard output (stdout) in that case +# the behavior will remain as with the setting FAIL_ON_WARNINGS. +# Possible values are: NO, YES, FAIL_ON_WARNINGS and FAIL_ON_WARNINGS_PRINT. # The default value is: NO. WARN_AS_ERROR = NO @@ -815,13 +900,27 @@ WARN_AS_ERROR = NO # and the warning text. Optionally the format may contain $version, which will # be replaced by the version of the file (if it could be obtained via # FILE_VERSION_FILTER) +# See also: WARN_LINE_FORMAT # The default value is: $file:$line: $text. WARN_FORMAT = "$file:$line: $text" +# In the $text part of the WARN_FORMAT command it is possible that a reference +# to a more specific place is given. To make it easier to jump to this place +# (outside of doxygen) the user can define a custom "cut" / "paste" string. +# Example: +# WARN_LINE_FORMAT = "'vi $file +$line'" +# See also: WARN_FORMAT +# The default value is: at line $line of file $file. + +WARN_LINE_FORMAT = "at line $line of file $file" + # The WARN_LOGFILE tag can be used to specify a file to which warning and error # messages should be written. If left blank the output is written to standard -# error (stderr). +# error (stderr). In case the file specified cannot be opened for writing the +# warning and error messages are written to standard error. When as file - is +# specified the warning and error messages are written to standard output +# (stdout). WARN_LOGFILE = @@ -842,12 +941,23 @@ INPUT = main_page.md \ # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses # libiconv (or the iconv built into libc) for the transcoding. See the libiconv -# documentation (see: https://www.gnu.org/software/libiconv/) for the list of -# possible encodings. +# documentation (see: +# https://www.gnu.org/software/libiconv/) for the list of possible encodings. +# See also: INPUT_FILE_ENCODING # The default value is: UTF-8. INPUT_ENCODING = UTF-8 +# This tag can be used to specify the character encoding of the source files +# that doxygen parses The INPUT_FILE_ENCODING tag can be used to specify +# character encoding on a per file pattern basis. Doxygen will compare the file +# name with each pattern and apply the encoding instead of the default +# INPUT_ENCODING) if there is a match. The character encodings are a list of the +# form: pattern=encoding (like *.php=ISO-8859-1). See cfg_input_encoding +# "INPUT_ENCODING" for further information on supported encodings. + +INPUT_FILE_ENCODING = + # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and # *.h) to filter out the source-files in the directories. @@ -856,13 +966,15 @@ INPUT_ENCODING = UTF-8 # need to set EXTENSION_MAPPING for the extension otherwise the files are not # read by doxygen. # -# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp, -# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, -# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, -# *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C comment), -# *.doc (to be provided as doxygen C comment), *.txt (to be provided as doxygen -# C comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd, -# *.vhdl, *.ucf, *.qsf and *.ice. +# Note the list of default checked file patterns might differ from the list of +# default file extension mappings. +# +# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cxxm, +# *.cpp, *.cppm, *.c++, *.c++m, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, +# *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, *.h++, *.ixx, *.l, *.cs, *.d, *.php, +# *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown, *.md, *.mm, *.dox (to be +# provided as doxygen C comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, +# *.f18, *.f, *.for, *.vhd, *.vhdl, *.ucf, *.qsf and *.ice. FILE_PATTERNS = *.cpp \ *.hpp \ @@ -907,10 +1019,7 @@ EXCLUDE_PATTERNS = */nvtx/* \ # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, -# AClass::ANamespace, ANamespace::*Test -# -# Note that the wildcards are matched against the file with absolute path, so to -# exclude all test directories use the pattern */test/* +# ANamespace::AClass, ANamespace::*Test EXCLUDE_SYMBOLS = org::apache @@ -955,6 +1064,11 @@ IMAGE_PATH = # code is scanned, but not when the output code is generated. If lines are added # or removed, the anchors will not be placed correctly. # +# Note that doxygen will use the data processed and written to standard output +# for further processing, therefore nothing else, like debug statements or used +# commands (so in case of a Windows batch file always use @echo OFF), should be +# written to standard output. +# # Note that for custom extensions or not directly supported extensions you also # need to set EXTENSION_MAPPING for the extension otherwise the files are not # properly processed by doxygen. @@ -996,6 +1110,15 @@ FILTER_SOURCE_PATTERNS = USE_MDFILE_AS_MAINPAGE = main_page.md +# The Fortran standard specifies that for fixed formatted Fortran code all +# characters from position 72 are to be considered as comment. A common +# extension is to allow longer lines before the automatic comment starts. The +# setting FORTRAN_COMMENT_AFTER will also make it possible that longer lines can +# be processed before the automatic comment starts. +# Minimum value: 7, maximum value: 10000, default value: 72. + +FORTRAN_COMMENT_AFTER = 72 + #--------------------------------------------------------------------------- # Configuration options related to source browsing #--------------------------------------------------------------------------- @@ -1093,17 +1216,11 @@ VERBATIM_HEADERS = YES ALPHABETICAL_INDEX = YES -# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in -# which the alphabetical index list will be split. -# Minimum value: 1, maximum value: 20, default value: 5. -# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. - -COLS_IN_ALPHA_INDEX = 5 - -# In case all classes in a project start with a common prefix, all classes will -# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag -# can be used to specify a prefix (or a list of prefixes) that should be ignored -# while generating the index headers. +# The IGNORE_PREFIX tag can be used to specify a prefix (or a list of prefixes) +# that should be ignored while generating the index headers. The IGNORE_PREFIX +# tag works for classes, function and member names. The entity will be placed in +# the alphabetical list under the first letter of the entity name that remains +# after removing the prefix. # This tag requires that the tag ALPHABETICAL_INDEX is set to YES. IGNORE_PREFIX = @@ -1115,7 +1232,7 @@ IGNORE_PREFIX = # If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output # The default value is: YES. -GENERATE_HTML = YES +GENERATE_HTML = NO # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a # relative path is entered the value of OUTPUT_DIRECTORY will be put in front of @@ -1182,7 +1299,12 @@ HTML_STYLESHEET = # Doxygen will copy the style sheet files to the output directory. # Note: The order of the extra style sheet files is of importance (e.g. the last # style sheet in the list overrules the setting of the previous ones in the -# list). For an example see the documentation. +# list). +# Note: Since the styling of scrollbars can currently not be overruled in +# Webkit/Chromium, the styling will be left out of the default doxygen.css if +# one or more extra stylesheets have been specified. So if scrollbar +# customization is desired it has to be added explicitly. For an example see the +# documentation. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_EXTRA_STYLESHEET = @@ -1197,9 +1319,22 @@ HTML_EXTRA_STYLESHEET = HTML_EXTRA_FILES = +# The HTML_COLORSTYLE tag can be used to specify if the generated HTML output +# should be rendered with a dark or light theme. +# Possible values are: LIGHT always generate light mode output, DARK always +# generate dark mode output, AUTO_LIGHT automatically set the mode according to +# the user preference, use light mode if no preference is set (the default), +# AUTO_DARK automatically set the mode according to the user preference, use +# dark mode if no preference is set and TOGGLE allow to user to switch between +# light and dark mode via a button. +# The default value is: AUTO_LIGHT. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE = AUTO_LIGHT + # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen # will adjust the colors in the style sheet and background images according to -# this color. Hue is specified as an angle on a colorwheel, see +# this color. Hue is specified as an angle on a color-wheel, see # https://en.wikipedia.org/wiki/Hue for more information. For instance the value # 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 # purple, and 360 is red again. @@ -1209,7 +1344,7 @@ HTML_EXTRA_FILES = HTML_COLORSTYLE_HUE = 270 # The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors -# in the HTML output. For a value of 0 the output will use grayscales only. A +# in the HTML output. For a value of 0 the output will use gray-scales only. A # value of 255 will produce the most vivid colors. # Minimum value: 0, maximum value: 255, default value: 100. # This tag requires that the tag GENERATE_HTML is set to YES. @@ -1227,15 +1362,6 @@ HTML_COLORSTYLE_SAT = 255 HTML_COLORSTYLE_GAMMA = 80 -# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML -# page will contain the date and time when the page was generated. Setting this -# to YES can help to show when doxygen was last run and thus if the -# documentation is up to date. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_TIMESTAMP = NO - # If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML # documentation will contain a main index with vertical navigation menus that # are dynamically created via JavaScript. If disabled, the navigation index will @@ -1255,6 +1381,13 @@ HTML_DYNAMIC_MENUS = YES HTML_DYNAMIC_SECTIONS = NO +# If the HTML_CODE_FOLDING tag is set to YES then classes and functions can be +# dynamically folded and expanded in the generated HTML source code. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_CODE_FOLDING = YES + # With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries # shown in the various tree structured indices initially; the user can expand # and collapse entries dynamically later on. Doxygen will expand the tree to @@ -1270,10 +1403,11 @@ HTML_INDEX_NUM_ENTRIES = 100 # If the GENERATE_DOCSET tag is set to YES, additional index files will be # generated that can be used as input for Apple's Xcode 3 integrated development -# environment (see: https://developer.apple.com/xcode/), introduced with OSX -# 10.5 (Leopard). To create a documentation set, doxygen will generate a -# Makefile in the HTML output directory. Running make will produce the docset in -# that directory and running make install will install the docset in +# environment (see: +# https://developer.apple.com/xcode/), introduced with OSX 10.5 (Leopard). To +# create a documentation set, doxygen will generate a Makefile in the HTML +# output directory. Running make will produce the docset in that directory and +# running make install will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at # startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy # genXcode/_index.html for more information. @@ -1290,6 +1424,13 @@ GENERATE_DOCSET = NO DOCSET_FEEDNAME = "Doxygen generated docs" +# This tag determines the URL of the docset feed. A documentation feed provides +# an umbrella under which multiple documentation sets from a single provider +# (such as a company or product suite) can be grouped. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_FEEDURL = + # This tag specifies a string that should uniquely identify the documentation # set bundle. This should be a reverse domain-name style string, e.g. # com.mycompany.MyDocSet. Doxygen will append .docset to the name. @@ -1315,8 +1456,12 @@ DOCSET_PUBLISHER_NAME = Publisher # If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three # additional HTML index files: index.hhp, index.hhc, and index.hhk. The # index.hhp is a project file that can be read by Microsoft's HTML Help Workshop -# (see: https://www.microsoft.com/en-us/download/details.aspx?id=21138) on -# Windows. +# on Windows. In the beginning of 2021 Microsoft took the original page, with +# a.o. the download links, offline the HTML help workshop was already many years +# in maintenance mode). You can download the HTML help workshop from the web +# archives at Installation executable (see: +# http://web.archive.org/web/20160201063255/http://download.microsoft.com/downlo +# ad/0/A/9/0A939EF6-E31C-430F-A3DF-DFAE7960D564/htmlhelp.exe). # # The HTML Help Workshop contains a compiler that can convert all HTML output # generated by doxygen into a single compiled HTML file (.chm). Compiled HTML @@ -1373,6 +1518,16 @@ BINARY_TOC = NO TOC_EXPAND = NO +# The SITEMAP_URL tag is used to specify the full URL of the place where the +# generated documentation will be placed on the server by the user during the +# deployment of the documentation. The generated sitemap is called sitemap.xml +# and placed on the directory specified by HTML_OUTPUT. In case no SITEMAP_URL +# is specified no sitemap is generated. For information about the sitemap +# protocol see https://www.sitemaps.org +# This tag requires that the tag GENERATE_HTML is set to YES. + +SITEMAP_URL = + # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and # QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that # can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help @@ -1391,7 +1546,8 @@ QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help # Project output. For more information please see Qt Help Project / Namespace -# (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace). +# (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace). # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_QHP is set to YES. @@ -1399,8 +1555,8 @@ QHP_NAMESPACE = org.doxygen.Project # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt # Help Project output. For more information please see Qt Help Project / Virtual -# Folders (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual- -# folders). +# Folders (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-folders). # The default value is: doc. # This tag requires that the tag GENERATE_QHP is set to YES. @@ -1408,16 +1564,16 @@ QHP_VIRTUAL_FOLDER = doc # If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom # filter to add. For more information please see Qt Help Project / Custom -# Filters (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom- -# filters). +# Filters (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_CUST_FILTER_NAME = # The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the # custom filter to add. For more information please see Qt Help Project / Custom -# Filters (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom- -# filters). +# Filters (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_CUST_FILTER_ATTRS = @@ -1429,9 +1585,9 @@ QHP_CUST_FILTER_ATTRS = QHP_SECT_FILTER_ATTRS = -# The QHG_LOCATION tag can be used to specify the location of Qt's -# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the -# generated .qhp file. +# The QHG_LOCATION tag can be used to specify the location (absolute path +# including file name) of Qt's qhelpgenerator. If non-empty doxygen will try to +# run qhelpgenerator on the generated .qhp file. # This tag requires that the tag GENERATE_QHP is set to YES. QHG_LOCATION = @@ -1474,16 +1630,28 @@ DISABLE_INDEX = NO # to work a browser that supports JavaScript, DHTML, CSS and frames is required # (i.e. any modern browser). Windows users are probably better off using the # HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can -# further fine-tune the look of the index. As an example, the default style -# sheet generated by doxygen has an example that shows how to put an image at -# the root of the tree instead of the PROJECT_NAME. Since the tree basically has -# the same information as the tab index, you could consider setting -# DISABLE_INDEX to YES when enabling this option. +# further fine tune the look of the index (see "Fine-tuning the output"). As an +# example, the default style sheet generated by doxygen has an example that +# shows how to put an image at the root of the tree instead of the PROJECT_NAME. +# Since the tree basically has the same information as the tab index, you could +# consider setting DISABLE_INDEX to YES when enabling this option. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_TREEVIEW = NO +# When both GENERATE_TREEVIEW and DISABLE_INDEX are set to YES, then the +# FULL_SIDEBAR option determines if the side bar is limited to only the treeview +# area (value NO) or if it should extend to the full height of the window (value +# YES). Setting this to YES gives a layout similar to +# https://docs.readthedocs.io with more room for contents, but less room for the +# project logo, title, and description. If either GENERATE_TREEVIEW or +# DISABLE_INDEX is set to NO, this option has no effect. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FULL_SIDEBAR = NO + # The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that # doxygen will group on one line in the generated HTML documentation. # @@ -1508,6 +1676,13 @@ TREEVIEW_WIDTH = 250 EXT_LINKS_IN_WINDOW = NO +# If the OBFUSCATE_EMAILS tag is set to YES, doxygen will obfuscate email +# addresses. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +OBFUSCATE_EMAILS = YES + # If the HTML_FORMULA_FORMAT option is set to svg, doxygen will use the pdf2svg # tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see # https://inkscape.org) to generate formulas as SVG images instead of PNGs for @@ -1528,17 +1703,6 @@ HTML_FORMULA_FORMAT = png FORMULA_FONTSIZE = 10 -# Use the FORMULA_TRANSPARENT tag to determine whether or not the images -# generated for formulas are transparent PNGs. Transparent PNGs are not -# supported properly for IE 6.0, but are supported on all modern browsers. -# -# Note that when changing this option you need to delete any form_*.png files in -# the HTML output directory before the changes have effect. -# The default value is: YES. -# This tag requires that the tag GENERATE_HTML is set to YES. - -FORMULA_TRANSPARENT = YES - # The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands # to create new LaTeX commands to be used in formulas as building blocks. See # the section "Including formulas" for details. @@ -1556,11 +1720,29 @@ FORMULA_MACROFILE = USE_MATHJAX = NO +# With MATHJAX_VERSION it is possible to specify the MathJax version to be used. +# Note that the different versions of MathJax have different requirements with +# regards to the different settings, so it is possible that also other MathJax +# settings have to be changed when switching between the different MathJax +# versions. +# Possible values are: MathJax_2 and MathJax_3. +# The default value is: MathJax_2. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_VERSION = MathJax_2 + # When MathJax is enabled you can set the default output format to be used for -# the MathJax output. See the MathJax site (see: -# http://docs.mathjax.org/en/latest/output.html) for more details. +# the MathJax output. For more details about the output format see MathJax +# version 2 (see: +# http://docs.mathjax.org/en/v2.7-latest/output.html) and MathJax version 3 +# (see: +# http://docs.mathjax.org/en/latest/web/components/output.html). # Possible values are: HTML-CSS (which is slower, but has the best -# compatibility), NativeMML (i.e. MathML) and SVG. +# compatibility. This is the name for Mathjax version 2, for MathJax version 3 +# this will be translated into chtml), NativeMML (i.e. MathML. Only supported +# for NathJax 2. For MathJax version 3 chtml will be used instead.), chtml (This +# is the name for Mathjax version 3, for MathJax version 2 this will be +# translated into HTML-CSS) and SVG. # The default value is: HTML-CSS. # This tag requires that the tag USE_MATHJAX is set to YES. @@ -1573,22 +1755,29 @@ MATHJAX_FORMAT = HTML-CSS # MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax # Content Delivery Network so you can quickly see the result without installing # MathJax. However, it is strongly recommended to install a local copy of -# MathJax from https://www.mathjax.org before deployment. -# The default value is: https://cdn.jsdelivr.net/npm/mathjax@2. +# MathJax from https://www.mathjax.org before deployment. The default value is: +# - in case of MathJax version 2: https://cdn.jsdelivr.net/npm/mathjax@2 +# - in case of MathJax version 3: https://cdn.jsdelivr.net/npm/mathjax@3 # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest # The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax # extension names that should be enabled during MathJax rendering. For example +# for MathJax version 2 (see +# https://docs.mathjax.org/en/v2.7-latest/tex.html#tex-and-latex-extensions): # MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols +# For example for MathJax version 3 (see +# http://docs.mathjax.org/en/latest/input/tex/extensions/index.html): +# MATHJAX_EXTENSIONS = ams # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_EXTENSIONS = # The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces # of code that will be used on startup of the MathJax code. See the MathJax site -# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an +# (see: +# http://docs.mathjax.org/en/v2.7-latest/output.html) for more details. For an # example see the documentation. # This tag requires that the tag USE_MATHJAX is set to YES. @@ -1635,7 +1824,8 @@ SERVER_BASED_SEARCH = NO # # Doxygen ships with an example indexer (doxyindexer) and search engine # (doxysearch.cgi) which are based on the open source search engine library -# Xapian (see: https://xapian.org/). +# Xapian (see: +# https://xapian.org/). # # See the section "External Indexing and Searching" for details. # The default value is: NO. @@ -1648,8 +1838,9 @@ EXTERNAL_SEARCH = NO # # Doxygen ships with an example indexer (doxyindexer) and search engine # (doxysearch.cgi) which are based on the open source search engine library -# Xapian (see: https://xapian.org/). See the section "External Indexing and -# Searching" for details. +# Xapian (see: +# https://xapian.org/). See the section "External Indexing and Searching" for +# details. # This tag requires that the tag SEARCHENGINE is set to YES. SEARCHENGINE_URL = @@ -1758,29 +1949,31 @@ PAPER_TYPE = a4 EXTRA_PACKAGES = -# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the -# generated LaTeX document. The header should contain everything until the first -# chapter. If it is left blank doxygen will generate a standard header. See -# section "Doxygen usage" for information on how to let doxygen write the -# default header to a separate file. +# The LATEX_HEADER tag can be used to specify a user-defined LaTeX header for +# the generated LaTeX document. The header should contain everything until the +# first chapter. If it is left blank doxygen will generate a standard header. It +# is highly recommended to start with a default header using +# doxygen -w latex new_header.tex new_footer.tex new_stylesheet.sty +# and then modify the file new_header.tex. See also section "Doxygen usage" for +# information on how to generate the default header that doxygen normally uses. # -# Note: Only use a user-defined header if you know what you are doing! The -# following commands have a special meaning inside the header: $title, -# $datetime, $date, $doxygenversion, $projectname, $projectnumber, -# $projectbrief, $projectlogo. Doxygen will replace $title with the empty -# string, for the replacement values of the other commands the user is referred -# to HTML_HEADER. +# Note: Only use a user-defined header if you know what you are doing! +# Note: The header is subject to change so you typically have to regenerate the +# default header when upgrading to a newer version of doxygen. The following +# commands have a special meaning inside the header (and footer): For a +# description of the possible markers and block names see the documentation. # This tag requires that the tag GENERATE_LATEX is set to YES. LATEX_HEADER = -# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the -# generated LaTeX document. The footer should contain everything after the last -# chapter. If it is left blank doxygen will generate a standard footer. See +# The LATEX_FOOTER tag can be used to specify a user-defined LaTeX footer for +# the generated LaTeX document. The footer should contain everything after the +# last chapter. If it is left blank doxygen will generate a standard footer. See # LATEX_HEADER for more information on how to generate a default footer and what -# special commands can be used inside the footer. -# -# Note: Only use a user-defined footer if you know what you are doing! +# special commands can be used inside the footer. See also section "Doxygen +# usage" for information on how to generate the default footer that doxygen +# normally uses. Note: Only use a user-defined footer if you know what you are +# doing! # This tag requires that the tag GENERATE_LATEX is set to YES. LATEX_FOOTER = @@ -1823,10 +2016,16 @@ PDF_HYPERLINKS = YES USE_PDFLATEX = YES -# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode -# command to the generated LaTeX files. This will instruct LaTeX to keep running -# if errors occur, instead of asking the user for help. This option is also used -# when generating formulas in HTML. +# The LATEX_BATCHMODE tag signals the behavior of LaTeX in case of an error. +# Possible values are: NO same as ERROR_STOP, YES same as BATCH, BATCH In batch +# mode nothing is printed on the terminal, errors are scrolled as if is +# hit at every error; missing files that TeX tries to input or request from +# keyboard input (\read on a not open input stream) cause the job to abort, +# NON_STOP In nonstop mode the diagnostic message will appear on the terminal, +# but there is no possibility of user interaction just like in batch mode, +# SCROLL In scroll mode, TeX will stop only for missing files to input or if +# keyboard input is necessary and ERROR_STOP In errorstop mode, TeX will stop at +# each error, asking for user intervention. # The default value is: NO. # This tag requires that the tag GENERATE_LATEX is set to YES. @@ -1839,16 +2038,6 @@ LATEX_BATCHMODE = NO LATEX_HIDE_INDICES = NO -# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source -# code with syntax highlighting in the LaTeX output. -# -# Note that which sources are shown also depends on other settings such as -# SOURCE_BROWSER. -# The default value is: NO. -# This tag requires that the tag GENERATE_LATEX is set to YES. - -LATEX_SOURCE_CODE = NO - # The LATEX_BIB_STYLE tag can be used to specify the style to use for the # bibliography, e.g. plainnat, or ieeetr. See # https://en.wikipedia.org/wiki/BibTeX and \cite for more info. @@ -1857,14 +2046,6 @@ LATEX_SOURCE_CODE = NO LATEX_BIB_STYLE = plain -# If the LATEX_TIMESTAMP tag is set to YES then the footer of each generated -# page will contain the date and time when the page was generated. Setting this -# to NO can help when comparing the output of multiple runs. -# The default value is: NO. -# This tag requires that the tag GENERATE_LATEX is set to YES. - -LATEX_TIMESTAMP = NO - # The LATEX_EMOJI_DIRECTORY tag is used to specify the (relative or absolute) # path from which the emoji images will be read. If a relative path is entered, # it will be relative to the LATEX_OUTPUT directory. If left blank the @@ -1929,16 +2110,6 @@ RTF_STYLESHEET_FILE = RTF_EXTENSIONS_FILE = -# If the RTF_SOURCE_CODE tag is set to YES then doxygen will include source code -# with syntax highlighting in the RTF output. -# -# Note that which sources are shown also depends on other settings such as -# SOURCE_BROWSER. -# The default value is: NO. -# This tag requires that the tag GENERATE_RTF is set to YES. - -RTF_SOURCE_CODE = NO - #--------------------------------------------------------------------------- # Configuration options related to the man page output #--------------------------------------------------------------------------- @@ -1991,7 +2162,7 @@ MAN_LINKS = NO # captures the structure of the code including all documentation. # The default value is: NO. -GENERATE_XML = NO +GENERATE_XML = YES # The XML_OUTPUT tag is used to specify where the XML pages will be put. If a # relative path is entered the value of OUTPUT_DIRECTORY will be put in front of @@ -2035,27 +2206,44 @@ GENERATE_DOCBOOK = NO DOCBOOK_OUTPUT = docbook -# If the DOCBOOK_PROGRAMLISTING tag is set to YES, doxygen will include the -# program listings (including syntax highlighting and cross-referencing -# information) to the DOCBOOK output. Note that enabling this will significantly -# increase the size of the DOCBOOK output. -# The default value is: NO. -# This tag requires that the tag GENERATE_DOCBOOK is set to YES. - -DOCBOOK_PROGRAMLISTING = NO - #--------------------------------------------------------------------------- # Configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- # If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an -# AutoGen Definitions (see http://autogen.sourceforge.net/) file that captures +# AutoGen Definitions (see https://autogen.sourceforge.net/) file that captures # the structure of the code including all documentation. Note that this feature # is still experimental and incomplete at the moment. # The default value is: NO. GENERATE_AUTOGEN_DEF = NO +#--------------------------------------------------------------------------- +# Configuration options related to Sqlite3 output +#--------------------------------------------------------------------------- + +# If the GENERATE_SQLITE3 tag is set to YES doxygen will generate a Sqlite3 +# database with symbols found by doxygen stored in tables. +# The default value is: NO. + +GENERATE_SQLITE3 = NO + +# The SQLITE3_OUTPUT tag is used to specify where the Sqlite3 database will be +# put. If a relative path is entered the value of OUTPUT_DIRECTORY will be put +# in front of it. +# The default directory is: sqlite3. +# This tag requires that the tag GENERATE_SQLITE3 is set to YES. + +SQLITE3_OUTPUT = sqlite3 + +# The SQLITE3_OVERWRITE_DB tag is set to YES, the existing doxygen_sqlite3.db +# database file will be recreated with each doxygen run. If set to NO, doxygen +# will warn if an a database file is already found and not modify it. +# The default value is: YES. +# This tag requires that the tag GENERATE_SQLITE3 is set to YES. + +SQLITE3_RECREATE_DB = YES + #--------------------------------------------------------------------------- # Configuration options related to the Perl module output #--------------------------------------------------------------------------- @@ -2130,7 +2318,8 @@ SEARCH_INCLUDES = YES # The INCLUDE_PATH tag can be used to specify one or more directories that # contain include files that are not input files but should be processed by the -# preprocessor. +# preprocessor. Note that the INCLUDE_PATH is not recursive, so the setting of +# RECURSIVE has no effect here. # This tag requires that the tag SEARCH_INCLUDES is set to YES. INCLUDE_PATH = @@ -2197,15 +2386,15 @@ TAGFILES = rmm.tag=https://docs.rapids.ai/api/librmm/22.08 GENERATE_TAGFILE = -# If the ALLEXTERNALS tag is set to YES, all external class will be listed in -# the class index. If set to NO, only the inherited external classes will be -# listed. +# If the ALLEXTERNALS tag is set to YES, all external classes and namespaces +# will be listed in the class and namespace index. If set to NO, only the +# inherited external classes will be listed. # The default value is: NO. ALLEXTERNALS = NO # If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed -# in the modules index. If set to NO, only the current project's groups will be +# in the topic index. If set to NO, only the current project's groups will be # listed. # The default value is: YES. @@ -2219,25 +2408,9 @@ EXTERNAL_GROUPS = YES EXTERNAL_PAGES = YES #--------------------------------------------------------------------------- -# Configuration options related to the dot tool +# Configuration options related to diagram generator tools #--------------------------------------------------------------------------- -# If the CLASS_DIAGRAMS tag is set to YES, doxygen will generate a class diagram -# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to -# NO turns the diagrams off. Note that this option also works with HAVE_DOT -# disabled, but it is recommended to install and use dot, since it yields more -# powerful graphs. -# The default value is: YES. - -CLASS_DIAGRAMS = YES - -# You can include diagrams made with dia in doxygen documentation. Doxygen will -# then run dia to produce the diagram and insert it in the documentation. The -# DIA_PATH tag allows you to specify the directory where the dia binary resides. -# If left empty dia is assumed to be found in the default search path. - -DIA_PATH = - # If set to YES the inheritance and collaboration graphs will hide inheritance # and usage relations if the target is undocumented or is not a class. # The default value is: YES. @@ -2246,7 +2419,7 @@ HIDE_UNDOC_RELATIONS = YES # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is # available from the path. This tool is part of Graphviz (see: -# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent +# https://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent # Bell Labs. The other options in this section have no effect if this option is # set to NO # The default value is: NO. @@ -2263,49 +2436,73 @@ HAVE_DOT = YES DOT_NUM_THREADS = 0 -# When you want a differently looking font in the dot files that doxygen -# generates you can specify the font name using DOT_FONTNAME. You need to make -# sure dot is able to find the font, which can be done by putting it in a -# standard location or by setting the DOTFONTPATH environment variable or by -# setting DOT_FONTPATH to the directory containing the font. -# The default value is: Helvetica. +# DOT_COMMON_ATTR is common attributes for nodes, edges and labels of +# subgraphs. When you want a differently looking font in the dot files that +# doxygen generates you can specify fontname, fontcolor and fontsize attributes. +# For details please see Node, +# Edge and Graph Attributes specification You need to make sure dot is able +# to find the font, which can be done by putting it in a standard location or by +# setting the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the +# directory containing the font. Default graphviz fontsize is 14. +# The default value is: fontname=Helvetica,fontsize=10. # This tag requires that the tag HAVE_DOT is set to YES. -DOT_FONTNAME = Helvetica +DOT_COMMON_ATTR = "fontname=Helvetica,fontsize=10" -# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of -# dot graphs. -# Minimum value: 4, maximum value: 24, default value: 10. +# DOT_EDGE_ATTR is concatenated with DOT_COMMON_ATTR. For elegant style you can +# add 'arrowhead=open, arrowtail=open, arrowsize=0.5'. Complete documentation about +# arrows shapes. +# The default value is: labelfontname=Helvetica,labelfontsize=10. # This tag requires that the tag HAVE_DOT is set to YES. -DOT_FONTSIZE = 10 +DOT_EDGE_ATTR = "labelfontname=Helvetica,labelfontsize=10" -# By default doxygen will tell dot to use the default font as specified with -# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set -# the path where dot can find it using this tag. +# DOT_NODE_ATTR is concatenated with DOT_COMMON_ATTR. For view without boxes +# around nodes set 'shape=plain' or 'shape=plaintext' Shapes specification +# The default value is: shape=box,height=0.2,width=0.4. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_NODE_ATTR = "shape=box,height=0.2,width=0.4" + +# You can set the path where dot can find font specified with fontname in +# DOT_COMMON_ATTR and others dot attributes. # This tag requires that the tag HAVE_DOT is set to YES. DOT_FONTPATH = -# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for -# each documented class showing the direct and indirect inheritance relations. -# Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO. +# If the CLASS_GRAPH tag is set to YES or GRAPH or BUILTIN then doxygen will +# generate a graph for each documented class showing the direct and indirect +# inheritance relations. In case the CLASS_GRAPH tag is set to YES or GRAPH and +# HAVE_DOT is enabled as well, then dot will be used to draw the graph. In case +# the CLASS_GRAPH tag is set to YES and HAVE_DOT is disabled or if the +# CLASS_GRAPH tag is set to BUILTIN, then the built-in generator will be used. +# If the CLASS_GRAPH tag is set to TEXT the direct and indirect inheritance +# relations will be shown as texts / links. +# Possible values are: NO, YES, TEXT, GRAPH and BUILTIN. # The default value is: YES. -# This tag requires that the tag HAVE_DOT is set to YES. CLASS_GRAPH = YES # If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a # graph for each documented class showing the direct and indirect implementation # dependencies (inheritance, containment, and class references variables) of the -# class with other documented classes. +# class with other documented classes. Explicit enabling a collaboration graph, +# when COLLABORATION_GRAPH is set to NO, can be accomplished by means of the +# command \collaborationgraph. Disabling a collaboration graph can be +# accomplished by means of the command \hidecollaborationgraph. # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. COLLABORATION_GRAPH = YES # If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for -# groups, showing the direct groups dependencies. +# groups, showing the direct groups dependencies. Explicit enabling a group +# dependency graph, when GROUP_GRAPHS is set to NO, can be accomplished by means +# of the command \groupgraph. Disabling a directory graph can be accomplished by +# means of the command \hidegroupgraph. See also the chapter Grouping in the +# manual. # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. @@ -2328,10 +2525,32 @@ UML_LOOK = NO # but if the number exceeds 15, the total amount of fields shown is limited to # 10. # Minimum value: 0, maximum value: 100, default value: 10. -# This tag requires that the tag HAVE_DOT is set to YES. +# This tag requires that the tag UML_LOOK is set to YES. UML_LIMIT_NUM_FIELDS = 10 +# If the DOT_UML_DETAILS tag is set to NO, doxygen will show attributes and +# methods without types and arguments in the UML graphs. If the DOT_UML_DETAILS +# tag is set to YES, doxygen will add type and arguments for attributes and +# methods in the UML graphs. If the DOT_UML_DETAILS tag is set to NONE, doxygen +# will not generate fields with class member information in the UML graphs. The +# class diagrams will look similar to the default class diagrams but using UML +# notation for the relationships. +# Possible values are: NO, YES and NONE. +# The default value is: NO. +# This tag requires that the tag UML_LOOK is set to YES. + +DOT_UML_DETAILS = NO + +# The DOT_WRAP_THRESHOLD tag can be used to set the maximum number of characters +# to display on a single line. If the actual line length exceeds this threshold +# significantly it will wrapped across multiple lines. Some heuristics are apply +# to avoid ugly line breaks. +# Minimum value: 0, maximum value: 1000, default value: 17. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_WRAP_THRESHOLD = 17 + # If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and # collaboration graphs will show the relations between templates and their # instances. @@ -2343,7 +2562,9 @@ TEMPLATE_RELATIONS = NO # If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to # YES then doxygen will generate a graph for each documented file showing the # direct and indirect include dependencies of the file with other documented -# files. +# files. Explicit enabling an include graph, when INCLUDE_GRAPH is is set to NO, +# can be accomplished by means of the command \includegraph. Disabling an +# include graph can be accomplished by means of the command \hideincludegraph. # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. @@ -2352,7 +2573,10 @@ INCLUDE_GRAPH = YES # If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are # set to YES then doxygen will generate a graph for each documented file showing # the direct and indirect include dependencies of the file with other documented -# files. +# files. Explicit enabling an included by graph, when INCLUDED_BY_GRAPH is set +# to NO, can be accomplished by means of the command \includedbygraph. Disabling +# an included by graph can be accomplished by means of the command +# \hideincludedbygraph. # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. @@ -2392,16 +2616,26 @@ GRAPHICAL_HIERARCHY = YES # If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the # dependencies a directory has on other directories in a graphical way. The # dependency relations are determined by the #include relations between the -# files in the directories. +# files in the directories. Explicit enabling a directory graph, when +# DIRECTORY_GRAPH is set to NO, can be accomplished by means of the command +# \directorygraph. Disabling a directory graph can be accomplished by means of +# the command \hidedirectorygraph. # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. DIRECTORY_GRAPH = YES +# The DIR_GRAPH_MAX_DEPTH tag can be used to limit the maximum number of levels +# of child directories generated in directory dependency graphs by dot. +# Minimum value: 1, maximum value: 25, default value: 1. +# This tag requires that the tag DIRECTORY_GRAPH is set to YES. + +DIR_GRAPH_MAX_DEPTH = 1 + # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images # generated by dot. For an explanation of the image formats see the section # output formats in the documentation of the dot tool (Graphviz (see: -# http://www.graphviz.org/)). +# https://www.graphviz.org/)). # Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order # to make the SVG files visible in IE 9+ (other browsers do not have this # requirement). @@ -2438,11 +2672,12 @@ DOT_PATH = DOTFILE_DIRS = -# The MSCFILE_DIRS tag can be used to specify one or more directories that -# contain msc files that are included in the documentation (see the \mscfile -# command). +# You can include diagrams made with dia in doxygen documentation. Doxygen will +# then run dia to produce the diagram and insert it in the documentation. The +# DIA_PATH tag allows you to specify the directory where the dia binary resides. +# If left empty dia is assumed to be found in the default search path. -MSCFILE_DIRS = +DIA_PATH = # The DIAFILE_DIRS tag can be used to specify one or more directories that # contain dia files that are included in the documentation (see the \diafile @@ -2451,10 +2686,10 @@ MSCFILE_DIRS = DIAFILE_DIRS = # When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the -# path where java can find the plantuml.jar file. If left blank, it is assumed -# PlantUML is not used or called during a preprocessing step. Doxygen will -# generate a warning when it encounters a \startuml command in this case and -# will not generate output for the diagram. +# path where java can find the plantuml.jar file or to the filename of jar file +# to be used. If left blank, it is assumed PlantUML is not used or called during +# a preprocessing step. Doxygen will generate a warning when it encounters a +# \startuml command in this case and will not generate output for the diagram. PLANTUML_JAR_PATH = @@ -2492,18 +2727,6 @@ DOT_GRAPH_MAX_NODES = 50 MAX_DOT_GRAPH_DEPTH = 0 -# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent -# background. This is disabled by default, because dot on Windows does not seem -# to support this out of the box. -# -# Warning: Depending on the platform used, enabling this option may lead to -# badly anti-aliased labels on the edges of a graph (i.e. they become hard to -# read). -# The default value is: NO. -# This tag requires that the tag HAVE_DOT is set to YES. - -DOT_TRANSPARENT = NO - # Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output # files in one run (i.e. multiple -o and -T options on the command line). This # makes dot run faster, but since only newer versions of dot (>1.8.10) support @@ -2516,14 +2739,34 @@ DOT_MULTI_TARGETS = NO # If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page # explaining the meaning of the various boxes and arrows in the dot generated # graphs. +# Note: This tag requires that UML_LOOK isn't set, i.e. the doxygen internal +# graphical representation for inheritance and collaboration diagrams is used. # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. GENERATE_LEGEND = YES -# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate dot +# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate # files that are used to generate the various graphs. +# +# Note: This setting is not only used for dot files but also for msc temporary +# files. # The default value is: YES. -# This tag requires that the tag HAVE_DOT is set to YES. DOT_CLEANUP = YES + +# You can define message sequence charts within doxygen comments using the \msc +# command. If the MSCGEN_TOOL tag is left empty (the default), then doxygen will +# use a built-in version of mscgen tool to produce the charts. Alternatively, +# the MSCGEN_TOOL tag can also specify the name an external tool. For instance, +# specifying prog as the value, doxygen will call the tool as prog -T +# -o . The external tool should support +# output file formats "png", "eps", "svg", and "ismap". + +MSCGEN_TOOL = + +# The MSCFILE_DIRS tag can be used to specify one or more directories that +# contain msc files that are included in the documentation (see the \mscfile +# command). + +MSCFILE_DIRS = diff --git a/cpp/include/cugraph_c/centrality_algorithms.h b/cpp/include/cugraph_c/centrality_algorithms.h index 0ac0e58540f..fb5d4b63b9c 100644 --- a/cpp/include/cugraph_c/centrality_algorithms.h +++ b/cpp/include/cugraph_c/centrality_algorithms.h @@ -23,8 +23,6 @@ #include /** @defgroup centrality Centrality algorithms - * @ingroup c_api - * @{ */ #ifdef __cplusplus @@ -39,7 +37,8 @@ typedef struct { } cugraph_centrality_result_t; /** - * @brief Get the vertex ids from the centrality result + * @ingroup centrality + * @brief Get the vertex ids from the centrality result * * @param [in] result The result from a centrality algorithm * @return type erased array of vertex ids @@ -48,7 +47,8 @@ cugraph_type_erased_device_array_view_t* cugraph_centrality_result_get_vertices( cugraph_centrality_result_t* result); /** - * @brief Get the centrality values from a centrality algorithm result + * @ingroup centrality + * @brief Get the centrality values from a centrality algorithm result * * @param [in] result The result from a centrality algorithm * @return type erased array view of centrality values @@ -57,6 +57,7 @@ cugraph_type_erased_device_array_view_t* cugraph_centrality_result_get_values( cugraph_centrality_result_t* result); /** + * @ingroup centrality * @brief Get the number of iterations executed from the algorithm metadata * * @param [in] result The result from a centrality algorithm @@ -65,6 +66,7 @@ cugraph_type_erased_device_array_view_t* cugraph_centrality_result_get_values( size_t cugraph_centrality_result_get_num_iterations(cugraph_centrality_result_t* result); /** + * @ingroup centrality * @brief Returns true if the centrality algorithm converged * * @param [in] result The result from a centrality algorithm @@ -73,6 +75,7 @@ size_t cugraph_centrality_result_get_num_iterations(cugraph_centrality_result_t* bool_t cugraph_centrality_result_converged(cugraph_centrality_result_t* result); /** + * @ingroup centrality * @brief Free centrality result * * @param [in] result The result from a centrality algorithm @@ -409,6 +412,7 @@ typedef struct { } cugraph_edge_centrality_result_t; /** + * @ingroup centrality * @brief Get the src vertex ids from an edge centrality result * * @param [in] result The result from an edge centrality algorithm @@ -418,6 +422,7 @@ cugraph_type_erased_device_array_view_t* cugraph_edge_centrality_result_get_src_ cugraph_edge_centrality_result_t* result); /** + * @ingroup centrality * @brief Get the dst vertex ids from an edge centrality result * * @param [in] result The result from an edge centrality algorithm @@ -427,6 +432,7 @@ cugraph_type_erased_device_array_view_t* cugraph_edge_centrality_result_get_dst_ cugraph_edge_centrality_result_t* result); /** + * @ingroup centrality * @brief Get the edge ids from an edge centrality result * * @param [in] result The result from an edge centrality algorithm @@ -436,6 +442,7 @@ cugraph_type_erased_device_array_view_t* cugraph_edge_centrality_result_get_edge cugraph_edge_centrality_result_t* result); /** + * @ingroup centrality * @brief Get the centrality values from an edge centrality algorithm result * * @param [in] result The result from an edge centrality algorithm @@ -445,6 +452,7 @@ cugraph_type_erased_device_array_view_t* cugraph_edge_centrality_result_get_valu cugraph_edge_centrality_result_t* result); /** + * @ingroup centrality * @brief Free centrality result * * @param [in] result The result from a centrality algorithm @@ -491,6 +499,7 @@ typedef struct { } cugraph_hits_result_t; /** + * @ingroup centrality * @brief Get the vertex ids from the hits result * * @param [in] result The result from hits @@ -500,6 +509,7 @@ cugraph_type_erased_device_array_view_t* cugraph_hits_result_get_vertices( cugraph_hits_result_t* result); /** + * @ingroup centrality * @brief Get the hubs values from the hits result * * @param [in] result The result from hits @@ -509,6 +519,7 @@ cugraph_type_erased_device_array_view_t* cugraph_hits_result_get_hubs( cugraph_hits_result_t* result); /** + * @ingroup centrality * @brief Get the authorities values from the hits result * * @param [in] result The result from hits @@ -518,6 +529,7 @@ cugraph_type_erased_device_array_view_t* cugraph_hits_result_get_authorities( cugraph_hits_result_t* result); /** + * @ingroup centrality * @brief Get the score differences between the last two iterations * * @param [in] result The result from hits @@ -526,6 +538,7 @@ cugraph_type_erased_device_array_view_t* cugraph_hits_result_get_authorities( double cugraph_hits_result_get_hub_score_differences(cugraph_hits_result_t* result); /** + * @ingroup centrality * @brief Get the actual number of iterations * * @param [in] result The result from hits @@ -534,6 +547,7 @@ double cugraph_hits_result_get_hub_score_differences(cugraph_hits_result_t* resu size_t cugraph_hits_result_get_number_of_iterations(cugraph_hits_result_t* result); /** + * @ingroup centrality * @brief Free hits result * * @param [in] result The result from hits @@ -585,7 +599,3 @@ cugraph_error_code_t cugraph_hits( #ifdef __cplusplus } #endif - -/** - * @} - */ diff --git a/cpp/include/cugraph_c/community_algorithms.h b/cpp/include/cugraph_c/community_algorithms.h index 8f1015f8632..feab15c7eeb 100644 --- a/cpp/include/cugraph_c/community_algorithms.h +++ b/cpp/include/cugraph_c/community_algorithms.h @@ -23,7 +23,6 @@ #include /** @defgroup community Community algorithms - * @ingroup c_api * @{ */ diff --git a/cpp/include/cugraph_c/core_algorithms.h b/cpp/include/cugraph_c/core_algorithms.h index c0e348c3cf4..6db3269f61e 100644 --- a/cpp/include/cugraph_c/core_algorithms.h +++ b/cpp/include/cugraph_c/core_algorithms.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, NVIDIA CORPORATION. + * Copyright (c) 2022-2023, NVIDIA CORPORATION. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,6 +21,9 @@ #include #include +/** @defgroup core Core algorithms + */ + #ifdef __cplusplus extern "C" { #endif @@ -40,6 +43,7 @@ typedef struct { } cugraph_k_core_result_t; /** + * @ingroup core * @brief Create a core_number result (in case it was previously extracted) * * @param [in] handle Handle for accessing resources @@ -58,6 +62,7 @@ cugraph_error_code_t cugraph_core_result_create( cugraph_error_t** error); /** + * @ingroup core * @brief Get the vertex ids from the core result * * @param [in] result The result from core number @@ -67,6 +72,7 @@ cugraph_type_erased_device_array_view_t* cugraph_core_result_get_vertices( cugraph_core_result_t* result); /** + * @ingroup core * @brief Get the core numbers from the core result * * @param [in] result The result from core number @@ -76,6 +82,7 @@ cugraph_type_erased_device_array_view_t* cugraph_core_result_get_core_numbers( cugraph_core_result_t* result); /** + * @ingroup core * @brief Free core result * * @param [in] result The result from core number @@ -83,6 +90,7 @@ cugraph_type_erased_device_array_view_t* cugraph_core_result_get_core_numbers( void cugraph_core_result_free(cugraph_core_result_t* result); /** + * @ingroup core * @brief Get the src vertex ids from the k-core result * * @param [in] result The result from k-core @@ -92,6 +100,7 @@ cugraph_type_erased_device_array_view_t* cugraph_k_core_result_get_src_vertices( cugraph_k_core_result_t* result); /** + * @ingroup core * @brief Get the dst vertex ids from the k-core result * * @param [in] result The result from k-core @@ -101,6 +110,7 @@ cugraph_type_erased_device_array_view_t* cugraph_k_core_result_get_dst_vertices( cugraph_k_core_result_t* result); /** + * @ingroup core * @brief Get the weights from the k-core result * * Returns NULL if the graph is unweighted @@ -112,6 +122,7 @@ cugraph_type_erased_device_array_view_t* cugraph_k_core_result_get_weights( cugraph_k_core_result_t* result); /** + * @ingroup core * @brief Free k-core result * * @param [in] result The result from k-core @@ -119,6 +130,7 @@ cugraph_type_erased_device_array_view_t* cugraph_k_core_result_get_weights( void cugraph_k_core_result_free(cugraph_k_core_result_t* result); /** + * @ingroup core * @brief Enumeration for computing core number */ typedef enum { diff --git a/cpp/include/cugraph_c/labeling_algorithms.h b/cpp/include/cugraph_c/labeling_algorithms.h index f3e634dafe6..53dcc0d9419 100644 --- a/cpp/include/cugraph_c/labeling_algorithms.h +++ b/cpp/include/cugraph_c/labeling_algorithms.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, NVIDIA CORPORATION. + * Copyright (c) 2022-2023, NVIDIA CORPORATION. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,8 +25,6 @@ extern "C" { #endif /** @defgroup labeling Labeling algorithms - * @ingroup c_api - * @{ */ /** @@ -37,6 +35,7 @@ typedef struct { } cugraph_labeling_result_t; /** + * @ingroup labeling * @brief Get the vertex ids from the labeling result * * @param [in] result The result from a labeling algorithm @@ -46,6 +45,7 @@ cugraph_type_erased_device_array_view_t* cugraph_labeling_result_get_vertices( cugraph_labeling_result_t* result); /** + * @ingroup labeling * @brief Get the label values from the labeling result * * @param [in] result The result from a labeling algorithm @@ -55,6 +55,7 @@ cugraph_type_erased_device_array_view_t* cugraph_labeling_result_get_labels( cugraph_labeling_result_t* result); /** + * @ingroup labeling * @brief Free labeling result * * @param [in] result The result from a labeling algorithm @@ -104,7 +105,3 @@ cugraph_error_code_t cugraph_strongly_connected_components(const cugraph_resourc #ifdef __cplusplus } #endif - -/** - * @} - */ diff --git a/cpp/include/cugraph_c/sampling_algorithms.h b/cpp/include/cugraph_c/sampling_algorithms.h index 92fe50ef622..782bb5a3790 100644 --- a/cpp/include/cugraph_c/sampling_algorithms.h +++ b/cpp/include/cugraph_c/sampling_algorithms.h @@ -21,8 +21,7 @@ #include #include -/** @defgroup sampling Sampling algorithms - * @ingroup c_api +/** @defgroup samplingC Sampling algorithms * @{ */ diff --git a/cpp/include/cugraph_c/similarity_algorithms.h b/cpp/include/cugraph_c/similarity_algorithms.h index 1417d8ac566..b8f61b46545 100644 --- a/cpp/include/cugraph_c/similarity_algorithms.h +++ b/cpp/include/cugraph_c/similarity_algorithms.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, NVIDIA CORPORATION. + * Copyright (c) 2022-2023, NVIDIA CORPORATION. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,6 +22,9 @@ #include #include +/** @defgroup similarity Similarity algorithms + */ + #ifdef __cplusplus extern "C" { #endif @@ -34,6 +37,7 @@ typedef struct { } cugraph_similarity_result_t; /** + * @ingroup similarity * @brief Get the similarity coefficient array * * @param [in] result The result from a similarity algorithm @@ -43,6 +47,7 @@ cugraph_type_erased_device_array_view_t* cugraph_similarity_result_get_similarit cugraph_similarity_result_t* result); /** + * @ingroup similarity * @brief Free similarity result * * @param [in] result The result from a similarity algorithm diff --git a/docs/cugraph/Makefile b/docs/cugraph/Makefile index 32237aa2cc0..f92d0be6910 100644 --- a/docs/cugraph/Makefile +++ b/docs/cugraph/Makefile @@ -2,7 +2,7 @@ # # You can set these variables from the command line. -SPHINXOPTS = +SPHINXOPTS = "-v" SPHINXBUILD = sphinx-build SPHINXPROJ = cugraph SOURCEDIR = source diff --git a/docs/cugraph/source/api_docs/cugraph-ops/bipartite_operators.rst b/docs/cugraph/source/api_docs/cugraph-ops/bipartite_operators.rst deleted file mode 100644 index e172309fae2..00000000000 --- a/docs/cugraph/source/api_docs/cugraph-ops/bipartite_operators.rst +++ /dev/null @@ -1,16 +0,0 @@ -============================= -Operators on Bipartite Graphs -============================= - -.. currentmodule:: pylibcugraphops - -Update Edges: Concatenation or Sum of Edge and Node Features ------------------------------------------------------------- -.. autosummary:: - :toctree: ../api/ops/ - - operators.update_efeat_bipartite_e2e_concat_fwd - operators.update_efeat_bipartite_e2e_concat_bwd - - operators.update_efeat_bipartite_e2e_sum_fwd - operators.update_efeat_bipartite_e2e_sum_bwd diff --git a/docs/cugraph/source/api_docs/cugraph-ops/c_cpp/index.rst b/docs/cugraph/source/api_docs/cugraph-ops/c_cpp/index.rst new file mode 100644 index 00000000000..5545bebe975 --- /dev/null +++ b/docs/cugraph/source/api_docs/cugraph-ops/c_cpp/index.rst @@ -0,0 +1,3 @@ +cugraph-ops C++ API Reference +============================= + diff --git a/docs/cugraph/source/api_docs/cugraph-ops/fg_operators.rst b/docs/cugraph/source/api_docs/cugraph-ops/fg_operators.rst deleted file mode 100644 index 387844f684a..00000000000 --- a/docs/cugraph/source/api_docs/cugraph-ops/fg_operators.rst +++ /dev/null @@ -1,83 +0,0 @@ -======================== -Operators on Full Graphs -======================== - -.. currentmodule:: pylibcugraphops - -Simple Neighborhood Aggregator (SAGEConv) ------------------------------------------ -.. autosummary:: - :toctree: ../api/ops/ - - operators.agg_simple_fg_n2n_fwd - operators.agg_simple_fg_n2n_bwd - operators.agg_simple_fg_e2n_fwd - operators.agg_simple_fg_e2n_bwd - operators.agg_simple_fg_n2n_e2n_fwd - operators.agg_simple_fg_n2n_e2n_bwd - - operators.agg_concat_fg_n2n_fwd - operators.agg_concat_fg_n2n_bwd - operators.agg_concat_fg_e2n_fwd - operators.agg_concat_fg_e2n_bwd - operators.agg_concat_fg_n2n_e2n_fwd - operators.agg_concat_fg_n2n_e2n_bwd - -Weighted Neighborhood Aggregation ---------------------------------- -.. autosummary:: - :toctree: ../api/ops/ - - operators.agg_weighted_fg_n2n_fwd - operators.agg_weighted_fg_n2n_bwd - operators.agg_concat_weighted_fg_n2n_fwd - operators.agg_concat_weighted_fg_n2n_bwd - -Heterogenous Aggregator using Basis Decomposition (RGCNConv) ------------------------------------------------------------- -.. autosummary:: - :toctree: ../api/ops/ - - operators.agg_hg_basis_fg_n2n_post_fwd - operators.agg_hg_basis_fg_n2n_post_bwd - -Graph Attention (GATConv/GATv2Conv) ------------------------------------ -.. autosummary:: - :toctree: ../api/ops/ - - operators.mha_gat_fg_n2n_fwd - operators.mha_gat_fg_n2n_bwd - operators.mha_gat_fg_n2n_efeat_fwd - operators.mha_gat_fg_n2n_efeat_bwd - - operators.mha_gat_v2_fg_n2n_fwd - operators.mha_gat_v2_fg_n2n_bwd - operators.mha_gat_v2_fg_n2n_efeat_fwd - operators.mha_gat_v2_fg_n2n_efeat_bwd - -Transformer-like Graph Attention (TransformerConv) --------------------------------------------------- -.. autosummary:: - :toctree: ../api/ops/ - - operators.mha_gat_v2_fg_n2n_fwd - operators.mha_gat_v2_fg_n2n_bwd - operators.mha_gat_v2_fg_n2n_efeat_fwd - operators.mha_gat_v2_fg_n2n_efeat_bwd - -Directional Message-Passing (DMPNN) ------------------------------------ -.. autosummary:: - :toctree: ../api/ops/ - - operators.agg_dmpnn_fg_e2e_fwd - operators.agg_dmpnn_fg_e2e_bwd - -Graph Pooling -------------- -.. autosummary:: - :toctree: ../api/ops/ - - operators.pool_fg_n2s_fwd - operators.pool_fg_n2s_bwd diff --git a/docs/cugraph/source/api_docs/cugraph-ops/graph_types.rst b/docs/cugraph/source/api_docs/cugraph-ops/graph_types.rst deleted file mode 100644 index 9289ce53e39..00000000000 --- a/docs/cugraph/source/api_docs/cugraph-ops/graph_types.rst +++ /dev/null @@ -1,33 +0,0 @@ -=========== -Graph types -=========== - -.. currentmodule:: pylibcugraphops - -Message-Flow Graph (MFG) -------------------------- -.. autosummary:: - :toctree: ../api/ops/ - - make_mfg_csr - -Heterogenous MFG ----------------- -.. autosummary:: - :toctree: ../api/ops/ - - make_mfg_csr_hg - -"Full" Graph (FG) ------------------ -.. autosummary:: - :toctree: ../api/ops/ - - make_fg_csr - -Heterogenous FG ---------------- -.. autosummary:: - :toctree: ../api/ops/ - - make_fg_csr_hg diff --git a/docs/cugraph/source/api_docs/cugraph-ops/index.rst b/docs/cugraph/source/api_docs/cugraph-ops/index.rst index e2338dc1833..fdfd5baab96 100644 --- a/docs/cugraph/source/api_docs/cugraph-ops/index.rst +++ b/docs/cugraph/source/api_docs/cugraph-ops/index.rst @@ -1,4 +1,3 @@ -========================= cugraph-ops API reference ========================= @@ -8,11 +7,5 @@ This page provides a list of all publicly accessible modules, methods and classe :maxdepth: 2 :caption: API Documentation - graph_types - pytorch - mfg_operators - bipartite_operators - static_operators - fg_operators - dimenet - pytorch + python/index + c_cpp/index \ No newline at end of file diff --git a/docs/cugraph/source/api_docs/cugraph-ops/mfg_operators.rst b/docs/cugraph/source/api_docs/cugraph-ops/mfg_operators.rst deleted file mode 100644 index f3dd1faa245..00000000000 --- a/docs/cugraph/source/api_docs/cugraph-ops/mfg_operators.rst +++ /dev/null @@ -1,31 +0,0 @@ -================================ -Operators on Message-Flow Graphs -================================ - -.. currentmodule:: pylibcugraphops - -Simple Neighborhood Aggregator (SAGEConv) ------------------------------------------ -.. autosummary:: - :toctree: ../api/ops/ - - operators.agg_simple_mfg_n2n_fwd - operators.agg_simple_mfg_n2n_bwd - operators.agg_concat_mfg_n2n_fwd - operators.agg_concat_mfg_n2n_bwd - -Graph Attention (GATConv) -------------------------- -.. autosummary:: - :toctree: ../api/ops/ - - operators.mha_gat_mfg_n2n_fwd - operators.mha_gat_mfg_n2n_bwd - -Heterogenous Aggregator using Basis Decomposition (RGCNConv) ------------------------------------------------------------- -.. autosummary:: - :toctree: ../api/ops/ - - operators.agg_hg_basis_mfg_n2n_post_fwd - operators.agg_hg_basis_mfg_n2n_post_bwd diff --git a/docs/cugraph/source/api_docs/cugraph-ops/dimenet.rst b/docs/cugraph/source/api_docs/cugraph-ops/python/dimenet.rst similarity index 89% rename from docs/cugraph/source/api_docs/cugraph-ops/dimenet.rst rename to docs/cugraph/source/api_docs/cugraph-ops/python/dimenet.rst index b709464c7e6..6fadcc57b22 100644 --- a/docs/cugraph/source/api_docs/cugraph-ops/dimenet.rst +++ b/docs/cugraph/source/api_docs/cugraph-ops/python/dimenet.rst @@ -7,7 +7,7 @@ Dimenet operators Radial Basis Functions ---------------------- .. autosummary:: - :toctree: ../api/ops/ + :toctree: ../../api/ops dimenet.radial_basis_fwd dimenet.radial_basis_bwd @@ -16,7 +16,7 @@ Radial Basis Functions Edge-to-Edge Aggregation ------------------------- .. autosummary:: - :toctree: ../api/ops/ + :toctree: ../../api/ops dimenet.agg_edge_to_edge_fwd dimenet.agg_edge_to_edge_bwd diff --git a/docs/cugraph/source/api_docs/cugraph-ops/python/graph_types.rst b/docs/cugraph/source/api_docs/cugraph-ops/python/graph_types.rst new file mode 100644 index 00000000000..141d40393a5 --- /dev/null +++ b/docs/cugraph/source/api_docs/cugraph-ops/python/graph_types.rst @@ -0,0 +1,34 @@ +=========== +Graph types +=========== + +.. currentmodule:: pylibcugraphops + + +CSC Graph +----------------- +.. autosummary:: + :toctree: ../../api/ops + + make_csc + +Heterogenous CSC Graph +---------------------- +.. autosummary:: + :toctree: ../../api/ops + + make_csc_hg + +Bipartite Graph +----------------- +.. autosummary:: + :toctree: ../../api/ops + + make_bipartite_csc + +Heterogenous Bipartite Graph +---------------------------- +.. autosummary:: + :toctree: ../../api/ops + + make_bipartite_csc_hg diff --git a/docs/cugraph/source/api_docs/cugraph-ops/python/index.rst b/docs/cugraph/source/api_docs/cugraph-ops/python/index.rst new file mode 100644 index 00000000000..082c7741f23 --- /dev/null +++ b/docs/cugraph/source/api_docs/cugraph-ops/python/index.rst @@ -0,0 +1,13 @@ +cugraph-ops Python API reference +================================ + +This page provides a list of all publicly accessible modules, methods and classes through `pylibcugraphops.*` namespace. + +.. toctree:: + :maxdepth: 2 + :caption: API Documentation + + graph_types + operators + dimenet + pytorch diff --git a/docs/cugraph/source/api_docs/cugraph-ops/python/operators.rst b/docs/cugraph/source/api_docs/cugraph-ops/python/operators.rst new file mode 100644 index 00000000000..3e6664b2db5 --- /dev/null +++ b/docs/cugraph/source/api_docs/cugraph-ops/python/operators.rst @@ -0,0 +1,93 @@ +============================= +Operators for Message-Passing +============================= + +.. currentmodule:: pylibcugraphops + +Simple Neighborhood Aggregator (SAGEConv) +----------------------------------------- +.. autosummary:: + :toctree: ../../api/ops + + operators.agg_simple_n2n_fwd + operators.agg_simple_n2n_bwd + operators.agg_simple_e2n_fwd + operators.agg_simple_e2n_bwd + operators.agg_simple_n2n_e2n_fwd + operators.agg_simple_n2n_e2n_bwd + + operators.agg_concat_n2n_fwd + operators.agg_concat_n2n_bwd + operators.agg_concat_e2n_fwd + operators.agg_concat_e2n_bwd + operators.agg_concat_n2n_e2n_fwd + operators.agg_concat_n2n_e2n_bwd + + +Weighted Neighborhood Aggregation +--------------------------------- +.. autosummary:: + :toctree: ../../api/ops + + operators.agg_weighted_n2n_fwd + operators.agg_weighted_n2n_bwd + operators.agg_concat_weighted_n2n_fwd + operators.agg_concat_weighted_n2n_bwd + +Heterogenous Aggregator using Basis Decomposition (RGCNConv) +------------------------------------------------------------ +.. autosummary:: + :toctree: ../../api/ops + + operators.agg_hg_basis_n2n_post_fwd + operators.agg_hg_basis_n2n_post_bwd + +Graph Attention (GATConv/GATv2Conv) +----------------------------------- +.. autosummary:: + :toctree: ../../api/ops + + operators.mha_gat_n2n_fwd + operators.mha_gat_n2n_bwd + operators.mha_gat_n2n_efeat_fwd + operators.mha_gat_n2n_efeat_bwd + + operators.mha_gat_v2_n2n_fwd + operators.mha_gat_v2_n2n_bwd + operators.mha_gat_v2_n2n_efeat_fwd + operators.mha_gat_v2_n2n_efeat_bwd + +Transformer-like Graph Attention (TransformerConv) +-------------------------------------------------- +.. autosummary:: + :toctree: ../../api/ops + + operators.mha_gat_v2_n2n_fwd + operators.mha_gat_v2_n2n_bwd + operators.mha_gat_v2_n2n_efeat_fwd + operators.mha_gat_v2_n2n_efeat_bwd + +Directional Message-Passing (DMPNN) +----------------------------------- +.. autosummary:: + :toctree: ../../api/ops + + operators.agg_dmpnn_e2e_fwd + operators.agg_dmpnn_e2e_bwd + +Update Edges: Concatenation or Sum of Edge and Node Features +------------------------------------------------------------ +.. autosummary:: + :toctree: ../../api/ops + + operators.update_efeat_e2e_concat_fwd + operators.update_efeat_e2e_concat_bwd + + operators.update_efeat_e2e_sum_fwd + operators.update_efeat_e2e_sum_bwd + + operators.update_efeat_e2e_concat_fwd + operators.update_efeat_e2e_concat_bwd + + operators.update_efeat_e2e_sum_fwd + operators.update_efeat_e2e_sum_bwd diff --git a/docs/cugraph/source/api_docs/cugraph-ops/pytorch.rst b/docs/cugraph/source/api_docs/cugraph-ops/python/pytorch.rst similarity index 59% rename from docs/cugraph/source/api_docs/cugraph-ops/pytorch.rst rename to docs/cugraph/source/api_docs/cugraph-ops/python/pytorch.rst index 83800fbc546..d2074df15b0 100644 --- a/docs/cugraph/source/api_docs/cugraph-ops/pytorch.rst +++ b/docs/cugraph/source/api_docs/cugraph-ops/python/pytorch.rst @@ -2,35 +2,35 @@ PyTorch Autograd Wrappers ========================== -.. currentmodule:: pylibcugraphops +.. currentmodule:: pylibcugraphops.pytorch Simple Neighborhood Aggregator (SAGEConv) ----------------------------------------- .. autosummary:: - :toctree: ../api/ops/ + :toctree: ../../api/ops - pytorch.operators.agg_concat_n2n + operators.agg_concat_n2n Graph Attention (GATConv/GATv2Conv) ----------------------------------- .. autosummary:: - :toctree: ../api/ops/ + :toctree: ../../api/ops - pytorch.operators.mha_gat_n2n - pytorch.operators.mha_gat_v2_n2n + operators.mha_gat_n2n + operators.mha_gat_v2_n2n Heterogenous Aggregator using Basis Decomposition (RGCNConv) ------------------------------------------------------------ .. autosummary:: - :toctree: ../api/ops/ + :toctree: ../../api/ops - pytorch.operators.agg_hg_basis_n2n_post + operators.agg_hg_basis_n2n_post Update Edges: Concatenation or Sum of Edge and Node Features ------------------------------------------------------------ .. autosummary:: - :toctree: ../api/ops/ + :toctree: ../../api/ops - pytorch.operators.update_efeat_bipartite_e2e - pytorch.operators.update_efeat_static_e2e + operators.update_efeat_e2e + operators.update_efeat_e2e diff --git a/docs/cugraph/source/api_docs/cugraph-ops/static_operators.rst b/docs/cugraph/source/api_docs/cugraph-ops/static_operators.rst deleted file mode 100644 index f3ecc068f22..00000000000 --- a/docs/cugraph/source/api_docs/cugraph-ops/static_operators.rst +++ /dev/null @@ -1,16 +0,0 @@ -========================== -Operators on Static Graphs -========================== - -.. currentmodule:: pylibcugraphops - -Update Edges: Concatenation or Sum of Edge and Node Features ------------------------------------------------------------- -.. autosummary:: - :toctree: ../api/ops/ - - operators.update_efeat_static_e2e_concat_fwd - operators.update_efeat_static_e2e_concat_bwd - - operators.update_efeat_static_e2e_sum_fwd - operators.update_efeat_static_e2e_sum_bwd diff --git a/docs/cugraph/source/api_docs/cugraph-pyg/cugraph_pyg.rst b/docs/cugraph/source/api_docs/cugraph-pyg/cugraph_pyg.rst index 2cd8969aa66..f7d7f5f2262 100644 --- a/docs/cugraph/source/api_docs/cugraph-pyg/cugraph_pyg.rst +++ b/docs/cugraph/source/api_docs/cugraph-pyg/cugraph_pyg.rst @@ -9,6 +9,6 @@ cugraph-pyg .. autosummary:: :toctree: ../api/cugraph-pyg/ - cugraph_pyg.data.cugraph_store.EXPERIMENTAL__CuGraphStore - cugraph_pyg.sampler.cugraph_sampler.EXPERIMENTAL__CuGraphSampler +.. cugraph_pyg.data.cugraph_store.EXPERIMENTAL__CuGraphStore +.. cugraph_pyg.sampler.cugraph_sampler.EXPERIMENTAL__CuGraphSampler diff --git a/docs/cugraph/source/api_docs/cugraph_c/c_and_cpp.rst b/docs/cugraph/source/api_docs/cugraph_c/c_and_cpp.rst deleted file mode 100644 index 34b812785d3..00000000000 --- a/docs/cugraph/source/api_docs/cugraph_c/c_and_cpp.rst +++ /dev/null @@ -1,4 +0,0 @@ -CuGraph C and C++ API Links -=========================== - -coming soon - see https://docs.rapids.ai/api/libcugraph/nightly/ \ No newline at end of file diff --git a/docs/cugraph/source/api_docs/cugraph_c/centrality.rst b/docs/cugraph/source/api_docs/cugraph_c/centrality.rst new file mode 100644 index 00000000000..f34e26ad76e --- /dev/null +++ b/docs/cugraph/source/api_docs/cugraph_c/centrality.rst @@ -0,0 +1,51 @@ +Centrality +========== + +PageRank +-------- +.. doxygenfunction:: cugraph_pagerank + :project: libcugraph + +.. doxygenfunction:: cugraph_pagerank_allow_nonconvergence + :project: libcugraph + +Personalized PageRank +--------------------- +.. doxygenfunction:: cugraph_personalized_pagerank + :project: libcugraph + +.. doxygenfunction:: cugraph_personalized_pagerank_allow_nonconvergence + :project: libcugraph + +Eigenvector Centrality +---------------------- +.. doxygenfunction:: cugraph_eigenvector_centrality + :project: libcugraph + +Katz Centrality +--------------- +.. doxygenfunction:: cugraph_katz_centrality + :project: libcugraph + +Betweenness Centrality +---------------------- +.. doxygenfunction:: cugraph_betweenness_centrality + :project: libcugraph + +Edge Betweenness Centrality +--------------------------- +.. doxygenfunction:: cugraph_edge_betweenness_centrality + :project: libcugraph + +HITS Centrality +--------------- +.. doxygenfunction:: cugraph_hits + :project: libcugraph + +Centrality Support Functions +---------------------------- + .. doxygengroup:: centrality + :project: libcugraph + :members: + :content-only: + diff --git a/docs/cugraph/source/api_docs/cugraph_c/community.rst b/docs/cugraph/source/api_docs/cugraph_c/community.rst new file mode 100644 index 00000000000..0bbfe365c4d --- /dev/null +++ b/docs/cugraph/source/api_docs/cugraph_c/community.rst @@ -0,0 +1,63 @@ +Community +========= + +.. role:: py(code) + :language: c + :class: highlight + +``#include `` + +Triangle Counting +----------------- +.. doxygenfunction:: cugraph_triangle_count + :project: libcugraph + +Louvain +------- +.. doxygenfunction:: cugraph_louvain + :project: libcugraph + +Leiden +------ +.. doxygenfunction:: cugraph_leiden + :project: libcugraph + +ECG +--- +.. doxygenfunction:: cugraph_ecg + :project: libcugraph + +Extract Egonet +-------------- +.. doxygenfunction:: cugraph_extract_ego + :project: libcugraph + +Balanced Cut +------------ +.. doxygenfunction:: cugraph_balanced_cut_clustering + :project: libcugraph + +Spectral Clustering - Modularity Maximization +--------------------------------------------- +.. doxygenfunction:: cugraph_spectral_modularity_maximization + :project: libcugraph + +.. doxygenfunction:: cugraph_analyze_clustering_modularity + :project: libcugraph + +Spectral Clusteriong - Edge Cut +------------------------------- +.. doxygenfunction:: cugraph_analyze_clustering_edge_cut + :project: libcugraph + +.. doxygenfunction:: cugraph_analyze_clustering_ratio_cut + :project: libcugraph + + +Community Support Functions +--------------------------- + .. doxygengroup:: community + :project: libcugraph + :members: + :content-only: + diff --git a/docs/cugraph/source/api_docs/cugraph_c/core.rst b/docs/cugraph/source/api_docs/cugraph_c/core.rst new file mode 100644 index 00000000000..34456c65e43 --- /dev/null +++ b/docs/cugraph/source/api_docs/cugraph_c/core.rst @@ -0,0 +1,21 @@ +Core +==== + + +Core Number +----------- +.. doxygenfunction:: cugraph_core_number + :project: libcugraph + +K-Core +------ +.. doxygenfunction:: cugraph_k_core + :project: libcugraph + + +Core Support Functions +---------------------- + .. doxygengroup:: core + :project: libcugraph + :members: + :content-only: diff --git a/docs/cugraph/source/api_docs/cugraph_c/index.rst b/docs/cugraph/source/api_docs/cugraph_c/index.rst new file mode 100644 index 00000000000..3dd37dbc374 --- /dev/null +++ b/docs/cugraph/source/api_docs/cugraph_c/index.rst @@ -0,0 +1,16 @@ +=========================== +cuGraph C API documentation +=========================== + + +.. toctree:: + :maxdepth: 3 + :caption: API Documentation + + centrality.rst + community.rst + core.rst + labeling.rst + sampling.rst + similarity.rst + traversal.rst diff --git a/docs/cugraph/source/api_docs/cugraph_c/labeling.rst b/docs/cugraph/source/api_docs/cugraph_c/labeling.rst new file mode 100644 index 00000000000..af105ee8fc9 --- /dev/null +++ b/docs/cugraph/source/api_docs/cugraph_c/labeling.rst @@ -0,0 +1,20 @@ +Components +========== + + +Weakly Connected Components +--------------------------- +.. doxygenfunction:: cugraph_weakly_connected_components + :project: libcugraph + +Strongly Connected Components +----------------------------- +.. doxygenfunction:: cugraph_strongly_connected_components + :project: libcugraph + +Support +------- + .. doxygengroup:: labeling + :project: libcugraph + :members: + :content-only: \ No newline at end of file diff --git a/docs/cugraph/source/api_docs/cugraph_c/sampling.rst b/docs/cugraph/source/api_docs/cugraph_c/sampling.rst new file mode 100644 index 00000000000..21b837daf93 --- /dev/null +++ b/docs/cugraph/source/api_docs/cugraph_c/sampling.rst @@ -0,0 +1,37 @@ +Sampling +======== + +Uniform Random Walks +-------------------- +.. doxygenfunction:: cugraph_uniform_random_walks + :project: libcugraph + +Biased Random Walks +-------------------- +.. doxygenfunction:: cugraph_biased_random_walks + :project: libcugraph + +Random Walks via Node2Vec +------------------------- +.. doxygenfunction:: cugraph_node2vec_random_walks + :project: libcugraph + +Node2Vec +-------- +.. doxygenfunction:: cugraph_node2vec + :project: libcugraph + +Uniform Neighborhood Sampling +----------------------------- +.. doxygenfunction:: cugraph_uniform_neighbor_sample_with_edge_properties + :project: libcugraph + +.. doxygenfunction:: cugraph_uniform_neighbor_sample + :project: libcugraph + +Support +------- +.. doxygengroup:: samplingC + :project: libcugraph + :members: + :content-only: diff --git a/docs/cugraph/source/api_docs/cugraph_c/similarity.rst b/docs/cugraph/source/api_docs/cugraph_c/similarity.rst new file mode 100644 index 00000000000..fba07ad206c --- /dev/null +++ b/docs/cugraph/source/api_docs/cugraph_c/similarity.rst @@ -0,0 +1,25 @@ +Similarity +========== + + +Jaccard +------- +.. doxygenfunction:: cugraph_jaccard_coefficients + :project: libcugraph + +Sorensen +-------- +.. doxygenfunction:: cugraph_sorensen_coefficients + :project: libcugraph + +Overlap +------- +.. doxygenfunction:: cugraph_overlap_coefficients + :project: libcugraph + +Support +------- +.. doxygengroup:: similarity + :project: libcugraph + :members: + :content-only: \ No newline at end of file diff --git a/docs/cugraph/source/api_docs/cugraph_c/traversal.rst b/docs/cugraph/source/api_docs/cugraph_c/traversal.rst new file mode 100644 index 00000000000..c90760e9e79 --- /dev/null +++ b/docs/cugraph/source/api_docs/cugraph_c/traversal.rst @@ -0,0 +1,30 @@ +Traversal +========== + + +Breadth First Search (BFS) +-------------------------- +.. doxygenfunction:: cugraph_bfs + :project: libcugraph + +Single-Source Shortest-Path (SSSP) +---------------------------------- +.. doxygenfunction:: cugraph_sssp + :project: libcugraph + +Path Extraction +--------------- +.. doxygenfunction:: cugraph_extract_paths + :project: libcugraph + +Extract Max Path Length +----------------------- +.. doxygenfunction:: cugraph_extract_paths_result_get_max_path_length + :project: libcugraph + +Support +------- +.. doxygengroup:: traversal + :project: libcugraph + :members: + :content-only: \ No newline at end of file diff --git a/docs/cugraph/source/api_docs/index.rst b/docs/cugraph/source/api_docs/index.rst index 45f7210f5a2..74ca98bb98d 100644 --- a/docs/cugraph/source/api_docs/index.rst +++ b/docs/cugraph/source/api_docs/index.rst @@ -1,16 +1,39 @@ -Python API reference -==================== +API Reference +============= This page provides a list of all publicly accessible Python modules with in the Graph collection +Core Graph API Documentation +---------------------------- + .. toctree:: - :maxdepth: 2 - :caption: Python API Documentation + :maxdepth: 3 + :caption: Core Graph API Documentation cugraph/index.rst plc/pylibcugraph.rst + cugraph_c/index.rst + cugraph_cpp/index.rst + +Graph Nerual Networks API Documentation +--------------------------------------- + +.. toctree:: + :maxdepth: 3 + :caption: Graph Nerual Networks API Documentation + cugraph-dgl/cugraph_dgl.rst cugraph-pyg/cugraph_pyg.rst - service/index.rst cugraph-ops/index.rst + wholegraph/index.rst + +Additional Graph Packages API Documentation +---------------------------------- + +.. toctree:: + :maxdepth: 3 + :caption: Additional Graph Packages API Documentation + + service/index.rst + diff --git a/docs/cugraph/source/api_docs/service/cugraph_service_client.rst b/docs/cugraph/source/api_docs/service/cugraph_service_client.rst index 383b31d269a..7e344d326f7 100644 --- a/docs/cugraph/source/api_docs/service/cugraph_service_client.rst +++ b/docs/cugraph/source/api_docs/service/cugraph_service_client.rst @@ -9,7 +9,7 @@ cugraph-service .. autosummary:: :toctree: ../api/service/ - cugraph_service_client.client.RunAsyncioThread +.. cugraph_service_client.client.RunAsyncioThread cugraph_service_client.client.run_async cugraph_service_client.client.DeviceArrayAllocator cugraph_service_client.client.CugraphServiceClient diff --git a/docs/cugraph/source/api_docs/service/cugraph_service_server.rst b/docs/cugraph/source/api_docs/service/cugraph_service_server.rst index a7e8b547573..09ca8360b6c 100644 --- a/docs/cugraph/source/api_docs/service/cugraph_service_server.rst +++ b/docs/cugraph/source/api_docs/service/cugraph_service_server.rst @@ -9,6 +9,6 @@ cugraph-service .. autosummary:: :toctree: ../api/service/ - cugraph_service_server.cugraph_handler.call_algo +.. cugraph_service_server.cugraph_handler.call_algo cugraph_service_server.cugraph_handler.ExtensionServerFacade cugraph_service_server.cugraph_handler.CugraphHandler diff --git a/docs/cugraph/source/api_docs/wholegraph/index.rst b/docs/cugraph/source/api_docs/wholegraph/index.rst new file mode 100644 index 00000000000..80e231d4610 --- /dev/null +++ b/docs/cugraph/source/api_docs/wholegraph/index.rst @@ -0,0 +1,11 @@ +WholeGraph API reference +======================== + +This page provides WholeGraph API reference + +.. toctree:: + :maxdepth: 2 + :caption: WholeGraph API Documentation + + libwholegraph/index.rst + pylibwholegraph/index.rst diff --git a/docs/cugraph/source/api_docs/wholegraph/libwholegraph/index.rst b/docs/cugraph/source/api_docs/wholegraph/libwholegraph/index.rst new file mode 100644 index 00000000000..4ef68abef2d --- /dev/null +++ b/docs/cugraph/source/api_docs/wholegraph/libwholegraph/index.rst @@ -0,0 +1,228 @@ +===================== +libwholegraph API doc +===================== + +Doxygen WholeGraph C API documentation +-------------------------------------- +For doxygen documentation, please refer to `Doxygen Documentation <../../doxygen_docs/libwholegraph/html/index.html>`_ + +WholeGraph C API documentation +------------------------------ + +Library Level APIs +++++++++++++++++++ + +.. doxygenenum:: wholememory_error_code_t + :project: libwholegraph +.. doxygenfunction:: wholememory_init + :project: libwholegraph +.. doxygenfunction:: wholememory_finalize + :project: libwholegraph +.. doxygenfunction:: fork_get_device_count + :project: libwholegraph + +WholeMemory Communicator APIs ++++++++++++++++++++++++++++++ + +.. doxygentypedef:: wholememory_comm_t + :project: libwholegraph +.. doxygenstruct:: wholememory_unique_id_t + :project: libwholegraph +.. doxygenfunction:: wholememory_create_unique_id + :project: libwholegraph +.. doxygenfunction:: wholememory_create_communicator + :project: libwholegraph +.. doxygenfunction:: wholememory_destroy_communicator + :project: libwholegraph +.. doxygenfunction:: wholememory_communicator_get_rank + :project: libwholegraph +.. doxygenfunction:: wholememory_communicator_get_size + :project: libwholegraph +.. doxygenfunction:: wholememory_communicator_barrier + :project: libwholegraph + +WholeMemoryHandle APIs +++++++++++++++++++++++ + +.. doxygenenum:: wholememory_memory_type_t + :project: libwholegraph +.. doxygenenum:: wholememory_memory_location_t + :project: libwholegraph +.. doxygentypedef:: wholememory_handle_t + :project: libwholegraph +.. doxygenstruct:: wholememory_gref_t + :project: libwholegraph +.. doxygenfunction:: wholememory_malloc + :project: libwholegraph +.. doxygenfunction:: wholememory_free + :project: libwholegraph +.. doxygenfunction:: wholememory_get_communicator + :project: libwholegraph +.. doxygenfunction:: wholememory_get_memory_type + :project: libwholegraph +.. doxygenfunction:: wholememory_get_memory_location + :project: libwholegraph +.. doxygenfunction:: wholememory_get_total_size + :project: libwholegraph +.. doxygenfunction:: wholememory_get_data_granularity + :project: libwholegraph +.. doxygenfunction:: wholememory_get_local_memory + :project: libwholegraph +.. doxygenfunction:: wholememory_get_rank_memory + :project: libwholegraph +.. doxygenfunction:: wholememory_get_global_pointer + :project: libwholegraph +.. doxygenfunction:: wholememory_get_global_reference + :project: libwholegraph +.. doxygenfunction:: wholememory_determine_partition_plan + :project: libwholegraph +.. doxygenfunction:: wholememory_determine_entry_partition_plan + :project: libwholegraph +.. doxygenfunction:: wholememory_get_partition_plan + :project: libwholegraph +.. doxygenfunction:: wholememory_load_from_file + :project: libwholegraph +.. doxygenfunction:: wholememory_store_to_file + :project: libwholegraph + +WholeMemoryTensor APIs +++++++++++++++++++++++ + +.. doxygenenum:: wholememory_dtype_t + :project: libwholegraph +.. doxygenstruct:: wholememory_array_description_t + :project: libwholegraph +.. doxygenstruct:: wholememory_matrix_description_t + :project: libwholegraph +.. doxygenstruct:: wholememory_tensor_description_t + :project: libwholegraph +.. doxygentypedef:: wholememory_tensor_t + :project: libwholegraph +.. doxygenfunction:: wholememory_dtype_get_element_size + :project: libwholegraph +.. doxygenfunction:: wholememory_dtype_is_floating_number + :project: libwholegraph +.. doxygenfunction:: wholememory_dtype_is_integer_number + :project: libwholegraph +.. doxygenfunction:: wholememory_create_array_desc + :project: libwholegraph +.. doxygenfunction:: wholememory_create_matrix_desc + :project: libwholegraph +.. doxygenfunction:: wholememory_initialize_tensor_desc + :project: libwholegraph +.. doxygenfunction:: wholememory_copy_array_desc_to_matrix + :project: libwholegraph +.. doxygenfunction:: wholememory_copy_array_desc_to_tensor + :project: libwholegraph +.. doxygenfunction:: wholememory_copy_matrix_desc_to_tensor + :project: libwholegraph +.. doxygenfunction:: wholememory_convert_tensor_desc_to_array + :project: libwholegraph +.. doxygenfunction:: wholememory_convert_tensor_desc_to_matrix + :project: libwholegraph +.. doxygenfunction:: wholememory_get_memory_element_count_from_array + :project: libwholegraph +.. doxygenfunction:: wholememory_get_memory_size_from_array + :project: libwholegraph +.. doxygenfunction:: wholememory_get_memory_element_count_from_matrix + :project: libwholegraph +.. doxygenfunction:: wholememory_get_memory_size_from_matrix + :project: libwholegraph +.. doxygenfunction:: wholememory_get_memory_element_count_from_tensor + :project: libwholegraph +.. doxygenfunction:: wholememory_get_memory_size_from_tensor + :project: libwholegraph +.. doxygenfunction:: wholememory_unsqueeze_tensor + :project: libwholegraph +.. doxygenfunction:: wholememory_create_tensor + :project: libwholegraph +.. doxygenfunction:: wholememory_destroy_tensor + :project: libwholegraph +.. doxygenfunction:: wholememory_make_tensor_from_pointer + :project: libwholegraph +.. doxygenfunction:: wholememory_make_tensor_from_handle + :project: libwholegraph +.. doxygenfunction:: wholememory_tensor_has_handle + :project: libwholegraph +.. doxygenfunction:: wholememory_tensor_get_memory_handle + :project: libwholegraph +.. doxygenfunction:: wholememory_tensor_get_tensor_description + :project: libwholegraph +.. doxygenfunction:: wholememory_tensor_get_global_reference + :project: libwholegraph +.. doxygenfunction:: wholememory_tensor_map_local_tensor + :project: libwholegraph +.. doxygenfunction:: wholememory_tensor_get_data_pointer + :project: libwholegraph +.. doxygenfunction:: wholememory_tensor_get_entry_per_partition + :project: libwholegraph +.. doxygenfunction:: wholememory_tensor_get_subtensor + :project: libwholegraph +.. doxygenfunction:: wholememory_tensor_get_root + :project: libwholegraph + +Ops on WholeMemory Tensors +++++++++++++++++++++++++++ + +.. doxygenfunction:: wholememory_gather + :project: libwholegraph +.. doxygenfunction:: wholememory_scatter + :project: libwholegraph + +WholeTensorEmbedding APIs ++++++++++++++++++++++++++ + +.. doxygentypedef:: wholememory_embedding_cache_policy_t + :project: libwholegraph +.. doxygentypedef:: wholememory_embedding_optimizer_t + :project: libwholegraph +.. doxygentypedef:: wholememory_embedding_t + :project: libwholegraph +.. doxygenenum:: wholememory_access_type_t + :project: libwholegraph +.. doxygenenum:: wholememory_optimizer_type_t + :project: libwholegraph +.. doxygenfunction:: wholememory_create_embedding_optimizer + :project: libwholegraph +.. doxygenfunction:: wholememory_optimizer_set_parameter + :project: libwholegraph +.. doxygenfunction:: wholememory_destroy_embedding_optimizer + :project: libwholegraph +.. doxygenfunction:: wholememory_create_embedding_cache_policy + :project: libwholegraph +.. doxygenfunction:: wholememory_destroy_embedding_cache_policy + :project: libwholegraph +.. doxygenfunction:: wholememory_create_embedding + :project: libwholegraph +.. doxygenfunction:: wholememory_destroy_embedding + :project: libwholegraph +.. doxygenfunction:: wholememory_embedding_get_embedding_tensor + :project: libwholegraph +.. doxygenfunction:: wholememory_embedding_gather + :project: libwholegraph +.. doxygenfunction:: wholememory_embedding_gather_gradient_apply + :project: libwholegraph +.. doxygenfunction:: wholememory_embedding_get_optimizer_state_names + :project: libwholegraph +.. doxygenfunction:: wholememory_embedding_get_optimizer_state + :project: libwholegraph +.. doxygenfunction:: wholememory_embedding_writeback_cache + :project: libwholegraph +.. doxygenfunction:: wholememory_embedding_drop_all_cache + :project: libwholegraph + +Ops on graphs stored in WholeMemory ++++++++++++++++++++++++++++++++++++ + +.. doxygenfunction:: wholegraph_csr_unweighted_sample_without_replacement + :project: libwholegraph +.. doxygenfunction:: wholegraph_csr_weighted_sample_without_replacement + :project: libwholegraph + +Miscellaneous Ops for graph ++++++++++++++++++++++++++++ + +.. doxygenfunction:: graph_append_unique + :project: libwholegraph +.. doxygenfunction:: csr_add_self_loop + :project: libwholegraph diff --git a/docs/cugraph/source/api_docs/wholegraph/pylibwholegraph/index.rst b/docs/cugraph/source/api_docs/wholegraph/pylibwholegraph/index.rst new file mode 100644 index 00000000000..67aab00acef --- /dev/null +++ b/docs/cugraph/source/api_docs/wholegraph/pylibwholegraph/index.rst @@ -0,0 +1,38 @@ +======================= +pylibwholegraph API doc +======================= + +.. currentmodule:: pylibwholegraph + +APIs +---- +.. autosummary:: + :toctree: ../../api/wg + + torch.initialize.init_torch_env + torch.initialize.init_torch_env_and_create_wm_comm + torch.initialize.finalize + torch.comm.WholeMemoryCommunicator + torch.comm.set_world_info + torch.comm.create_group_communicator + torch.comm.destroy_communicator + torch.comm.get_global_communicator + torch.comm.get_local_node_communicator + torch.comm.get_local_device_communicator + torch.tensor.WholeMemoryTensor + torch.tensor.create_wholememory_tensor + torch.tensor.create_wholememory_tensor_from_filelist + torch.tensor.destroy_wholememory_tensor + torch.embedding.WholeMemoryOptimizer + torch.embedding.create_wholememory_optimizer + torch.embedding.destroy_wholememory_optimizer + torch.embedding.WholeMemoryCachePolicy + torch.embedding.create_wholememory_cache_policy + torch.embedding.create_builtin_cache_policy + torch.embedding.destroy_wholememory_cache_policy + torch.embedding.WholeMemoryEmbedding + torch.embedding.create_embedding + torch.embedding.create_embedding_from_filelist + torch.embedding.destroy_embedding + torch.embedding.WholeMemoryEmbeddingModule + torch.graph_structure.GraphStructure diff --git a/docs/cugraph/source/basics/cugraph_intro.md b/docs/cugraph/source/basics/cugraph_intro.md index 0684129503f..10d14f8a0d7 100644 --- a/docs/cugraph/source/basics/cugraph_intro.md +++ b/docs/cugraph/source/basics/cugraph_intro.md @@ -21,7 +21,7 @@ call graph algorithms using data stored in a GPU DataFrame, NetworkX Graphs, or CuPy or SciPy sparse Matrix. -# Vision +## Vision The vision of RAPIDS cuGraph is to ___make graph analysis ubiquitous to the point that users just think in terms of analysis and not technologies or frameworks___. This is a goal that many of us on the cuGraph team have been @@ -49,7 +49,7 @@ RAPIDS and DASK allows cuGraph to scale to multiple GPUs to support multi-billion edge graphs. -# Terminology +## Terminology cuGraph is a collection of GPU accelerated graph algorithms and graph utility functions. The application of graph analysis covers a lot of areas. @@ -67,8 +67,7 @@ documentation we will mostly use the terms __Node__ and __Edge__ to better match NetworkX preferred term use, as well as other Python-based tools. At the CUDA/C layer, we favor the mathematical terms of __Vertex__ and __Edge__. -# Roadmap -GitHub does not provide a robust project management interface, and so a roadmap turns into simply a projection of when work will be completed and not a complete picture of everything that needs to be done. To capture the work that requires multiple steps, issues are labels as “EPIC” and include multiple subtasks that could span multiple releases. The EPIC will be in the release where work in expected to be completed. A better roadmap is being worked an image of the roadmap will be posted when ready. - * GitHub Project Board: https://github.com/rapidsai/cugraph/projects/28 + + \ No newline at end of file diff --git a/docs/cugraph/source/conf.py b/docs/cugraph/source/conf.py index 470086b4faa..3f7ef7deb03 100644 --- a/docs/cugraph/source/conf.py +++ b/docs/cugraph/source/conf.py @@ -181,11 +181,10 @@ # dir menu entry, description, category) texinfo_documents = [ (master_doc, 'cugraph', 'cugraph Documentation', - author, 'cugraph', 'One line description of project.', + author, 'cugraph', 'GPU-accelerated graph analysis.', 'Miscellaneous'), ] - # Example configuration for intersphinx: refer to the Python standard library. intersphinx_mapping = {'https://docs.python.org/': None} @@ -209,7 +208,9 @@ def setup(app): ) breathe_projects = { + 'libcugraph': os.environ['XML_DIR_LIBCUGRAPH'], 'libcugraphops': os.environ['XML_DIR_LIBCUGRAPHOPS'], 'libwholegraph': os.environ['XML_DIR_LIBWHOLEGRAPH'] } + breathe_default_project = "libcugraph" diff --git a/docs/cugraph/source/graph_support/algorithms.md b/docs/cugraph/source/graph_support/algorithms.md index f6cb7c0d8b1..a1b80e92751 100644 --- a/docs/cugraph/source/graph_support/algorithms.md +++ b/docs/cugraph/source/graph_support/algorithms.md @@ -22,7 +22,7 @@ Note: Multi-GPU, or MG, includes support for Multi-Node Multi-GPU (also called M | Category | Notebooks | Scale | Notes | | ----------------- | ---------------------------------- | ------------------- | --------------------------------------------------------------- | -| [Centrality](./algorithms/Centrality.md) | [Centrality](https://github.com/rapidsai/cugraph/blob/main/notebooks/algorithms/centrality/Centrality.ipynb) | | | +| [Centrality](./algorithms/Centrality.html ) | [Centrality](https://github.com/rapidsai/cugraph/blob/main/notebooks/algorithms/centrality/Centrality.ipynb) | | | | | [Katz](https://github.com/rapidsai/cugraph/blob/main/notebooks/algorithms/centrality/Katz.ipynb) | __Multi-GPU__ | | | | [Betweenness Centrality](https://github.com/rapidsai/cugraph/blob/main/notebooks/algorithms/centrality/Betweenness.ipynb) | __Multi-GPU__ | MG as of 23.06 | | | [Edge Betweenness Centrality](https://github.com/rapidsai/cugraph/blob/main/notebooks/algorithms/centrality/Betweenness.ipynb) | __Multi-GPU__ | MG as of 23.08 | @@ -31,12 +31,12 @@ Note: Multi-GPU, or MG, includes support for Multi-Node Multi-GPU (also called M | Community | | | | | | [Leiden](https://github.com/rapidsai/cugraph/blob/main/notebooks/algorithms/community/Louvain.ipynb) | __Multi-GPU__ | MG as of 23.06 | | | [Louvain](https://github.com/rapidsai/cugraph/blob/main/notebooks/algorithms/community/Louvain.ipynb) | __Multi-GPU__ | | -| | [Ensemble Clustering for Graphs](https://github.com/rapidsai/cugraph/blob/main/notebooks/algorithms/community/ECG.ipynb) | Single-GPU | MG planned for 23.10 | +| | [Ensemble Clustering for Graphs](https://github.com/rapidsai/cugraph/blob/main/notebooks/algorithms/community/ECG.ipynb) | Single-GPU | MG planned for 24.02 | | | [Spectral-Clustering - Balanced Cut](https://github.com/rapidsai/cugraph/blob/main/notebooks/algorithms/community/Spectral-Clustering.ipynb) | Single-GPU | | | | [Spectral-Clustering - Modularity](https://github.com/rapidsai/cugraph/blob/main/notebooks/algorithms/community/Spectral-Clustering.ipynb) | Single-GPU | | | | [Subgraph Extraction](https://github.com/rapidsai/cugraph/blob/main/notebooks/algorithms/community/Subgraph-Extraction.ipyn) | Single-GPU | | | | [Triangle Counting](https://github.com/rapidsai/cugraph/blob/main/notebooks/algorithms/community/Triangle-Counting.ipynb) | __Multi-GPU__ | | -| | [K-Truss](https://github.com/rapidsai/cugraph/blob/main/notebooks/algorithms/community/ktruss.ipynb) | Single-GPU | MG planned for 23.10 | +| | [K-Truss](https://github.com/rapidsai/cugraph/blob/main/notebooks/algorithms/community/ktruss.ipynb) | Single-GPU | MG planned for 2024 | | Components | | | | | | [Weakly Connected Components](https://github.com/rapidsai/cugraph/blob/main/notebooks/algorithms/components/ConnectedComponents.ipynb) | __Multi-GPU__ | | | | [Strongly Connected Components](https://github.com/rapidsai/cugraph/blob/main/notebooks/algorithms/components/ConnectedComponents.ipynb) | Single-GPU | | @@ -55,7 +55,7 @@ Note: Multi-GPU, or MG, includes support for Multi-Node Multi-GPU (also called M | | [Pagerank](https://github.com/rapidsai/cugraph/blob/main/notebooks/algorithms/link_analysis/Pagerank.ipynb) | __Multi-GPU__ | [C++ README](cpp/src/centrality/README.md#Pagerank) | | | [Personal Pagerank]() | __Multi-GPU__ | [C++ README](cpp/src/centrality/README.md#Personalized-Pagerank) | | | [HITS](https://github.com/rapidsai/cugraph/blob/main/notebooks/algorithms/link_analysis/HITS.ipynb) | __Multi-GPU__ | | -| [Link Prediction](./algorithms/Similarity.md) | | | | +| [Link Prediction](algorithms/Similarity.html) | | | | | | [Jaccard Similarity](https://github.com/rapidsai/cugraph/blob/main/notebooks/algorithms/link_prediction/Jaccard-Similarity.ipynb) | __Multi-GPU__ | Directed graph only | | | [Weighted Jaccard Similarity](https://github.com/rapidsai/cugraph/blob/main/notebooks/algorithms/link_prediction/Jaccard-Similarity.ipynb) | Single-GPU | | | | [Overlap Similarity](https://github.com/rapidsai/cugraph/blob/main/notebooks/algorithms/link_prediction/Overlap-Similarity.ipynb) | **Multi-GPU** | | @@ -65,8 +65,8 @@ Note: Multi-GPU, or MG, includes support for Multi-Node Multi-GPU (also called M | | [Uniform Random Walks RW](https://github.com/rapidsai/cugraph/blob/main/notebooks/algorithms/sampling/RandomWalk.ipynb) | __Multi-GPU__ | | | | *Biased Random Walks (RW)* | --- | | | | Egonet | __Multi-GPU__ | | -| | Node2Vec | Single-GPU | | -| | Uniform Neighborhood sampling | __Multi-GPU__ | | +| | Node2Vec | __Multi-GPU__ | | +| | Neighborhood sampling | __Multi-GPU__ | | | Traversal | | | | | | Breadth First Search (BFS) | __Multi-GPU__ | with cutoff support [C++ README](cpp/src/traversal/README.md#BFS) | | | Single Source Shortest Path (SSSP) | __Multi-GPU__ | [C++ README](cpp/src/traversal/README.md#SSSP) | diff --git a/docs/cugraph/source/graph_support/algorithms/Centrality.md b/docs/cugraph/source/graph_support/algorithms/Centrality.md index e42bbe238c6..f82a1fe123b 100644 --- a/docs/cugraph/source/graph_support/algorithms/Centrality.md +++ b/docs/cugraph/source/graph_support/algorithms/Centrality.md @@ -1,7 +1,7 @@ # cuGraph Centrality Notebooks - + The RAPIDS cuGraph Centrality folder contains a collection of Jupyter Notebooks that demonstrate algorithms to identify and quantify the importance of vertices to the structure of the graph. In the diagram above, the highlighted vertices are highly important and are likely answers to questions like: @@ -15,13 +15,13 @@ But which vertices are most important? The answer depends on which measure/algor |Algorithm |Notebooks Containing |Description | | --------------- | ------------------------------------------------------------ | ------------------------------------------------------------ | -|[Degree Centrality](./degree_centrality.md)| [Centrality](./Centrality.ipynb), [Degree](./Degree.ipynb) |Measure based on counting direct connections for each vertex| -|[Betweenness Centrality](./betweenness_centrality.md)| [Centrality](./Centrality.ipynb), [Betweenness](./Betweenness.ipynb) |Number of shortest paths through the vertex| -|[Eigenvector Centrality](./eigenvector_centrality.md)|[Centrality](./Centrality.ipynb), [Eigenvector](./Eigenvector.ipynb)|Measure of connectivity to other important vertices (which also have high connectivity) often referred to as the influence measure of a vertex| -|[Katz Centrality](./katz_centrality.md)|[Centrality](./Centrality.ipynb), [Katz](./Katz.ipynb) |Similar to Eigenvector but has tweaks to measure more weakly connected graph | +|[Degree Centrality](./degree_centrality.html)| [Centrality](./Centrality.ipynb), [Degree](./Degree.ipynb) |Measure based on counting direct connections for each vertex| +|[Betweenness Centrality](./betweenness_centrality.html)| [Centrality](./Centrality.ipynb), [Betweenness](./Betweenness.ipynb) |Number of shortest paths through the vertex| +|[Eigenvector Centrality](./eigenvector_centrality.html)|[Centrality](./Centrality.ipynb), [Eigenvector](./Eigenvector.ipynb)|Measure of connectivity to other important vertices (which also have high connectivity) often referred to as the influence measure of a vertex| +|[Katz Centrality](./katz_centrality.html)|[Centrality](./Centrality.ipynb), [Katz](./Katz.ipynb) |Similar to Eigenvector but has tweaks to measure more weakly connected graph | |Pagerank|[Centrality](./Centrality.ipynb), [Pagerank](../../link_analysis/Pagerank.ipynb) |Classified as both a link analysis and centrality measure by quantifying incoming links from central vertices. | -[System Requirements](../../README.md#requirements) +[System Requirements](../../README.html#requirements) | Author Credit | Date | Update | cuGraph Version | Test Hardware | | --------------|------------|------------------|-----------------|----------------| diff --git a/docs/cugraph/source/graph_support/algorithms/Similarity.md b/docs/cugraph/source/graph_support/algorithms/Similarity.md index 450beb373a2..18c0a94d519 100644 --- a/docs/cugraph/source/graph_support/algorithms/Similarity.md +++ b/docs/cugraph/source/graph_support/algorithms/Similarity.md @@ -15,9 +15,9 @@ Manipulation of the data before or after the graph analytic is not covered here. |Algorithm |Notebooks Containing |Description | | --------------- | ------------------------------------------------------------ | ------------------------------------------------------------ | -|[Jaccard Smiliarity](./jaccard_similarity.md)| [Jaccard Similarity](https://github.com/rapidsai/cugraph/blob/main/notebooks/algorithms/link_prediction/Jaccard-Similarity.ipynb) || -|[Overlap Similarity](./overlap_similarity.md)| [Overlap Similarity](https://github.com/rapidsai/cugraph/blob/main/notebooks/algorithms/link_prediction/Overlap-Similarity.ipynb) || -|[Sorensen](./sorensen_coefficient.md)|[Sorensen Similarity](https://github.com/rapidsai/cugraph/blob/main/notebooks/algorithms/link_prediction/Sorensen_coefficient.ipynb)|| +|[Jaccard Smiliarity](./jaccard_similarity.html)| [Jaccard Similarity](https://github.com/rapidsai/cugraph/blob/main/notebooks/algorithms/link_prediction/Jaccard-Similarity.ipynb) || +|[Overlap Similarity](./overlap_similarity.html)| [Overlap Similarity](https://github.com/rapidsai/cugraph/blob/main/notebooks/algorithms/link_prediction/Overlap-Similarity.ipynb) || +|[Sorensen](./sorensen_coefficient.html)|[Sorensen Similarity](https://github.com/rapidsai/cugraph/blob/main/notebooks/algorithms/link_prediction/Sorensen_coefficient.ipynb)|| |Personal Pagerank|[Pagerank](https://github.com/rapidsai/cugraph/blob/main/notebooks/algorithms/link_analysis/Pagerank.ipynb) || diff --git a/docs/cugraph/source/index.rst b/docs/cugraph/source/index.rst index c5303c21674..955eb6d54db 100644 --- a/docs/cugraph/source/index.rst +++ b/docs/cugraph/source/index.rst @@ -25,12 +25,12 @@ RAPIDS Graph documentation * - :abbr:`libcugraph_etl (C++ renumbering function for strings)` - :abbr:`wholegraph (Shared memory-based GPU-accelerated GNN training)` - - .. - -| | +~~~~~~~~~~~~ +Introduction +~~~~~~~~~~~~ cuGraph is a library of graph algorithms that seamlessly integrates into the RAPIDS data science ecosystem and allows the data scientist to easily call graph algorithms using data stored in GPU DataFrames, NetworkX Graphs, or @@ -39,6 +39,7 @@ even CuPy or SciPy sparse Matrices. Note: We are redoing all of our documents, please be patient as we update the docs and links +| .. toctree:: :maxdepth: 2 @@ -48,9 +49,8 @@ the docs and links installation/index tutorials/index graph_support/index + wholegraph/index references/index - dev_resources/index - releases/index api_docs/index Indices and tables diff --git a/docs/cugraph/source/wholegraph/basics/index.rst b/docs/cugraph/source/wholegraph/basics/index.rst new file mode 100644 index 00000000000..429fe35d601 --- /dev/null +++ b/docs/cugraph/source/wholegraph/basics/index.rst @@ -0,0 +1,11 @@ +====== +Basics +====== + + +.. toctree:: + :maxdepth: 2 + + wholegraph_intro + wholememory_intro + wholememory_implementation_details diff --git a/docs/cugraph/source/wholegraph/basics/wholegraph_intro.md b/docs/cugraph/source/wholegraph/basics/wholegraph_intro.md new file mode 100644 index 00000000000..360f8e0e36b --- /dev/null +++ b/docs/cugraph/source/wholegraph/basics/wholegraph_intro.md @@ -0,0 +1,135 @@ +# WholeGraph Introduction +WholeGraph helps train large-scale Graph Neural Networks(GNN). +WholeGraph provides underlying storage structure called WholeMemory. +WholeMemory is a Tensor like storage and provides multi-GPU support. +It is optimized for NVLink systems like DGX A100 servers. +By working together with cuGraph, cuGraph-Ops, cuGraph-DGL, cuGraph-PyG, and upstream DGL and PyG, +it will be easy to build GNN applications. + +## WholeMemory +WholeMemory can be regarded as a whole view of GPU memory. +WholeMemory exposes a handle of the memory instance no matter how the underlying data is stored across multiple GPUs. +WholeMemory assumes that separate process is used to control each GPU. + +### WholeMemory Basics +To define WholeMemory, we need to specify the following: + +#### 1. Specify the set of GPU to handle the Memory + +Since WholeMemory is owned by a set of GPUs, you must specify the set of GPUs. +This is done by creating [WholeMemory Communicator](#wholememory-communicator) and specifying the WholeMemory Communicator when creating WholeMemory. + +#### 2. Specify the location of the memory + +Although WholeMemory is owned by a set of GPUs, the memory itself can be located in host memory or in device memory. +The location of the memory need to be specified, two types of locations can be specified. + +- **Host memory**: will use pinned host memory as underlying storage. +- **Device memory**: will use GPU device memory as underlying storage. + +#### 3. Specify the address mapping mode of the memory + +As WholeMemory is owned by multiple GPUs, each GPU will access the whole memory space, so we need address mapping. +There are three types of address mapping modes (also known as WholeMemory types), they are: + +- **Continuous**: All memory from each GPU will be mapped into a single continuous memory address space for each GPU. + In this mode, each GPU can directly access the whole memory using a single pointer and offset, just like using normal + device memory. Software will see no difference. Hardware peer to peer access will handle the underlying communication. + +- **Chunked**: Memory from each GPU will be mapped into different memory chunks, one chunk for each GPU. + In this mode, direct access is also supported, but not using a single pointer. Software will see the chunked memory. + However, an abstract layer may help to hide this. + +- **Distributed**: Memory from other GPUs are not mapped into current GPU, so no direct access is supported. + To access memory of other GPU, explicit communication is needed. + +To learn more details about WholeMemory locations and WholeMemory types, please refer to +[WholeMemory Implementation Details](wholememory_implementation_details.md) + +### WholeMemory Communicator +WholeMemory Communicator has two main purpose: + +- **Defines a set of GPUs which works together on WholeMemory.** WholeMemory Communicator is created by all GPUs that + wants to work together. A WholeMemory Communicator can be reused as long as the GPU set needed is the same. +- **Provides underlying communication channel needed by WholeMemory.** WholeMemory may need commuincator between GPUs + during the WholeMemory creation and some OPs on some types of WholeMemory. + +To Create WholeMemory Communicator, a WholeMemory Unique ID needs to be created first, it is usually created by the first GPU in the set of GPUs, and then broadcasted to all GPUs that want to work together. Then all GPUs in this communicator +will call WholeMemory Communicator creation function using this WholeMemory Unique ID, and the rank of current GPU as +well as all GPU count. + +### WholeMemory Granularity +As underlying storage may be partitioned into multiple GPUs physically, this is usually not wanted inside one single +user data block. To help on this, when creating WholeMemory, the granularity of data can be specified. Then the +WholeMemory is considered as multiple block of the same granularity and will not get split inside the granularity. + +### WholeMemory Mapping +As WholeMemory provides a whole view of memory to GPU, to access WholeMemory, mapping is usually needed. +Different types of WholeMemory have different mapping methods supported as their names. +Some mappings supported include +- All the WholeMemory types support mapping the memory range that local GPU is responsible for. + That is, each rank can directly access "Local" memory in all types of WholeMemory. + Here "Local" memory doesn't have to be on current GPU's memory, it can be on host memory or even maybe on other GPU, + but it is guaranteed to be directly accessed by current GPU. +- Chunked and Continuous WholeMemory also support Chunked mapping. That is, memory of all GPUs can be mapped into + current GPU, one continuous chunk for one GPU. Each chunk can be directly accessed by current GPU. But the memory of + different chunks are not guaranteed to be continuous. +- Continuous WholeMemory can be mapped into continuous memory space. That is, memory of all GPUs are mapped into a + single range of virtual memory, accessing to different position of this memory will physically access to different + GPUs. This mapping will be handled by hardware (CPU pagetable or GPU pagetable). + +### Operations on WholeMemory +There are some operations that can be performed on WholeMemory. They are based on the mapping of WholeMemory. +#### Local Operation +As all WholeMemory supports mapping of local memory, so operation on local memory is supported. The operation can be +either read or write. Just use it as GPU memory of current device is OK. +#### Load and Store +To facilitate file operation, Load / Store WholeMemory from file or to file is supported. WholeMemory uses raw binary +file format for disk operation. For Load, the input file can be a single file or a list of files, if it is a list, they +will be logically concatenated together and then loaded. For store, each GPU stores its local memory to file, producing +a list of files. +#### Gather and Scatter +WholeMemory also supports Gather / Scatter operation, usually they operate on a +[WholeMemory Tensor](#wholememory-tensor). + +### WholeMemory Tensor +Compared to PyTorch, WholeMemory is like PyTorch Storage while a WholeMemory Tensor is like a PyTorch Tensor. +For now, WholeMemory supports only 1D and 2D tensors, or arrays and matrices. Only first dimension is partitioned. + +### WholeMemory Embedding +WholeMemory Embedding is just like a 2D WholeMemory Tensor, with two features added. They support cache and sparse +optimizers. +#### Cache Support +To create WholeMemory Embedding with a cache, WholeMemory CachePolicy needs to be be created first. WholeMemoryCachePolicy can be created with following fields: +- **WholeMemory Communicator**: WholeMemory CachePolicy also needs WholeMemory Communicator. + WholeMemory Communicator defines the set of GPUs that cache all the Embedding. + It can be the same as the WholeMemory Communicator used to create WholeMemory Embedding. +- **WholeMemory type**: WholeMemory CachePolicy uses WholeMemory type to specify the WholeMemory type of cache. +- **WholeMemory location**: WholeMemory CachePolicy use WholeMemory location to specify the location of the cache. +- **Access type**: Access type can be readonly or readwrite. +- **Cache ratio**: Specify how much memory the cache will use. This ratio is computed for each GPU set that caches the + whole embedding. + +The two most commonly used caches are: +- **Device cached host memory**: When the WholeMemory Communicator for Cache Policy is the same as the WholeMemory + Communicator used to create WholeMemory Embedding, it means that the cache has same GPU set as WholeMemory Embedding. + So each GPU just caches its own part of raw Embedding. + Most commonly, when raw WholeMemory Embedding is located on host memory, and the cache is on device + memory, each GPU just caches its own part of host memory. +- **Local cached global memory**: The WholeMemory Communicator of WholeMemory CachePolicy can also be a subset of the + WholeMemory Communicator of WholeMemory Embedding. In this case, the subset of GPUs together cache all the embeddings. + Normally, when raw WholeMemory Embedding is partitioned on different machine nodes, and we + want to cache some embeddings in local machine or local GPU, then the subset of GPU can be all the GPUs in the local + machine. For local cached global memory, only readonly is supported. + +#### WholeMemory Embedding Sparse Optimizer +Another feature of WholeMemory Embedding is that WholeMemory Embedding supports embedding training. +To efficiently train large embedding tables, a sparse optimizer is needed. +WholeMemory Embedding Sparse Optimizer can run on a cached or noncached WholeMemory Embedding. +Currently supported optimizers include SGD, Adam, RMSProp and AdaGrad. + +## Graph Structure +Graph structure in WholeGraph is also based on WholeMemory. +In WholeGraph, graph is stored in [CSR format](https://en.wikipedia.org/wiki/Sparse_matrix#Compressed_sparse_row_(CSR,_CRS_or_Yale_format)). +Both ROW_INDEX (noted as `csr_row_ptr`) and COL_INDEX (notated as `csr_col_ind`) are stored in a +WholeMemory Tensor. So loading Graph Structure can use [WholeMemory Tensor Loading mechanism](#load-and-store). diff --git a/docs/cugraph/source/wholegraph/basics/wholememory_implementation_details.md b/docs/cugraph/source/wholegraph/basics/wholememory_implementation_details.md new file mode 100644 index 00000000000..a5541109c4f --- /dev/null +++ b/docs/cugraph/source/wholegraph/basics/wholememory_implementation_details.md @@ -0,0 +1,58 @@ +# WholeMemory Implementation Details +As described in [WholeMemory Introduction](wholegraph_intro.md), there are two WholeMemory location and three +WholeMemory types. So there will be total six WholeMemory. + +| Type | CONTINUOUS | CONTINUOUS | CHUNKED | CHUNKED | DISTRIBUTED | DISTRIBUTED | +|:-------------:|:-----------:|:----------:|:---------:|:---------:|:-----------:|:-----------:| +| Location | DEVICE | HOST | DEVICE | HOST | DEVICE | HOST | +| Allocated by | EACH | FIRST | EACH | FIRST | EACH | EACH | +| Allocate API | Driver | Host | Runtime | Host | Runtime | Runtime | +| IPC Mapping | Unix fd | mmap | cudaIpc | mmap | No IPC map | No IPC map | + +For "Continuous" and "Chunked" types of WholeMemory, all memory is mapped to each GPU, +so these two types are all "Mapped" WholeMemory, in contrast to "Distributed" WholeMemory where all are not mapped. + +## WholeMemory Layout +Since the underlying memory of a single WholeMemory object may be on multiple GPU devices, the WholeGraph library will +partition data into these GPU devices. +The partition method guarantees that each GPU can access one continuous part of the entire memory. +Here "can access" means can directly access from CUDA kernels, but the memory doesn't have to be physically on that GPU. +For example,it can be on host memory or other GPU's device memory that can be access using P2P. +In that case the stored data has its own granularity that shouldn't be split. Data granularity can be specified while +creating WholeMemory. Then each data granularity can be considered as a block of data. + +The follow figure shows the layout of 15 data block over 4 GPUs. +![WholeMemory Layout](../imgs/general_wholememory.png) + +For WholeMemory Tensors, they can be 1D or 2D tensors. +For 1D tensor, data granularity is one element. For 2D tensor, data granularity is its 1D tensor. +The layout will be like this: +![WholeMemory Tensor Layout](../imgs/wholememory_tensor.png) + +## WholeMemory Allocation +As there are six types of WholeMemory, the allocation process of each type are as follows: + +### Device Continuous WholeMemory +For Device Continuous WholeMemory, first a range of virtual address space is reserved in each GPU, which covers the +entire memory range. Then a part of pyhsical memory is allocated in each GPU, as shown in the following figure. +![Device Continuous WholeMemory Allocation Step 1](../imgs/device_continuous_wholememory_step1.png) +After that, each GPU gathers all the memory handles from all GPUs, and maps them to the reserved address space. +![Device Continuous WholeMemory Allocation Step 2](../imgs/device_continuous_wholememory_step2.png) + +### Device Chunked WholeMemory +For Device Chunked WholeMemory, first each GPU allocates its own part of memory using CUDA runtime API, this will create +both a virtual address space and physical memory for its own memory. +![Device Chunked WholeMemory Allocation Step 1](../imgs/device_chunked_wholememory_step1.png) +Each GPU gathers the Ipc handle of memory from all other GPUs, and maps that into its own virtual address space. +![Device Chunked WholeMemory Allocation Step 2](../imgs/device_chunked_wholememory_step2.png) + +### Host Mapped WholeMemory +For Host, Continuous and Chunked are using the same method. First, rank and allocate the host physical and share that to all +ranks. +![Host Mapped WholeMemory Allocation Step 1](../imgs/host_mapped_wholememory_step1.png) +Then each rank registers that host memory to GPU address space. +![Host Mapped WholeMemory Allocation Step 2](../imgs/host_mapped_wholememory_step2.png) + +### Distributed WholeMemory +For Distributed WholeMemory, each GPU just malloc its own part of memory, no need to share to other GPUs. +![Distributed WholeMemory Allocation](../imgs/distributed_wholememory.png) diff --git a/docs/cugraph/source/wholegraph/basics/wholememory_intro.md b/docs/cugraph/source/wholegraph/basics/wholememory_intro.md new file mode 100644 index 00000000000..7209da9471c --- /dev/null +++ b/docs/cugraph/source/wholegraph/basics/wholememory_intro.md @@ -0,0 +1,123 @@ +## WholeMemory +WholeMemory can be regarded as a whole view of GPU memory. +WholeMemory exposes a handle to the memory instance no matter how the underlying data is stored across multiple GPUs. +WholeMemory assumes that a separate process is used to control each GPU. + +### WholeMemory Basics +To define WholeMemory, we need to specify the following: + +#### 1. Specify the set of GPU to handle the Memory + +As WholeMemory is owned by a set of GPUs, so the set of GPUs need to be specified. +This is done by creating [WholeMemory Communicator](#wholememory-communicator) and specify the WholeMemory Communicator +when creating WholeMemory. + +#### 2. Specify the location of the memory + +Although WholeMemory is owned by a set of GPUs, the memory itself can be located on host memory or on device memory. +So the location of the memory needs to be specified. Two types of location can be specified. + +- **Host memory**: will use pinned host memory as underlying storage. +- **Device memory**: will use GPU device memory as underlying storage. + +#### 3. Specify the address mapping mode of the memory + +As WholeMemory is owned by multiple GPUs, each GPU will access the whole memory space, so we need address mapping. +There are three types of address mapping modes (also known as WholeMemory types), they are: + +- **Continuous**: All memory from each GPU will be mapped into a single continuous memory address space for each GPU. + In this mode, each GPU can directly access the whole memory using a single pointer and offset, just like using normal + device memory. Software will see no difference. Hardware peer-to-peer access will handle the underlying communication. + +- **Chunked**: Memory from each GPU will be mapped into different memory chunks, one chunk for each GPU. + In this mode, direct access is also supported, but not using a single pointer. Software will see the chunked memory. + However, an abstract layer can hide this. + +- **Distributed**: Memory from other GPUs is not mapped into current GPU, so no direct access is supported. + To access memory of another GPU, explicit communication is needed. + +If you would like to know more details about WholeMemory locations and WholeMemory types, please refer to +[WholeMemory Implementation Details](wholememory_implementation_details.md) + +### WholeMemory Communicator +WholeMemory Communicator has two main purpose: + +- **Defines a set of GPUs which works together on WholeMemory.** WholeMemory Communicator is created by all GPUs that + wants to work together. A WholeMemory Communicator can be reused as long as the GPU set needed is the same. +- **Provides underlying communication channel needed by WholeMemory.** WholeMemory may need commuincator between GPUs + during the WholeMemory creation and some OPs on some types of WholeMemory. + +To Create WholeMemory Communicator, a WholeMemory Unique ID need to be created first, it is usually created by the first +GPU in the set of GPUs, and then broadcasted to all GPUs that want to work together. Then all GPUs in this communicator +will call WholeMemory Communicator creation function using this WholeMemory Unique ID, and the rank of current GPU as +well as all GPU count. + +### WholeMemory Granularity +As underlying storage may be physically partitioned into multiple GPUs, it is usually not wanted inside one single +user data block. To help with this, when creating WholeMemory, the granularity of data can be specified. Therefore +WholeMemory is considered as multiple blocks of the same granularity and will not get split inside the granularity. + +### WholeMemory Mapping +Since WholeMemory provides a whole view of memory to GPU, mapping is usually needed to access WholeMemory. +Different types of WholeMemory have different mapping methods supported as their names. +Some mappings supported include: +- All the WholeMemory types support mapping the memory range that local GPU is responsible for. + That is, each rank can directly access "Local" memory in all types of WholeMemory. + Here "Local" memory doesn't have to be on current GPU's memory, it can be on host memory or even maybe on other GPU, + but it is guaranteed to be directly accessed by current GPU. +- Chunked and Continuous WholeMemory also support Chunked mapping. That is, memory of all GPUs can be mapped into + current GPU, one continuous chunk for one GPU. Each chunk can be directly accessed by current GPU. But the memory of + different chunks are not guaranteed to be continuous. +- Continuous WholeMemory can be mapped into continuous memory space. That is, memory of all GPUs are mapped into a + single range of virtual memory, accessing different positions of this memory will physically access different + GPUs. This mapping will be handled by hardware (CPU pagetable or GPU pagetable). + +### Operations on WholeMemory +There are some operations that can be performed on WholeMemory. They are based on the mapping of WholeMemory. +#### Local Operation +As all WholeMemory supports mapping of local memory, so operation on local memory is supported. The operation can be +either read or write. Just use it as GPU memory of current device is OK. +#### Load / Store +To facilitate file operation, Load / Store WholeMemory from file or to file is supported. WholeMemory use raw binary +file format for disk operation. For Load, the input file can be single file or a list of files, if it is a list, they +will be logically concatenated together and then loaded. For store, each GPU stores its local memory to file, producing +a list of files. +#### Gather / Scatter +WholeMemory also supports Gather / Scatter operations, usually they operate on a +[WholeMemory Tensor](#wholememory-tensor). + +### WholeMemory Tensor +Compared to PyTorch, WholeMemory is like PyTorch Storage while WholeMemory Tensor is like PyTorch Tensor. +For now, WholeMemory supports only 1D and 2D tensor, or array and matrix. Only first dimension is partitioned. + +### WholeMemory Embedding +WholeMemory Embedding is just like 2D WholeMemory Tensor, with cache support and sparse optimizer support added. +#### Cache Support +WholeMemory Embedding supports cache. To create WholeMemory Embedding with cache, WholeMemory CachePolicy need first be +created. WholeMemoryCachePolicy can be created with following fields: +- **WholeMemory Communicator**: WholeMemory CachePolicy also need WholeMemory Communicator. + This WholeMemory Communicator defines the set of GPUs that cache the all the Embedding. + It can be the same as the WholeMemory Communicator used to create WholeMemory Embedding. +- **WholeMemory type**: WholeMemory CachePolicy uses WholeMemory type to specify the WholeMemory type of the cache. +- **WholeMemory location**: WholeMemory CachePolicy uses WholeMemory location to specify the location of the cache. +- **Access type**: Access type can be readonly or readwrite. +- **Cache ratio**: Specify how much memory the cache will use. This ratio is computed for each GPU set that caches the + whole embedding. + +There are two most commonly used caches. They are: +- **Device cached host memory**: When the WholeMemory Communicator for Cache Policy is the same as the WholeMemory + Communicator used to create WholeMemory Embedding, it means that cache has the same GPU set as WholeMemory Embedding. + So each GPU just cache its own part of raw Embedding. + Normally, when raw WholeMemory Embedding is located on host memory, and the cache is on device + memory, each GPU just caches its own part of host memory. +- **Local cached global memory**: The WholeMemory Communicator of WholeMemory CachePolicy can also be a subset of the + WholeMemory Communicator of WholeMemory Embedding. In this case, the subset of GPUs together cache all the embeddings. + Typically, raw WholeMemory Embedding is partitioned on different machine nodes, and we + want to cache some embeddings in local machine or local GPU, then the subset of GPUs can be all the GPUs on the local + machine. For local cached global memory supports just readonly. + +#### WholeMemory Embedding Sparse Optimizer +Another feature of WholeMemory Embedding is that WholeMemory Embedding supports embedding training. +To efficiently train large embedding tables, a sparse optimizer is needed. +The WholeMemory Embedding Sparse Optimizer can run on cached or non-cached WholeMemory Embedding. +Currently supported optimizers include SGD, Adam, RMSProp and AdaGrad. diff --git a/docs/cugraph/source/wholegraph/imgs/device_chunked_wholememory_step1.png b/docs/cugraph/source/wholegraph/imgs/device_chunked_wholememory_step1.png new file mode 100644 index 0000000000000000000000000000000000000000..b8a0447e6fb9dde5691c987aa734a6ba4ae24663 GIT binary patch literal 23136 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 A_5c6? literal 0 HcmV?d00001 diff --git a/docs/cugraph/source/wholegraph/imgs/device_chunked_wholememory_step2.png b/docs/cugraph/source/wholegraph/imgs/device_chunked_wholememory_step2.png new file mode 100644 index 0000000000000000000000000000000000000000..8b203ce22467786b4b8f7fca87a6cfe9d5798027 GIT binary patch literal 28201 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 VzTd9vQIZ42tEa1<%Q~loCIE!7SJMCh literal 0 HcmV?d00001 diff --git a/docs/cugraph/source/wholegraph/imgs/device_continuous_wholememory_step2.png b/docs/cugraph/source/wholegraph/imgs/device_continuous_wholememory_step2.png new file mode 100644 index 0000000000000000000000000000000000000000..b773b1ef6e9872e8622c808de28510acb5033cb0 GIT binary patch literal 26434 zcmeFZ2~?9;+ct`|)>bVzpg1tp37G_i3WAcfDxv~XWR@Wc0xEB13>E2!RAh1wxoYBtSwELjIj7{oe1K_xsoS*ZR--&ROSVwaX+VPxij| zJzUpy@BKXZ?V|aQ8+LDylau@L{JAri%i+DZk=;R%E@gFlK#JD*jw$koSZlH{Fz_vZo9LGHhwMBH->z3L9J^Vnu7BBx@Z2tYE?_g|)*qK_l4Fi&+-r%u(`vhGmv@&b2^B8($qB9t z<)cIRvy_6O>4jlQV>oyj$)~XheGrpScj+KykJm5U93&h6d39ft_s!~ybh&2t)#q~C z{_OwfQ&+xGj99b!7t1IQz18PYKm0cH&y|m?dDyu62RZd$%9U1My#7|>|NjxGG5+6S zu3JH~4dE(s-yu0Dz9Xkb)YN-|voaBl;Rwzx&MHK{6fPa&6hqLQf*lg{$grr$8a>q$ zyitzFysCgYxA_usKzkt~uqq~A?A62z670+SqO-Fx7D9T;=!(4Zzk^7K5y`v*h zXZ_AY#$83%OkN*v3&NitUK^LxP@o4~aMK7;r1rv{kFPI=p{GSVB*TJw{zuH3D@CQ^ zH!Y5O!jVIEdg$@qSWH>X;F&iumZ@u%cNjxp`YM0FY7NS4jfiU6$FZ4N_j>r`El8k$ zgxIauIveGFYBf2(IcC)iLo&5G7Yw7fDQ1>_sNrhjZCdGC2N5@wt-qX<;j6Id1BcBV_D$ zQFl5~F-rVUQ`GbG3buyd(`zo7q3C5!NmN2H*NzThvTjSz0}p(6sB|V#>gtH8nv+?~ z*iD$_=uK%%xzV3m+S-DmCZ9|$9OSIQSq%52fj|oxFF>>T#T-m0!m$iBEt>Y#<;@&2 zt;_XTc+eY5T6)m?tgNnLWuu(!{L=)6i`#!w_NdZ>$w zi?EfC!rkwHxjODDLnHN7uBcuUHnc@Vg+1nf3+(Ra7PnhSq&C!sH@RW#VDo?apoX6% z6l_7!Sda?hj;Pb~rO@SxeF`H5-++rN^im-c>!^_z7jyJbv-=cwsyWLaK?E+}Y7L4C zOPZYjiL++hYM7D)CV?L}LQC%UhLWa!>{UUatx=tK-2=px#c8kztHn1yCyB)GqH1r8 zPs|p&2eaIIwK+>U!p!wZO7neGTh?~#S4O&)}bXwtYc32z{Vo&E}!+HL*z9R`&tTq#jSmBhO@?k zUn57XERFW^C4-z+(OO*p#;8Zj?Q~A*N}o2SFr2?0;s8Vb1r-Zfj*P`%*d#uANjBL1OB3FFh zQW6RkjZeYE$(GoyVG6Iczy+*xIZcvO$nu&?z^!TMv1&xwN*Ct18?5ec-(3FK)JHNg zFMP$;a?OU60S7p!$nHaxpz})l8NbQ3;V?Z1yV&glOZmeVnmWR7PmmB^gdF*`K7)! z7D1ey>`hWb+6Rw^ata}WLTkNJ%wid-zE1KxvbW90t+A%V|7S0Og1Ax1ULth4*cvEj z(l$5ucv8;q6wdl?!}=@RxGSI22R25focS9=R#-l@SVU#m!))zgymbl1=momOW6{hy z(hl{b+{%4Jnc9?J!EZ#jUFcqVm8t;I3HQ{6Ol&{>C;J`@I0%cAA z_6&$BS2fBlk>A7eQC>5_aMgE^8UXo)OUA#<10r*2?Hts7-n z4>vQ5)x@8h6?65%#=i3yyL=llT)zB3A`VxvkmK#+ss|q89y+6HrfUZI zkN|vNBQUZa`2Oj{Nzs1Jnw<|Du&Q9as)VU(OVLx0Ld7mBomDgylsg{QMg9)#7dN&v1?=Z} zpobte{aG%0S+`iSE^10zNM-qnXeW!S`UyhDnk(0(wmU!OFs7sg!t$MEJpWM7nw1u4N0a!Y(&Xl8#NgeV8d6*8_YhaAp_%*MLPsBxrEaikU)nzGzJgQ#)deiVBriyx zcg2#qXjf@I{=A1I`D?1rQR0m%EL!O#WWOLzJV+=4D|!12#O&h1L|CFH5~SPoZ;Xen zfWg=4qYS`6H1GQf2@K&I;gbfnZNMFLBKgItFW;_YvP^5gW;3}s>oL1A`zNe^SFm8X zQ}KFWBIz44h!Eh@c4y8GeLiSiq}L`Y2Qc-QVEQztX!iDe#FT}7@RS$KrVI>#8MfVc zN0^}_2#LbP(N17Q+x`jZ??MpgRDpeL>_LLzGgBrm$%7d@&2kEC(UB zv#hnawd4S$vNn+^XZc7Op$>k0cLvb}{P;)LR*_x_`01nIr;i|^i@qdSE%6$FxVoEKqL`^K9R4^sS zvT?>D*E123h>W?Iy0-o?>-Lz*Op`M5GYWR{qq$`E%qSjRxO~EZ)?>dknj`Vqjhodl94y z^{>v;FQL&R+12M;YGz!YBVYNQc&Xmy(_LN%F zt!Rel=?}z+{XKkblxlE1%SSea0;_O}UpT{(lY?RyjpD87=g38zCMKlBBMEQS9pWLO zw~EBE^G>-4*3#!TQR4CSuXV&JE@+QC_E(nE(ZHM zd~5CLLu5^>y=0@d2XJpoVu=^)&jt#_BKxi;Il*h7cA(()#eCGrt|ATrn)%}1@%}h9uvQ16O2tp<4?>w z&6v+n(wU-AqJe!GoTAwE*Uo%az{!NVw@9P2N&!yXld;*c4JYg>vY+U{IwyMyrmKtU z=HBg@u4dmy{^W^2WJ)lOM7GamTNWtp+A0_^dK<>(|Hqj>l`qQ6M8`-mfG~~S?7Uhbcn>$*I2F-Q*G73JH z>$qXWi?OG020mTNdI83zlts3yS$&|36@8kg1r(Tlflb|NGsm?Mm=oFmyx zOut|R>%x4`$#yVWQ6-#^JZ&W6oQXqT1S>ijp_Z04MwqOVbgRRYma`WJ&N4)PChMnX zM<07&=RNz=xM+HAlVCJ$hrGqu<@TD^`qL>sg3#s9Wgn^f=2jeE&n~W2pohz@B#uv- z{cL3eoXT#|Vo1Ca1iHsevE6U3W?96u7AWU4hxx(=g@WbD?$CNLSHwP3^*K%j7~gdem2uKtY!WM+gO>3<=FM5>F%6bWNx;buk(Xh(Sc8pxGo1X&>TxsU`57?NWuF-s`EUj+Zy-^sLz1xnr=iLBadT`x+af z5+=#W!(81!&imHDt_+B-(lj$rOEnL4UrOOA0~2jRikSX z>Pwtod7NP43HiQ%WaZU0?Wo-$NFA#U)!=%DzuR_Akln`ZWg7hI?|UZ5zjqYTPInp+ zItg3p2duDLM$q^S0uzmxHaK;kavJ4R_370~@9&Ird!UDsIs7@*N%5e|X5!)O zl?Bg35gGKz6|+P~?58jF#q2Gn+i-%BWbJUL1UO1H)7?dSZ!6jk%-=rM5! z7veWBS_;;jg;;XY$EDLyFDv4yy`4YSwnryip~U%B6%4HM)lb+=eyd;$5-w-do}G z@OR+@YvNjNroh#$RLF2h7wW`Hz3@&{K{{H7>?2evs{FZdwb8Xb z{%R5qt7@rI+<$h!c~=ic^&NT~cD9s$^Z`7XrE3BS5Er#DeAN0Tzt^MBs;g8Sj|p3E z!Ye6!u?fGILO98nkJc$TK(1%D5ZYd9GQ(p1ESz(vh2K;O=_Tow zSEC{4pNTYLi@-#HqW9eW?oe!(r!A{!#wog7%(@D>+gnPyA9s<8H~A&Z=haL@nYKhf zZ*t>ZbK_MDi1do@cI#k(Fha9Eq?(Uy#smibcafPFm&IWf-F*RUacoy*AEgjxRsriSe(U)x58hD{1vUb@N)3fETfXJ{^R_0lAy=}E> zu~{)T33p_G@KlKP2Xa?TUT zav&FN;JE~Wn1;@?{AQBwl>NsPfSyJ5wO@P{lW7=UiXS*SqaCa|-({~8;1>CrQ8kL! z7NoJKmuJzMCX^cwJiF9lCpPm0dB^;>!MY7mM>o?g$UA>#d%6;e^^&QRHStW>a?@4s zxO$&oTIe0to(IpVJV_p1LZV1Dar9%TE!p~!Is+e*N1rMm+(iH4A%4#iYUMM~zgse_ zapn>z)I8wgJE5{?{dn>uxi#X==CGeQdg->qF}RL|X`cPxIlfv;BcEGEp8&y&+ND*% zcJn%0XvNGac6z7FdCT^|kgEuor} z`9Rpavp7V{<_vdq%qP~K-;KLtIOf9$iY_Su*8G@)IvxZn(3N}bHYY^6??PQ11~|bc z`e9ja+b^1}uN>r6$Xg34RX^N_Z9#UZEcUnblO`6%@yG7m&WXni|H^1qS}#{0qTx)u z04YPpfv-KftUF&a4V@zEJ$NQ|GPjGVN?`h=9*^Hk)cEfB&t8~YMVu0V7>zVHwvC`$ zmc{E{mrgYC_j3rIR7Ojg$BPWVtMWjYcx`Fca^QhHS<%O*^~7bj_I-troH)4T56`R$ z=iGOw$Lc9YnQx0 zMCi0{(jwhf$yzWA*_x4owScm|e^pyiX8iHx#;hlk`i+W|(dBY^A>(y6lT$yp@ej-i?R(dT51dA_~8oYSAlN(gEGh9A#Qdlp>?MO2J3C@a|t!s(0lab zU0kSn9r1aW`F*x9c5RefrblWTX8<){ix_5c4eN88%a;{;ZtL_7u*z?kAO!QXzr}C6 za?VRl)ADJ$1J0TZ)``a_b4=yN&#Iw~!$>NkB?x-^^rkoKg#f`Cx&}l7b<=cSxVAvX5 z-Xs*05Fa1jNRqeTsoo>X^2DWK%}3Fhxdw8`t(?2U&+IYrtP~w=ZlPD_;8=GkYhRg4 zo$B4lY4pHfmN2cB^@|#Y4;994JRr{zPzfuoDbM^*1?@~_BL=q2_SAMH1i+qIwO{n> zQ7RFX8;MQd23xfZ1<{}SMF=Nw86Vo=@8la(&76c25&>U=MB?x6`01F6r)#9k#Zdu0L}k=`}eWP z^;52qI!>X+44W5(pz>uy7POU0^*1N7?Njln&SrQwdvwrmABh8D{Y2t8wTtv5x&BG1 zTp>45lc!0tN{h_O@-G`CS*6gYFAGm9rI()Wsy_`e>?2G?T&b;UJTy)i@J>w&`w)7g zJy%^I=pPfKd$Tt&J#B_DJ)YaC`S}eH(qNE|fKjjd>^hc#PGYCfY2)+z30^(=Z7KOD!zIQ0TCvP4Y~n zm+UHI0vmtd-sqY?BNx?o6qrRW;hj*A9|`Q$)Q!4@Bd)+Ul-?UV@xcoQ4TQ^M&4`61)rZ zO6}Uz&^*@DL6zAP3Y%vnatt>18#b>n6U^e`+sxm+WsDvV49KcF5a8dQ=)uI_bI7IR z6Kzn7DAg9@T~lEjUuoNtx#>esJ1TWG>B4gvJK;Q(9}|+g-b+IDAhRP|-cB+UG-GPzrBN zNSo>#KU|IdrfBJJm7KJSwcAGOK0b}3pUpyr5(7W{+RCpzPb8uE$B6xw_l^^fm(_A( z`}zy;9)zHV!g)oB?g{AWhoh~e1NXb;aFX-c#M}2hb@N$>?4-R|f?U0RwT^XIACIWg zz4DGNL?2C8GQ^$?t8k~6ac{Tl{Wj{v-DJrfeKem`QNdo`bU1!LCRd=~9@W_#foXqm zTY@0Z>+dI{#~^J%8LD|utfat5jy%}}`-#Ip#D-P0@#mQHgk)%F%yRaIn3;*?*dw^E zwO7>RiE}A{`sIk)96$QX-ExvQ=3FzDi}BZW2GZ>Dpo%VIxxYVuU9GPh1pYs|?!$PcjefUfb??OgcH6uh%OVM~il05( z4}P@VDTH`t{1vKV5IofpUA`vpZwr#XKjWJ?*_6oN0iF*~_i+l$K;uJOFsCM&3;$6r z#A1Br-u#Bw#MbPgK=hh^r3?dQmTD9#(YPm2x_1DToOkEeSpE06{5Ei$%^6GW;Uz^~ z;xX;1>Xnh5r{& zP6;*j1i$gnRCVkPQa(>I8y$=BZ}6O>9P}5EI};>UPzGm?iWc(-?wC)EU}SJl!=6gG zSJ*D4gMxYnFKw)5`K9BAwbj;TSyO6_D{n|DW1L(X0lckZF2Ye>qf`RpSs{s&+|VW7 zBMM63QWn&ITJ&t;Y4P43CMSwLZHZ{#BsdW%vJ$e8Z3|w!4>4(^rYh)c3!w^1`aL}V z?sBQ)hCNlVd<8uce~>uQLT1cWQDdattF<-rT_=iV^3s{K(O)2(R2Tn3|Ax7D!tN@# zsK&o>v2O~O{LQLQyc)jj)s!a5H@7AAh~> z&1+9y=Ttn5Fs|&*F!0gM{Hu6bw72t8wS{1`Vj8()U6@T=sCbd;2qeh}6x-iVRMXC) zw!&!ZeX$(pXmt9(~^BC|anS%pA}@`o;&t z?wslMr#tInT0Rhiz01ykSw@>yBeL>sSLCJUN`H#1eM-w3D5m4}QO-)gt;~|dW*NGe z>6AB7?4Id&6q8`OujrmLWQ=p0mWFDz?>N|#HsG!1z7k)I@`bv~u7^j2ORaGOac?&N zo?n>))D+C`%wJaiy#qPiQ&~qoS|2<6*QFUzk;vLU$24N^_1T)y_~x*0zuF_J{79wa zr`y*kVZ|M@$=-ilpn2bXh>eLmmFL!}oI;rm(lCf&Cba1$6h>|eHLa)O6UZIzqpvvp zm%)C>u~%Clqc^dtSzQ^8Tfdlr#xjUFcy=l7nRsp*hc?~y$sm{K&AUnHwE4?eaKcYC zY%H5!Je|-vlhVq#N-Ll~+GWx*F#O_gfBFnIG?66oWgI8RESG?|sG>kOk{Mg7Vfx?= z6(Y(-tsFwWVitbM=O!h49F4j_OvRB2dI!6C7gNW_z1Bt?}n&hJ2 znjRb6%+F}LlhNpq#IUX?6Vcy^(+7N&dE(sac(*uZ!#6(4>>f|#vhvSw3fw1O#qg|T z6W>IKJW|zV)B{%_HfA)^*dBJbF0RP;tSkjg8kkxxjb4Wlv2B!qY9p;0 zx}kyFN_>I18>}^=@lwAh2>v(R&~Mpk9a1ZOqLCLmXDSr*FfnjOy_%lXLx97fZVOJ7 z@+N~l@6FCtgE;W?E<5FXtH%fTa!EjPrN4BKa}?}U_r9?9>lCl(bj-t7 zkl8yw*@A3$Rpj->t{;hB-eqsU$9|#}G&mbMDun&~0bl9568}tmkP;?v1|PK*;elD3 z@J6RuHS|5(2jMOmxmZtWcFYHtkfx5Ar>OP?9A8~5VLVU)adXJSuMAQ;pphQcYK^JR;yNs%bp|W&1Gl$@DmO zGh>8d&lF|Gimqoq)iuRW_P3)RkZ)j8a|lZ=gB%lj|MSCNm+p0P#z3)|V%1pm0k~-M zx`Sa)Eju>YcWEfnFVx|k{q-KkU&^wJO4enudr)I^g|H8oE_J<4QBsvHqfxmyFv4cD zVzXgwB(xp1$%=K@IauP5f}O7ta|0$s%6=xi_rc5L_~J<2d!_7X9IA`r<+mS^h2KRz z#Arz*02&q3JN2g80*Y^2#5-0q6Zw zJSXBjk!1vs^>eozc%%xVo-m+lY!@}L3L5y0$JR|u;(p7+}yyDux*UFYNMVO?+|Ih8@-Nc^Evvp zIC@E=!Y{%gD1p!jc4-(yR8O3!cXZ#Zn7JV}um50bU>556H>n!;F3=w+Ik|S_lk(rB zZEn_`6h;nIA73B9*t_yb+{uCJ7LX22vVMqp483>5u(;yf3-%o4*!F$RqaTBo1*27# z&J)|&nRrKtn(!R`Fk#m;^QAkp+$@V*ZY4@oa~U)bn{98Yo2<>rRb!66I`6WYZ3_)5 zsap&8oj9|`LNR%){hM?ReP@R&-9GpiT*JK^oM_&STFmBO-9$-aEdvEs)DHK(T~<^` zS_>^CyLgtNm1?SviZ@8(_)LTk*2NbO5=v1M^UGTNa!Nq7J*By##gGw0qHoCTPnh*LL7|F1ikD&#gNVahnh%`U3Tgv|X)ep&cu%+CnJvFDPL&;< ztfbT*hZVPPd@|z7;%4+`s!CiHjhGIa?|myTJf+qN{S4v{Lsua1_BYks>L)=tR4>CD ztJ{H(n0fOq=Vj--$VO?}8>zdA27JczAp~b{9fOF)UKY`Dh(v797|7gEB^>4#QKDb@ z(cLJG>Zsfk)7j);H4Z+6i7vgglW?AndVp-0nhs%r^rR8r^F218nsiN;g~Ib+weSSf z>Y&UYV7qwpF2sG_RVr5BAJYUHZ8YYKzq6Nz0AdMrMT46EJ57zBZvs9f1$S9bD?d2V zj8Nar`szNr6Z(I|c3uXkR>m5-UIz2@iacYu!#!)j929){o>c^)67n9MA_YW9;N!4s zmh8n(1uY%!7Rk{^3aqiER9(dl%)E&|ylwv677Z$h;VO8rPZvir)TBY| zr9$hhV0;#tPvCS|1;i&o;52VPt(2DTe74l6kap)r8lKLY(;)PQ;tRc6dilyM;mouN zOCM2L%H7$HOr55cS>yX#mIqK%PQZFD;H z3#tUwRNjaYiEIq{Rq^8=4KLI;Sk^cxY9HGkZuY_Y@;$q*sa}Vl?A(F4t3{~(5nk!> z<6%|8ZDLtna@jMDd$E7sC#8&pZM)iS=t5P8uM#R;M~AyS&>jL^=Bf~f0q?MrOwUUYZ+iNGw#22!*uNqnqY)px zV!ZqhP3c^qI5ym-#D=U4~%do1wOCZ3@6>i)=gy^u!#I+?Ey zz#>3mq=%&bCm3bTrh88&lhzW-U{LP~SN|6PxlQ3qEMa}?l1U;2#s)_QCOvWmKwM|A zDElz{e&SV7LqT=)QVZNJB_Q|#X+FNmpe0Ci`_%+?)-Yj=a}IcYMB!fgbdxn0c=-KO z`3D}NFXouo!|JZwzw_j%_QuA%-)<Gxv%L1^ygX0zIDhjisj;2jIw zS%)<_m~%1@$7|=RZBe$dOpVQJFLkc_m$*L&#;eJD)~02k^3}Av!oADv`6LRetPbPW zo>Aw}`*V*2F#+W>ayi768RqJ9uNa3Ub=A&f4*1dAN1d2Q8PD1Hx5mSZ#nUC>eo>6la>PL&sa(W_@diAfgO4$DVQ)M!~HzV$qoI#vkzRMg1_h zgdg%AV7#di4?br@-iphFbwh;z=R)P&%nfJr9$)jjn0wN>%x!bCGw_cr8Z9~6-Kzaf zp%=YsK&HIsOiZdgPIhpMMg2r^q$C ztlzy#FD2TY-iOz->Yi(c(|E^Lb`Wj|Bf|W-tdIYa1G+lgweO#L($HT>nK@fq@l(NM zjc^cWQH$NXeP8}GnXT))t(|nx%D}0lqNGNuabA4(;?Rc3z#9?89b9{VpI&Ed`{>Qg zfTBUl0XBjj=0{J%Av1#Dm6NqeMfpudEu^lw=EdTZ&^j6dAc1!axxL;l`yXVke_^Or zzvYzZ2IgdV&0zc%h!lO{VPEp=FaMM=RC`u1a`CbGHuwp$6ad4x*=bntQ9xbEQs4&a zPJDf%&0jFN-8w;M4KXPy;cE0MzMmq-9LIb)$LSZ{8`V%1~N9QyXl)x82b+mRfzDop)Q^ zeXy_wXO&&RHDuyXDTbu*>2{WitlDg6EyhI{6YrR_SeSjRBzs7k?b9RBOlX`|Ru9k} zt?j!u1TQU~3^>=0`T>aLlwx%_Tcqd<|Bo7Xp<|{x@8`76!xmnRE z18KW(;<<@WsXUg^Z)HJ8PQ7r~^n1UuBo1u@d1MO&R65G{PtGcUgp>;?U!{MkD))Mx zn^&z2W5r7ob5lG$w6j}N2C=%L%khi@WR||xZI_H;u%C)^i=CjB2KkbBKt%`xxqPmQ z!k~ zc6sUqpiKD8MRGDr%Av@<`b~ z9CPH2&)>Yo=>oV61B(epm5%kOxDEeS=L2lc;>V$3ouB^E`Fvz7?rX;RU(xyQ&7SqV z{v|*B`|%!ADcAyO!b8!1b@#${Nv#~x4^cDr`-4&g^d%{c0udzgmDTAkkix#;O!P@!61HA@kPIFAVcm_$G6b(mH|pT-#kBMgXHJ)T z=w=W*3`L*0p{{HfkxZVPDM$5)QkPZEgEXmk>{aRQ8O;Ez1;1-qUk2_gIf#k!z54IgDl66GB zlkZU+uKf43-)jFp5TWNqsWOS{o-e7b_0z4^XAeS*wT>Uc@aZd0l%;s->Yt!a_ekYL z3}32kJ$lzooHV!vlU5wCxf8%9MC7cjZmZJtvL*rD??<=ks7YHr-aJ|dLUf{z7CIChOaEuG#h`lb zd2p&sV`OStI9*oePZINlTa|M)8+_ot2W-&UQn4O2E%Pdwl-|?&3CCZ*Kk_?&7zs`L zLKiWzsBV$T8rENhyQ>s7nev(&dH{q+?Z@}*&TqQmX`p)%<4Xzxq)^(b`Xb(PL!)@k zQn*o#5JGCoqyoMO=&c+vp7i{SmJNJ_e^_v$0+eO{pYMH>?Zy>q&^MS%Qx1eEX%*HkXYrOa!5H-x>R3#Etr#+ zy#`**W=paA)%EOo*%aP=AS#25Ot~%XB@G$MP{b>C1WP87obYGJxO&VDP{ONeXCDJu)Y6 z!drv(0^c9F211Gm>UYYRdleMzDzv0*mGxh2MEWx71C&-S()9~>mx08ut}R7zlTzyb ztLUJV&ww^2=pxihy9ih5{8`XtmB}t)Qi4+~Qvf0&*-SC$NyS#DG@IMhRL$x*=i6Cs zkG^{WYp+?BdL zea4RLU3Ihj=Ss&OKL;xV-)1=7oB5DM(W2j^bL=Kke6!kg;6^{Q9Tmfw{9Sb8fRVQc z`Sc+F#vShv(lWFA{Ku2OR15kaZKhdEMSNyRoXk+K#1%5P`tIgG2-SA@Wma*_$;|6t zx|u6aSPh_YZ8B4casd+3RgjaAgEA|XSW+nEpj9XC2AB9ET|m6Tdm@sSRZ3Ce&}jqE z$Q!~$WDr-wLk+WI8njCj88Uy)k|sT6HW?BJfI$Q}pXtr>*l#mq*L7xl-Vv?gm(-j` zS`VLRJ}e!nTXI2TZI1VbKy4a&t4mz9y0ucMEg^Jtg+81x7;;~%6HE3uVI@+crXg@{ zl?ki)@K#O7OtZt!osqyi>$Ej}%BSYj3)&g)tHoa241A{d588ZqlIXGzmTGLY$>)Dw zjT0D>FR;Fn#aoQ~>BfSz9V!j8>G3J43~Vp6%@?WCWTpm1l_JeQK>~2y=_|6Thy?mC zA!X7^iYTbE{va?8z!WF^us$+iv@Es+ilt0U|0)PsKw{gZOb=9yX_>D9{?YK&u}bb| z6ploz8Q33t;E@lfT-e@x>AV0LQ+!-@RO4#ut8Uo=q>X)t7qooTVzK@+&l-m6D)Uz@ za|8~IeJJlIO|8#*)5@<-pUWSd5W*wmzOY=y?H%XM60z4yvg zu`jQZn5uH9DGQJt-LqS(fgVm`C-{7OUO^@=T#e!hG(dy=Y#$Lwm*9^}yfm=y{e#px z!BXqUIEGB9jh7*Hsf8$@poArsSAXTNS)iu0^sMBuh%#OZ;I*Z!NLpn`QTVW>+CLz% zT2@T}#$y~i5&D?47HGNrH#@(oj{Ofb&OXe05r`|($b7i31U_M4;GpPaR_>taYe~!eshy=9Pi;w;Y+`ElHDY+4U!qG<%A#s$Fd5AQjTdQGcgq$~Zl9w-4;?U)rfSBu zv0KNb9Wj}DD1u|o$CD|5)cz(KMPCJS$b;n}Tfid>cU#DIgMgzspLKzZEzcm%)k?d- z@)6fbep0e}WmQ7V8t^p`N!#Q9Vp@4xS$9ktBf=lFUSi`4zcj~Vb7H@FS@BA9L%CtK zAMPc{G5s+b-^UugNDtOIJiWsWuJsV#=e%l%bf6iQE4fwPDgh0w3PUENT|^EZ;s-49 z!;bEEQoLW{nS%1DlhT_1O4H98Rs*Q11|=5W0D?Sl1&M5Xgxa$^NNy;U?SgcbZD%0p zLD!NQiB+J93$}?EZ9GSCld^=;udVU!t1;{}S;1XH^N~6Rl{hK-n1BZv{5ewfVs|rI^8rjJDOu z{&~$?k2i8}kAKaJ--;qF9dibXvo&942WZ>p-qYY+=FP{r@f$mL)z)>b?J@cSHNJ^L zImw#RiQsJMg}yd!ZQO=ba5VLSB@EmK;>v|FaS+J;kIwA1(;=U^>fru&G9|gX)VAPa zudkA{t}W80Qad;AXg^i1p&#ox)g6?TbjZ5TSLw{0V;Y3NPPAYo8E3qt%3UE`>+(Nd zCN~q?F>sirB;C@~6fi5>;dEGuRs%Sg!y3eVYkOE~@F!G}sCp`{<AOjAT0d(y$M>u*PoNe}K~w#KT4>7e%$^O=oT7QqXUX zDm7b{zh#Vs6f0`y!&BGU2zYfHTDn(`ZMSbW5hhnI6ssb8Ub>$j^clpl(W64A z5n?KQ6_!MWeKv@X1N)leRgt)M%JFYrA6E_>2B)~?hg>VbvHM#S*K&|@uRkRPHy!y` zg8#qwRzf?Y_aGX|O+se}=T`N5Iaka7^;;LI+sp(91ziK1=#rdP{XE2Lq1m0)l~d`Y zlX`x2r-5Akt)Pou-@JaQj2hJj=T%MkNo6mn4)Z3J>5uwi-H2^sRndCDiO_P^znBg-8XzZn9X;gq(oO|bxHM~#|K6~G^OCLjZb zf~kjBm>!e06%XzH*dbYUs@FL0qm@*Z&_SEBW3eO#IOyCGeG#eCWgNlt0|!07$zRVj ziz-(9HWz{t=3!RNa%CI(UHPwy=w?6IX6~t)3Jw1ZyKDXSe*dg6s-v`jTDq|i90o@= zyk$CFu=&%V;7*y;sq0pR(@N8G2-opDk=~^m-_AVCUgOsV-6Flone_2G1bkWhVR~sJ zk2ZQxwzB}^eD{^uZO_bx_c@x^+qION% z)8$?7Ae0A@hOhqqXT#fToMOaw4}6&`1IzY=2u~hr|E9cR=^xWM_P{XmK8hoQ0 zCsIMLr<8*F120-N?@n-6i9P3{4<7BYb?(?kl`;Eok}oa0Uen!RB|IKHW!D(;^ryp} zr*??8@VD_DXU$#X5Au9m%ycGKXz`}cM-%8jZy$UzUj z7OS^&;b%YWTpip_O`cYAw6j~ZnvI{-fu?wL1WczK4R){z%anfJp8e9K-qBejnKEU+ zgOn`V)~pwRa{o5zIek&{*7|rqOI>)yk&wOMy!l~5BJEH>9BSho z{0-+VzxAw)C3w<}^-@^JsiL85Yh=M?g7u@Ax}B{GIW+W>a{#z*p-#3pSD|=D3_x zO3e{HhQ`|{r$R9_?+h(WZY8_?0_y-Hrr+D~s4dMqEH|qw;Cek=FnH5srQ&+Y4VS&~ zWfb4%=c}{{8Q8KVmlfJ&!)KR2Z;M|wsT~N*;=&{94=qOuSnUJRd7qyZ{&vurk2nvp zwS(-rR5-};T6f(o`tbBaWHQGSlGI%%tWv93!s$RBirHGj?GzjANFBGqa62reogHF2 zj`A+B;GU|uq>Mq+D}E_I33KDSKvv+*w`z%$`i3JV#XspnC2pckUk7mH`M~xFvtPkx z^$%tiig%Nn9c(^!7ku{4@QObAREMS+>-D4}{g7UWCgH=e)BSbrY@X&d+CAdBfcjKb za>aWMy?yO!BnUxax=X8fad7t=6G~!5ji0lPgOkHVx;7=kiqx(#^Lsb&x_Q#*+FS##mTW4eQdWS&@yIsrdJnGEv)`~-KAuO}o0b#ADaClLd z+TDix8YGna^Hgk^SLLnzmQsrTwNEZtkys(bT(|>q5aqpif!k6D?|Zd;)HnQ}Z6a|G zJjyOB7Q77vZrZp#JUUYSsYa@%&u%Rrlbc%c55$LKeYtsMr2|idtsBGei?*TVut}Cd zmbYUpnHhY#EuofOpgfQ2RHJ&ae05p~@M(y79r{V&cBXIsWbk`l`f7rS`!Mscu`A~U zL1UsltwwhP4jkql{}bM}vATWWd8+siPhFmAe174C_QS#u;~uI>-e+}Qabdsa_TiM4 z3WR{j1PyU$r_*exOMsImt$F5Bv94^(%(}q25 z(g<=@AcUJlLPE@aXQH(G+kN-#`*r!04`j}q=R7lK&Sd6)j(!YdjA;(11Lyo{3d*M4 z=ez9*$5=ES zpXK(&r`HmXmFJf3KyT%Yo!o#GKi-m)*Sl>yD`q) zk9Rwr{Jiplsq1#hv$S2YLX6IhO9i>57ai?zM{wb6!75S(>nQi$RMmZ1z|cg*`cp*X zc4C;%=qI6Zt=9Wy&vj4!qy?lz%*_CVbdeGF7M{jsH63SwU7XnImrz7H1Y3du$i!FPc93MpS3qaz6W; z?Tb4Yo`Zh<6Z@Ar`>^s#duusKoe#5$pS`-zm!IO)w*=f@=-(6c@~cscL4n(?)@0qt zD>5I)uzV}!wSS7OZACA0cjxZkP85OFzUQBv&i-nd9rY*bZ?2iQejV~FKEJN`(y(=x zowiz9v(3>;Tw3)C!s7iSik{@7N%kgaGPT1IqjRe?66ej>yKQjbez3qKcrSU&-ope=A4cC9vr&5v-xfR;`t28tUJ>$Rrl6rLYJ=x(m`*gY6HtNuGMP2 zeZ)_rfyQEQtIL6mr8d(tH|gP;aotTeKEMnMppJ6oFPzp~HH!N>$XUy=nT~UJ31{t> zv|wNQVnXJ&0*I<~B7~ci8W7oi-E5Ut)}`?wxApvr&f%HpmbH&`#ktF*W*HAsr^-q8 zBO&uBlbsv>uSbBU4xB;Z^A%O7vqFCV()g2d2*f-0fb=!9fI{Z0zf5IRJtI3T&5CD! z(u_0sAM~!Uo}>F9Klb#C9-XGWw`iqs9En?6-=3)n{wn~E+Q3lBlhDTMcv7{yp=#-* zDTgb1V@_A)gM4mIVeQv$Hd1Zy_`(gN9S+CJxTRIPjo&%X?zvOx!+PnfRm(|t*S_1R zGZ@giUf92EdBX-lc`Vp?PgSX+!nFv!#E;B@UW`qsYS%nM@1bf3_l6joEpW%V@23Ty zZ;r1ABVd=I@Ua`hM~##hzB7Y0LCYJ=v>5z&u=&VZ(I%bVgD}@y+JlXVoqY~zDK+ym zLC*v~0^KdvaahY9oIpYOj@9{3C2$^GxvbAt+&_96d%$?cV^~@Yei+fT&-06zTfZD= zclwqBJr>~ZG+(JU#%nd|r;3_X_zBii{2U%MfOh__nz_}V9TRvE;}|wlUWumlB3}EC zi$mk?UV~fa{;i6$2Bb*8Kcs>Ph^x{tY|A-?f5T^pSOb-oU`KT1)k_Uv=&r(z@Cs1U z#bYWw>-Eisg}kJfz6)JaeaC{|pUo;9pY$M=CxxeE8uP!XeTj(O^m{?TE^GXo1vt?u zvbsO>!>)6L=1J={5nU1J%iX$Bw=KiERL^(Uhj!(?D~jwqya}P{q6$*HEOR&_3BY!m z?fKmBV}LHS*;@$J9#GP_++TLxps~YB{zcM*-cfYi4Z}}fzZ(~)bBFVpNo~5#2)*oo zt2_{yAY7IeXxJTVP4okZ9|aZo_PPBk&s+`?2yA-S(RtrNIY`aiHE1$N1Qb>9VWuhR z1_UZCpR=xas%iX}Dv)P^TA-lTu zpsfY63-DP0XxwR|(r+{Hp})E*Z;PUfB72d<;0jHIi%tX(ayD@{@ifzn!E92;Ho!Qn zkFnX+`yu4##^vw8e$$TUt26K?2yYkA3?KqlHz>+LR#SGcDw#FFY1-VbX1-emX2B9v zE*3%$=uuWadR`Rz)YG2nMKJ==MMja zOV32iII^b>?FIXlms$YBy4MZ}tm`OEcRn(*a1M!L;9*UW(%?Jee#haqBd+>`W!n8MaopO2sl5bAL z*^9zO5$Q3wHe)Nr+7@ZHM7zXJs0J<{58nskq|Fp`0vMCB@!`}5h?Y-u1i9-!`&&Ei zhXb5|B)133wykrebfE^RLb4#8!+U%dV;biXA03~&JlQ=N@39fEpt~%Fp|aU=MiL5@ zO=FHcRw-cVW^h#g;p;xw5ahks=nxPDqFiKM{&@gmO#<^iv(PYb2R~s2=z_hsv`T@1 zcsZQ<6eRG89q?4IaSw1Rpqxr2 za^8laOh$l!?TRHWHc`3Kznn#U#>W$7W4z4g8FR&`Arex%WY;{QWOWp1{Lgs6@;QM z1}%9@s)+2sU`ysaLT*S!;o7B{pS~&e{jDVHf$U zLhFXdbA_Zatr91K{Cb8F*tWyrWlDxR$Y};K^ui|%`Nxf zq?oP)5_0ooJ~OKjt*D&#?Xz`HSN4U;?oa1y&F@em9ylfPGQa?!Zc|UI&9Hj;4n9!= zva38riZsU({?%M|zpT_fFz+nfgDB6gW+#KZErjxo1(3O%D7O7)&KVs4*QOh1g z?Wh?c7qprIL!MSFSk~p{9=-9srSeTMVd`gE#BeIr1tpE27RS>OlkX^Dq;g1spFoRp zl$G>}pOFfUtyG3Q1yzEoZ~%AOkhb}N0eX)i;wn3Z)g5>{11HITb9YvbxWHThiBSo~D*U%Ie;_>rrHkQ!6 z3S1Bk5Totm@58}B@kD@r4{0Wv!YHOse3E4iSyW8)>${|?$)*whg$U9)cxC=GU7m-} za{(MX5{F;W=+Pj8Edr3V)(lywaBAUIa@{=5nzkQ!ny%Pfmgb^UI#A0KoqhpSdV(Vv zXt>{Ih-CAvRGzYCBweHdf8lUnP_evqc;VIQ#C?oH`owp%$BI|%c3J}^^3ARdc)WU} zN4;Xb$dy*RP&N&3slaV7Bh96jz#Bmcicmscw?_T{X|jjFu}1S``MyDDZ)-)qC!3R&$lw`txy|s%@jckuXyW*vHbU|m5&`n9!xDKS~%zYL_ zbMVH1`ZfVT>k$iEc-$l4Tr3w{7>iQ2h%r%3?WGIl;DSZHQ@(K^yQuEU^sA;b&-gy*scvqN?280W zGpqhoy1L`7|BcO`9h|DJ_j7h84!o)>fgr7<-(l&U2NhN&k&;y#YTk<=jK*%FMIR1f zr)iafHzcfPH*JvIl)OaDwr@oXr<$drR-W8dcr6O;Nf59U^^vVZ$g<%Gk-e^xJ_b5# zUOm@9Sc+QB8$nqHbe#t=Sv+fVwlWR$b=6*pXDqH*U$d|N_Jss literal 0 HcmV?d00001 diff --git a/docs/cugraph/source/wholegraph/imgs/distributed_wholememory.png b/docs/cugraph/source/wholegraph/imgs/distributed_wholememory.png new file mode 100644 index 0000000000000000000000000000000000000000..e6bbe9f13e9e0b38f5b3459c513ab0992c177c62 GIT binary patch literal 23138 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 literal 0 HcmV?d00001 diff --git a/docs/cugraph/source/wholegraph/imgs/general_wholememory.png b/docs/cugraph/source/wholegraph/imgs/general_wholememory.png new file mode 100644 index 0000000000000000000000000000000000000000..3ece02b007b6f4b0ad6e97b868dedb2fb0996d98 GIT binary patch literal 14073 zcmeI3cT|(f`{-X56$>cnDowz$impl#5u~b!C@MunKnztuN+BM)iN9yGkz&{ef7cEW#l|9?1!NMx<6BkYZ zKuz)nf%|H(yzb^1r(gis93uXa81Yhz0Dz-OXHT8D6yd@k!*Rca!_>YqgS|bahl1J_ z36q0IO<^`q@0i(KJD-2`;<&>0o%Xi2DGIi>Pb_6F{QOp9>dwW*Q(B_WM=!=)g5E!+ zy!FZ{<(R4S_Y2&DeV4AEl2tH!^c(N$*NjZoCN1IIf=QGP$H=vJwr7?_CV%`$Jf~!= z!UGb$Jpyz8ub_x z)bbhjuYlWAPC!<6PYh2OGT7e}H8!~+XPGtKF^Sb+d^D)IP4FmH1ad_K(!g(6n?Oc>ncb#y_&ch7<}s!)uW&ry?tnLc;O+7aw+l9 zIXnQ+t-)U*HIFj$io$?cb^~r$0&v`mEuA6+G z^KvTF*0bwnZ&Rbv2B178N>xBBnH#T~jSj#4*@9BhnSG1U!91G=X$%&bCT2txypIVZsaBLI zFWqf`qj$0qCvG7vTo}OXHXO-NrNopgA^MOSOYDUNDWRF}kqmKfRj2S@z198VtwB?| zy|)~Y)~%NWq+Q>NO%SNO1sBbnbWIam(RW{=yb*u0@2O^!Nfw-AeMxMNm-N?@eO=fv zD38|+w@o}d-HIg^=|SfI@GZ##TR)lr{+jX_`DR%B3zE21cocl{c^SNV?AHNjrcje$ zV_7AL675=kXUbzv&**R*z=rc4EDNk>S`4xxmK@Pw9~R?DrVI58?{`zqNpY56-Ow)v z?T!bH51`YFy-G?|05{eQIC+40_pfa@XKbVy@3jii-fTZ4dRFtfYs@qp84tD^_4cJh zVPMWu2zoKEDmlJodD*(y`engGzpqqM}q8W4#{d~;i4{=436>m z?*;C-{cT42Ob*;p8KU~%s0A)WOaFGE3(!FkK*M+VvOYxviyDXNaa^N=>ZpZ;{ zwOO!E;X=giBd~E1?A8$hS*qnvY!C(Sy9RKR0tfQm+!=p!dbP-RfVq7&@ECE)5CGbB z-WYnta4HvzqEwrp1ETU`WA)`3^FU>`Byb8M%RX`c_6bmeI$N=uPUxCF%ek5K6QEFB zA-0##<2D>A30_A#H|?!~-kXJ8#QClThC*!RPr)ABfi*6GUFRk=dEIAi>9GZX@j9`4 zYsqM&bC);;(+3u#pDS64LsBJ863{QKFtGva99xmJ*5rGgl!i*e9MPuMLWMb!S|m1W`7be1EV&gSkI?UWb%G{JYFn+t6n!AuyIS&l zO+ufp1NGmV-E2?ki2-8wu1HDZuASTm#0F)Cp41r!EhFwhJtm?YR7iPomIGS?m|6I` zd($PDAzuQ>T$xAE$=%Ni0HD)na2@`UB%rlD4(mz%=si=#1As8zsOO^nfB*0-xRGc- zGGpkKJ?eW2$R$f|f;Z4eA2$K-pNl=|hN0RhXjCn$TC1B~=K+NxP_(9P(_M?!ac5^|>kwwlA_iFl z0M>;8plD*F*4lt`t$KQuPG0fY4qfzt%0`c_Hb2%5PO5W2sf+Q)MxOzQzyw_>)j5Zz zQhGh~;H08#E1XEbCYyHM9~)ka2vyO7lrIWcmBf0sw9Vz{s?&M_uJSGPi+blXPm>9h zaR~i!1eC?}u0gaw?X1;P-M5FD!C!mJ(UoIwc~G{J8<(#NtnlwEyzWy75LPpSTuzUj z>x2dmYPWiHgwHfGKTn}!q^!Zl@VqS5DyVSzU>K_bN}GOt_|cZ`tt~lqsx(TMpe71R z$_kYRS`V-DI@voQ^^oWi#VEndbt)q%Ge5UPv5~IEw;6Tu4}0cmn@Q1VIGQIKF(4&O%ueBi6@LoV>fA#6p!arlm(O&%ky!eK5s7Tm8T6^ zc`8fhO6!`1L;T{=s_AvA^P!&=BEo8VXwU{IM2|R!_V)zn#~JS-#NiWB?eniu3iK){ z30gU>K{^BgGx2VF7v4ONrv{DHf3UH=XpG0IJst_^zW~l?;?>{_1c%e*l8LIU(uQ%L z!Rv;SQ%LN9OsH8HrBr#)Vqi^SgI(-}BCmA(F26Nht+ffEgP|ly8dN%$Ik2(z!^nHn zdz;iMd3%u!&TK^hNC4-Iv78hUDx*Slg)f(2xKu>Wlt<%!wd@Jqsz|(D(N@T#UN$%O zNae{2q64YPy6>v+Ed6*v*NjJ$Pe2!&gKBPGK9yEAS(*3?VC3TzYDTIX7GjzF^P%m$ z`=O4!YpSh#?R0Qn;EH6zVZ7M*Ou*EjT=p>TGN>TQ)7YPW&dFhK&@!!W_pj4i=>KE4 zMYdydO#ot2eR!}2oa?|r+}m9tHkB{6jX$)Z^&@2)RYXbn*@-rHHfe557a~l=SS?OS z(Cnyt;)B`zL9*EV7x3I?Ua5;XS3?FHMtCMVn-l4SUxbpve7RvLT;$y=wO%%Q)mHP& z_mz|awn@OB(Wd%mg~L<%sc-Oy@t*>OsIz1(r(mIH#^t<p=$Zy1Ld#EJ+JVX4Ud*sTv(1B+n$D-@LB(Kc)i#avI z0U1Ec9}EWv2AlFKE<=Q_P^tCgJRP9&>jp;YvgQ&(vN=xJwi^+xqJg3L0izP{cQ z^D+iEP6yHxGu^`y$`voaoFziaF@mw4qlJ>%0OB-5%a9>MXY`%ZtN^2=ThJ;U_uuDR zJ&){K%&WmNrJmz42f|)@gkP@JfJY}eTn*~j=lFicBU-4K+%G^}`Sjc2ctroB#Rrv7 zqOmNDOAqo~?#cGet^V^cOIKPvHfNQC-a<>j+8ULP2QB8&rvb!XN=}I)+9?FDMW64Q zY(!+{ImsWXzn~X?Zk8zTr1Pl1>2ocUq;#Az!QWbMrZ=oSSkNp10GH2dZMnP;9vz%! z2vj$A#o=}-t)X?^X}r?FNJ`c=K|3jJra^ZVsf6d^s#Ya|@|+q!C4dEEcb`8N80T!F z2~kR%C(eXL0`XR4EwxZH5iS1es>7a9v+WN|V|Uu<;Jn}Hnl)*lgZdLkOZJR4hx`2j zu0jcUVd=e({qE(}>ES#EcN_$_3p>HdF<>+_(@)3&qfG_RYPw&-hh>R3CkNHg=O+`l z&nXT`U!my{s^f? zR59IsN!DRA5h6`&srn+t>wiry$m~xPHjR2_W1Q-A!(K0(qrJf2m;$CMt3u5v)_0Jg z2?BuJ?YiyJ+aNj<{Lew%EmN60IpdMdvbv}229DNkT8(^ac zRNqEi)j8GtIA1nQ#3NYCFf)f_X<~0P(I{X&Ynb(DFq`~L!4*>n8Dn$ou4ev%b z`vf`)y^S;?z!Xp6TCZ2}58}uuNuj{abb&Du1_y zPr51@0*-37yC*`;oLhHr@OwCjJ>UATmiCRG_TUv-2{rS*?sJj#kO9LUao1XV8*BAi zc{YY21Vp5H!0B=2H_9nXaLVP0+y&Ww28Wu$IN zLq-<(ZIi-tT?+SIoIhP*0>c6aI}g+1)59oMf0ihRS&)J1$N}ku3dMYvbnlM@>G2MO z8`yAvSK8LtFSdp^0@J&G^}FYo^PvL`4QS9dt-%79_h6M-Oa`h_7{lxiH_*bMFSFoH zPT;mi9qp2*AR`wlKl=91F}IKrNujylNQHZYLtf{62O5bfLK)t(+H8p57f(k&U780zt@!QR#R&He!_P}oc^62>Gm7nXW* z*!o)>E0%I9{OXsc;Ss^0xgJg=M>?h&M0_;*oUGB=(KZ#Jyk%RV#u-BqWTBnMA9CC8ZE}Ep!S|=XC=;!V4XrL?zB#LF76QXOE+`5}r~FD= z1H7}Axu5hm}SPhD-2R}tek4); z-nzbq<)>QGMIq)9(R2II3bP66U0&l>ICyDR#7HcW{mvIazg;ABmn&zoPnxK~o(Jsn z{38^MmnlpnXzN30Fa3}y8$!4xNhSXlIQ_I*OxW*D7cV(+ak&pVMi{)tme-+I$?=-u1)bDyP|`biJQL zY(*D{#NGp%*Sa8u898U(Dxc#O1PB${o%cVDAHFFE#6)AJ!sIl)tTv=|5X! zk1!K2j97nG+*0Vi%=!3C9|!1Q5m_<+0xM8#(W0iEJCv?aGbXV<&Vl#?T9${rv9o)1 zN4~UlH|z(pEy-G0X!pfbdoYsfgO*Bl(wR`l?V35fs=HFr+Dt z=(vvAMmW0B>S_%SN|u_RZ^P;-^|telHG*v&wnv~)XefGDsD0vY+nFz8Wq6+xO-=5< zxN?qmM;M*4i?R11c0LP2M<*d=bKF_)NRB<^9>0<5!#;c*oQS$0@%0id{_TKFLe2ee zHad^Ot=Nd1eUMa_na2q3p=$wQ77VQnPJD;sHHOx#RY5%png>PvG=)TY;>oj`Q@V117~P6P=X7 z?sV50%$Z0zguFf1K4tu2CF;xU{C(#2w^dDKsN@xs=B1hW&@H4>kNg5KP?9OY7?2q+ zRo;dSAp@;owy)FK9RFBKfB2-7Z~WZ6&jec@z!Zlm1cF8GQ;YG&rRx&)FL z)@lF@Fz=Fe&4kUwC(L#P_Go!?=vV(TQi6+r!++B&GN&uVO|d0I!qX3aVvfq zO2XD4BL~~+U@#$n%Ht~e%Q&JP+TABuu7ibtZ>p$yv5Cr+cKc&My1Z6^i5k@4oP%!T zLb9weetH2e!FU*y6jOtwMumS=r{L8wCO-iPc-USq;S^Et~ zS^sH`JSR_IbJ?7H91Blp8&)&;Ci0X-F8@5uLkUWu*E5?L%$_=1O^vz;++E?(+UYzu;qG{F28`sBwE}17MaL z5bPN{%&nEb-ok`TaW^hbAONg?<>V1U@lAJSl-z_Fgg z0$H=gbHk3BwK(f0la`e6U)T^;Op`^E7i2cKu^j4~+8gXN&$v0rw(0ge7iO)s<@PVw zgX4f!*ykXvAx@$EczyyUFfcILrRAX}=L#+uXJOn6o#!(0Uj{QT+Gw1_8bxZ`!7sn0 z&Uf-21oZKo>K&7)C=|)!_W@GS!N6d2y@DFu^C{_2t89sK_VWP8>~GJ-fyPe9$cLZ{ z`enf%Mu7qc9iy{Z64gq+_te3iSoY?6vqPv3$63cg>JdLS`T@J<9b;SW-E*4Fbu`1m zLpO+@m>uD6(IRts%F|FH3a+pSxeD(+o#tm%JKMN9pj-->FVHz$S8FB(XaOutF?3Myw`Cn`VON?olJVEB1;mV8pdyV z+G3H$*tv;ZSw>ZPQIVoHXlMFCH;mDh6Mb38PTS=B0f@bmscQ!AhXJTK73KhL8QiwL zkU6!lO!0Pt8{uHy9LjL9-aK{;w(Iq={bz8^MXV+?U8;P8` zw?erlZ%rpJikyw!s_1~HwUw7!?5=0Jhu&{%>18&`yT35M!j4ukz74AF`b9^LP78&q zQhGE_X>0?;C8xzJ)zH}r5fd}{8WDglrO_L%``fXiv%4Q_hNm&|6yM<*0^iBvR()?d zRwmU{saey#$sF&XdKIy4%JAE6gUEH97|&yAK|bjQWok$?`GjA&8jkr`#1k|!jcywF zM7?P*QT4?F0G z&VKPOwAPA;uFyOuOy@*nWVoo9i8p_gCHMWk$;1uoPp|ds+nwleoxplpRke055rc7i zluXh4BGGtpta;ay{UOIN*Z_L>6VoDNN`M*53OSql((!NFIE`MldUwg-HXItvFjbpl=e`iguCsNPJwSjG) z&e3O4IrUS9Edd5?=f7JFaNGh755vXXfrdwxOZv_Y3)l(km8iTUu5)WAjh-p8wk5x+ z#27tMtV*_--1R!^Fk-CPqxp4oC5?4O3Pv7u*h4Ydv~tE>Uw!nDuQT=Me>Qy+_ibwJ zx#J64`%Im^j%kun#1OA4!w#=khY|TAHLdUaeh+b?e%Fo3Tw#?Ke_xd;t}`OkK5?u zz2^IB70cJlDfw^6Kg17+fF?Tx_7`IoI*h;+8uR!SDXoHpE;k~?Xg2bLdkg9&l!Djk zjRfkEXY+dVDBK#l=&~KQ`E-QtrqHNI+_As4Y)z1O1uEvZy2{bAv!j)_d9`oUa!i=y z&0uUf2%h@|uMMDUwD>o>I;gvgNvB%1TkIyvIt1tWMw3~SKJ)^gr=&~k3WW{x(??oN zjs7_ef8Q0c%^lN38Xi_6f2cY1 z+?$RI(Rhzf;F&5Ja4i?16Go$5&b@8V)efAC;qs7^$U~nSXDl9c8BLD1Ea5uw!GcPp z(M_WO*j*E--5wkm#}Gdca}-!#hK7^gEXH2t&sCIZ7Sc7<2J$6<)nF1m7ffmogExDz zm0%{W_24z-`)-7$@J)|ws9?^qEN^bto1^EZ4i=H3m+Ka%4VH~E>!!H0UtguN9(H9l zkezCe;hiRR?u6Cem1nUx4^#cJBj1@81kJtJ%rXiXP7zw9)?Z*yS><24wF5{#nTzlP zMAXJf$VRWpr2nMo-v`T;JhX8j+SoT~2)#1Fgpix#DoiKXo1iBqA-k#)jceDhy(@=# zSnYB!P(G;mouei+y!>iWB=se-aom>cSz2v>%e@Jq*!dVFK5LcVAWgF)fh4D>=FzlmnP-R@&2S=zpULmK4?oa>fL2h5Y*qpc1QfWpQ=ke=Fl5_ z(2gV;Ji?0Ji0dlANyH{j%z1#9(&DS51K@d0Ys1Gij@Elrp)%dq5D=B{|3p|IJ-;g4 z;jRWaUta!*F+fqHI2Ug+)glPugMswwkI>P2`sJ5Y=hMlN*s+HP%Cu9yR0S`d;2lIU z{i>Q#O1Bz__CLZmaP-}1`#lTOagFrQ<2o(vZg)vEz&oble7M^4WDn&eD3?$b6RJyn zVs>w%J?vn@53-|LdzXKP)FDs(SuKT~ICR2IXVse<^eEB61gT|NcifPdr*cmNW5`7l z%+>ndzb^M|pNxOr86(0BKg~I;u=+u%XiYvjn$3R`R4qhm!;CVf!I)ZtgZ2U|)%o&MHu$ z?@x=b6vbCFUOP$P4IuT$yaN6OQT|#C{>fjwsE1u7n64$pL?gi)wIk71 zx~|^}89#K*RWK&sf6kTH=X?J`xL|3nE|7u+{|#L{7gI|!G$*$bTZHU|JMvArN**a$ z`a!$0V4RNxS;TB@&a(gQjsq^XGBpndr0cj7>ZTbnyka<7$)n!~o?o=QE>seVWmg0i zZqR51D~sRU#;|5abQzZIJZi5)y1D!%?jaNa)MX6P-+J)}LaQtn$>BL$_B?80!kFhA zAffgKlh1<7wEfErXB&5u!${EScL%D(Phy*O8w*RjT>m%m;{y-5Yuozi|MJu)M-Hf|SPLbQS zw6U;im5yS0qY{@x3bbw!@mpLI-BTbDqrVr#eUfjUGAAe;B}kbrwJoT_=hdg%)r2>} zzMm4M*K_b3B7B)RO^jg>`zjhnu7LHLM`Z#PRxjy33Z5CN`2eXJ3n2Mx=t@F4vt)#7 z#OL2as2SduJ%=voeYzRPQP0j@(XIyQi6P`rVxjmZ1AI7~bE8jq>c-$k+M4v_N3w0< z`(X9|B3|Dd<6ms;dw}>?5&usq;_m_4vmeG?bV*B6GtG7p@rVwdL)VzMiI_eFML=DN z%hCuM7^*MwK24gWjVyKQiJhJj#ce6&S#jr_DfaA|2iRpsVDU;As|bYoiKAgLoL4;E z`kUnRpXW46!Qg52O#hF7#?knNr0}K!FrZ!3c~6A~MmeKr(e%FQ;vAPmcRTW}?nA>l z5qCK112T2oIMb!L5bl?>;z?5x7Yu7pIqI4D%hL@p^oBlygDWJ_eT#0b4AIr3WN6L7 z#BYITPx@Q8X1=cdCZMj7+F4fY(IW93#wk?>(mR|-=2I6e;0qSuQgTLXOPKZP`=1S; zV3k6&>$0cpr>(()4~x^9CiZ~6%~WD$Ehlr}F}kmd_&9Hq zl$d#S_zNYEEv1S#wj@_!!Ha0HZU#8SI@7#>Ts)(JlI^mPFK=(=Z@{n~0LT62kKJH9 zmfs~h-?2eVuXwEmvjVwdmTt7H!_Wzak9)n2JVnCJ)h}un!gO^eN7f6%0DGW|X>s#JLfvm7t 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 literal 0 HcmV?d00001 diff --git a/docs/cugraph/source/wholegraph/imgs/host_mapped_wholememory_step2.png b/docs/cugraph/source/wholegraph/imgs/host_mapped_wholememory_step2.png new file mode 100644 index 0000000000000000000000000000000000000000..20597f3e5154c7608d9113822b70567797530dbc GIT binary patch literal 32110 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 literal 0 HcmV?d00001 diff --git a/docs/cugraph/source/wholegraph/imgs/wholememory_tensor.png b/docs/cugraph/source/wholegraph/imgs/wholememory_tensor.png new file mode 100644 index 0000000000000000000000000000000000000000..e725d6c28edffbfa9879e42cb9b36fb6a15e2516 GIT binary patch literal 40367 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 DoB#6Y literal 0 HcmV?d00001 diff --git a/docs/cugraph/source/wholegraph/index.rst b/docs/cugraph/source/wholegraph/index.rst new file mode 100644 index 00000000000..2a69544b4c9 --- /dev/null +++ b/docs/cugraph/source/wholegraph/index.rst @@ -0,0 +1,14 @@ +WholeGraph +========== +RAPIDS WholeGraph has following package: + +* pylibwholegraph: shared memory-based GPU-accelerated GNN training + + +.. toctree:: + :maxdepth: 2 + :caption: Contents: + + basics/index + installation/index + diff --git a/docs/cugraph/source/wholegraph/installation/container.md b/docs/cugraph/source/wholegraph/installation/container.md new file mode 100644 index 00000000000..3a2c627c56a --- /dev/null +++ b/docs/cugraph/source/wholegraph/installation/container.md @@ -0,0 +1,29 @@ +# Build Container for WholeGraph +To run WholeGraph or build WholeGraph from source, set up the environment first. +We recommend using Docker images. +For example, to build the WholeGraph base image from the NGC pytorch 22.10 image, you can follow `Dockerfile`: +```dockerfile +FROM nvcr.io/nvidia/pytorch:22.10-py3 + +RUN apt update && DEBIAN_FRONTEND=noninteractive apt install -y lsb-core software-properties-common wget libspdlog-dev + +#RUN remove old cmake to update +RUN conda remove --force -y cmake +RUN rm -rf /usr/local/bin/cmake && rm -rf /usr/local/lib/cmake && rm -rf /usr/lib/cmake + +RUN apt-key adv --fetch-keys https://apt.kitware.com/keys/kitware-archive-latest.asc && \ + export LSB_CODENAME=$(lsb_release -cs) && \ + apt-add-repository -y "deb https://apt.kitware.com/ubuntu/ ${LSB_CODENAME} main" && \ + apt update && apt install -y cmake + +# update py for pytest +RUN pip3 install -U py +RUN pip3 install Cython setuputils3 scikit-build nanobind pytest-forked pytest +``` + +To run GNN applications, you may also need cuGraphOps, DGL and/or PyG libraries to run the GNN layers. +You may refer to [DGL](https://www.dgl.ai/pages/start.html) or [PyG](https://pytorch-geometric.readthedocs.io/en/latest/notes/installation.html) +For example, to install DGL, you may need to add: +```dockerfile +RUN pip3 install dgl -f https://data.dgl.ai/wheels/cu118/repo.html +``` diff --git a/docs/cugraph/source/wholegraph/installation/getting_wholegraph.md b/docs/cugraph/source/wholegraph/installation/getting_wholegraph.md new file mode 100644 index 00000000000..5b2072b0523 --- /dev/null +++ b/docs/cugraph/source/wholegraph/installation/getting_wholegraph.md @@ -0,0 +1,48 @@ + +# Getting the WholeGraph Packages + +Start by reading the [RAPIDS Instalation guide](https://docs.rapids.ai/install) +and checkout the [RAPIDS install selector](https://rapids.ai/start.html) for a pick list of install options. + + +There are 4 ways to get WholeGraph packages: +1. [Quick start with Docker Repo](#docker) +2. [Conda Installation](#conda) +3. [Pip Installation](#pip) +4. [Build from Source](./source_build.md) + + +
+ +## Docker +The RAPIDS Docker containers (as of Release 23.10) contain all RAPIDS packages, including WholeGraph, as well as all required supporting packages. To download a container, please see the [Docker Repository](https://hub.docker.com/r/rapidsai/rapidsai/), choosing a tag based on the NVIDIA CUDA version you’re running. This provides a ready to run Docker container with example notebooks and data, showcasing how you can utilize all of the RAPIDS libraries. + +
+ + +## Conda +It is easy to install WholeGraph using conda. You can get a minimal conda installation with [Miniconda](https://conda.io/miniconda.html) or get the full installation with [Anaconda](https://www.anaconda.com/download). + +WholeGraph conda packages + * libwholegraph + * pylibwholegraph + +Replace the package name in the example below to the one you want to install. + + +Install and update WholeGraph using the conda command: + +```bash +conda install -c rapidsai -c conda-forge -c nvidia wholegraph cudatoolkit=11.8 +``` + +
+ +## PIP +wholegraph, and all of RAPIDS, is available via pip. + +``` +pip install wholegraph-cu11 --extra-index-url=https://pypi.nvidia.com +``` + +
diff --git a/docs/cugraph/source/wholegraph/installation/index.rst b/docs/cugraph/source/wholegraph/installation/index.rst new file mode 100644 index 00000000000..09f1cb44a24 --- /dev/null +++ b/docs/cugraph/source/wholegraph/installation/index.rst @@ -0,0 +1,9 @@ +Installation +============ + +.. toctree:: + :maxdepth: 2 + + getting_wholegraph + container + source_build diff --git a/docs/cugraph/source/wholegraph/installation/source_build.md b/docs/cugraph/source/wholegraph/installation/source_build.md new file mode 100644 index 00000000000..c468048c351 --- /dev/null +++ b/docs/cugraph/source/wholegraph/installation/source_build.md @@ -0,0 +1,187 @@ +# Building from Source + +The following instructions are for users wishing to build wholegraph from source code. These instructions are tested on supported distributions of Linux,CUDA, +and Python - See [RAPIDS Getting Started](https://rapids.ai/start.html) for a list of supported environments. +Other operating systems _might be_ compatible, but are not currently tested. + +The wholegraph package includes both a C/C++ CUDA portion and a python portion. Both libraries need to be installed in order for cuGraph to operate correctly. +The C/C++ CUDA library is `libwholegraph` and the python library is `pylibwholegraph`. + +## Prerequisites + +__Compiler__: +* `gcc` version 11.0+ +* `nvcc` version 11.0+ +* `cmake` version 3.26.4+ + +__CUDA__: +* CUDA 11.8+ +* NVIDIA driver 450.80.02+ +* Pascal architecture or better + +You can obtain CUDA from [https://developer.nvidia.com/cuda-downloads](https://developer.nvidia.com/cuda-downloads). + +__Other Packages__: +* ninja +* nccl +* cython +* setuputils3 +* scikit-learn +* scikit-build +* nanobind>=0.2.0 + +## Building wholegraph +To install wholegraph from source, ensure the dependencies are met. + +### Clone Repo and Configure Conda Environment +__GIT clone a version of the repository__ + + ```bash + # Set the location to wholegraph in an environment variable WHOLEGRAPH_HOME + export WHOLEGRAPH_HOME=$(pwd)/wholegraph + + # Download the wholegraph repo - if you have a forked version, use that path here instead + git clone https://github.com/rapidsai/wholegraph.git $WHOLEGRAPH_HOME + + cd $WHOLEGRAPH_HOME + ``` + +__Create the conda development environment__ + +```bash +# create the conda environment (assuming in base `wholegraph` directory) + +# for CUDA 11.x +conda env create --name wholegraph_dev --file conda/environments/all_cuda-118_arch-x86_64.yaml + +# activate the environment +conda activate wholegraph_dev + +# to deactivate an environment +conda deactivate +``` + + - The environment can be updated as development includes/changes the dependencies. To do so, run: + + +```bash + +# Where XXX is the CUDA version +conda env update --name wholegraph_dev --file conda/environments/all_cuda-XXX_arch-x86_64.yaml + +conda activate wholegraph_dev +``` + + +### Build and Install Using the `build.sh` Script +Using the `build.sh` script make compiling and installing wholegraph a +breeze. To build and install, simply do: + +```bash +$ cd $WHOLEGRAPH_HOME +$ ./build.sh clean +$ ./build.sh libwholegraph +$ ./build.sh pylibwholegraph +``` + +There are several other options available on the build script for advanced users. +`build.sh` options: +```bash +build.sh [ ...] [ ...] + where is: + clean - remove all existing build artifacts and configuration (start over). + uninstall - uninstall libwholegraph and pylibwholegraph from a prior build/install (see also -n) + libwholegraph - build the libwholegraph C++ library. + pylibwholegraph - build the pylibwholegraph Python package. + tests - build the C++ (OPG) tests. + benchmarks - build benchmarks. + docs - build the docs + and is: + -v - verbose build mode + -g - build for debug + -n - no install step + --allgpuarch - build for all supported GPU architectures + --cmake-args=\\\"\\\" - add arbitrary CMake arguments to any cmake call + --compile-cmd - only output compile commands (invoke CMake without build) + --clean - clean an individual target (note: to do a complete rebuild, use the clean target described above) + -h | --h[elp] - print this text + + default action (no args) is to build and install 'libwholegraph' then 'pylibwholegraph' targets + +examples: +$ ./build.sh clean # remove prior build artifacts (start over) +$ ./build.sh + +# make parallelism options can also be defined: Example build jobs using 4 threads (make -j4) +$ PARALLEL_LEVEL=4 ./build.sh libwholegraph + +Note that the libraries will be installed to the location set in `$PREFIX` if set (i.e. `export PREFIX=/install/path`), otherwise to `$CONDA_PREFIX`. +``` + + +## Building each section independently +### Build and Install the C++/CUDA `libwholegraph` Library +CMake depends on the `nvcc` executable being on your path or defined in `$CUDACXX`. + +This project uses cmake for building the C/C++ library. To configure cmake, run: + + ```bash + # Set the location to wholegraph in an environment variable WHOLEGRAPH_HOME + export WHOLEGRAPH_HOME=$(pwd)/wholegraph + + cd $WHOLEGRAPH_HOME + cd cpp # enter cpp directory + mkdir build # create build directory + cd build # enter the build directory + cmake .. -DCMAKE_INSTALL_PREFIX=$CONDA_PREFIX + + # now build the code + make -j # "-j" starts multiple threads + make install # install the libraries + ``` +The default installation locations are `$CMAKE_INSTALL_PREFIX/lib` and `$CMAKE_INSTALL_PREFIX/include/wholegraph` respectively. + +### Building and installing the Python package + +Build and Install the Python packages to your Python path: + +```bash +cd $WHOLEGRAPH_HOME +cd python +cd pylibwholegraph +python setup.py build_ext --inplace +python setup.py install # install pylibwholegraph +``` + +## Run tests + +Run either the C++ or the Python tests with datasets + + - **Python tests with datasets** + + ```bash + cd $WHOLEGRAPH_HOME + cd python + pytest + ``` + + - **C++ stand alone tests** + + From the build directory : + + ```bash + # Run the tests + cd $WHOLEGRAPH_HOME + cd cpp/build + gtests/PARALLEL_UTILS_TESTS # this is an executable file + ``` + + +Note: This conda installation only applies to Linux and Python versions 3.8/3.10. + +## Creating documentation + +Python API documentation can be generated from _./docs/wholegraph directory_. Or through using "./build.sh docs" + +## Attribution +Portions adopted from https://github.com/pytorch/pytorch/blob/master/CONTRIBUTING.md From 332676e78bbbd209b77caf81ab756fa6bff2a07b Mon Sep 17 00:00:00 2001 From: Huiyu Xie Date: Tue, 21 Nov 2023 11:40:36 -0800 Subject: [PATCH 096/111] [DOC]: Fix invalid links and add materials to notebooks (#4002) I'm new to CuGraph repository and would like to contribute further in the future. So I start with minor tasks with docs to familiarize myself with the contribution process in this repository. Here is what I have done in this PR: - Fixed some invalid links in the documentation. - Corrected and added formulas related to centrality. - Added more references to the centrality to ensure consistency. Also, the images located at https://github.com/rapidsai/cugraph/tree/main/python/cugraph cannot be displayed because it is symbolic link to the repository's README file. But it's not a major issue. Authors: - Huiyu Xie (https://github.com/huiyuxie) - Ralph Liu (https://github.com/nv-rliu) - Naim (https://github.com/naimnv) - Brad Rees (https://github.com/BradReesWork) Approvers: - Don Acosta (https://github.com/acostadon) - Brad Rees (https://github.com/BradReesWork) URL: https://github.com/rapidsai/cugraph/pull/4002 --- .../graph_support/algorithms/Centrality.md | 14 +++++++----- .../source/installation/getting_cugraph.md | 2 +- .../algorithms/centrality/Centrality.ipynb | 22 ++++++++++++++----- notebooks/algorithms/centrality/Degree.ipynb | 2 +- notebooks/algorithms/centrality/Katz.ipynb | 7 ------ readme_pages/CONTRIBUTING.md | 4 ++-- 6 files changed, 29 insertions(+), 22 deletions(-) diff --git a/docs/cugraph/source/graph_support/algorithms/Centrality.md b/docs/cugraph/source/graph_support/algorithms/Centrality.md index f82a1fe123b..8119e655236 100644 --- a/docs/cugraph/source/graph_support/algorithms/Centrality.md +++ b/docs/cugraph/source/graph_support/algorithms/Centrality.md @@ -15,13 +15,15 @@ But which vertices are most important? The answer depends on which measure/algor |Algorithm |Notebooks Containing |Description | | --------------- | ------------------------------------------------------------ | ------------------------------------------------------------ | -|[Degree Centrality](./degree_centrality.html)| [Centrality](./Centrality.ipynb), [Degree](./Degree.ipynb) |Measure based on counting direct connections for each vertex| -|[Betweenness Centrality](./betweenness_centrality.html)| [Centrality](./Centrality.ipynb), [Betweenness](./Betweenness.ipynb) |Number of shortest paths through the vertex| -|[Eigenvector Centrality](./eigenvector_centrality.html)|[Centrality](./Centrality.ipynb), [Eigenvector](./Eigenvector.ipynb)|Measure of connectivity to other important vertices (which also have high connectivity) often referred to as the influence measure of a vertex| -|[Katz Centrality](./katz_centrality.html)|[Centrality](./Centrality.ipynb), [Katz](./Katz.ipynb) |Similar to Eigenvector but has tweaks to measure more weakly connected graph | -|Pagerank|[Centrality](./Centrality.ipynb), [Pagerank](../../link_analysis/Pagerank.ipynb) |Classified as both a link analysis and centrality measure by quantifying incoming links from central vertices. | +|[Degree Centrality](./degree_centrality.md)| [Centrality](https://github.com/rapidsai/cugraph/blob/main/notebooks/algorithms/centrality/Centrality.ipynb), [Degree](https://github.com/rapidsai/cugraph/blob/main/notebooks/algorithms/centrality/Degree.ipynb) |Measure based on counting direct connections for each vertex| +|[Betweenness Centrality](./betweenness_centrality.md)| [Centrality](https://github.com/rapidsai/cugraph/blob/main/notebooks/algorithms/centrality/Centrality.ipynb), [Betweenness](https://github.com/rapidsai/cugraph/blob/main/notebooks/algorithms/centrality/Betweenness.ipynb) |Number of shortest paths through the vertex| +|[Eigenvector Centrality](./eigenvector_centrality.md)|[Centrality](https://github.com/rapidsai/cugraph/blob/main/notebooks/algorithms/centrality/Centrality.ipynb), [Eigenvector](https://github.com/rapidsai/cugraph/blob/main/notebooks/algorithms/centrality/Eigenvector.ipynb)|Measure of connectivity to other important vertices (which also have high connectivity) often referred to as the influence measure of a vertex| +|[Katz Centrality](./katz_centrality.md)|[Centrality](https://github.com/rapidsai/cugraph/blob/main/notebooks/algorithms/centrality/Centrality.ipynb), [Katz](https://github.com/rapidsai/cugraph/blob/main/notebooks/algorithms/centrality/Katz.ipynb) |Similar to Eigenvector but has tweaks to measure more weakly connected graph | +|Pagerank|[Centrality](https://github.com/rapidsai/cugraph/blob/main/notebooks/algorithms/centrality/Centrality.ipynb), [Pagerank](https://github.com/rapidsai/cugraph/blob/main/notebooks/algorithms/link_analysis/Pagerank.ipynb) |Classified as both a link analysis and centrality measure by quantifying incoming links from central vertices. | + +[System Requirements](https://github.com/rapidsai/cugraph/blob/main/notebooks/README.md#requirements) + -[System Requirements](../../README.html#requirements) | Author Credit | Date | Update | cuGraph Version | Test Hardware | | --------------|------------|------------------|-----------------|----------------| diff --git a/docs/cugraph/source/installation/getting_cugraph.md b/docs/cugraph/source/installation/getting_cugraph.md index 509508c5283..625f2c64c27 100644 --- a/docs/cugraph/source/installation/getting_cugraph.md +++ b/docs/cugraph/source/installation/getting_cugraph.md @@ -9,7 +9,7 @@ There are 4 ways to get cuGraph packages: 1. [Quick start with Docker Repo](#docker) 2. [Conda Installation](#conda) 3. [Pip Installation](#pip) -4. [Build from Source](#SOURCE) +4. [Build from Source](./source_build.md)
diff --git a/notebooks/algorithms/centrality/Centrality.ipynb b/notebooks/algorithms/centrality/Centrality.ipynb index d19dd646b15..e470febb975 100644 --- a/notebooks/algorithms/centrality/Centrality.ipynb +++ b/notebooks/algorithms/centrality/Centrality.ipynb @@ -36,22 +36,34 @@ "__Degree Centrality__
\n", "Degree centrality is based on the notion that whoever has the most connections must be important. \n", "\n", - "$C_d(v) = \\frac{{\\text{{degree of vertex }} v}}{{\\text{{total number of vertices}} - 1}}$\n", - "\n", + "$C_{degree}(v) = \\frac{{\\text{degree of vertex} \\ v}}{{\\text{total number of vertices} - 1}}$\n", "\n", + "See:\n", + "* [Degree (graph theory) on Wikipedia](https://en.wikipedia.org/wiki/Degree_(graph_theory)) for more details on the algorithm.\n", + "* [Learn more about Degree Centrality](https://www.sci.unich.it/~francesc/teaching/network/degree.html)\n", "\n", - "___Closeness centrality – coming soon___
\n", + "__Closeness Centrality__
\n", "Closeness is a measure of the shortest path to every other node in the graph. A node that is close to every other node, can reach over other node in the fewest number of hops, means that it has greater influence on the network versus a node that is not close.\n", "\n", + "$C_{closeness}(v)=\\frac{n-1}{\\sum_{t} d(v,t)}$\n", + "\n", + "See:\n", + "* [Closeness Centrality on Wikipedia](https://en.wikipedia.org/wiki/Closeness_centrality) for more details on the algorithm.\n", + "* [Learn more about Closeness Centrality](https://www.sci.unich.it/~francesc/teaching/network/closeness.html)\n", + "\n", "__Betweenness Centrality__
\n", "Betweenness is a measure of the number of shortest paths that cross through a node, or over an edge. A node with high betweenness means that it had a greater influence on the flow of information. \n", "\n", "Betweenness centrality of a node 𝑣 is the sum of the fraction of all-pairs shortest paths that pass through 𝑣\n", "\n", - "$C_{betweenness}=\\sum_{s \\neq v \\neq t} \\frac{\\sigma_{st}(v)}{\\sigma_{st}}$\n", + "$C_{betweenness}(v)=\\sum_{s \\neq v \\neq t} \\frac{\\sigma_{st}(v)}{\\sigma_{st}}$\n", "\n", "To speedup runtime of betweenness centrailty, the metric can be computed on a limited number of nodes (randomly selected) and then used to estimate the other scores. For this example, the graphs are relatively small (under 5,000 nodes) so betweenness on every node will be computed.\n", "\n", + "See:\n", + "* [Betweenness Centrality on Wikipedia](https://en.wikipedia.org/wiki/Betweenness_centrality) for more details on the algorithm.\n", + "* [Learn more about Betweenness Centrality](https://www.sci.unich.it/~francesc/teaching/network/betweeness.html)\n", + "\n", "__Katz Centrality__
\n", "Katz is a variant of degree centrality and of eigenvector centrality. \n", "Katz centrality is a measure of the relative importance of a node within the graph based on measuring the influence across the total number of walks between vertex pairs.\n", @@ -60,7 +72,7 @@ "\n", "\n", "See:\n", - "* [Katz on Wikipedia](https://en.wikipedia.org/wiki/Katz_centrality) for more details on the algorithm.\n", + "* [Katz Centrality on Wikipedia](https://en.wikipedia.org/wiki/Katz_centrality) for more details on the algorithm.\n", "* [Learn more about Katz Centrality](https://www.sci.unich.it/~francesc/teaching/network/katz.html)\n", "\n", "__Eigenvector Centrality__
\n", diff --git a/notebooks/algorithms/centrality/Degree.ipynb b/notebooks/algorithms/centrality/Degree.ipynb index e7535420b65..5a5213a904f 100644 --- a/notebooks/algorithms/centrality/Degree.ipynb +++ b/notebooks/algorithms/centrality/Degree.ipynb @@ -27,7 +27,7 @@ "\n", "See [Degree Centrality on Wikipedia](https://en.wikipedia.org/wiki/Degree_centrality) for more details on the algorithm.\n", "\n", - "$C_d(v) = \\frac{{\\text{{degree of vertex }} v}}{{\\text{{number of vertices in graph}} - 1}}$" + "$C_d(v) = \\frac{{\\text{degree of vertex } \\ v}}{{\\text{number of vertices in graph} - 1}}$" ] }, { diff --git a/notebooks/algorithms/centrality/Katz.ipynb b/notebooks/algorithms/centrality/Katz.ipynb index c94a14bb14a..08ee42df788 100755 --- a/notebooks/algorithms/centrality/Katz.ipynb +++ b/notebooks/algorithms/centrality/Katz.ipynb @@ -333,13 +333,6 @@ "Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.\n", "___" ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] } ], "metadata": { diff --git a/readme_pages/CONTRIBUTING.md b/readme_pages/CONTRIBUTING.md index 4b736b25155..ffe1ef1831b 100644 --- a/readme_pages/CONTRIBUTING.md +++ b/readme_pages/CONTRIBUTING.md @@ -68,7 +68,7 @@ If you need more context on a particular issue, please ask. # So you want to contribute code **TL;DR General Development Process** -1. Read the documentation on [building from source](./SOURCEBUILD.md) to learn how to setup, and validate, the development environment +1. Read the documentation on [building from source](../docs/cugraph/source/installation/source_build.md) to learn how to setup, and validate, the development environment 2. Read the RAPIDS [Code of Conduct](https://docs.rapids.ai/resources/conduct/) 3. Find or submit an issue to work on (include a comment that you are working issue) 4. Fork the cuGraph [repo](#fork) and Code (make sure to add unit tests)! @@ -99,7 +99,7 @@ The RAPIDS cuGraph repo cannot directly be modified. Contributions must come in ```git clone https://github.com//cugraph.git``` -Read the section on [building cuGraph from source](./SOURCEBUILD.md) to validate that the environment is correct. +Read the section on [building cuGraph from source](../docs/cugraph/source/installation/source_build.md) to validate that the environment is correct. **Pro Tip** add an upstream remote repository so that you can keep your forked repo in sync ```git remote add upstream https://github.com/rapidsai/cugraph.git``` From f2df81d27fbf72c569c850504fd055196899b40c Mon Sep 17 00:00:00 2001 From: Erik Welch Date: Tue, 21 Nov 2023 20:44:54 -0600 Subject: [PATCH 097/111] nx-cugraph: add `eigenvector_centrality`, `katz_centrality`, `hits`, `pagerank` (#3968) Add `eigenvector_centrality`, `katz_centrality`, and `hits`. I may add pagerank next. Authors: - Erik Welch (https://github.com/eriknw) - Ralph Liu (https://github.com/nv-rliu) - Naim (https://github.com/naimnv) - Rick Ratzel (https://github.com/rlratzel) Approvers: - Rick Ratzel (https://github.com/rlratzel) URL: https://github.com/rapidsai/cugraph/pull/3968 --- python/nx-cugraph/_nx_cugraph/__init__.py | 21 ++++ python/nx-cugraph/lint.yaml | 2 +- .../nx_cugraph/algorithms/__init__.py | 10 +- .../algorithms/centrality/__init__.py | 2 + .../algorithms/centrality/eigenvector.py | 64 ++++++++++ .../nx_cugraph/algorithms/centrality/katz.py | 100 ++++++++++++++++ .../algorithms/community/louvain.py | 34 ++++-- .../algorithms/link_analysis/__init__.py | 14 +++ .../algorithms/link_analysis/hits_alg.py | 81 +++++++++++++ .../algorithms/link_analysis/pagerank_alg.py | 112 ++++++++++++++++++ python/nx-cugraph/nx_cugraph/classes/graph.py | 50 ++++---- python/nx-cugraph/nx_cugraph/interface.py | 21 +++- .../nx_cugraph/tests/test_ktruss.py | 2 +- python/nx-cugraph/nx_cugraph/utils/misc.py | 34 +++++- 14 files changed, 509 insertions(+), 38 deletions(-) create mode 100644 python/nx-cugraph/nx_cugraph/algorithms/centrality/eigenvector.py create mode 100644 python/nx-cugraph/nx_cugraph/algorithms/centrality/katz.py create mode 100644 python/nx-cugraph/nx_cugraph/algorithms/link_analysis/__init__.py create mode 100644 python/nx-cugraph/nx_cugraph/algorithms/link_analysis/hits_alg.py create mode 100644 python/nx-cugraph/nx_cugraph/algorithms/link_analysis/pagerank_alg.py diff --git a/python/nx-cugraph/_nx_cugraph/__init__.py b/python/nx-cugraph/_nx_cugraph/__init__.py index 910db1bc379..ef5f8f3fc23 100644 --- a/python/nx-cugraph/_nx_cugraph/__init__.py +++ b/python/nx-cugraph/_nx_cugraph/__init__.py @@ -47,12 +47,14 @@ "diamond_graph", "dodecahedral_graph", "edge_betweenness_centrality", + "eigenvector_centrality", "empty_graph", "florentine_families_graph", "from_pandas_edgelist", "from_scipy_sparse_array", "frucht_graph", "heawood_graph", + "hits", "house_graph", "house_x_graph", "icosahedral_graph", @@ -62,6 +64,7 @@ "isolates", "k_truss", "karate_club_graph", + "katz_centrality", "krackhardt_kite_graph", "ladder_graph", "les_miserables_graph", @@ -75,6 +78,7 @@ "number_of_selfloops", "octahedral_graph", "out_degree_centrality", + "pagerank", "pappus_graph", "path_graph", "petersen_graph", @@ -96,19 +100,36 @@ # BEGIN: extra_docstrings "betweenness_centrality": "`weight` parameter is not yet supported.", "edge_betweenness_centrality": "`weight` parameter is not yet supported.", + "eigenvector_centrality": "`nstart` parameter is not used, but it is checked for validity.", "from_pandas_edgelist": "cudf.DataFrame inputs also supported.", "k_truss": ( "Currently raises `NotImplementedError` for graphs with more than one connected\n" "component when k >= 3. We expect to fix this soon." ), + "katz_centrality": "`nstart` isn't used (but is checked), and `normalized=False` is not supported.", "louvain_communities": "`seed` parameter is currently ignored.", + "pagerank": "`dangling` parameter is not supported, but it is checked for validity.", # END: extra_docstrings }, "extra_parameters": { # BEGIN: extra_parameters + "eigenvector_centrality": { + "dtype : dtype or None, optional": "The data type (np.float32, np.float64, or None) to use for the edge weights in the algorithm. If None, then dtype is determined by the edge values.", + }, + "hits": { + "dtype : dtype or None, optional": "The data type (np.float32, np.float64, or None) to use for the edge weights in the algorithm. If None, then dtype is determined by the edge values.", + 'weight : string or None, optional (default="weight")': "The edge attribute to use as the edge weight.", + }, + "katz_centrality": { + "dtype : dtype or None, optional": "The data type (np.float32, np.float64, or None) to use for the edge weights in the algorithm. If None, then dtype is determined by the edge values.", + }, "louvain_communities": { + "dtype : dtype or None, optional": "The data type (np.float32, np.float64, or None) to use for the edge weights in the algorithm. If None, then dtype is determined by the edge values.", "max_level : int, optional": "Upper limit of the number of macro-iterations (max: 500).", }, + "pagerank": { + "dtype : dtype or None, optional": "The data type (np.float32, np.float64, or None) to use for the edge weights in the algorithm. If None, then dtype is determined by the edge values.", + }, # END: extra_parameters }, } diff --git a/python/nx-cugraph/lint.yaml b/python/nx-cugraph/lint.yaml index 01a806e6162..a94aa9f0448 100644 --- a/python/nx-cugraph/lint.yaml +++ b/python/nx-cugraph/lint.yaml @@ -53,7 +53,7 @@ repos: rev: v0.1.3 hooks: - id: ruff - args: [--fix-only, --show-fixes] + args: [--fix-only, --show-fixes] # --unsafe-fixes] - repo: https://github.com/PyCQA/flake8 rev: 6.1.0 hooks: diff --git a/python/nx-cugraph/nx_cugraph/algorithms/__init__.py b/python/nx-cugraph/nx_cugraph/algorithms/__init__.py index 32cd6f31a47..63841b15bd5 100644 --- a/python/nx-cugraph/nx_cugraph/algorithms/__init__.py +++ b/python/nx-cugraph/nx_cugraph/algorithms/__init__.py @@ -10,10 +10,18 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -from . import bipartite, centrality, community, components, shortest_paths +from . import ( + bipartite, + centrality, + community, + components, + shortest_paths, + link_analysis, +) from .bipartite import complete_bipartite_graph from .centrality import * from .components import * from .core import * from .isolate import * from .shortest_paths import * +from .link_analysis import * diff --git a/python/nx-cugraph/nx_cugraph/algorithms/centrality/__init__.py b/python/nx-cugraph/nx_cugraph/algorithms/centrality/__init__.py index af91f227843..496dc6aff81 100644 --- a/python/nx-cugraph/nx_cugraph/algorithms/centrality/__init__.py +++ b/python/nx-cugraph/nx_cugraph/algorithms/centrality/__init__.py @@ -12,3 +12,5 @@ # limitations under the License. from .betweenness import * from .degree_alg import * +from .eigenvector import * +from .katz import * diff --git a/python/nx-cugraph/nx_cugraph/algorithms/centrality/eigenvector.py b/python/nx-cugraph/nx_cugraph/algorithms/centrality/eigenvector.py new file mode 100644 index 00000000000..c0f02a6258e --- /dev/null +++ b/python/nx-cugraph/nx_cugraph/algorithms/centrality/eigenvector.py @@ -0,0 +1,64 @@ +# Copyright (c) 2023, NVIDIA CORPORATION. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import networkx as nx +import numpy as np +import pylibcugraph as plc + +from nx_cugraph.convert import _to_graph +from nx_cugraph.utils import ( + _dtype_param, + _get_float_dtype, + networkx_algorithm, + not_implemented_for, +) + +__all__ = ["eigenvector_centrality"] + + +@not_implemented_for("multigraph") +@networkx_algorithm(extra_params=_dtype_param) +def eigenvector_centrality( + G, max_iter=100, tol=1.0e-6, nstart=None, weight=None, *, dtype=None +): + """`nstart` parameter is not used, but it is checked for validity.""" + G = _to_graph(G, weight, np.float32) + if len(G) == 0: + raise nx.NetworkXPointlessConcept( + "cannot compute centrality for the null graph" + ) + if dtype is not None: + dtype = _get_float_dtype(dtype) + elif weight in G.edge_values: + dtype = _get_float_dtype(G.edge_values[weight].dtype) + else: + dtype = np.float32 + if nstart is not None: + # Check if given nstart is valid even though we don't use it + nstart = G._dict_to_nodearray(nstart, dtype=dtype) + if (nstart == 0).all(): + raise nx.NetworkXError("initial vector cannot have all zero values") + if nstart.sum() == 0: + raise ZeroDivisionError + # nstart /= total # Uncomment (and assign total) when nstart is used below + try: + node_ids, values = plc.eigenvector_centrality( + resource_handle=plc.ResourceHandle(), + graph=G._get_plc_graph(weight, 1, dtype, store_transposed=True), + epsilon=tol, + max_iterations=max_iter, + do_expensive_check=False, + ) + except RuntimeError as exc: + # Errors from PLC are sometimes a little scary and not very helpful + raise nx.PowerIterationFailedConvergence(max_iter) from exc + return G._nodearrays_to_dict(node_ids, values) diff --git a/python/nx-cugraph/nx_cugraph/algorithms/centrality/katz.py b/python/nx-cugraph/nx_cugraph/algorithms/centrality/katz.py new file mode 100644 index 00000000000..b61b811b8fa --- /dev/null +++ b/python/nx-cugraph/nx_cugraph/algorithms/centrality/katz.py @@ -0,0 +1,100 @@ +# Copyright (c) 2023, NVIDIA CORPORATION. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import networkx as nx +import numpy as np +import pylibcugraph as plc + +from nx_cugraph.convert import _to_graph +from nx_cugraph.utils import ( + _dtype_param, + _get_float_dtype, + networkx_algorithm, + not_implemented_for, +) + +__all__ = ["katz_centrality"] + + +@not_implemented_for("multigraph") +@networkx_algorithm(extra_params=_dtype_param) +def katz_centrality( + G, + alpha=0.1, + beta=1.0, + max_iter=1000, + tol=1.0e-6, + nstart=None, + normalized=True, + weight=None, + *, + dtype=None, +): + """`nstart` isn't used (but is checked), and `normalized=False` is not supported.""" + if not normalized: + # Redundant with the `_can_run` check below when being dispatched by NetworkX, + # but we raise here in case this funcion is called directly. + raise NotImplementedError("normalized=False is not supported.") + G = _to_graph(G, weight, np.float32) + if (N := len(G)) == 0: + return {} + if dtype is not None: + dtype = _get_float_dtype(dtype) + elif weight in G.edge_values: + dtype = _get_float_dtype(G.edge_values[weight].dtype) + else: + dtype = np.float32 + if nstart is not None: + # Check if given nstart is valid even though we don't use it + nstart = G._dict_to_nodearray(nstart, 0, dtype) + b = bs = None + try: + b = float(beta) + except (TypeError, ValueError) as exc: + try: + bs = G._dict_to_nodearray(beta, dtype=dtype) + b = 1.0 # float value must be given to PLC (and will be ignored) + except (KeyError, ValueError): + raise nx.NetworkXError( + "beta dictionary must have a value for every node" + ) from exc + try: + node_ids, values = plc.katz_centrality( + resource_handle=plc.ResourceHandle(), + graph=G._get_plc_graph(weight, 1, dtype, store_transposed=True), + betas=bs, + alpha=alpha, + beta=b, + epsilon=N * tol, + max_iterations=max_iter, + do_expensive_check=False, + ) + except RuntimeError as exc: + # Errors from PLC are sometimes a little scary and not very helpful + raise nx.PowerIterationFailedConvergence(max_iter) from exc + return G._nodearrays_to_dict(node_ids, values) + + +@katz_centrality._can_run +def _( + G, + alpha=0.1, + beta=1.0, + max_iter=1000, + tol=1.0e-6, + nstart=None, + normalized=True, + weight=None, + *, + dtype=None, +): + return normalized diff --git a/python/nx-cugraph/nx_cugraph/algorithms/community/louvain.py b/python/nx-cugraph/nx_cugraph/algorithms/community/louvain.py index 45a3429d2ee..936d837dacd 100644 --- a/python/nx-cugraph/nx_cugraph/algorithms/community/louvain.py +++ b/python/nx-cugraph/nx_cugraph/algorithms/community/louvain.py @@ -16,6 +16,7 @@ from nx_cugraph.convert import _to_undirected_graph from nx_cugraph.utils import ( + _dtype_param, _groupby, _seed_to_int, networkx_algorithm, @@ -32,11 +33,19 @@ extra_params={ "max_level : int, optional": ( "Upper limit of the number of macro-iterations (max: 500)." - ) + ), + **_dtype_param, } ) def louvain_communities( - G, weight="weight", resolution=1, threshold=0.0000001, seed=None, *, max_level=None + G, + weight="weight", + resolution=1, + threshold=0.0000001, + seed=None, + *, + max_level=None, + dtype=None, ): """`seed` parameter is currently ignored.""" # NetworkX allows both directed and undirected, but cugraph only allows undirected. @@ -54,20 +63,20 @@ def louvain_communities( stacklevel=2, ) max_level = 500 - vertices, clusters, modularity = plc.louvain( + node_ids, clusters, modularity = plc.louvain( resource_handle=plc.ResourceHandle(), - graph=G._get_plc_graph(), + graph=G._get_plc_graph(weight, 1, dtype), max_level=max_level, # TODO: add this parameter to NetworkX threshold=threshold, resolution=resolution, do_expensive_check=False, ) - groups = _groupby(clusters, vertices, groups_are_canonical=True) - rv = [set(G._nodearray_to_list(node_ids)) for node_ids in groups.values()] - # TODO: PLC doesn't handle isolated vertices yet, so this is a temporary fix + groups = _groupby(clusters, node_ids, groups_are_canonical=True) + rv = [set(G._nodearray_to_list(ids)) for ids in groups.values()] + # TODO: PLC doesn't handle isolated node_ids yet, so this is a temporary fix isolates = _isolates(G) if isolates.size > 0: - isolates = isolates[isolates > vertices.max()] + isolates = isolates[isolates > node_ids.max()] if isolates.size > 0: rv.extend({node} for node in G._nodearray_to_list(isolates)) return rv @@ -75,7 +84,14 @@ def louvain_communities( @louvain_communities._can_run def _( - G, weight="weight", resolution=1, threshold=0.0000001, seed=None, *, max_level=None + G, + weight="weight", + resolution=1, + threshold=0.0000001, + seed=None, + *, + max_level=None, + dtype=None, ): # NetworkX allows both directed and undirected, but cugraph only allows undirected. return not G.is_directed() diff --git a/python/nx-cugraph/nx_cugraph/algorithms/link_analysis/__init__.py b/python/nx-cugraph/nx_cugraph/algorithms/link_analysis/__init__.py new file mode 100644 index 00000000000..a68d6940d02 --- /dev/null +++ b/python/nx-cugraph/nx_cugraph/algorithms/link_analysis/__init__.py @@ -0,0 +1,14 @@ +# Copyright (c) 2023, NVIDIA CORPORATION. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +from .hits_alg import * +from .pagerank_alg import * diff --git a/python/nx-cugraph/nx_cugraph/algorithms/link_analysis/hits_alg.py b/python/nx-cugraph/nx_cugraph/algorithms/link_analysis/hits_alg.py new file mode 100644 index 00000000000..1c8a47c24b1 --- /dev/null +++ b/python/nx-cugraph/nx_cugraph/algorithms/link_analysis/hits_alg.py @@ -0,0 +1,81 @@ +# Copyright (c) 2023, NVIDIA CORPORATION. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import cupy as cp +import networkx as nx +import numpy as np +import pylibcugraph as plc + +from nx_cugraph.convert import _to_graph +from nx_cugraph.utils import ( + _dtype_param, + _get_float_dtype, + index_dtype, + networkx_algorithm, +) + +__all__ = ["hits"] + + +@networkx_algorithm( + extra_params={ + 'weight : string or None, optional (default="weight")': ( + "The edge attribute to use as the edge weight." + ), + **_dtype_param, + } +) +def hits( + G, + max_iter=100, + tol=1.0e-8, + nstart=None, + normalized=True, + *, + weight="weight", + dtype=None, +): + G = _to_graph(G, weight, np.float32) + if (N := len(G)) == 0: + return {}, {} + if dtype is not None: + dtype = _get_float_dtype(dtype) + elif weight in G.edge_values: + dtype = _get_float_dtype(G.edge_values[weight].dtype) + else: + dtype = np.float32 + if nstart is not None: + nstart = G._dict_to_nodearray(nstart, 0, dtype) + if max_iter <= 0: + if nx.__version__[:3] <= "3.2": + raise ValueError("`maxiter` must be a positive integer.") + raise nx.PowerIterationFailedConvergence(max_iter) + try: + node_ids, hubs, authorities = plc.hits( + resource_handle=plc.ResourceHandle(), + graph=G._get_plc_graph(weight, 1, dtype, store_transposed=True), + tol=tol, + initial_hubs_guess_vertices=None + if nstart is None + else cp.arange(N, dtype=index_dtype), + initial_hubs_guess_values=nstart, + max_iter=max_iter, + normalized=normalized, + do_expensive_check=False, + ) + except RuntimeError as exc: + # Errors from PLC are sometimes a little scary and not very helpful + raise nx.PowerIterationFailedConvergence(max_iter) from exc + return ( + G._nodearrays_to_dict(node_ids, hubs), + G._nodearrays_to_dict(node_ids, authorities), + ) diff --git a/python/nx-cugraph/nx_cugraph/algorithms/link_analysis/pagerank_alg.py b/python/nx-cugraph/nx_cugraph/algorithms/link_analysis/pagerank_alg.py new file mode 100644 index 00000000000..63f6e89c33a --- /dev/null +++ b/python/nx-cugraph/nx_cugraph/algorithms/link_analysis/pagerank_alg.py @@ -0,0 +1,112 @@ +# Copyright (c) 2023, NVIDIA CORPORATION. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import cupy as cp +import networkx as nx +import numpy as np +import pylibcugraph as plc + +from nx_cugraph.convert import _to_graph +from nx_cugraph.utils import ( + _dtype_param, + _get_float_dtype, + index_dtype, + networkx_algorithm, +) + +__all__ = ["pagerank"] + + +@networkx_algorithm(extra_params=_dtype_param) +def pagerank( + G, + alpha=0.85, + personalization=None, + max_iter=100, + tol=1.0e-6, + nstart=None, + weight="weight", + dangling=None, + *, + dtype=None, +): + """`dangling` parameter is not supported, but it is checked for validity.""" + G = _to_graph(G, weight, 1, np.float32) + if (N := len(G)) == 0: + return {} + if dtype is not None: + dtype = _get_float_dtype(dtype) + elif weight in G.edge_values: + dtype = _get_float_dtype(G.edge_values[weight].dtype) + else: + dtype = np.float32 + if nstart is not None: + nstart = G._dict_to_nodearray(nstart, 0, dtype=dtype) + if (total := nstart.sum()) == 0: + raise ZeroDivisionError + nstart /= total + if personalization is not None: + personalization = G._dict_to_nodearray(personalization, 0, dtype=dtype) + if (total := personalization.sum()) == 0: + raise ZeroDivisionError + personalization /= total + if dangling is not None: + # Check if given dangling is valid even though we don't use it + dangling = G._dict_to_nodearray(dangling, 0) # Check validity + if dangling.sum() == 0: + raise ZeroDivisionError + if (G._out_degrees_array() == 0).any(): + raise NotImplementedError("custom dangling weights is not supported") + if max_iter <= 0: + raise nx.PowerIterationFailedConvergence(max_iter) + kwargs = { + "resource_handle": plc.ResourceHandle(), + "graph": G._get_plc_graph(weight, 1, dtype, store_transposed=True), + "precomputed_vertex_out_weight_vertices": None, + "precomputed_vertex_out_weight_sums": None, + "initial_guess_vertices": None + if nstart is None + else cp.arange(N, dtype=index_dtype), + "initial_guess_values": nstart, + "alpha": alpha, + "epsilon": N * tol, + "max_iterations": max_iter, + "do_expensive_check": False, + "fail_on_nonconvergence": False, + } + if personalization is None: + node_ids, values, is_converged = plc.pagerank(**kwargs) + else: + node_ids, values, is_converged = plc.personalized_pagerank( + personalization_vertices=cp.arange(N, dtype=index_dtype), # Why? + personalization_values=personalization, + **kwargs, + ) + if not is_converged: + raise nx.PowerIterationFailedConvergence(max_iter) + return G._nodearrays_to_dict(node_ids, values) + + +@pagerank._can_run +def pagerank( + G, + alpha=0.85, + personalization=None, + max_iter=100, + tol=1.0e-6, + nstart=None, + weight="weight", + dangling=None, + *, + dtype=None, +): + return dangling is None diff --git a/python/nx-cugraph/nx_cugraph/classes/graph.py b/python/nx-cugraph/nx_cugraph/classes/graph.py index fea318e036e..e32f93d8bfe 100644 --- a/python/nx-cugraph/nx_cugraph/classes/graph.py +++ b/python/nx-cugraph/nx_cugraph/classes/graph.py @@ -79,7 +79,7 @@ class Graph: np.dtype(np.uint32): np.dtype(np.float64), np.dtype(np.uint64): np.dtype(np.float64), # raise if x > 2**53 # other - np.dtype(np.bool_): np.dtype(np.float16), + np.dtype(np.bool_): np.dtype(np.float32), np.dtype(np.float16): np.dtype(np.float32), } _plc_allowed_edge_types: ClassVar[set[np.dtype]] = { @@ -562,12 +562,13 @@ def _get_plc_graph( switch_indices: bool = False, edge_array: cp.ndarray[EdgeValue] | None = None, ): - if edge_array is not None: + if edge_array is not None or edge_attr is None: pass - elif edge_attr is None: - edge_array = None elif edge_attr not in self.edge_values: - raise KeyError("Graph has no edge attribute {edge_attr!r}") + if edge_default is None: + raise KeyError("Graph has no edge attribute {edge_attr!r}") + # If we were given a default edge value, then it's probably okay to + # use None for the edge_array if we don't have this edge attribute. elif edge_attr not in self.edge_masks: edge_array = self.edge_values[edge_attr] elif not self.edge_masks[edge_attr].all(): @@ -685,6 +686,9 @@ def _degrees_array(self): degrees += cp.bincount(self.dst_indices, minlength=self._N) return degrees + _in_degrees_array = _degrees_array + _out_degrees_array = _degrees_array + # Data conversions def _nodeiter_to_iter(self, node_ids: Iterable[IndexValue]) -> Iterable[NodeKey]: """Convert an iterable of node IDs to an iterable of node keys.""" @@ -748,20 +752,22 @@ def _dict_to_nodearrays( values = cp.fromiter(d.values(), dtype) return node_ids, values - # def _dict_to_nodearray( - # self, - # d: dict[NodeKey, NodeValue] | cp.ndarray[NodeValue], - # default: NodeValue | None = None, - # dtype: Dtype | None = None, - # ) -> cp.ndarray[NodeValue]: - # if isinstance(d, cp.ndarray): - # if d.shape[0] != len(self): - # raise ValueError - # return d - # if default is None: - # val_iter = map(d.__getitem__, self) - # else: - # val_iter = (d.get(node, default) for node in self) - # if dtype is None: - # return cp.array(list(val_iter)) - # return cp.fromiter(val_iter, dtype) + def _dict_to_nodearray( + self, + d: dict[NodeKey, NodeValue] | cp.ndarray[NodeValue], + default: NodeValue | None = None, + dtype: Dtype | None = None, + ) -> cp.ndarray[NodeValue]: + if isinstance(d, cp.ndarray): + if d.shape[0] != len(self): + raise ValueError + if dtype is not None and d.dtype != dtype: + return d.astype(dtype) + return d + if default is None: + val_iter = map(d.__getitem__, self) + else: + val_iter = (d.get(node, default) for node in self) + if dtype is None: + return cp.array(list(val_iter)) + return cp.fromiter(val_iter, dtype) diff --git a/python/nx-cugraph/nx_cugraph/interface.py b/python/nx-cugraph/nx_cugraph/interface.py index 8903fdc541e..be6b3596030 100644 --- a/python/nx-cugraph/nx_cugraph/interface.py +++ b/python/nx-cugraph/nx_cugraph/interface.py @@ -72,12 +72,29 @@ def key(testpath): from packaging.version import parse nxver = parse(nx.__version__) - if nxver.major == 3 and nxver.minor in {0, 1}: + + if nxver.major == 3 and nxver.minor <= 2: + # Networkx versions prior to 3.2.1 have tests written to expect + # sp.sparse.linalg.ArpackNoConvergence exceptions raised on no + # convergence in HITS. Newer versions since the merge of + # https://github.com/networkx/networkx/pull/7084 expect + # nx.PowerIterationFailedConvergence, which is what nx_cugraph.hits + # raises, so we mark them as xfail for previous versions of NX. + xfail.update( + { + key( + "test_hits.py:TestHITS.test_hits_not_convergent" + ): "nx_cugraph.hits raises updated exceptions not caught in " + "these tests", + } + ) + + if nxver.major == 3 and nxver.minor <= 1: # MAINT: networkx 3.0, 3.1 # NetworkX 3.2 added the ability to "fallback to nx" if backend algorithms # raise NotImplementedError or `can_run` returns False. The tests below # exercise behavior we have not implemented yet, so we mark them as xfail - # for previous versions of NetworkX. + # for previous versions of NX. xfail.update( { key( diff --git a/python/nx-cugraph/nx_cugraph/tests/test_ktruss.py b/python/nx-cugraph/nx_cugraph/tests/test_ktruss.py index a3e4cee3124..92fe2360688 100644 --- a/python/nx-cugraph/nx_cugraph/tests/test_ktruss.py +++ b/python/nx-cugraph/nx_cugraph/tests/test_ktruss.py @@ -22,7 +22,7 @@ def test_k_truss(get_graph): Gnx = get_graph() Gcg = nxcg.from_networkx(Gnx, preserve_all_attrs=True) - for k in range(10): + for k in range(6): Hnx = nx.k_truss(Gnx, k) Hcg = nxcg.k_truss(Gcg, k) assert nx.utils.graphs_equal(Hnx, nxcg.to_networkx(Hcg)) diff --git a/python/nx-cugraph/nx_cugraph/utils/misc.py b/python/nx-cugraph/nx_cugraph/utils/misc.py index 26f023bdcec..e303375918d 100644 --- a/python/nx-cugraph/nx_cugraph/utils/misc.py +++ b/python/nx-cugraph/nx_cugraph/utils/misc.py @@ -16,11 +16,14 @@ import operator as op import sys from random import Random -from typing import SupportsIndex +from typing import TYPE_CHECKING, SupportsIndex import cupy as cp import numpy as np +if TYPE_CHECKING: + from ..typing import Dtype + try: from itertools import pairwise # Python >=3.10 except ImportError: @@ -33,11 +36,26 @@ def pairwise(it): prev = cur -__all__ = ["index_dtype", "_groupby", "_seed_to_int", "_get_int_dtype"] +__all__ = [ + "index_dtype", + "_groupby", + "_seed_to_int", + "_get_int_dtype", + "_get_float_dtype", + "_dtype_param", +] # This may switch to np.uint32 at some point index_dtype = np.int32 +# To add to `extra_params=` of `networkx_algorithm` +_dtype_param = { + "dtype : dtype or None, optional": ( + "The data type (np.float32, np.float64, or None) to use for the edge weights " + "in the algorithm. If None, then dtype is determined by the edge values." + ), +} + def _groupby( groups: cp.ndarray, values: cp.ndarray, groups_are_canonical: bool = False @@ -144,3 +162,15 @@ def _get_int_dtype( return np.dtype(dtype_string) except TypeError as exc: raise ValueError("Value is too large to store as integer: {val}") from exc + + +def _get_float_dtype(dtype: Dtype): + """Promote dtype to float32 or float64 as appropriate.""" + if dtype is None: + return np.dtype(np.float32) + rv = np.promote_types(dtype, np.float32) + if np.float32 != rv != np.float64: + raise TypeError( + f"Dtype {dtype} cannot be safely promoted to float32 or float64" + ) + return rv From 4b1618b7c6e474420e8ad07cefebb45064476475 Mon Sep 17 00:00:00 2001 From: Rick Ratzel <3039903+rlratzel@users.noreply.github.com> Date: Wed, 22 Nov 2023 12:12:50 -0600 Subject: [PATCH 098/111] Adds missing connected_components algo to table. (#4019) --- python/nx-cugraph/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/python/nx-cugraph/README.md b/python/nx-cugraph/README.md index 273a6112d77..f6a9aac1088 100644 --- a/python/nx-cugraph/README.md +++ b/python/nx-cugraph/README.md @@ -101,6 +101,7 @@ supported in nx-cugraph. | balanced_cut_clustering | ? | | betweenness_centrality | 23.10 | | bfs | ? | +| connected_components | 23.12 | | core_number | ? | | degree_centrality | 23.12 | | ecg | ? | From 3116eed763acad5808bd355d66406a3a630947f6 Mon Sep 17 00:00:00 2001 From: Joseph Nke <76006812+jnke2016@users.noreply.github.com> Date: Sun, 26 Nov 2023 21:24:19 -0600 Subject: [PATCH 099/111] Moves more MG graph ETL to libcugraph and re-enables MG tests in CI (#3941) This PR includes changes that moves some of the MG graph etl steps (such as computing number of edges) to libcugraph to reduce the amount of dask overhead involved in graph creation. Those ETL steps were also responsible for various dask-related transient errors that caused us to temporarily disable MG testing in CI. These changes allow us to re-enable MG testing in CI, so this PR includes that update too. Authors: - Joseph Nke (https://github.com/jnke2016) - Chuck Hastings (https://github.com/ChuckHastings) - Naim (https://github.com/naimnv) - Vibhu Jawa (https://github.com/VibhuJawa) - Rick Ratzel (https://github.com/rlratzel) Approvers: - Jake Awe (https://github.com/AyodeAwe) - Chuck Hastings (https://github.com/ChuckHastings) - Rick Ratzel (https://github.com/rlratzel) URL: https://github.com/rapidsai/cugraph/pull/3941 --- ci/test_python.sh | 2 +- ci/test_wheel.sh | 2 +- cpp/include/cugraph_c/graph.h | 3 +- .../cugraph/cugraph/dask/community/egonet.py | 14 +- .../dask/community/induced_subgraph.py | 19 +- .../cugraph/cugraph/dask/community/leiden.py | 16 +- .../cugraph/cugraph/dask/community/louvain.py | 16 +- .../cugraph/dask/link_analysis/pagerank.py | 17 +- .../cugraph/dask/sampling/random_walks.py | 18 +- python/cugraph/cugraph/dask/traversal/bfs.py | 15 +- .../simpleDistributedGraph.py | 130 +++++---- .../cugraph/cugraph/structure/symmetrize.py | 6 +- python/cugraph/cugraph/testing/mg_utils.py | 2 + python/cugraph/cugraph/tests/conftest.py | 7 +- .../tests/link_analysis/test_hits_mg.py | 2 +- .../cugraph/tests/structure/test_graph_mg.py | 12 +- .../pylibcugraph/_cugraph_c/graph.pxd | 62 +++- .../analyze_clustering_edge_cut.pyx | 2 +- .../analyze_clustering_modularity.pyx | 2 +- .../analyze_clustering_ratio_cut.pyx | 2 +- .../pylibcugraph/balanced_cut_clustering.pyx | 2 +- python/pylibcugraph/pylibcugraph/ecg.pyx | 2 +- .../edge_betweenness_centrality.pyx | 2 +- python/pylibcugraph/pylibcugraph/egonet.pyx | 2 +- .../pylibcugraph/eigenvector_centrality.pyx | 4 +- python/pylibcugraph/pylibcugraph/graphs.pxd | 6 +- python/pylibcugraph/pylibcugraph/graphs.pyx | 267 ++++++++++++------ .../pylibcugraph/induced_subgraph.pyx | 2 +- .../pylibcugraph/k_truss_subgraph.pyx | 2 +- python/pylibcugraph/pylibcugraph/leiden.pyx | 2 +- python/pylibcugraph/pylibcugraph/louvain.pyx | 2 +- python/pylibcugraph/pylibcugraph/node2vec.pyx | 2 +- python/pylibcugraph/pylibcugraph/pagerank.pyx | 2 +- .../pylibcugraph/personalized_pagerank.pyx | 2 +- .../spectral_modularity_maximization.pyx | 2 +- python/pylibcugraph/pylibcugraph/sssp.pyx | 4 +- .../pylibcugraph/tests/conftest.py | 12 +- .../tests/test_eigenvector_centrality.py | 12 +- .../pylibcugraph/tests/test_graph_sg.py | 25 +- .../tests/test_katz_centrality.py | 12 +- .../pylibcugraph/tests/test_louvain.py | 20 +- .../pylibcugraph/tests/test_node2vec.py | 22 +- .../pylibcugraph/tests/test_pagerank.py | 2 +- .../pylibcugraph/tests/test_sssp.py | 2 +- .../pylibcugraph/tests/test_triangle_count.py | 22 +- .../tests/test_uniform_neighbor_sample.py | 30 +- .../pylibcugraph/tests/test_utils.py | 2 +- .../pylibcugraph/uniform_random_walks.pyx | 2 +- .../weakly_connected_components.pyx | 2 +- 49 files changed, 521 insertions(+), 298 deletions(-) diff --git a/ci/test_python.sh b/ci/test_python.sh index 273d3c93482..d6e92e8d1a5 100755 --- a/ci/test_python.sh +++ b/ci/test_python.sh @@ -79,7 +79,7 @@ pytest \ --cov=cugraph \ --cov-report=xml:"${RAPIDS_COVERAGE_DIR}/cugraph-coverage.xml" \ --cov-report=term \ - -k "not _mg" \ + -k "not test_property_graph_mg" \ tests popd diff --git a/ci/test_wheel.sh b/ci/test_wheel.sh index 28f59f0209e..428efd4ed21 100755 --- a/ci/test_wheel.sh +++ b/ci/test_wheel.sh @@ -26,5 +26,5 @@ else DASK_DISTRIBUTED__SCHEDULER__WORKER_TTL="1000s" \ DASK_DISTRIBUTED__COMM__TIMEOUTS__CONNECT="1000s" \ DASK_CUDA_WAIT_WORKERS_MIN_TIMEOUT="1000s" \ - python -m pytest -k "not _mg" ./python/${package_name}/${python_package_name}/tests + python -m pytest ./python/${package_name}/${python_package_name}/tests fi diff --git a/cpp/include/cugraph_c/graph.h b/cpp/include/cugraph_c/graph.h index 88176a9c1b6..00fce0493a3 100644 --- a/cpp/include/cugraph_c/graph.h +++ b/cpp/include/cugraph_c/graph.h @@ -103,7 +103,8 @@ cugraph_error_code_t cugraph_sg_graph_create( * Note that setting this flag will arbitrarily select one instance of a multi edge to be the * edge that survives. If the edges have properties that should be honored (e.g. sum the weights, - * or take the maximum weight), the caller should do that on not rely on this flag. + * or take the maximum weight), the caller should remove specific edges themselves and not rely + * on this flag. * @param [in] do_expensive_check If true, do expensive checks to validate the input data * is consistent with software assumptions. If false bypass these checks. * @param [out] graph A pointer to the graph object diff --git a/python/cugraph/cugraph/dask/community/egonet.py b/python/cugraph/cugraph/dask/community/egonet.py index 06f5d5b9a79..e49d4777cef 100644 --- a/python/cugraph/cugraph/dask/community/egonet.py +++ b/python/cugraph/cugraph/dask/community/egonet.py @@ -18,7 +18,9 @@ import cugraph.dask.comms.comms as Comms import dask_cudf import cudf -from cugraph.dask.common.input_utils import get_distributed_data +from cugraph.dask.common.part_utils import ( + persist_dask_df_equal_parts_per_worker, +) from pylibcugraph import ResourceHandle, ego_graph as pylibcugraph_ego_graph @@ -135,11 +137,7 @@ def ego_graph(input_graph, n, radius=1, center=True): n = dask_cudf.from_cudf(n, npartitions=min(input_graph._npartitions, len(n))) n = n.astype(n_type) - n = get_distributed_data(n) - wait(n) - - n = n.worker_to_parts - + n = persist_dask_df_equal_parts_per_worker(n, client, return_type="dict") do_expensive_check = False result = [ @@ -147,13 +145,13 @@ def ego_graph(input_graph, n, radius=1, center=True): _call_ego_graph, Comms.get_session_id(), input_graph._plc_graph[w], - n[w][0], + n_[0] if n_ else cudf.Series(dtype=n_type), radius, do_expensive_check, workers=[w], allow_other_workers=False, ) - for w in Comms.get_workers() + for w, n_ in n.items() ] wait(result) diff --git a/python/cugraph/cugraph/dask/community/induced_subgraph.py b/python/cugraph/cugraph/dask/community/induced_subgraph.py index 5d902f667a4..d079bcaf653 100644 --- a/python/cugraph/cugraph/dask/community/induced_subgraph.py +++ b/python/cugraph/cugraph/dask/community/induced_subgraph.py @@ -19,7 +19,9 @@ import dask_cudf import cudf import cupy as cp -from cugraph.dask.common.input_utils import get_distributed_data +from cugraph.dask.common.part_utils import ( + persist_dask_df_equal_parts_per_worker, +) from typing import Union, Tuple from pylibcugraph import ( @@ -154,15 +156,12 @@ def induced_subgraph( vertices_type = input_graph.input_df.dtypes[0] if isinstance(vertices, (cudf.Series, cudf.DataFrame)): - vertices = dask_cudf.from_cudf( - vertices, npartitions=min(input_graph._npartitions, len(vertices)) - ) + vertices = dask_cudf.from_cudf(vertices, npartitions=input_graph._npartitions) vertices = vertices.astype(vertices_type) - vertices = get_distributed_data(vertices) - wait(vertices) - - vertices = vertices.worker_to_parts + vertices = persist_dask_df_equal_parts_per_worker( + vertices, client, return_type="dict" + ) do_expensive_check = False @@ -171,13 +170,13 @@ def induced_subgraph( _call_induced_subgraph, Comms.get_session_id(), input_graph._plc_graph[w], - vertices[w][0], + vertices_[0] if vertices_ else cudf.Series(dtype=vertices_type), offsets, do_expensive_check, workers=[w], allow_other_workers=False, ) - for w in Comms.get_workers() + for w, vertices_ in vertices.items() ] wait(result) diff --git a/python/cugraph/cugraph/dask/community/leiden.py b/python/cugraph/cugraph/dask/community/leiden.py index 67bd0876ce6..10a266ed519 100644 --- a/python/cugraph/cugraph/dask/community/leiden.py +++ b/python/cugraph/cugraph/dask/community/leiden.py @@ -125,9 +125,19 @@ def leiden( Examples -------- - >>> from cugraph.datasets import karate - >>> G = karate.get_graph(fetch=True) - >>> parts, modularity_score = cugraph.leiden(G) + >>> import cugraph.dask as dcg + >>> import dask_cudf + >>> # ... Init a DASK Cluster + >>> # see https://docs.rapids.ai/api/cugraph/stable/dask-cugraph.html + >>> # Download dataset from https://github.com/rapidsai/cugraph/datasets/.. + >>> chunksize = dcg.get_chunksize(datasets_path / "karate.csv") + >>> ddf = dask_cudf.read_csv(datasets_path / "karate.csv", + ... chunksize=chunksize, delimiter=" ", + ... names=["src", "dst", "value"], + ... dtype=["int32", "int32", "float32"]) + >>> dg = cugraph.Graph() + >>> dg.from_dask_cudf_edgelist(ddf, source='src', destination='dst') + >>> parts, modularity_score = dcg.leiden(dg) """ diff --git a/python/cugraph/cugraph/dask/community/louvain.py b/python/cugraph/cugraph/dask/community/louvain.py index 1b091817a1a..e83d41811ea 100644 --- a/python/cugraph/cugraph/dask/community/louvain.py +++ b/python/cugraph/cugraph/dask/community/louvain.py @@ -129,9 +129,19 @@ def louvain( Examples -------- - >>> from cugraph.datasets import karate - >>> G = karate.get_graph(fetch=True) - >>> parts = cugraph.louvain(G) + >>> import cugraph.dask as dcg + >>> import dask_cudf + >>> # ... Init a DASK Cluster + >>> # see https://docs.rapids.ai/api/cugraph/stable/dask-cugraph.html + >>> # Download dataset from https://github.com/rapidsai/cugraph/datasets/.. + >>> chunksize = dcg.get_chunksize(datasets_path / "karate.csv") + >>> ddf = dask_cudf.read_csv(datasets_path / "karate.csv", + ... chunksize=chunksize, delimiter=" ", + ... names=["src", "dst", "value"], + ... dtype=["int32", "int32", "float32"]) + >>> dg = cugraph.Graph() + >>> dg.from_dask_cudf_edgelist(ddf, source='src', destination='dst') + >>> parts, modularity_score = dcg.louvain(dg) """ diff --git a/python/cugraph/cugraph/dask/link_analysis/pagerank.py b/python/cugraph/cugraph/dask/link_analysis/pagerank.py index 2dfd25fa522..1dffb3cba78 100644 --- a/python/cugraph/cugraph/dask/link_analysis/pagerank.py +++ b/python/cugraph/cugraph/dask/link_analysis/pagerank.py @@ -28,7 +28,9 @@ ) import cugraph.dask.comms.comms as Comms -from cugraph.dask.common.input_utils import get_distributed_data +from cugraph.dask.common.part_utils import ( + persist_dask_df_equal_parts_per_worker, +) from cugraph.exceptions import FailedToConvergeError @@ -352,7 +354,14 @@ def pagerank( personalization, npartitions=len(Comms.get_workers()) ) - data_prsztn = get_distributed_data(personalization_ddf) + data_prsztn = persist_dask_df_equal_parts_per_worker( + personalization_ddf, client, return_type="dict" + ) + + empty_df = cudf.DataFrame(columns=list(personalization_ddf.columns)) + empty_df = empty_df.astype( + dict(zip(personalization_ddf.columns, personalization_ddf.dtypes)) + ) result = [ client.submit( @@ -361,7 +370,7 @@ def pagerank( input_graph._plc_graph[w], precomputed_vertex_out_weight_vertices, precomputed_vertex_out_weight_sums, - data_personalization[0], + data_personalization[0] if data_personalization else empty_df, initial_guess_vertices, initial_guess_values, alpha, @@ -372,7 +381,7 @@ def pagerank( workers=[w], allow_other_workers=False, ) - for w, data_personalization in data_prsztn.worker_to_parts.items() + for w, data_personalization in data_prsztn.items() ] else: result = [ diff --git a/python/cugraph/cugraph/dask/sampling/random_walks.py b/python/cugraph/cugraph/dask/sampling/random_walks.py index 993544ac45c..bb9baf2c92c 100644 --- a/python/cugraph/cugraph/dask/sampling/random_walks.py +++ b/python/cugraph/cugraph/dask/sampling/random_walks.py @@ -16,6 +16,9 @@ import dask_cudf import cudf import operator as op +from cugraph.dask.common.part_utils import ( + persist_dask_df_equal_parts_per_worker, +) from pylibcugraph import ResourceHandle @@ -24,7 +27,6 @@ ) from cugraph.dask.comms import comms as Comms -from cugraph.dask.common.input_utils import get_distributed_data def convert_to_cudf(cp_paths, number_map=None, is_vertex_paths=False): @@ -104,7 +106,7 @@ def random_walks( max_path_length : int The maximum path length """ - + client = default_client() if isinstance(start_vertices, int): start_vertices = [start_vertices] @@ -126,23 +128,21 @@ def random_walks( start_vertices, npartitions=min(input_graph._npartitions, len(start_vertices)) ) start_vertices = start_vertices.astype(start_vertices_type) - start_vertices = get_distributed_data(start_vertices) - wait(start_vertices) - start_vertices = start_vertices.worker_to_parts - - client = default_client() + start_vertices = persist_dask_df_equal_parts_per_worker( + start_vertices, client, return_type="dict" + ) result = [ client.submit( _call_plc_uniform_random_walks, Comms.get_session_id(), input_graph._plc_graph[w], - start_vertices[w][0], + start_v[0] if start_v else cudf.Series(dtype=start_vertices_type), max_depth, workers=[w], allow_other_workers=False, ) - for w in Comms.get_workers() + for w, start_v in start_vertices.items() ] wait(result) diff --git a/python/cugraph/cugraph/dask/traversal/bfs.py b/python/cugraph/cugraph/dask/traversal/bfs.py index cf467aaa18f..412fd851ad6 100644 --- a/python/cugraph/cugraph/dask/traversal/bfs.py +++ b/python/cugraph/cugraph/dask/traversal/bfs.py @@ -16,7 +16,9 @@ from pylibcugraph import ResourceHandle, bfs as pylibcugraph_bfs from dask.distributed import wait, default_client -from cugraph.dask.common.input_utils import get_distributed_data +from cugraph.dask.common.part_utils import ( + persist_dask_df_equal_parts_per_worker, +) import cugraph.dask.comms.comms as Comms import cudf import dask_cudf @@ -159,8 +161,13 @@ def bfs(input_graph, start, depth_limit=None, return_distances=True, check_start tmp_col_names = None start = input_graph.lookup_internal_vertex_id(start, tmp_col_names) + vertex_dtype = start.dtype # if the edgelist was renumbered, update + # the vertex type accordingly + + data_start = persist_dask_df_equal_parts_per_worker( + start, client, return_type="dict" + ) - data_start = get_distributed_data(start) do_expensive_check = False # FIXME: Why is 'direction_optimizing' not part of the python cugraph API # and why is it set to 'False' by default @@ -171,7 +178,7 @@ def bfs(input_graph, start, depth_limit=None, return_distances=True, check_start _call_plc_bfs, Comms.get_session_id(), input_graph._plc_graph[w], - st[0], + st[0] if st else cudf.Series(dtype=vertex_dtype), depth_limit, direction_optimizing, return_distances, @@ -179,7 +186,7 @@ def bfs(input_graph, start, depth_limit=None, return_distances=True, check_start workers=[w], allow_other_workers=False, ) - for w, st in data_start.worker_to_parts.items() + for w, st in data_start.items() ] wait(cupy_result) diff --git a/python/cugraph/cugraph/structure/graph_implementation/simpleDistributedGraph.py b/python/cugraph/cugraph/structure/graph_implementation/simpleDistributedGraph.py index 935d0c597d4..f666900b226 100644 --- a/python/cugraph/cugraph/structure/graph_implementation/simpleDistributedGraph.py +++ b/python/cugraph/cugraph/structure/graph_implementation/simpleDistributedGraph.py @@ -36,8 +36,8 @@ from cugraph.structure.symmetrize import symmetrize from cugraph.dask.common.part_utils import ( get_persisted_df_worker_map, - get_length_of_parts, persist_dask_df_equal_parts_per_worker, + _chunk_lst, ) from cugraph.dask import get_n_workers import cugraph.dask.comms.comms as Comms @@ -81,6 +81,10 @@ def __init__(self, properties): self.destination_columns = None self.weight_column = None self.vertex_columns = None + self.vertex_type = None + self.weight_type = None + self.edge_id_type = None + self.edge_type_id_type = None def _make_plc_graph( sID, @@ -89,51 +93,69 @@ def _make_plc_graph( src_col_name, dst_col_name, store_transposed, - num_edges, + vertex_type, + weight_type, + edge_id_type, + edge_type_id, ): - weights = None edge_ids = None edge_types = None - if simpleDistributedGraphImpl.edgeWeightCol in edata_x[0]: - weights = _get_column_from_ls_dfs( - edata_x, simpleDistributedGraphImpl.edgeWeightCol - ) - if weights.dtype == "int32": - weights = weights.astype("float32") - elif weights.dtype == "int64": - weights = weights.astype("float64") - - if simpleDistributedGraphImpl.edgeIdCol in edata_x[0]: - edge_ids = _get_column_from_ls_dfs( - edata_x, simpleDistributedGraphImpl.edgeIdCol - ) - if edata_x[0][src_col_name].dtype == "int64" and edge_ids.dtype != "int64": - edge_ids = edge_ids.astype("int64") + num_arrays = len(edata_x) + if weight_type is not None: + weights = [ + edata_x[i][simpleDistributedGraphImpl.edgeWeightCol] + for i in range(num_arrays) + ] + if weight_type == "int32": + weights = [w_array.astype("float32") for w_array in weights] + elif weight_type == "int64": + weights = [w_array.astype("float64") for w_array in weights] + + if edge_id_type is not None: + edge_ids = [ + edata_x[i][simpleDistributedGraphImpl.edgeIdCol] + for i in range(num_arrays) + ] + if vertex_type == "int64" and edge_id_type != "int64": + edge_ids = [e_id_array.astype("int64") for e_id_array in edge_ids] warnings.warn( - f"Vertex type is int64 but edge id type is {edge_ids.dtype}" + f"Vertex type is int64 but edge id type is {edge_ids[0].dtype}" ", automatically casting edge id type to int64. " "This may cause extra memory usage. Consider passing" " a int64 list of edge ids instead." ) - if simpleDistributedGraphImpl.edgeTypeCol in edata_x[0]: - edge_types = _get_column_from_ls_dfs( - edata_x, simpleDistributedGraphImpl.edgeTypeCol - ) + if edge_type_id is not None: + edge_types = [ + edata_x[i][simpleDistributedGraphImpl.edgeTypeCol] + for i in range(num_arrays) + ] - return MGGraph( + src_array = [edata_x[i][src_col_name] for i in range(num_arrays)] + dst_array = [edata_x[i][dst_col_name] for i in range(num_arrays)] + plc_graph = MGGraph( resource_handle=ResourceHandle(Comms.get_handle(sID).getHandle()), graph_properties=graph_props, - src_array=_get_column_from_ls_dfs(edata_x, src_col_name), - dst_array=_get_column_from_ls_dfs(edata_x, dst_col_name), - weight_array=weights, - edge_id_array=edge_ids, - edge_type_array=edge_types, + src_array=src_array if src_array else cudf.Series(dtype=vertex_type), + dst_array=dst_array if dst_array else cudf.Series(dtype=vertex_type), + weight_array=weights + if weights + else ([cudf.Series(dtype=weight_type)] if weight_type else None), + edge_id_array=edge_ids + if edge_ids + else ([cudf.Series(dtype=edge_id_type)] if edge_id_type else None), + edge_type_array=edge_types + if edge_types + else ([cudf.Series(dtype=edge_type_id)] if edge_type_id else None), + num_arrays=num_arrays, store_transposed=store_transposed, - num_edges=num_edges, do_expensive_check=False, ) + del edata_x + gc.collect() + + return plc_graph # Functions def __from_edgelist( @@ -182,7 +204,6 @@ def __from_edgelist( workers = _client.scheduler_info()["workers"] # Repartition to 2 partitions per GPU for memory efficient process input_ddf = input_ddf.repartition(npartitions=len(workers) * 2) - input_ddf = input_ddf.map_partitions(lambda df: df.copy()) # The dataframe will be symmetrized iff the graph is undirected # otherwise, the inital dataframe will be returned if edge_attr is not None: @@ -314,19 +335,25 @@ def __from_edgelist( dst_col_name = self.renumber_map.renumbered_dst_col_name ddf = self.edgelist.edgelist_df + + # Get the edgelist dtypes + self.vertex_type = ddf[src_col_name].dtype + if simpleDistributedGraphImpl.edgeWeightCol in ddf.columns: + self.weight_type = ddf[simpleDistributedGraphImpl.edgeWeightCol].dtype + if simpleDistributedGraphImpl.edgeIdCol in ddf.columns: + self.edge_id_type = ddf[simpleDistributedGraphImpl.edgeIdCol].dtype + if simpleDistributedGraphImpl.edgeTypeCol in ddf.columns: + self.edge_type_id_type = ddf[simpleDistributedGraphImpl.edgeTypeCol].dtype + graph_props = GraphProperties( is_multigraph=self.properties.multi_edge, is_symmetric=not self.properties.directed, ) ddf = ddf.repartition(npartitions=len(workers) * 2) - persisted_keys_d = persist_dask_df_equal_parts_per_worker( - ddf, _client, return_type="dict" - ) - del ddf - length_of_parts = get_length_of_parts(persisted_keys_d, _client) - num_edges = sum( - [item for sublist in length_of_parts.values() for item in sublist] - ) + ddf_keys = ddf.to_delayed() + workers = _client.scheduler_info()["workers"].keys() + ddf_keys_ls = _chunk_lst(ddf_keys, len(workers)) + delayed_tasks_d = { w: delayed(simpleDistributedGraphImpl._make_plc_graph)( Comms.get_session_id(), @@ -335,9 +362,12 @@ def __from_edgelist( src_col_name, dst_col_name, store_transposed, - num_edges, + self.vertex_type, + self.weight_type, + self.edge_id_type, + self.edge_type_id_type, ) - for w, edata in persisted_keys_d.items() + for w, edata in zip(workers, ddf_keys_ls) } self._plc_graph = { w: _client.compute( @@ -346,8 +376,9 @@ def __from_edgelist( for w, delayed_task in delayed_tasks_d.items() } wait(list(self._plc_graph.values())) - del persisted_keys_d + del ddf_keys del delayed_tasks_d + gc.collect() _client.run(gc.collect) @property @@ -1189,18 +1220,3 @@ def vertex_column_size(self): @property def _npartitions(self) -> int: return len(self._plc_graph) - - -def _get_column_from_ls_dfs(lst_df, col_name): - """ - This function concatenates the column - and drops it from the input list - """ - len_df = sum([len(df) for df in lst_df]) - if len_df == 0: - return lst_df[0][col_name] - output_col = cudf.concat([df[col_name] for df in lst_df], ignore_index=True) - for df in lst_df: - df.drop(columns=[col_name], inplace=True) - gc.collect() - return output_col diff --git a/python/cugraph/cugraph/structure/symmetrize.py b/python/cugraph/cugraph/structure/symmetrize.py index 4c00e68344d..b324ff65834 100644 --- a/python/cugraph/cugraph/structure/symmetrize.py +++ b/python/cugraph/cugraph/structure/symmetrize.py @@ -299,10 +299,8 @@ def _memory_efficient_drop_duplicates(ddf, vertex_col_name, num_workers): Drop duplicate edges from the input dataframe. """ # drop duplicates has a 5x+ overhead - # and does not seem to be working as expected - # TODO: Triage an MRE ddf = ddf.reset_index(drop=True).repartition(npartitions=num_workers * 2) - ddf = ddf.groupby(by=[*vertex_col_name], as_index=False).min( - split_out=num_workers * 2 + ddf = ddf.drop_duplicates( + subset=[*vertex_col_name], ignore_index=True, split_out=num_workers * 2 ) return ddf diff --git a/python/cugraph/cugraph/testing/mg_utils.py b/python/cugraph/cugraph/testing/mg_utils.py index bd165ba3db5..32854652f05 100644 --- a/python/cugraph/cugraph/testing/mg_utils.py +++ b/python/cugraph/cugraph/testing/mg_utils.py @@ -33,6 +33,7 @@ def start_dask_client( rmm_pool_size=None, dask_worker_devices=None, jit_unspill=False, + worker_class=None, device_memory_limit=0.8, ): """ @@ -141,6 +142,7 @@ def start_dask_client( rmm_async=rmm_async, CUDA_VISIBLE_DEVICES=dask_worker_devices, jit_unspill=jit_unspill, + worker_class=worker_class, device_memory_limit=device_memory_limit, ) client = Client(cluster) diff --git a/python/cugraph/cugraph/tests/conftest.py b/python/cugraph/cugraph/tests/conftest.py index 916e445cfdb..cb5755128eb 100644 --- a/python/cugraph/cugraph/tests/conftest.py +++ b/python/cugraph/cugraph/tests/conftest.py @@ -20,6 +20,9 @@ import os import tempfile +# Avoid timeout during shutdown +from dask_cuda.utils_test import IncreasedCloseTimeoutNanny + # module-wide fixtures @@ -40,7 +43,9 @@ def dask_client(): # start_dask_client will check for the SCHEDULER_FILE and # DASK_WORKER_DEVICES env vars and use them when creating a client if # set. start_dask_client will also initialize the Comms singleton. - dask_client, dask_cluster = start_dask_client() + dask_client, dask_cluster = start_dask_client( + worker_class=IncreasedCloseTimeoutNanny + ) yield dask_client diff --git a/python/cugraph/cugraph/tests/link_analysis/test_hits_mg.py b/python/cugraph/cugraph/tests/link_analysis/test_hits_mg.py index 5590eb17401..73ec13c674c 100644 --- a/python/cugraph/cugraph/tests/link_analysis/test_hits_mg.py +++ b/python/cugraph/cugraph/tests/link_analysis/test_hits_mg.py @@ -45,7 +45,7 @@ def setup_function(): fixture_params = gen_fixture_params_product( (datasets, "graph_file"), ([50], "max_iter"), - ([1.0e-6], "tol"), + ([1.0e-4], "tol"), # FIXME: Temporarily lower tolerance (IS_DIRECTED, "directed"), ) diff --git a/python/cugraph/cugraph/tests/structure/test_graph_mg.py b/python/cugraph/cugraph/tests/structure/test_graph_mg.py index 3024e50402a..7837916ae53 100644 --- a/python/cugraph/cugraph/tests/structure/test_graph_mg.py +++ b/python/cugraph/cugraph/tests/structure/test_graph_mg.py @@ -30,6 +30,9 @@ from cugraph.dask.traversal.bfs import convert_to_cudf from cugraph.dask.common.input_utils import get_distributed_data from pylibcugraph.testing.utils import gen_fixture_params_product +from cugraph.dask.common.part_utils import ( + persist_dask_df_equal_parts_per_worker, +) # ============================================================================= @@ -141,10 +144,13 @@ def test_create_mg_graph(dask_client, input_combo): assert len(G._plc_graph) == len(dask_client.has_what()) start = dask_cudf.from_cudf(cudf.Series([1], dtype="int32"), len(G._plc_graph)) + vertex_dtype = start.dtype if G.renumbered: start = G.lookup_internal_vertex_id(start, None) - data_start = get_distributed_data(start) + data_start = persist_dask_df_equal_parts_per_worker( + start, dask_client, return_type="dict" + ) res = [ dask_client.submit( @@ -159,10 +165,10 @@ def test_create_mg_graph(dask_client, input_combo): ), Comms.get_session_id(), G._plc_graph[w], - data_start.worker_to_parts[w][0], + st[0] if st else cudf.Series(dtype=vertex_dtype), workers=[w], ) - for w in Comms.get_workers() + for w, st in data_start.items() ] wait(res) diff --git a/python/pylibcugraph/pylibcugraph/_cugraph_c/graph.pxd b/python/pylibcugraph/pylibcugraph/_cugraph_c/graph.pxd index 590c5679264..28a9f5a3be5 100644 --- a/python/pylibcugraph/pylibcugraph/_cugraph_c/graph.pxd +++ b/python/pylibcugraph/pylibcugraph/_cugraph_c/graph.pxd @@ -1,4 +1,4 @@ -# Copyright (c) 2022, NVIDIA CORPORATION. +# Copyright (c) 2022-2023, NVIDIA CORPORATION. # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at @@ -51,12 +51,38 @@ cdef extern from "cugraph_c/graph.h": bool_t check, cugraph_graph_t** graph, cugraph_error_t** error) + + # Supports isolated vertices + cdef cugraph_error_code_t \ + cugraph_graph_create_sg( + const cugraph_resource_handle_t* handle, + const cugraph_graph_properties_t* properties, + const cugraph_type_erased_device_array_view_t* vertices, + const cugraph_type_erased_device_array_view_t* src, + const cugraph_type_erased_device_array_view_t* dst, + const cugraph_type_erased_device_array_view_t* weights, + const cugraph_type_erased_device_array_view_t* edge_ids, + const cugraph_type_erased_device_array_view_t* edge_types, + bool_t store_transposed, + bool_t renumber, + bool_t drop_self_loops, + bool_t drop_multi_edges, + bool_t check, + cugraph_graph_t** graph, + cugraph_error_t** error) # This may get renamed to cugraph_graph_free() cdef void \ cugraph_sg_graph_free( cugraph_graph_t* graph ) + + # FIXME: Might want to delete 'cugraph_sg_graph_free' and replace + # 'cugraph_mg_graph_free' by 'cugraph_graph_free' + cdef void \ + cugraph_graph_free( + cugraph_graph_t* graph + ) cdef cugraph_error_code_t \ cugraph_mg_graph_create( @@ -96,6 +122,22 @@ cdef extern from "cugraph_c/graph.h": cugraph_error_t** error ) + cdef cugraph_error_code_t \ + cugraph_graph_create_sg_from_csr( + const cugraph_resource_handle_t* handle, + const cugraph_graph_properties_t* properties, + const cugraph_type_erased_device_array_view_t* offsets, + const cugraph_type_erased_device_array_view_t* indices, + const cugraph_type_erased_device_array_view_t* weights, + const cugraph_type_erased_device_array_view_t* edge_ids, + const cugraph_type_erased_device_array_view_t* edge_type_ids, + bool_t store_transposed, + bool_t renumber, + bool_t check, + cugraph_graph_t** graph, + cugraph_error_t** error + ) + cdef void \ cugraph_sg_graph_free( cugraph_graph_t* graph @@ -117,6 +159,24 @@ cdef extern from "cugraph_c/graph.h": cugraph_error_t** error ) + cdef cugraph_error_code_t \ + cugraph_graph_create_mg( + const cugraph_resource_handle_t* handle, + const cugraph_graph_properties_t* properties, + const cugraph_type_erased_device_array_view_t** vertices, + const cugraph_type_erased_device_array_view_t** src, + const cugraph_type_erased_device_array_view_t** dst, + const cugraph_type_erased_device_array_view_t** weights, + const cugraph_type_erased_device_array_view_t** edge_ids, + const cugraph_type_erased_device_array_view_t** edge_type_ids, + bool_t store_transposed, + size_t num_arrays, + bool_t drop_self_loops, + bool_t drop_multi_edges, + bool_t do_expensive_check, + cugraph_graph_t** graph, + cugraph_error_t** error) + cdef void \ cugraph_mg_graph_free( cugraph_graph_t* graph diff --git a/python/pylibcugraph/pylibcugraph/analyze_clustering_edge_cut.pyx b/python/pylibcugraph/pylibcugraph/analyze_clustering_edge_cut.pyx index 60613f27a0d..3370e71f469 100644 --- a/python/pylibcugraph/pylibcugraph/analyze_clustering_edge_cut.pyx +++ b/python/pylibcugraph/pylibcugraph/analyze_clustering_edge_cut.pyx @@ -86,7 +86,7 @@ def analyze_clustering_edge_cut(ResourceHandle resource_handle, >>> graph_props = pylibcugraph.GraphProperties( ... is_symmetric=True, is_multigraph=False) >>> G = pylibcugraph.SGGraph( - ... resource_handle, graph_props, srcs, dsts, weights, + ... resource_handle, graph_props, srcs, dsts, weight_array=weights, ... store_transposed=True, renumber=False, do_expensive_check=False) >>> (vertex, cluster) = pylibcugraph.spectral_modularity_maximization( ... resource_handle, G, num_clusters=5, num_eigen_vects=2, evs_tolerance=0.00001 diff --git a/python/pylibcugraph/pylibcugraph/analyze_clustering_modularity.pyx b/python/pylibcugraph/pylibcugraph/analyze_clustering_modularity.pyx index 76ba48f52b7..2e7c1d2f649 100644 --- a/python/pylibcugraph/pylibcugraph/analyze_clustering_modularity.pyx +++ b/python/pylibcugraph/pylibcugraph/analyze_clustering_modularity.pyx @@ -87,7 +87,7 @@ def analyze_clustering_modularity(ResourceHandle resource_handle, >>> graph_props = pylibcugraph.GraphProperties( ... is_symmetric=True, is_multigraph=False) >>> G = pylibcugraph.SGGraph( - ... resource_handle, graph_props, srcs, dsts, weights, + ... resource_handle, graph_props, srcs, dsts, weight_array=weights, ... store_transposed=True, renumber=False, do_expensive_check=False) >>> (vertex, cluster) = pylibcugraph.spectral_modularity_maximization( ... resource_handle, G, num_clusters=5, num_eigen_vects=2, evs_tolerance=0.00001 diff --git a/python/pylibcugraph/pylibcugraph/analyze_clustering_ratio_cut.pyx b/python/pylibcugraph/pylibcugraph/analyze_clustering_ratio_cut.pyx index 39b317e107d..c06f870d048 100644 --- a/python/pylibcugraph/pylibcugraph/analyze_clustering_ratio_cut.pyx +++ b/python/pylibcugraph/pylibcugraph/analyze_clustering_ratio_cut.pyx @@ -86,7 +86,7 @@ def analyze_clustering_ratio_cut(ResourceHandle resource_handle, >>> graph_props = pylibcugraph.GraphProperties( ... is_symmetric=True, is_multigraph=False) >>> G = pylibcugraph.SGGraph( - ... resource_handle, graph_props, srcs, dsts, weights, + ... resource_handle, graph_props, srcs, dsts, weight_array=weights, ... store_transposed=True, renumber=False, do_expensive_check=False) >>> (vertex, cluster) = pylibcugraph.spectral_modularity_maximization( ... resource_handle, G, num_clusters=5, num_eigen_vects=2, evs_tolerance=0.00001 diff --git a/python/pylibcugraph/pylibcugraph/balanced_cut_clustering.pyx b/python/pylibcugraph/pylibcugraph/balanced_cut_clustering.pyx index 5a61f9e0dd7..a1a5c8182eb 100644 --- a/python/pylibcugraph/pylibcugraph/balanced_cut_clustering.pyx +++ b/python/pylibcugraph/pylibcugraph/balanced_cut_clustering.pyx @@ -109,7 +109,7 @@ def balanced_cut_clustering(ResourceHandle resource_handle, >>> graph_props = pylibcugraph.GraphProperties( ... is_symmetric=True, is_multigraph=False) >>> G = pylibcugraph.SGGraph( - ... resource_handle, graph_props, srcs, dsts, weights, + ... resource_handle, graph_props, srcs, dsts, weight_array=weights, ... store_transposed=True, renumber=False, do_expensive_check=False) >>> (vertices, clusters) = pylibcugraph.balanced_cut_clustering( ... resource_handle, G, num_clusters=5, num_eigen_vects=2, evs_tolerance=0.00001 diff --git a/python/pylibcugraph/pylibcugraph/ecg.pyx b/python/pylibcugraph/pylibcugraph/ecg.pyx index c5c1fe2eda7..4188aaa213e 100644 --- a/python/pylibcugraph/pylibcugraph/ecg.pyx +++ b/python/pylibcugraph/pylibcugraph/ecg.pyx @@ -101,7 +101,7 @@ def ecg(ResourceHandle resource_handle, >>> graph_props = pylibcugraph.GraphProperties( ... is_symmetric=True, is_multigraph=False) >>> G = pylibcugraph.SGGraph( - ... resource_handle, graph_props, srcs, dsts, weights, + ... resource_handle, graph_props, srcs, dsts, weight_array=weights, ... store_transposed=True, renumber=False, do_expensive_check=False) >>> (vertices, clusters) = pylibcugraph.ecg(resource_handle, G) # FIXME: Check this docstring example diff --git a/python/pylibcugraph/pylibcugraph/edge_betweenness_centrality.pyx b/python/pylibcugraph/pylibcugraph/edge_betweenness_centrality.pyx index c88c9fe8a67..e1dae1ff10a 100644 --- a/python/pylibcugraph/pylibcugraph/edge_betweenness_centrality.pyx +++ b/python/pylibcugraph/pylibcugraph/edge_betweenness_centrality.pyx @@ -180,7 +180,7 @@ def edge_betweenness_centrality(ResourceHandle resource_handle, cdef cugraph_type_erased_device_array_view_t* values_ptr = \ cugraph_edge_centrality_result_get_values(result_ptr) - if graph.edge_id_view_ptr is NULL: + if graph.edge_id_view_ptr is NULL and graph.edge_id_view_ptr_ptr is NULL: cupy_edge_ids = None else: edge_ids_ptr = cugraph_edge_centrality_result_get_edge_ids(result_ptr) diff --git a/python/pylibcugraph/pylibcugraph/egonet.pyx b/python/pylibcugraph/pylibcugraph/egonet.pyx index d011d946e46..e7237cc3ba4 100644 --- a/python/pylibcugraph/pylibcugraph/egonet.pyx +++ b/python/pylibcugraph/pylibcugraph/egonet.pyx @@ -101,7 +101,7 @@ def ego_graph(ResourceHandle resource_handle, >>> graph_props = pylibcugraph.GraphProperties( ... is_symmetric=False, is_multigraph=False) >>> G = pylibcugraph.SGGraph( - ... resource_handle, graph_props, srcs, dsts, weights, + ... resource_handle, graph_props, srcs, dsts, weight_array=weights, ... store_transposed=False, renumber=False, do_expensive_check=False) >>> (sources, destinations, edge_weights, subgraph_offsets) = ... pylibcugraph.ego_graph(resource_handle, G, source_vertices, 2, False) diff --git a/python/pylibcugraph/pylibcugraph/eigenvector_centrality.pyx b/python/pylibcugraph/pylibcugraph/eigenvector_centrality.pyx index 88612c242e2..568f072ee3d 100644 --- a/python/pylibcugraph/pylibcugraph/eigenvector_centrality.pyx +++ b/python/pylibcugraph/pylibcugraph/eigenvector_centrality.pyx @@ -1,4 +1,4 @@ -# Copyright (c) 2022, NVIDIA CORPORATION. +# Copyright (c) 2022-2023, NVIDIA CORPORATION. # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at @@ -97,7 +97,7 @@ def eigenvector_centrality(ResourceHandle resource_handle, >>> graph_props = pylibcugraph.GraphProperties( ... is_symmetric=False, is_multigraph=False) >>> G = pylibcugraph.SGGraph( - ... resource_handle, graph_props, srcs, dsts, weights, + ... resource_handle, graph_props, srcs, dsts, weight_array=weights, ... store_transposed=True, renumber=False, do_expensive_check=False) >>> (vertices, values) = pylibcugraph.eigenvector_centrality( resource_handle, G, 1e-6, 1000, False) diff --git a/python/pylibcugraph/pylibcugraph/graphs.pxd b/python/pylibcugraph/pylibcugraph/graphs.pxd index a2df44ba26e..dac69e0ad04 100644 --- a/python/pylibcugraph/pylibcugraph/graphs.pxd +++ b/python/pylibcugraph/pylibcugraph/graphs.pxd @@ -25,11 +25,13 @@ from pylibcugraph._cugraph_c.graph cimport ( cdef class _GPUGraph: cdef cugraph_graph_t* c_graph_ptr cdef cugraph_type_erased_device_array_view_t* edge_id_view_ptr - cdef cugraph_type_erased_device_array_view_t* weights_view_ptr + cdef cugraph_type_erased_device_array_view_t** edge_id_view_ptr_ptr + cdef cugraph_type_erased_device_array_view_t* weights_view_ptr + cdef cugraph_type_erased_device_array_view_t** weights_view_ptr_ptr cdef class SGGraph(_GPUGraph): pass cdef class MGGraph(_GPUGraph): - pass + pass diff --git a/python/pylibcugraph/pylibcugraph/graphs.pyx b/python/pylibcugraph/pylibcugraph/graphs.pyx index 33a8a09c6f4..b3065fa0684 100644 --- a/python/pylibcugraph/pylibcugraph/graphs.pyx +++ b/python/pylibcugraph/pylibcugraph/graphs.pyx @@ -18,16 +18,20 @@ from pylibcugraph._cugraph_c.error cimport ( cugraph_error_code_t, cugraph_error_t, ) +from cython.operator cimport dereference as deref from pylibcugraph._cugraph_c.array cimport ( cugraph_type_erased_device_array_view_t, cugraph_type_erased_device_array_view_free, ) from pylibcugraph._cugraph_c.graph cimport ( - cugraph_sg_graph_create, - cugraph_mg_graph_create, - cugraph_sg_graph_create_from_csr, - cugraph_sg_graph_free, - cugraph_mg_graph_free, + cugraph_graph_create_sg, + cugraph_graph_create_mg, + cugraph_sg_graph_create_from_csr, #FIXME: Remove this once + # 'cugraph_graph_create_sg_from_csr' is exposed + cugraph_graph_create_sg_from_csr, + cugraph_sg_graph_free, #FIXME: Remove this + cugraph_graph_free, + cugraph_mg_graph_free, #FIXME: Remove this ) from pylibcugraph.resource_handle cimport ( ResourceHandle, @@ -38,8 +42,11 @@ from pylibcugraph.graph_properties cimport ( from pylibcugraph.utils cimport ( assert_success, assert_CAI_type, + get_c_type_from_numpy_type, create_cugraph_type_erased_device_array_view_from_py_obj, ) +from libc.stdlib cimport malloc + cdef class SGGraph(_GPUGraph): @@ -70,6 +77,9 @@ cdef class SGGraph(_GPUGraph): CSR format. In the case of a COO, The order of the array corresponds to the ordering of the src_offset_array, where the ith item in src_offset_array and the ith item in dst_index_array define the ith edge of the graph. + + vertices_array : device array type + Device array containing the isolated vertices of the graph. weight_array : device array type Device array containing the weight values of each directed edge. The @@ -105,6 +115,12 @@ cdef class SGGraph(_GPUGraph): COO: arrays represent src_array and dst_array CSR: arrays represent offset_array and index_array + drop_self_loops : bool, optional (default='False') + If true, drop any self loops that exist in the provided edge list. + + drop_multi_edges: bool, optional (default='False') + If true, drop any multi edges that exist in the provided edge list + Examples --------- >>> import pylibcugraph, cupy, numpy @@ -116,7 +132,7 @@ cdef class SGGraph(_GPUGraph): >>> graph_props = pylibcugraph.GraphProperties( ... is_symmetric=False, is_multigraph=False) >>> G = pylibcugraph.SGGraph( - ... resource_handle, graph_props, srcs, dsts, weights, + ... resource_handle, graph_props, srcs, dsts, weight_array=weights, ... store_transposed=False, renumber=False, do_expensive_check=False) """ @@ -131,7 +147,10 @@ cdef class SGGraph(_GPUGraph): do_expensive_check=False, edge_id_array=None, edge_type_array=None, - input_array_format="COO"): + input_array_format="COO", + vertices_array=None, + drop_self_loops=False, + drop_multi_edges=False): # FIXME: add tests for these if not(isinstance(store_transposed, (int, bool))): @@ -145,13 +164,13 @@ cdef class SGGraph(_GPUGraph): f"{type(do_expensive_check)}") assert_CAI_type(src_or_offset_array, "src_or_offset_array") assert_CAI_type(dst_or_index_array, "dst_or_index_array") + assert_CAI_type(vertices_array, "vertices_array", True) assert_CAI_type(weight_array, "weight_array", True) - if edge_id_array is not None: - assert_CAI_type(edge_id_array, "edge_id_array") - if edge_type_array is not None: - assert_CAI_type(edge_type_array, "edge_type_array") + assert_CAI_type(edge_id_array, "edge_id_array", True) + assert_CAI_type(edge_type_array, "edge_type_array", True) - # FIXME: assert that src_or_offset_array and dst_or_index_array have the same type + # FIXME: assert that src_or_offset_array and dst_or_index_array have + # the same type cdef cugraph_error_t* error_ptr cdef cugraph_error_code_t error_code @@ -159,31 +178,31 @@ cdef class SGGraph(_GPUGraph): cdef cugraph_type_erased_device_array_view_t* srcs_or_offsets_view_ptr = \ create_cugraph_type_erased_device_array_view_from_py_obj( src_or_offset_array - ) - + ) cdef cugraph_type_erased_device_array_view_t* dsts_or_indices_view_ptr = \ create_cugraph_type_erased_device_array_view_from_py_obj( dst_or_index_array ) - - + cdef cugraph_type_erased_device_array_view_t* vertices_view_ptr = \ + create_cugraph_type_erased_device_array_view_from_py_obj( + vertices_array + ) self.weights_view_ptr = create_cugraph_type_erased_device_array_view_from_py_obj( weight_array ) - self.edge_id_view_ptr = create_cugraph_type_erased_device_array_view_from_py_obj( edge_id_array - ) - + ) cdef cugraph_type_erased_device_array_view_t* edge_type_view_ptr = \ create_cugraph_type_erased_device_array_view_from_py_obj( edge_type_array ) if input_array_format == "COO": - error_code = cugraph_sg_graph_create( + error_code = cugraph_graph_create_sg( resource_handle.c_resource_handle_ptr, &(graph_properties.c_graph_properties), + vertices_view_ptr, srcs_or_offsets_view_ptr, dsts_or_indices_view_ptr, self.weights_view_ptr, @@ -191,12 +210,13 @@ cdef class SGGraph(_GPUGraph): edge_type_view_ptr, store_transposed, renumber, + drop_self_loops, + drop_multi_edges, do_expensive_check, &(self.c_graph_ptr), &error_ptr) - assert_success(error_code, error_ptr, - "cugraph_sg_graph_create()") + "cugraph_graph_create_sg()") elif input_array_format == "CSR": error_code = cugraph_sg_graph_create_from_csr( @@ -209,6 +229,8 @@ cdef class SGGraph(_GPUGraph): edge_type_view_ptr, store_transposed, renumber, + # drop_self_loops, #FIXME: Not supported yet + # drop_multi_edges, #FIXME: Not supported yet do_expensive_check, &(self.c_graph_ptr), &error_ptr) @@ -223,7 +245,8 @@ cdef class SGGraph(_GPUGraph): cugraph_type_erased_device_array_view_free(srcs_or_offsets_view_ptr) cugraph_type_erased_device_array_view_free(dsts_or_indices_view_ptr) - cugraph_type_erased_device_array_view_free(self.weights_view_ptr) + if self.weights_view_ptr is not NULL: + cugraph_type_erased_device_array_view_free(self.weights_view_ptr) if self.edge_id_view_ptr is not NULL: cugraph_type_erased_device_array_view_free(self.edge_id_view_ptr) if edge_type_view_ptr is not NULL: @@ -259,6 +282,9 @@ cdef class MGGraph(_GPUGraph): each directed edge. The order of the array corresponds to the ordering of the src_array, where the ith item in src_array and the ith item in dst_array define the ith edge of the graph. + + vertices_array : device array type + Device array containing the isolated vertices of the graph. weight_array : device array type Device array containing the weight values of each directed edge. The @@ -270,8 +296,10 @@ cdef class MGGraph(_GPUGraph): Set to True if the graph should be transposed. This is required for some algorithms, such as pagerank. - num_edges : int - Number of edges + num_arrays : size_t + Number of arrays. + + If provided, all list of device arrays should be of the same size. do_expensive_check : bool If True, performs more extensive tests on the inputs to ensure @@ -286,6 +314,12 @@ cdef class MGGraph(_GPUGraph): Device array containing the edge types of each directed edge. Must match the ordering of the src/dst/edge_id arrays. Optional (may be null). If provided, edge_id_array must be provided. + + drop_self_loops : bool, optional (default='False') + If true, drop any self loops that exist in the provided edge list. + + drop_multi_edges: bool, optional (default='False') + If true, drop any multi edges that exist in the provided edge list """ def __cinit__(self, ResourceHandle resource_handle, @@ -294,85 +328,156 @@ cdef class MGGraph(_GPUGraph): dst_array, weight_array=None, store_transposed=False, - num_edges=-1, - do_expensive_check=False, + do_expensive_check=False, # default to False edge_id_array=None, - edge_type_array=None): + edge_type_array=None, + vertices_array=None, + size_t num_arrays=1, # default value to not break users + drop_self_loops=False, + drop_multi_edges=False): - # FIXME: add tests for these if not(isinstance(store_transposed, (int, bool))): raise TypeError("expected int or bool for store_transposed, got " f"{type(store_transposed)}") - if not(isinstance(num_edges, (int))): - raise TypeError("expected int for num_edges, got " - f"{type(num_edges)}") - if num_edges < 0: - raise TypeError("num_edges must be > 0") + if not(isinstance(do_expensive_check, (int, bool))): raise TypeError("expected int or bool for do_expensive_check, got " f"{type(do_expensive_check)}") - assert_CAI_type(src_array, "src_array") - assert_CAI_type(dst_array, "dst_array") - assert_CAI_type(weight_array, "weight_array", True) - - assert_CAI_type(edge_id_array, "edge_id_array", True) - if edge_id_array is not None and len(edge_id_array) != len(src_array): - raise ValueError('Edge id array must be same length as edgelist') - - assert_CAI_type(edge_type_array, "edge_type_array", True) - if edge_type_array is not None and len(edge_type_array) != len(src_array): - raise ValueError('Edge type array must be same length as edgelist') - - # FIXME: assert that src_array and dst_array have the same type cdef cugraph_error_t* error_ptr cdef cugraph_error_code_t error_code - cdef cugraph_type_erased_device_array_view_t* srcs_view_ptr = \ - create_cugraph_type_erased_device_array_view_from_py_obj( - src_array - ) - cdef cugraph_type_erased_device_array_view_t* dsts_view_ptr = \ - create_cugraph_type_erased_device_array_view_from_py_obj( - dst_array - ) - self.weights_view_ptr = \ - create_cugraph_type_erased_device_array_view_from_py_obj( - weight_array - ) - self.edge_id_view_ptr = \ - create_cugraph_type_erased_device_array_view_from_py_obj( - edge_id_array - ) - cdef cugraph_type_erased_device_array_view_t* edge_type_view_ptr = \ - create_cugraph_type_erased_device_array_view_from_py_obj( - edge_type_array - ) - error_code = cugraph_mg_graph_create( + if not isinstance(src_array, list): + src_array = [src_array] + if not any(src_array): + src_array = src_array * num_arrays + + if not isinstance(dst_array, list): + dst_array = [dst_array] + if not any(dst_array): + dst_array = dst_array * num_arrays + + if not isinstance(weight_array, list): + weight_array = [weight_array] + if not any(weight_array): + weight_array = weight_array * num_arrays + + if not isinstance(edge_id_array, list): + edge_id_array = [edge_id_array] + if not any(edge_id_array): + edge_id_array = edge_id_array * num_arrays + + if not isinstance(edge_type_array, list): + edge_type_array = [edge_type_array] + if not any(edge_type_array): + edge_type_array = edge_type_array * num_arrays + + if not isinstance(vertices_array, list): + vertices_array = [vertices_array] + if not any(vertices_array): + vertices_array = vertices_array * num_arrays + + cdef cugraph_type_erased_device_array_view_t** srcs_view_ptr_ptr = NULL + cdef cugraph_type_erased_device_array_view_t** dsts_view_ptr_ptr = NULL + cdef cugraph_type_erased_device_array_view_t** vertices_view_ptr_ptr = NULL + cdef cugraph_type_erased_device_array_view_t** edge_type_view_ptr_ptr = NULL + + for i in range(num_arrays): + if do_expensive_check: + assert_CAI_type(src_array[i], "src_array") + assert_CAI_type(dst_array[i], "dst_array") + assert_CAI_type(weight_array[i], "weight_array", True) + assert_CAI_type(vertices_array[i], "vertices_array", True) + + assert_CAI_type(edge_id_array[i], "edge_id_array", True) + + if edge_id_array is not None and len(edge_id_array[i]) != len(src_array[i]): + raise ValueError('Edge id array must be same length as edgelist') + + assert_CAI_type(edge_type_array[i], "edge_type_array", True) + if edge_type_array[i] is not None and len(edge_type_array[i]) != len(src_array[i]): + raise ValueError('Edge type array must be same length as edgelist') + + if src_array[i] is not None: + if i == 0: + srcs_view_ptr_ptr = \ + malloc( + num_arrays * sizeof(cugraph_type_erased_device_array_view_t*)) + srcs_view_ptr_ptr[i] = \ + create_cugraph_type_erased_device_array_view_from_py_obj(src_array[i]) + + if dst_array[i] is not None: + if i == 0: + dsts_view_ptr_ptr = \ + malloc( + num_arrays * sizeof(cugraph_type_erased_device_array_view_t*)) + dsts_view_ptr_ptr[i] = \ + create_cugraph_type_erased_device_array_view_from_py_obj(dst_array[i]) + + if vertices_array[i] is not None: + if i == 0: + vertices_view_ptr_ptr = \ + malloc( + num_arrays * sizeof(cugraph_type_erased_device_array_view_t*)) + vertices_view_ptr_ptr[i] = \ + create_cugraph_type_erased_device_array_view_from_py_obj(vertices_array[i]) + + if weight_array[i] is not None: + if i == 0: + self.weights_view_ptr_ptr = \ + malloc( + num_arrays * sizeof(cugraph_type_erased_device_array_view_t*)) + self.weights_view_ptr_ptr[i] = \ + create_cugraph_type_erased_device_array_view_from_py_obj(weight_array[i]) + + if edge_id_array[i] is not None: + if i == 0: + self.edge_id_view_ptr_ptr = \ + malloc( + num_arrays * sizeof(cugraph_type_erased_device_array_view_t*)) + self.edge_id_view_ptr_ptr[i] = \ + create_cugraph_type_erased_device_array_view_from_py_obj(edge_id_array[i]) + + if edge_type_array[i] is not None: + if i == 0: + edge_type_view_ptr_ptr = \ + malloc( + num_arrays * sizeof(cugraph_type_erased_device_array_view_t*)) + edge_type_view_ptr_ptr[i] = \ + create_cugraph_type_erased_device_array_view_from_py_obj(edge_type_array[i]) + + error_code = cugraph_graph_create_mg( resource_handle.c_resource_handle_ptr, &(graph_properties.c_graph_properties), - srcs_view_ptr, - dsts_view_ptr, - self.weights_view_ptr, - self.edge_id_view_ptr, - edge_type_view_ptr, + vertices_view_ptr_ptr, + srcs_view_ptr_ptr, + dsts_view_ptr_ptr, + self.weights_view_ptr_ptr, + self.edge_id_view_ptr_ptr, + edge_type_view_ptr_ptr, store_transposed, - num_edges, + num_arrays, do_expensive_check, + drop_self_loops, + drop_multi_edges, &(self.c_graph_ptr), &error_ptr) assert_success(error_code, error_ptr, "cugraph_mg_graph_create()") - cugraph_type_erased_device_array_view_free(srcs_view_ptr) - cugraph_type_erased_device_array_view_free(dsts_view_ptr) - cugraph_type_erased_device_array_view_free(self.weights_view_ptr) - if self.edge_id_view_ptr is not NULL: - cugraph_type_erased_device_array_view_free(self.edge_id_view_ptr) - if edge_type_view_ptr is not NULL: - cugraph_type_erased_device_array_view_free(edge_type_view_ptr) + for i in range(num_arrays): + cugraph_type_erased_device_array_view_free(srcs_view_ptr_ptr[i]) + cugraph_type_erased_device_array_view_free(dsts_view_ptr_ptr[i]) + if vertices_view_ptr_ptr is not NULL: + cugraph_type_erased_device_array_view_free(vertices_view_ptr_ptr[i]) + if self.weights_view_ptr_ptr is not NULL: + cugraph_type_erased_device_array_view_free(self.weights_view_ptr_ptr[i]) + if self.edge_id_view_ptr_ptr is not NULL: + cugraph_type_erased_device_array_view_free(self.edge_id_view_ptr_ptr[i]) + if edge_type_view_ptr_ptr is not NULL: + cugraph_type_erased_device_array_view_free(edge_type_view_ptr_ptr[i]) def __dealloc__(self): if self.c_graph_ptr is not NULL: diff --git a/python/pylibcugraph/pylibcugraph/induced_subgraph.pyx b/python/pylibcugraph/pylibcugraph/induced_subgraph.pyx index aab36d3d5e0..99b89ec2a58 100644 --- a/python/pylibcugraph/pylibcugraph/induced_subgraph.pyx +++ b/python/pylibcugraph/pylibcugraph/induced_subgraph.pyx @@ -98,7 +98,7 @@ def induced_subgraph(ResourceHandle resource_handle, >>> graph_props = pylibcugraph.GraphProperties( ... is_symmetric=False, is_multigraph=False) >>> G = pylibcugraph.SGGraph( - ... resource_handle, graph_props, srcs, dsts, weights, + ... resource_handle, graph_props, srcs, dsts, weight_array=weights, ... store_transposed=False, renumber=False, do_expensive_check=False) >>> (sources, destinations, edge_weights, subgraph_offsets) = ... pylibcugraph.induced_subgraph( diff --git a/python/pylibcugraph/pylibcugraph/k_truss_subgraph.pyx b/python/pylibcugraph/pylibcugraph/k_truss_subgraph.pyx index cc91e76dd55..2c22c618249 100644 --- a/python/pylibcugraph/pylibcugraph/k_truss_subgraph.pyx +++ b/python/pylibcugraph/pylibcugraph/k_truss_subgraph.pyx @@ -96,7 +96,7 @@ def k_truss_subgraph(ResourceHandle resource_handle, >>> graph_props = pylibcugraph.GraphProperties( ... is_symmetric=True, is_multigraph=False) >>> G = pylibcugraph.SGGraph( - ... resource_handle, graph_props, srcs, dsts, weights, + ... resource_handle, graph_props, srcs, dsts, weight_array=weights, ... store_transposed=False, renumber=False, do_expensive_check=False) >>> (sources, destinations, edge_weights, subgraph_offsets) = ... pylibcugraph.k_truss_subgraph(resource_handle, G, k, False) diff --git a/python/pylibcugraph/pylibcugraph/leiden.pyx b/python/pylibcugraph/pylibcugraph/leiden.pyx index 87286234f16..04f8887551c 100644 --- a/python/pylibcugraph/pylibcugraph/leiden.pyx +++ b/python/pylibcugraph/pylibcugraph/leiden.pyx @@ -116,7 +116,7 @@ def leiden(ResourceHandle resource_handle, >>> graph_props = pylibcugraph.GraphProperties( ... is_symmetric=True, is_multigraph=False) >>> G = pylibcugraph.SGGraph( - ... resource_handle, graph_props, srcs, dsts, weights, + ... resource_handle, graph_props, srcs, dsts, weight_array=weights, ... store_transposed=True, renumber=False, do_expensive_check=False) >>> (vertices, clusters, modularity) = pylibcugraph.Leiden( resource_handle, G, 100, 1., False) diff --git a/python/pylibcugraph/pylibcugraph/louvain.pyx b/python/pylibcugraph/pylibcugraph/louvain.pyx index eca569d7da1..58f4f10bc18 100644 --- a/python/pylibcugraph/pylibcugraph/louvain.pyx +++ b/python/pylibcugraph/pylibcugraph/louvain.pyx @@ -103,7 +103,7 @@ def louvain(ResourceHandle resource_handle, >>> graph_props = pylibcugraph.GraphProperties( ... is_symmetric=True, is_multigraph=False) >>> G = pylibcugraph.SGGraph( - ... resource_handle, graph_props, srcs, dsts, weights, + ... resource_handle, graph_props, srcs, dsts, weight_array=weights, ... store_transposed=True, renumber=False, do_expensive_check=False) >>> (vertices, clusters, modularity) = pylibcugraph.louvain( resource_handle, G, 100, 1e-7, 1., False) diff --git a/python/pylibcugraph/pylibcugraph/node2vec.pyx b/python/pylibcugraph/pylibcugraph/node2vec.pyx index d0ab3f22b00..5d83fc46c3c 100644 --- a/python/pylibcugraph/pylibcugraph/node2vec.pyx +++ b/python/pylibcugraph/pylibcugraph/node2vec.pyx @@ -115,7 +115,7 @@ def node2vec(ResourceHandle resource_handle, >>> graph_props = pylibcugraph.GraphProperties( ... is_symmetric=False, is_multigraph=False) >>> G = pylibcugraph.SGGraph( - ... resource_handle, graph_props, srcs, dsts, weights, + ... resource_handle, graph_props, srcs, dsts, weight_array=weights, ... store_transposed=False, renumber=False, do_expensive_check=False) >>> (paths, weights, sizes) = pylibcugraph.node2vec( ... resource_handle, G, seeds, 3, True, 1.0, 1.0) diff --git a/python/pylibcugraph/pylibcugraph/pagerank.pyx b/python/pylibcugraph/pylibcugraph/pagerank.pyx index f831d844338..9fec1328bbf 100644 --- a/python/pylibcugraph/pylibcugraph/pagerank.pyx +++ b/python/pylibcugraph/pylibcugraph/pagerank.pyx @@ -154,7 +154,7 @@ def pagerank(ResourceHandle resource_handle, >>> graph_props = pylibcugraph.GraphProperties( ... is_symmetric=False, is_multigraph=False) >>> G = pylibcugraph.SGGraph( - ... resource_handle, graph_props, srcs, dsts, weights, + ... resource_handle, graph_props, srcs, dsts, weight_array=weights, ... store_transposed=True, renumber=False, do_expensive_check=False) >>> (vertices, pageranks) = pylibcugraph.pagerank( ... resource_handle, G, None, None, None, None, alpha=0.85, diff --git a/python/pylibcugraph/pylibcugraph/personalized_pagerank.pyx b/python/pylibcugraph/pylibcugraph/personalized_pagerank.pyx index 79ef80be549..85addffa694 100644 --- a/python/pylibcugraph/pylibcugraph/personalized_pagerank.pyx +++ b/python/pylibcugraph/pylibcugraph/personalized_pagerank.pyx @@ -161,7 +161,7 @@ def personalized_pagerank(ResourceHandle resource_handle, >>> graph_props = pylibcugraph.GraphProperties( ... is_symmetric=False, is_multigraph=False) >>> G = pylibcugraph.SGGraph( - ... resource_handle, graph_props, srcs, dsts, weights, + ... resource_handle, graph_props, srcs, dsts, weight_array=weights, ... store_transposed=True, renumber=False, do_expensive_check=False) >>> (vertices, pageranks) = pylibcugraph.personalized_pagerank( ... resource_handle, G, None, None, None, None, alpha=0.85, diff --git a/python/pylibcugraph/pylibcugraph/spectral_modularity_maximization.pyx b/python/pylibcugraph/pylibcugraph/spectral_modularity_maximization.pyx index c74b1f0db41..fa01714744d 100644 --- a/python/pylibcugraph/pylibcugraph/spectral_modularity_maximization.pyx +++ b/python/pylibcugraph/pylibcugraph/spectral_modularity_maximization.pyx @@ -109,7 +109,7 @@ def spectral_modularity_maximization(ResourceHandle resource_handle, >>> graph_props = pylibcugraph.GraphProperties( ... is_symmetric=True, is_multigraph=False) >>> G = pylibcugraph.SGGraph( - ... resource_handle, graph_props, srcs, dsts, weights, + ... resource_handle, graph_props, srcs, dsts, weight_array=weights, ... store_transposed=True, renumber=False, do_expensive_check=False) >>> (vertices, clusters) = pylibcugraph.spectral_modularity_maximization( ... resource_handle, G, num_clusters=5, num_eigen_vects=2, evs_tolerance=0.00001 diff --git a/python/pylibcugraph/pylibcugraph/sssp.pyx b/python/pylibcugraph/pylibcugraph/sssp.pyx index b2cd829cb2e..56765c4a1b8 100644 --- a/python/pylibcugraph/pylibcugraph/sssp.pyx +++ b/python/pylibcugraph/pylibcugraph/sssp.pyx @@ -1,4 +1,4 @@ -# Copyright (c) 2022, NVIDIA CORPORATION. +# Copyright (c) 2022-2023, NVIDIA CORPORATION. # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at @@ -109,7 +109,7 @@ def sssp(ResourceHandle resource_handle, >>> graph_props = pylibcugraph.GraphProperties( ... is_symmetric=False, is_multigraph=False) >>> G = pylibcugraph.SGGraph( - ... resource_handle, graph_props, srcs, dsts, weights, + ... resource_handle, graph_props, srcs, dsts, weight_array=weights, ... store_transposed=False, renumber=False, do_expensive_check=False) >>> (vertices, distances, predecessors) = pylibcugraph.sssp( ... resource_handle, G, source=1, cutoff=999, diff --git a/python/pylibcugraph/pylibcugraph/tests/conftest.py b/python/pylibcugraph/pylibcugraph/tests/conftest.py index a7fcbfdb42a..228147a6e9f 100644 --- a/python/pylibcugraph/pylibcugraph/tests/conftest.py +++ b/python/pylibcugraph/pylibcugraph/tests/conftest.py @@ -1,4 +1,4 @@ -# Copyright (c) 2021-2022, NVIDIA CORPORATION. +# Copyright (c) 2021-2023, NVIDIA CORPORATION. # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at @@ -135,11 +135,11 @@ def create_SGGraph(device_srcs, device_dsts, device_weights, transposed=False): graph_props = GraphProperties(is_symmetric=False, is_multigraph=False) g = SGGraph( - resource_handle, - graph_props, - device_srcs, - device_dsts, - device_weights, + resource_handle=resource_handle, + graph_properties=graph_props, + src_or_offset_array=device_srcs, + dst_or_index_array=device_dsts, + weight_array=device_weights, store_transposed=transposed, renumber=False, do_expensive_check=False, diff --git a/python/pylibcugraph/pylibcugraph/tests/test_eigenvector_centrality.py b/python/pylibcugraph/pylibcugraph/tests/test_eigenvector_centrality.py index b4ff29f31c4..551dd58bdd6 100644 --- a/python/pylibcugraph/pylibcugraph/tests/test_eigenvector_centrality.py +++ b/python/pylibcugraph/pylibcugraph/tests/test_eigenvector_centrality.py @@ -1,4 +1,4 @@ -# Copyright (c) 2022, NVIDIA CORPORATION. +# Copyright (c) 2022-2023, NVIDIA CORPORATION. # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at @@ -56,11 +56,11 @@ def _generic_eigenvector_test( resource_handle = ResourceHandle() graph_props = GraphProperties(is_symmetric=False, is_multigraph=False) G = SGGraph( - resource_handle, - graph_props, - src_arr, - dst_arr, - wgt_arr, + resource_handle=resource_handle, + graph_properties=graph_props, + src_or_offset_array=src_arr, + dst_or_index_array=dst_arr, + weight_array=wgt_arr, store_transposed=False, renumber=False, do_expensive_check=True, diff --git a/python/pylibcugraph/pylibcugraph/tests/test_graph_sg.py b/python/pylibcugraph/pylibcugraph/tests/test_graph_sg.py index 4ebb6f1895e..b555a9a16bb 100644 --- a/python/pylibcugraph/pylibcugraph/tests/test_graph_sg.py +++ b/python/pylibcugraph/pylibcugraph/tests/test_graph_sg.py @@ -85,11 +85,11 @@ def test_sg_graph(graph_data): if is_valid: g = SGGraph( # noqa:F841 - resource_handle, - graph_props, - device_srcs, - device_dsts, - device_weights, + resource_handle=resource_handle, + graph_properties=graph_props, + src_or_offset_array=device_srcs, + dst_or_index_array=device_dsts, + weight_array=device_weights, store_transposed=False, renumber=False, do_expensive_check=False, @@ -100,11 +100,11 @@ def test_sg_graph(graph_data): else: with pytest.raises(ValueError): SGGraph( - resource_handle, - graph_props, - device_srcs, - device_dsts, - device_weights, + resource_handle=resource_handle, + graph_properties=graph_props, + src_or_offset_array=device_srcs, + dst_or_index_array=device_dsts, + weight_array=device_weights, store_transposed=False, renumber=False, do_expensive_check=False, @@ -130,7 +130,6 @@ def test_SGGraph_create_from_cudf(): SGGraph, ) - print("get edgelist...", end="", flush=True) edgelist = cudf.DataFrame( { "src": [0, 1, 2], @@ -139,10 +138,6 @@ def test_SGGraph_create_from_cudf(): } ) - print("edgelist = ", edgelist) - print("done", flush=True) - print("create Graph...", end="", flush=True) - graph_props = GraphProperties(is_multigraph=False, is_symmetric=False) plc_graph = SGGraph( diff --git a/python/pylibcugraph/pylibcugraph/tests/test_katz_centrality.py b/python/pylibcugraph/pylibcugraph/tests/test_katz_centrality.py index d12f90426fa..9550d3be481 100644 --- a/python/pylibcugraph/pylibcugraph/tests/test_katz_centrality.py +++ b/python/pylibcugraph/pylibcugraph/tests/test_katz_centrality.py @@ -1,4 +1,4 @@ -# Copyright (c) 2022, NVIDIA CORPORATION. +# Copyright (c) 2022-2023, NVIDIA CORPORATION. # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at @@ -53,11 +53,11 @@ def _generic_katz_test( resource_handle = ResourceHandle() graph_props = GraphProperties(is_symmetric=False, is_multigraph=False) G = SGGraph( - resource_handle, - graph_props, - src_arr, - dst_arr, - wgt_arr, + resource_handle=resource_handle, + graph_properties=graph_props, + src_or_offset_array=src_arr, + dst_or_index_array=dst_arr, + weight_array=wgt_arr, store_transposed=False, renumber=False, do_expensive_check=True, diff --git a/python/pylibcugraph/pylibcugraph/tests/test_louvain.py b/python/pylibcugraph/pylibcugraph/tests/test_louvain.py index adea5e01f15..620c50f8412 100644 --- a/python/pylibcugraph/pylibcugraph/tests/test_louvain.py +++ b/python/pylibcugraph/pylibcugraph/tests/test_louvain.py @@ -81,11 +81,11 @@ def test_sg_louvain_cupy(): resolution = 1.0 sg = SGGraph( - resource_handle, - graph_props, - device_srcs, - device_dsts, - device_weights, + resource_handle=resource_handle, + graph_properties=graph_props, + src_or_offset_array=device_srcs, + dst_or_index_array=device_dsts, + weight_array=device_weights, store_transposed=False, renumber=True, do_expensive_check=False, @@ -135,11 +135,11 @@ def test_sg_louvain_cudf(): resolution = 1.0 sg = SGGraph( - resource_handle, - graph_props, - device_srcs, - device_dsts, - device_weights, + resource_handle=resource_handle, + graph_properties=graph_props, + src_or_offset_array=device_srcs, + dst_or_index_array=device_dsts, + weight_array=device_weights, store_transposed=False, renumber=True, do_expensive_check=False, diff --git a/python/pylibcugraph/pylibcugraph/tests/test_node2vec.py b/python/pylibcugraph/pylibcugraph/tests/test_node2vec.py index 0e400a5306c..fb303ce8047 100644 --- a/python/pylibcugraph/pylibcugraph/tests/test_node2vec.py +++ b/python/pylibcugraph/pylibcugraph/tests/test_node2vec.py @@ -1,4 +1,4 @@ -# Copyright (c) 2022, NVIDIA CORPORATION. +# Copyright (c) 2022-2023, NVIDIA CORPORATION. # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at @@ -94,11 +94,11 @@ def _run_node2vec( resource_handle = ResourceHandle() graph_props = GraphProperties(is_symmetric=False, is_multigraph=False) G = SGGraph( - resource_handle, - graph_props, - src_arr, - dst_arr, - wgt_arr, + resource_handle=resource_handle, + graph_properties=graph_props, + src_or_offset_array=src_arr, + dst_or_index_array=dst_arr, + weight_array=wgt_arr, store_transposed=False, renumber=renumbered, do_expensive_check=True, @@ -795,11 +795,11 @@ def test_node2vec_renumber_cupy(graph_file, renumber): resource_handle = ResourceHandle() graph_props = GraphProperties(is_symmetric=False, is_multigraph=False) G = SGGraph( - resource_handle, - graph_props, - src_arr, - dst_arr, - wgt_arr, + resource_handle=resource_handle, + graph_properties=graph_props, + src_or_offset_array=src_arr, + dst_or_index_array=dst_arr, + weight_array=wgt_arr, store_transposed=False, renumber=renumber, do_expensive_check=True, diff --git a/python/pylibcugraph/pylibcugraph/tests/test_pagerank.py b/python/pylibcugraph/pylibcugraph/tests/test_pagerank.py index 56c4878324f..2a313a33f83 100644 --- a/python/pylibcugraph/pylibcugraph/tests/test_pagerank.py +++ b/python/pylibcugraph/pylibcugraph/tests/test_pagerank.py @@ -1,4 +1,4 @@ -# Copyright (c) 2022, NVIDIA CORPORATION. +# Copyright (c) 2022-2023, NVIDIA CORPORATION. # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at diff --git a/python/pylibcugraph/pylibcugraph/tests/test_sssp.py b/python/pylibcugraph/pylibcugraph/tests/test_sssp.py index ab46af4ff55..6ffbab76ae2 100644 --- a/python/pylibcugraph/pylibcugraph/tests/test_sssp.py +++ b/python/pylibcugraph/pylibcugraph/tests/test_sssp.py @@ -1,4 +1,4 @@ -# Copyright (c) 2022, NVIDIA CORPORATION. +# Copyright (c) 2022-2023, NVIDIA CORPORATION. # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at diff --git a/python/pylibcugraph/pylibcugraph/tests/test_triangle_count.py b/python/pylibcugraph/pylibcugraph/tests/test_triangle_count.py index aa0d5cd35f5..1862f94ac26 100644 --- a/python/pylibcugraph/pylibcugraph/tests/test_triangle_count.py +++ b/python/pylibcugraph/pylibcugraph/tests/test_triangle_count.py @@ -1,4 +1,4 @@ -# Copyright (c) 2022, NVIDIA CORPORATION. +# Copyright (c) 2022-2023, NVIDIA CORPORATION. # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at @@ -81,11 +81,11 @@ def test_sg_triangle_count_cupy(): start_list = None sg = SGGraph( - resource_handle, - graph_props, - device_srcs, - device_dsts, - device_weights, + resource_handle=resource_handle, + graph_properties=graph_props, + src_or_offset_array=device_srcs, + dst_or_index_array=device_dsts, + weight_array=device_weights, store_transposed=False, renumber=True, do_expensive_check=False, @@ -131,11 +131,11 @@ def test_sg_triangle_count_cudf(): start_list = None sg = SGGraph( - resource_handle, - graph_props, - device_srcs, - device_dsts, - device_weights, + resource_handle=resource_handle, + graph_properties=graph_props, + src_or_offset_array=device_srcs, + dst_or_index_array=device_dsts, + weight_array=device_weights, store_transposed=False, renumber=True, do_expensive_check=False, diff --git a/python/pylibcugraph/pylibcugraph/tests/test_uniform_neighbor_sample.py b/python/pylibcugraph/pylibcugraph/tests/test_uniform_neighbor_sample.py index ac04635edcf..ffa90731483 100644 --- a/python/pylibcugraph/pylibcugraph/tests/test_uniform_neighbor_sample.py +++ b/python/pylibcugraph/pylibcugraph/tests/test_uniform_neighbor_sample.py @@ -95,11 +95,11 @@ def test_neighborhood_sampling_cupy( num_edges = max(len(device_srcs), len(device_dsts)) sg = SGGraph( - resource_handle, - graph_props, - device_srcs, - device_dsts, - device_weights, + resource_handle=resource_handle, + graph_properties=graph_props, + src_or_offset_array=device_srcs, + dst_or_index_array=device_dsts, + weight_array=device_weights, store_transposed=store_transposed, renumber=renumber, do_expensive_check=False, @@ -153,11 +153,11 @@ def test_neighborhood_sampling_cudf( num_edges = max(len(device_srcs), len(device_dsts)) sg = SGGraph( - resource_handle, - graph_props, - device_srcs, - device_dsts, - device_weights, + resource_handle=resource_handle, + graph_properties=graph_props, + src_or_offset_array=device_srcs, + dst_or_index_array=device_dsts, + weight_array=device_weights, store_transposed=store_transposed, renumber=renumber, do_expensive_check=False, @@ -203,11 +203,11 @@ def test_neighborhood_sampling_large_sg_graph(gpubenchmark): fanout_vals = np.asarray([1, 2], dtype=np.int32) sg = SGGraph( - resource_handle, - graph_props, - device_srcs, - device_dsts, - device_weights, + resource_handle=resource_handle, + graph_properties=graph_props, + src_or_offset_array=device_srcs, + dst_or_index_array=device_dsts, + weight_array=device_weights, store_transposed=True, renumber=False, do_expensive_check=False, diff --git a/python/pylibcugraph/pylibcugraph/tests/test_utils.py b/python/pylibcugraph/pylibcugraph/tests/test_utils.py index 036a62b9c1e..64947c21b74 100644 --- a/python/pylibcugraph/pylibcugraph/tests/test_utils.py +++ b/python/pylibcugraph/pylibcugraph/tests/test_utils.py @@ -1,4 +1,4 @@ -# Copyright (c) 2022, NVIDIA CORPORATION. +# Copyright (c) 2022-2023, NVIDIA CORPORATION. # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at diff --git a/python/pylibcugraph/pylibcugraph/uniform_random_walks.pyx b/python/pylibcugraph/pylibcugraph/uniform_random_walks.pyx index 1570523beb8..677695f93a9 100644 --- a/python/pylibcugraph/pylibcugraph/uniform_random_walks.pyx +++ b/python/pylibcugraph/pylibcugraph/uniform_random_walks.pyx @@ -116,7 +116,7 @@ def uniform_random_walks(ResourceHandle resource_handle, cdef cugraph_type_erased_device_array_view_t* path_ptr = \ cugraph_random_walk_result_get_paths(result_ptr) - if input_graph.weights_view_ptr is NULL: + if input_graph.weights_view_ptr is NULL and input_graph.weights_view_ptr_ptr is NULL: cupy_weights = None else: weights_ptr = cugraph_random_walk_result_get_weights(result_ptr) diff --git a/python/pylibcugraph/pylibcugraph/weakly_connected_components.pyx b/python/pylibcugraph/pylibcugraph/weakly_connected_components.pyx index 7cc0d8ab4c1..240c374353d 100644 --- a/python/pylibcugraph/pylibcugraph/weakly_connected_components.pyx +++ b/python/pylibcugraph/pylibcugraph/weakly_connected_components.pyx @@ -129,7 +129,7 @@ def weakly_connected_components(ResourceHandle resource_handle, >>> graph_props = pylibcugraph.GraphProperties( ... is_symmetric=True, is_multigraph=False) >>> G = pylibcugraph.SGGraph( - ... resource_handle, graph_props, srcs, dsts, weights, + ... resource_handle, graph_props, srcs, dsts, weight_array=weights, ... store_transposed=False, renumber=True, do_expensive_check=False) >>> (vertices, labels) = weakly_connected_components( ... resource_handle, G, None, None, None, None, False) From 0a297526042460a16c48a1aca06e2dcec58fcfe7 Mon Sep 17 00:00:00 2001 From: Rick Ratzel Date: Sun, 26 Nov 2023 21:32:47 -0600 Subject: [PATCH 100/111] Updates version for 24.02 --- build.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sh b/build.sh index eef19046d85..fa7a4f6f363 100755 --- a/build.sh +++ b/build.sh @@ -18,7 +18,7 @@ ARGS=$* # script, and that this script resides in the repo dir! REPODIR=$(cd $(dirname $0); pwd) -RAPIDS_VERSION=23.12 +RAPIDS_VERSION=24.02 # Valid args to this script (all possible targets and options) - only one per line VALIDARGS=" From 1e446c4da80dc3e023c9765094c4507d1334ea45 Mon Sep 17 00:00:00 2001 From: Chuck Hastings <45364586+ChuckHastings@users.noreply.github.com> Date: Mon, 27 Nov 2023 20:35:39 -0500 Subject: [PATCH 101/111] Correct defect found in DLFW testing (#4021) In MG tests, we had 2 different coding approaches, and one of the paths had a defect. This makes all of the coding consistent and corrects the bug. Authors: - Chuck Hastings (https://github.com/ChuckHastings) Approvers: - Seunghwa Kang (https://github.com/seunghwak) URL: https://github.com/rapidsai/cugraph/pull/4021 --- cpp/src/c_api/graph_mg.cpp | 6 +- cpp/tests/c_api/mg_test_utils.cpp | 110 ++++++++++++------------------ 2 files changed, 46 insertions(+), 70 deletions(-) diff --git a/cpp/src/c_api/graph_mg.cpp b/cpp/src/c_api/graph_mg.cpp index 5413949e3a3..326022a3fa9 100644 --- a/cpp/src/c_api/graph_mg.cpp +++ b/cpp/src/c_api/graph_mg.cpp @@ -523,9 +523,9 @@ extern "C" cugraph_error_code_t cugraph_mg_graph_create( NULL, &src, &dst, - &weights, - &edge_ids, - &edge_type_ids, + (weights == nullptr) ? nullptr : &weights, + (edge_ids == nullptr) ? nullptr : &edge_ids, + (edge_type_ids == nullptr) ? nullptr : &edge_type_ids, store_transposed, 1, FALSE, diff --git a/cpp/tests/c_api/mg_test_utils.cpp b/cpp/tests/c_api/mg_test_utils.cpp index 15df613ae05..6eec436e77d 100644 --- a/cpp/tests/c_api/mg_test_utils.cpp +++ b/cpp/tests/c_api/mg_test_utils.cpp @@ -158,30 +158,22 @@ extern "C" int create_mg_test_graph(const cugraph_resource_handle_t* handle, rank = cugraph_resource_handle_get_rank(handle); - if (rank == 0) { - ret_code = - cugraph_type_erased_device_array_create(handle, num_edges, vertex_tid, &src, ret_error); - TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "src create failed."); - TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, cugraph_error_message(*ret_error)); + size_t original_num_edges = num_edges; - ret_code = - cugraph_type_erased_device_array_create(handle, num_edges, vertex_tid, &dst, ret_error); - TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "dst create failed."); + if (rank != 0) num_edges = 0; - ret_code = - cugraph_type_erased_device_array_create(handle, num_edges, weight_tid, &wgt, ret_error); - TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "wgt create failed."); - } else { - ret_code = cugraph_type_erased_device_array_create(handle, 0, vertex_tid, &src, ret_error); - TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "src create failed."); - TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, cugraph_error_message(*ret_error)); + ret_code = + cugraph_type_erased_device_array_create(handle, num_edges, vertex_tid, &src, ret_error); + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "src create failed."); + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, cugraph_error_message(*ret_error)); - ret_code = cugraph_type_erased_device_array_create(handle, 0, vertex_tid, &dst, ret_error); - TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "dst create failed."); + ret_code = + cugraph_type_erased_device_array_create(handle, num_edges, vertex_tid, &dst, ret_error); + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "dst create failed."); - ret_code = cugraph_type_erased_device_array_create(handle, 0, weight_tid, &wgt, ret_error); - TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "wgt create failed."); - } + ret_code = + cugraph_type_erased_device_array_create(handle, num_edges, weight_tid, &wgt, ret_error); + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "wgt create failed."); src_view = cugraph_type_erased_device_array_view(src); dst_view = cugraph_type_erased_device_array_view(dst); @@ -207,7 +199,7 @@ extern "C" int create_mg_test_graph(const cugraph_resource_handle_t* handle, NULL, NULL, store_transposed, - num_edges, + original_num_edges, // UNUSED FALSE, p_graph, ret_error); @@ -260,30 +252,22 @@ extern "C" int create_mg_test_graph_double(const cugraph_resource_handle_t* hand rank = cugraph_resource_handle_get_rank(handle); - if (rank == 0) { - ret_code = - cugraph_type_erased_device_array_create(handle, num_edges, vertex_tid, &src, ret_error); - TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "src create failed."); - TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, cugraph_error_message(*ret_error)); + size_t original_num_edges = num_edges; - ret_code = - cugraph_type_erased_device_array_create(handle, num_edges, vertex_tid, &dst, ret_error); - TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "dst create failed."); + if (rank != 0) num_edges = 0; - ret_code = - cugraph_type_erased_device_array_create(handle, num_edges, weight_tid, &wgt, ret_error); - TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "wgt create failed."); - } else { - ret_code = cugraph_type_erased_device_array_create(handle, 0, vertex_tid, &src, ret_error); - TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "src create failed."); - TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, cugraph_error_message(*ret_error)); + ret_code = + cugraph_type_erased_device_array_create(handle, num_edges, vertex_tid, &src, ret_error); + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "src create failed."); + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, cugraph_error_message(*ret_error)); - ret_code = cugraph_type_erased_device_array_create(handle, 0, vertex_tid, &dst, ret_error); - TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "dst create failed."); + ret_code = + cugraph_type_erased_device_array_create(handle, num_edges, vertex_tid, &dst, ret_error); + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "dst create failed."); - ret_code = cugraph_type_erased_device_array_create(handle, 0, weight_tid, &wgt, ret_error); - TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "wgt create failed."); - } + ret_code = + cugraph_type_erased_device_array_create(handle, num_edges, weight_tid, &wgt, ret_error); + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "wgt create failed."); src_view = cugraph_type_erased_device_array_view(src); dst_view = cugraph_type_erased_device_array_view(dst); @@ -309,7 +293,7 @@ extern "C" int create_mg_test_graph_double(const cugraph_resource_handle_t* hand NULL, NULL, store_transposed, - num_edges, + original_num_edges, // UNUSED FALSE, p_graph, ret_error); @@ -357,30 +341,22 @@ extern "C" int create_mg_test_graph_with_edge_ids(const cugraph_resource_handle_ rank = cugraph_resource_handle_get_rank(handle); - if (rank == 0) { - ret_code = - cugraph_type_erased_device_array_create(handle, num_edges, vertex_tid, &src, ret_error); - TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "src create failed."); - TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, cugraph_error_message(*ret_error)); + size_t original_num_edges = num_edges; - ret_code = - cugraph_type_erased_device_array_create(handle, num_edges, vertex_tid, &dst, ret_error); - TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "dst create failed."); + if (rank != 0) num_edges = 0; - ret_code = - cugraph_type_erased_device_array_create(handle, num_edges, edge_tid, &idx, ret_error); - TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "idx create failed."); - } else { - ret_code = cugraph_type_erased_device_array_create(handle, 0, vertex_tid, &src, ret_error); - TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "src create failed."); - TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, cugraph_error_message(*ret_error)); + ret_code = + cugraph_type_erased_device_array_create(handle, num_edges, vertex_tid, &src, ret_error); + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "src create failed."); + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, cugraph_error_message(*ret_error)); - ret_code = cugraph_type_erased_device_array_create(handle, 0, vertex_tid, &dst, ret_error); - TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "dst create failed."); + ret_code = + cugraph_type_erased_device_array_create(handle, num_edges, vertex_tid, &dst, ret_error); + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "dst create failed."); - ret_code = cugraph_type_erased_device_array_create(handle, 0, edge_tid, &idx, ret_error); - TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "wgt create failed."); - } + ret_code = + cugraph_type_erased_device_array_create(handle, num_edges, edge_tid, &idx, ret_error); + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "idx create failed."); src_view = cugraph_type_erased_device_array_view(src); dst_view = cugraph_type_erased_device_array_view(dst); @@ -406,7 +382,7 @@ extern "C" int create_mg_test_graph_with_edge_ids(const cugraph_resource_handle_ idx_view, NULL, store_transposed, - num_edges, + original_num_edges, // UNUSED FALSE, p_graph, ret_error); @@ -464,7 +440,7 @@ extern "C" int create_mg_test_graph_with_properties(const cugraph_resource_handl size_t original_num_edges = num_edges; - if (rank == 0) num_edges = 0; + if (rank != 0) num_edges = 0; ret_code = cugraph_type_erased_device_array_create(handle, num_edges, vertex_tid, &src, ret_error); @@ -529,7 +505,7 @@ extern "C" int create_mg_test_graph_with_properties(const cugraph_resource_handl idx_view, type_view, store_transposed, - original_num_edges, + original_num_edges, // UNUSED FALSE, p_graph, ret_error); @@ -593,7 +569,7 @@ int create_mg_test_graph_new(const cugraph_resource_handle_t* handle, size_t original_num_edges = num_edges; if (rank != 0) num_edges = 0; - + ret_code = cugraph_type_erased_device_array_create(handle, num_edges, vertex_tid, &src, ret_error); TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "src create failed."); @@ -658,7 +634,7 @@ int create_mg_test_graph_new(const cugraph_resource_handle_t* handle, edge_id_view, edge_type_view, store_transposed, - renumber, + original_num_edges, // UNUSED FALSE, graph, ret_error); From 6041ab9446ffc4625a5da10f70b1a4e00003f98e Mon Sep 17 00:00:00 2001 From: Rick Ratzel <3039903+rlratzel@users.noreply.github.com> Date: Wed, 29 Nov 2023 15:23:00 -0600 Subject: [PATCH 102/111] Removes unsupported `setup.py` calls, cleans up text (#4024) closes #4008 * Removes unsupported `setup.py` calls, which resulted in issue #4008 . The document now describes how to use `build.sh`, which is more consistent with docs for other RAPIDS libs. * Removes `build.sh` output from docs in favor of documenting how to use `--help` to eliminate the doc being out-of-date with the script. * Removes extra wording in places and cleans up various sections. Authors: - Rick Ratzel (https://github.com/rlratzel) Approvers: - Don Acosta (https://github.com/acostadon) - Brad Rees (https://github.com/BradReesWork) URL: https://github.com/rapidsai/cugraph/pull/4024 --- .../source/installation/source_build.md | 195 ++++++------------ 1 file changed, 65 insertions(+), 130 deletions(-) diff --git a/docs/cugraph/source/installation/source_build.md b/docs/cugraph/source/installation/source_build.md index f5ee0741da6..1a129d45295 100644 --- a/docs/cugraph/source/installation/source_build.md +++ b/docs/cugraph/source/installation/source_build.md @@ -1,53 +1,46 @@ # Building from Source -The following instructions are for users wishing to build cuGraph from source code. These instructions are tested on supported distributions of Linux, CUDA, and Python - See [RAPIDS Getting Started](https://rapids.ai/start.html) for list of supported environments. Other operating systems _might be_ compatible, but are not currently tested. - -The cuGraph package include both a C/C++ CUDA portion and a python portion. Both libraries need to be installed in order for cuGraph to operate correctly. +These instructions are tested on supported versions/distributions of Linux, +CUDA, and Python - See [RAPIDS Getting Started](https://rapids.ai/start.html) +for the list of supported environments. Other environments _might be_ +compatible, but are not currently tested. ## Prerequisites -__Compiler:__ +__Compilers:__ * `gcc` version 9.3+ -* `nvcc` version 11.0+ -* `cmake` version 3.20.1+ +* `nvcc` version 11.5+ __CUDA:__ -* CUDA 11.0+ +* CUDA 11.2+ * NVIDIA driver 450.80.02+ * Pascal architecture or better -You can obtain CUDA from [https://developer.nvidia.com/cuda-downloads](https://developer.nvidia.com/cuda-downloads). - -__Packages:__ -* `cmake` version 3.20.1+ -* `libcugraphops` (version matching source branch version, eg. `23.10`) - -You can obtain `libcugraphops` using `conda`/`mamba` from the `nvidia` channel, or using `pip` with the `--extra-index-url=https://pypi.nvidia.com` option. See the [RAPIDS docs](https://docs.rapids.ai/install#environment) for more details. - -## Building cuGraph -To install cuGraph from source, ensure the dependencies are met. +Further details and download links for these prerequisites are available on the +[RAPIDS System Requirements page](https://docs.rapids.ai/install#system-req). +## Setting up the development environment -### Clone Repo and Configure Conda Environment -__GIT clone a version of the repository__ - - ```bash - # Set the localtion to cuGraph in an environment variable CUGRAPH_HOME - export CUGRAPH_HOME=$(pwd)/cugraph - - # Download the cuGraph repo - if you have a folked version, use that path here instead - git clone https://github.com/rapidsai/cugraph.git $CUGRAPH_HOME +### Clone the repository: +```bash +CUGRAPH_HOME=$(pwd)/cugraph +git clone https://github.com/rapidsai/cugraph.git $CUGRAPH_HOME +cd $CUGRAPH_HOME +``` - cd $CUGRAPH_HOME - ``` +### Create the conda environment -__Create the conda development environment__ +Using conda is the easiest way to install both the build and runtime +dependencies for cugraph. While it is possible to build and run cugraph without +conda, the required packages occasionally change, making it difficult to +document here. The best way to see the current dependencies needed for a build +and run environment is to examine the list of packages in the [conda +environment YAML +files](https://github.com/rapidsai/cugraph/blob/main/conda/environments). ```bash -# create the conda environment (assuming in base `cugraph` directory) - # for CUDA 11.x -conda env create --name cugraph_dev --file conda/environments/all_cuda-118_arch-x86_64.yaml +conda env create --name cugraph_dev --file $CUGRAPH_HOME/conda/environments/all_cuda-118_arch-x86_64.yaml # activate the environment conda activate cugraph_dev @@ -56,101 +49,53 @@ conda activate cugraph_dev conda deactivate ``` - - The environment can be updated as development includes/changes the dependencies. To do so, run: - +The environment can be updated as cugraph adds/removes/updates its dependencies. To do so, run: ```bash - -# Where XXX is the CUDA 11 version -conda env update --name cugraph_dev --file conda/environments/cugraph_dev_cuda11.XXX.yml - +# for CUDA 11.x +conda env update --name cugraph_dev --file $CUGRAPH_HOME/conda/environments/all_cuda-118_arch-x86_64.yaml conda activate cugraph_dev ``` +### Build and Install -### Build and Install Using the `build.sh` Script -Using the `build.sh` script make compiling and installing cuGraph a breeze. To build and install, simply do: +#### Build and install using `build.sh` +Using the `build.sh` script, located in the `$CUGRAPH_HOME` directory, is the +recommended way to build and install the cugraph libraries. By default, +`build.sh` will build and install a predefined set of targets +(packages/libraries), but can also accept a list of targets to build. -```bash -$ cd $CUGRAPH_HOME -$ ./build.sh clean -$ ./build.sh libcugraph -$ ./build.sh cugraph -``` +For example, to build only the cugraph C++ library (`libcugraph`) and the +high-level python library (`cugraph`) without building the C++ test binaries, +run this command: -There are several other options available on the build script for advanced users. -`build.sh` options: ```bash -build.sh [ ...] [ ...] - where is: - clean - remove all existing build artifacts and configuration (start over) - uninstall - uninstall libcugraph and cugraph from a prior build/install (see also -n) - libcugraph - build libcugraph.so and SG test binaries - libcugraph_etl - build libcugraph_etl.so and SG test binaries - pylibcugraph - build the pylibcugraph Python package - cugraph - build the cugraph Python package - nx-cugraph - build the nx-cugraph Python package - cugraph-service - build the cugraph-service_client and cugraph-service_server Python package - cpp-mgtests - build libcugraph and libcugraph_etl MG tests. Builds MPI communicator, adding MPI as a dependency. - cugraph-dgl - build the cugraph-dgl extensions for DGL - cugraph-pyg - build the cugraph-dgl extensions for PyG - docs - build the docs - and is: - -v - verbose build mode - -g - build for debug - -n - do not install after a successful build - --pydevelop - use setup.py develop instead of install - --allgpuarch - build for all supported GPU architectures - --skip_cpp_tests - do not build the SG test binaries as part of the libcugraph and libcugraph_etl targets - --without_cugraphops - do not build algos that require cugraph-ops - --cmake_default_generator - use the default cmake generator instead of ninja - --clean - clean an individual target (note: to do a complete rebuild, use the clean target described above) - -h - print this text - - default action (no args) is to build and install 'libcugraph' then 'libcugraph_etl' then 'pylibcugraph' then 'cugraph' then 'cugraph-service' targets - -examples: -$ ./build.sh clean # remove prior build artifacts (start over) -$ ./build.sh libcugraph -v # compile and install libcugraph with verbose output -$ ./build.sh libcugraph -g # compile and install libcugraph for debug -$ ./build.sh libcugraph -n # compile libcugraph but do not install - -# make parallelism options can also be defined: Example build jobs using 4 threads (make -j4) -$ PARALLEL_LEVEL=4 ./build.sh libcugraph - -Note that the libraries will be installed to the location set in `$PREFIX` if set (i.e. `export PREFIX=/install/path`), otherwise to `$CONDA_PREFIX`. +$ cd $CUGRAPH_HOME +$ ./build.sh libcugraph pylibcugraph cugraph --skip_cpp_tests ``` +There are several other options available on the build script for advanced +users. Refer to the output of `--help` for details. -## Building each section independently -#### Build and Install the C++/CUDA `libcugraph` Library -CMake depends on the `nvcc` executable being on your path or defined in `$CUDACXX`. - -This project uses cmake for building the C/C++ library. To configure cmake, run: - - ```bash - # Set the localtion to cuGraph in an environment variable CUGRAPH_HOME - export CUGRAPH_HOME=$(pwd)/cugraph - - cd $CUGRAPH_HOME - cd cpp # enter cpp directory - mkdir build # create build directory - cd build # enter the build directory - cmake .. -DCMAKE_INSTALL_PREFIX=$CONDA_PREFIX - - # now build the code - make -j # "-j" starts multiple threads - make install # install the libraries - ``` -The default installation locations are `$CMAKE_INSTALL_PREFIX/lib` and `$CMAKE_INSTALL_PREFIX/include/cugraph` respectively. +Note that libraries will be installed to the location set in `$PREFIX` if set +(i.e. `export PREFIX=/install/path`), otherwise to `$CONDA_PREFIX`. #### Updating the RAFT branch -`libcugraph` uses the [RAFT](https://github.com/rapidsai/raft) library and there are times when it might be desirable to build against a different RAFT branch, such as when working on new features that might span both RAFT and cuGraph. +`libcugraph` uses the [RAFT](https://github.com/rapidsai/raft) library and +there are times when it might be desirable to build against a different RAFT +branch, such as when working on new features that might span both RAFT and +cuGraph. -For local development, the `CPM_raft_SOURCE=` option can be passed to the `cmake` command to enable `libcugraph` to use the local RAFT branch. +For local development, the `CPM_raft_SOURCE=` option can +be passed to the `cmake` command to enable `libcugraph` to use the local RAFT +branch. The `build.sh` script calls `cmake` to build the C/C++ targets, but +developers can call `cmake` directly in order to pass it options like those +described here. Refer to the `build.sh` script to see how to call `cmake` and +other commands directly. -To have CI test a `cugraph` pull request against a different RAFT branch, modify the bottom of the `cpp/cmake/thirdparty/get_raft.cmake` file as follows: +To have CI test a `cugraph` pull request against a different RAFT branch, +modify the bottom of the `cpp/cmake/thirdparty/get_raft.cmake` file as follows: ```cmake # Change pinned tag and fork here to test a commit in CI @@ -167,24 +112,10 @@ find_and_configure_raft(VERSION ${CUGRAPH_MIN_VERSION_raft} ) ``` -When the above change is pushed to a pull request, the continuous integration servers will use the specified RAFT branch to run the cuGraph tests. After the changes in the RAFT branch are merged to the release branch, remember to revert the `get_raft.cmake` file back to the original cuGraph branch. - -### Building and installing the Python package - -2) Install the Python packages to your Python path: - -```bash -cd $CUGRAPH_HOME -cd python -cd pylibcugraph -python setup.py build_ext --inplace -python setup.py install # install pylibcugraph -cd ../cugraph -python setup.py build_ext --inplace -python setup.py install # install cugraph python bindings - -``` - +When the above change is pushed to a pull request, the continuous integration +servers will use the specified RAFT branch to run the cuGraph tests. After the +changes in the RAFT branch are merged to the release branch, remember to revert +the `get_raft.cmake` file back to the original cuGraph branch. ## Run tests @@ -240,7 +171,10 @@ Note: This conda installation only applies to Linux and Python versions 3.8/3.10 ### (OPTIONAL) Set environment variable on activation -It is possible to configure the conda environment to set environmental variables on activation. Providing instructions to set PATH to include the CUDA toolkit bin directory and LD_LIBRARY_PATH to include the CUDA lib64 directory will be helpful. +It is possible to configure the conda environment to set environment variables +on activation. Providing instructions to set PATH to include the CUDA toolkit +bin directory and LD_LIBRARY_PATH to include the CUDA lib64 directory will be +helpful. ```bash cd ~/anaconda3/envs/cugraph_dev @@ -271,7 +205,8 @@ unset LD_LIBRARY_PATH ## Creating documentation -Python API documentation can be generated from _./docs/cugraph directory_. Or through using "./build.sh docs" +Python API documentation can be generated from _./docs/cugraph directory_. Or +through using "./build.sh docs" ## Attribution Portions adopted from https://github.com/pytorch/pytorch/blob/master/CONTRIBUTING.md From 5eaae7d6543de708a21545f8fc236cedfb05f018 Mon Sep 17 00:00:00 2001 From: Seunghwa Kang <45857425+seunghwak@users.noreply.github.com> Date: Thu, 30 Nov 2023 13:03:19 -0800 Subject: [PATCH 103/111] Update count_if_e, transform_reduce_e, and transform_e to support edge masking (#4001) Update transform_e to support graphs with edge masking. This is necessary for K-truss. Authors: - Seunghwa Kang (https://github.com/seunghwak) - Naim (https://github.com/naimnv) Approvers: - Naim (https://github.com/naimnv) - Chuck Hastings (https://github.com/ChuckHastings) URL: https://github.com/rapidsai/cugraph/pull/4001 --- cpp/include/cugraph/graph_view.hpp | 54 +++--- cpp/include/cugraph/utilities/misc_utils.cuh | 1 + cpp/src/prims/count_if_e.cuh | 2 - cpp/src/prims/fill_edge_property.cuh | 73 +++++++- cpp/src/prims/transform_e.cuh | 123 ++++++++---- cpp/src/prims/transform_reduce_e.cuh | 177 +++++++++++------- .../prims/update_edge_src_dst_property.cuh | 8 - cpp/src/structure/graph_view_impl.cuh | 24 +-- cpp/tests/prims/mg_count_if_e.cu | 47 +++-- ...r_v_pair_transform_dst_nbr_intersection.cu | 63 +------ ...er_v_random_select_transform_outgoing_e.cu | 5 +- cpp/tests/prims/mg_transform_e.cu | 24 ++- cpp/tests/prims/mg_transform_reduce_e.cu | 25 ++- cpp/tests/prims/property_generator.cuh | 37 +++- 14 files changed, 404 insertions(+), 259 deletions(-) diff --git a/cpp/include/cugraph/graph_view.hpp b/cpp/include/cugraph/graph_view.hpp index f30a8b7e2af..d79d4635c54 100644 --- a/cpp/include/cugraph/graph_view.hpp +++ b/cpp/include/cugraph/graph_view.hpp @@ -268,7 +268,11 @@ class graph_base_t { properties_(properties){}; vertex_t number_of_vertices() const { return number_of_vertices_; } - edge_t number_of_edges() const { return number_of_edges_; } + edge_t number_of_edges() const + { + CUGRAPH_EXPECTS(!(this->has_edge_mask()), "unimplemented."); + return number_of_edges_; + } template std::enable_if_t::value, bool> is_valid_vertex(vertex_type v) const @@ -285,6 +289,20 @@ class graph_base_t { bool is_symmetric() const { return properties_.is_symmetric; } bool is_multigraph() const { return properties_.is_multigraph; } + void attach_edge_mask(edge_property_view_t edge_mask_view) + { + edge_mask_view_ = edge_mask_view; + } + + void clear_edge_mask() { edge_mask_view_ = std::nullopt; } + + bool has_edge_mask() const { return edge_mask_view_.has_value(); } + + std::optional> edge_mask_view() const + { + return edge_mask_view_; + } + protected: raft::handle_t const* handle_ptr() const { return handle_ptr_; }; graph_properties_t graph_properties() const { return properties_; } @@ -296,6 +314,8 @@ class graph_base_t { edge_t number_of_edges_{0}; graph_properties_t properties_{}; + + std::optional> edge_mask_view_{std::nullopt}; }; } // namespace detail @@ -731,20 +751,6 @@ class graph_view_t edge_mask_view) - { - edge_mask_view_ = edge_mask_view; - } - - void clear_edge_mask() { edge_mask_view_ = std::nullopt; } - - bool has_edge_mask() const { return edge_mask_view_.has_value(); } - - std::optional> edge_mask_view() const - { - return edge_mask_view_; - } - private: std::vector edge_partition_offsets_{}; std::vector edge_partition_indices_{}; @@ -790,8 +796,6 @@ class graph_view_t>, std::optional /* dummy */> local_sorted_unique_edge_dst_vertex_partition_offsets_{std::nullopt}; - - std::optional> edge_mask_view_{std::nullopt}; }; // single-GPU version @@ -1012,28 +1016,12 @@ class graph_view_t edge_mask_view) - { - edge_mask_view_ = edge_mask_view; - } - - void clear_edge_mask() { edge_mask_view_ = std::nullopt; } - - bool has_edge_mask() const { return edge_mask_view_.has_value(); } - - std::optional> edge_mask_view() const - { - return edge_mask_view_; - } - private: edge_t const* offsets_{nullptr}; vertex_t const* indices_{nullptr}; // segment offsets based on vertex degree, relevant only if vertex IDs are renumbered std::optional> segment_offsets_{std::nullopt}; - - std::optional> edge_mask_view_{std::nullopt}; }; } // namespace cugraph diff --git a/cpp/include/cugraph/utilities/misc_utils.cuh b/cpp/include/cugraph/utilities/misc_utils.cuh index a62e8ce85ec..28e2853727f 100644 --- a/cpp/include/cugraph/utilities/misc_utils.cuh +++ b/cpp/include/cugraph/utilities/misc_utils.cuh @@ -19,6 +19,7 @@ #include #include +#include #include #include #include diff --git a/cpp/src/prims/count_if_e.cuh b/cpp/src/prims/count_if_e.cuh index f6e4bc9bead..9cff4f5eceb 100644 --- a/cpp/src/prims/count_if_e.cuh +++ b/cpp/src/prims/count_if_e.cuh @@ -74,8 +74,6 @@ typename GraphViewType::edge_type count_if_e(raft::handle_t const& handle, using vertex_t = typename GraphViewType::vertex_type; using edge_t = typename GraphViewType::edge_type; - CUGRAPH_EXPECTS(!graph_view.has_edge_mask(), "unimplemented."); - if (do_expensive_check) { // currently, nothing to do } diff --git a/cpp/src/prims/fill_edge_property.cuh b/cpp/src/prims/fill_edge_property.cuh index d446944b65b..e6875576044 100644 --- a/cpp/src/prims/fill_edge_property.cuh +++ b/cpp/src/prims/fill_edge_property.cuh @@ -15,6 +15,7 @@ */ #pragma once +#include #include #include #include @@ -23,6 +24,7 @@ #include #include +#include #include @@ -38,21 +40,78 @@ void fill_edge_property(raft::handle_t const& handle, { static_assert(std::is_same_v); + using edge_t = typename GraphViewType::edge_type; + + auto edge_mask_view = graph_view.edge_mask_view(); + auto value_firsts = edge_property_output.value_firsts(); auto edge_counts = edge_property_output.edge_counts(); for (size_t i = 0; i < graph_view.number_of_local_edge_partitions(); ++i) { + auto edge_partition_e_mask = + edge_mask_view + ? thrust::make_optional< + detail::edge_partition_edge_property_device_view_t>( + *edge_mask_view, i) + : thrust::nullopt; + if constexpr (cugraph::has_packed_bool_element< std::remove_reference_t, T>()) { static_assert(std::is_arithmetic_v, "unimplemented for thrust::tuple types."); auto packed_input = input ? packed_bool_full_mask() : packed_bool_empty_mask(); - thrust::fill_n(handle.get_thrust_policy(), - value_firsts[i], - packed_bool_size(static_cast(edge_counts[i])), - packed_input); + auto rem = edge_counts[i] % packed_bools_per_word(); + if (edge_partition_e_mask) { + auto input_first = + thrust::make_zip_iterator(value_firsts[i], (*edge_partition_e_mask).value_first()); + thrust::transform(handle.get_thrust_policy(), + input_first, + input_first + packed_bool_size(static_cast(edge_counts[i] - rem)), + value_firsts[i], + [packed_input] __device__(thrust::tuple pair) { + auto old_value = thrust::get<0>(pair); + auto mask = thrust::get<1>(pair); + return (old_value & ~mask) | (packed_input & mask); + }); + if (rem > 0) { + thrust::transform( + handle.get_thrust_policy(), + input_first + packed_bool_size(static_cast(edge_counts[i] - rem)), + input_first + packed_bool_size(static_cast(edge_counts[i])), + value_firsts[i] + packed_bool_size(static_cast(edge_counts[i] - rem)), + [packed_input, rem] __device__(thrust::tuple pair) { + auto old_value = thrust::get<0>(pair); + auto mask = thrust::get<1>(pair); + return ((old_value & ~mask) | (packed_input & mask)) & packed_bool_partial_mask(rem); + }); + } + } else { + thrust::fill_n(handle.get_thrust_policy(), + value_firsts[i], + packed_bool_size(static_cast(edge_counts[i] - rem)), + packed_input); + if (rem > 0) { + thrust::fill_n( + handle.get_thrust_policy(), + value_firsts[i] + packed_bool_size(static_cast(edge_counts[i] - rem)), + 1, + packed_input & packed_bool_partial_mask(rem)); + } + } } else { - thrust::fill_n( - handle.get_thrust_policy(), value_firsts[i], static_cast(edge_counts[i]), input); + if (edge_partition_e_mask) { + thrust::transform_if(handle.get_thrust_policy(), + thrust::make_constant_iterator(input), + thrust::make_constant_iterator(input) + edge_counts[i], + thrust::make_counting_iterator(edge_t{0}), + value_firsts[i], + thrust::identity{}, + [edge_partition_e_mask = *edge_partition_e_mask] __device__(edge_t i) { + return edge_partition_e_mask.get(i); + }); + } else { + thrust::fill_n( + handle.get_thrust_policy(), value_firsts[i], static_cast(edge_counts[i]), input); + } } } } @@ -79,8 +138,6 @@ void fill_edge_property(raft::handle_t const& handle, edge_property_t& edge_property_output, bool do_expensive_check = false) { - CUGRAPH_EXPECTS(!graph_view.has_edge_mask(), "unimplemented."); - if (do_expensive_check) { // currently, nothing to do } diff --git a/cpp/src/prims/transform_e.cuh b/cpp/src/prims/transform_e.cuh index edacdc8a970..c6623621d24 100644 --- a/cpp/src/prims/transform_e.cuh +++ b/cpp/src/prims/transform_e.cuh @@ -16,10 +16,12 @@ #pragma once #include +#include #include #include #include #include +#include #include #include @@ -44,6 +46,7 @@ template __global__ void transform_e_packed_bool( @@ -53,6 +56,7 @@ __global__ void transform_e_packed_bool( EdgePartitionSrcValueInputWrapper edge_partition_src_value_input, EdgePartitionDstValueInputWrapper edge_partition_dst_value_input, EdgePartitionEdgeValueInputWrapper edge_partition_e_value_input, + thrust::optional edge_partition_e_mask, EdgePartitionEdgeValueOutputWrapper edge_partition_e_value_output, EdgeOp e_op) { @@ -68,11 +72,14 @@ __global__ void transform_e_packed_bool( auto num_edges = edge_partition.number_of_edges(); while (idx < static_cast(packed_bool_size(num_edges))) { + auto edge_mask = packed_bool_full_mask(); + if (edge_partition_e_mask) { edge_mask = *((*edge_partition_e_mask).value_first() + idx); } + auto local_edge_idx = idx * static_cast(packed_bools_per_word()) + static_cast(lane_id); - uint32_t mask{0}; int predicate{0}; - if (local_edge_idx < num_edges) { + + if ((local_edge_idx < num_edges) && (edge_mask & packed_bool_mask(lane_id))) { auto major_idx = edge_partition.major_idx_from_local_edge_idx_nocheck(local_edge_idx); auto major = edge_partition.major_from_major_idx_nocheck(major_idx); auto major_offset = edge_partition.major_offset_from_major_nocheck(major); @@ -91,8 +98,15 @@ __global__ void transform_e_packed_bool( ? int{1} : int{0}; } - mask = __ballot_sync(uint32_t{0xffffffff}, predicate); - if (lane_id == 0) { *(edge_partition_e_value_output.value_first() + idx) = mask; } + uint32_t new_val = __ballot_sync(uint32_t{0xffffffff}, predicate); + if (lane_id == 0) { + if (edge_mask == packed_bool_full_mask()) { + *(edge_partition_e_value_output.value_first() + idx) = new_val; + } else { + auto old_val = *(edge_partition_e_value_output.value_first() + idx); + *(edge_partition_e_value_output.value_first() + idx) = (old_val & ~edge_mask) | new_val; + } + } idx += static_cast(gridDim.x * (blockDim.x / raft::warp_size())); } @@ -178,12 +192,18 @@ void transform_e(raft::handle_t const& handle, typename EdgeValueOutputWrapper::value_iterator, typename EdgeValueOutputWrapper::value_type>; - CUGRAPH_EXPECTS(!graph_view.has_edge_mask(), "unimplemented."); + auto edge_mask_view = graph_view.edge_mask_view(); for (size_t i = 0; i < graph_view.number_of_local_edge_partitions(); ++i) { auto edge_partition = edge_partition_device_view_t( graph_view.local_edge_partition_view(i)); + auto edge_partition_e_mask = + edge_mask_view + ? thrust::make_optional< + detail::edge_partition_edge_property_device_view_t>( + *edge_mask_view, i) + : thrust::nullopt; edge_partition_src_input_device_view_t edge_partition_src_value_input{}; edge_partition_dst_input_device_view_t edge_partition_dst_value_input{}; @@ -214,35 +234,40 @@ void transform_e(raft::handle_t const& handle, edge_partition_src_value_input, edge_partition_dst_value_input, edge_partition_e_value_input, + edge_partition_e_mask, edge_partition_e_value_output, e_op); } } else { - thrust::transform( + thrust::for_each( handle.get_thrust_policy(), thrust::make_counting_iterator(edge_t{0}), thrust::make_counting_iterator(num_edges), - edge_partition_e_value_output.value_first(), [e_op, edge_partition, edge_partition_src_value_input, edge_partition_dst_value_input, - edge_partition_e_value_input] __device__(edge_t i) { - auto major_idx = edge_partition.major_idx_from_local_edge_idx_nocheck(i); - auto major = edge_partition.major_from_major_idx_nocheck(major_idx); - auto major_offset = edge_partition.major_offset_from_major_nocheck(major); - auto minor = *(edge_partition.indices() + i); - auto minor_offset = edge_partition.minor_offset_from_minor_nocheck(minor); - - auto src = GraphViewType::is_storage_transposed ? minor : major; - auto dst = GraphViewType::is_storage_transposed ? major : minor; - auto src_offset = GraphViewType::is_storage_transposed ? minor_offset : major_offset; - auto dst_offset = GraphViewType::is_storage_transposed ? major_offset : minor_offset; - return e_op(src, - dst, - edge_partition_src_value_input.get(src_offset), - edge_partition_dst_value_input.get(dst_offset), - edge_partition_e_value_input.get(i)); + edge_partition_e_value_input, + edge_partition_e_mask, + edge_partition_e_value_output] __device__(edge_t i) { + if (!edge_partition_e_mask || (*edge_partition_e_mask).get(i)) { + auto major_idx = edge_partition.major_idx_from_local_edge_idx_nocheck(i); + auto major = edge_partition.major_from_major_idx_nocheck(major_idx); + auto major_offset = edge_partition.major_offset_from_major_nocheck(major); + auto minor = *(edge_partition.indices() + i); + auto minor_offset = edge_partition.minor_offset_from_minor_nocheck(minor); + + auto src = GraphViewType::is_storage_transposed ? minor : major; + auto dst = GraphViewType::is_storage_transposed ? major : minor; + auto src_offset = GraphViewType::is_storage_transposed ? minor_offset : major_offset; + auto dst_offset = GraphViewType::is_storage_transposed ? major_offset : minor_offset; + auto e_op_result = e_op(src, + dst, + edge_partition_src_value_input.get(src_offset), + edge_partition_dst_value_input.get(dst_offset), + edge_partition_e_value_input.get(i)); + edge_partition_e_value_output.set(i, e_op_result); + } }); } } @@ -336,14 +361,12 @@ void transform_e(raft::handle_t const& handle, typename EdgeValueOutputWrapper::value_iterator, typename EdgeValueOutputWrapper::value_type>; - CUGRAPH_EXPECTS(!graph_view.has_edge_mask(), "unimplemented."); - auto major_first = GraphViewType::is_storage_transposed ? edge_list.dst_begin() : edge_list.src_begin(); auto minor_first = GraphViewType::is_storage_transposed ? edge_list.src_begin() : edge_list.dst_begin(); - auto edge_first = thrust::make_zip_iterator(thrust::make_tuple(major_first, minor_first)); + auto edge_first = thrust::make_zip_iterator(major_first, minor_first); if (do_expensive_check) { CUGRAPH_EXPECTS( @@ -382,10 +405,18 @@ void transform_e(raft::handle_t const& handle, edge_partition_offsets.back() = edge_list.size(); } + auto edge_mask_view = graph_view.edge_mask_view(); + for (size_t i = 0; i < graph_view.number_of_local_edge_partitions(); ++i) { auto edge_partition = edge_partition_device_view_t( graph_view.local_edge_partition_view(i)); + auto edge_partition_e_mask = + edge_mask_view + ? thrust::make_optional< + detail::edge_partition_edge_property_device_view_t>( + *edge_mask_view, i) + : thrust::nullopt; if (do_expensive_check) { CUGRAPH_EXPECTS( @@ -393,7 +424,8 @@ void transform_e(raft::handle_t const& handle, handle.get_thrust_policy(), edge_first + edge_partition_offsets[i], edge_first + edge_partition_offsets[i + 1], - [edge_partition] __device__(thrust::tuple edge) { + [edge_partition, + edge_partition_e_mask] __device__(thrust::tuple edge) { auto major = thrust::get<0>(edge); auto minor = thrust::get<1>(edge); vertex_t major_idx{}; @@ -416,8 +448,19 @@ void transform_e(raft::handle_t const& handle, edge_t edge_offset{}; edge_t local_degree{}; thrust::tie(indices, edge_offset, local_degree) = edge_partition.local_edges(major_idx); - auto it = thrust::lower_bound(thrust::seq, indices, indices + local_degree, minor); - return *it != minor; + auto lower_it = + thrust::lower_bound(thrust::seq, indices, indices + local_degree, minor); + if (*lower_it != minor) { return true; } + if (edge_partition_e_mask) { + auto upper_it = + thrust::upper_bound(thrust::seq, lower_it, indices + local_degree, minor); + if (detail::count_set_bits((*edge_partition_e_mask).value_first(), + edge_offset + thrust::distance(indices, lower_it), + thrust::distance(lower_it, upper_it)) == 0) { + return true; + } + } + return false; }) == 0, "Invalid input arguments: edge_list contains edges that do not exist in the input graph."); } @@ -446,6 +489,7 @@ void transform_e(raft::handle_t const& handle, edge_partition_src_value_input, edge_partition_dst_value_input, edge_partition_e_value_input, + edge_partition_e_mask, edge_partition_e_value_output] __device__(thrust::tuple edge) { auto major = thrust::get<0>(edge); auto minor = thrust::get<1>(edge); @@ -469,7 +513,7 @@ void transform_e(raft::handle_t const& handle, edge_t local_degree{}; thrust::tie(indices, edge_offset, local_degree) = edge_partition.local_edges(major_idx); auto lower_it = thrust::lower_bound(thrust::seq, indices, indices + local_degree, minor); - auto upper_it = thrust::upper_bound(thrust::seq, indices, indices + local_degree, minor); + auto upper_it = thrust::upper_bound(thrust::seq, lower_it, indices + local_degree, minor); auto src = GraphViewType::is_storage_transposed ? minor : major; auto dst = GraphViewType::is_storage_transposed ? major : minor; @@ -478,14 +522,17 @@ void transform_e(raft::handle_t const& handle, for (auto it = lower_it; it != upper_it; ++it) { assert(*it == minor); - auto e_op_result = - e_op(src, - dst, - edge_partition_src_value_input.get(src_offset), - edge_partition_dst_value_input.get(dst_offset), - edge_partition_e_value_input.get(edge_offset + thrust::distance(indices, it))); - edge_partition_e_value_output.set(edge_offset + thrust::distance(indices, it), - e_op_result); + if (!edge_partition_e_mask || + ((*edge_partition_e_mask).get(edge_offset + thrust::distance(indices, it)))) { + auto e_op_result = + e_op(src, + dst, + edge_partition_src_value_input.get(src_offset), + edge_partition_dst_value_input.get(dst_offset), + edge_partition_e_value_input.get(edge_offset + thrust::distance(indices, it))); + edge_partition_e_value_output.set(edge_offset + thrust::distance(indices, it), + e_op_result); + } } }); } diff --git a/cpp/src/prims/transform_reduce_e.cuh b/cpp/src/prims/transform_reduce_e.cuh index 9c23f3fca18..483ab64dcd9 100644 --- a/cpp/src/prims/transform_reduce_e.cuh +++ b/cpp/src/prims/transform_reduce_e.cuh @@ -56,6 +56,7 @@ template __global__ void transform_reduce_e_hypersparse( @@ -65,6 +66,7 @@ __global__ void transform_reduce_e_hypersparse( EdgePartitionSrcValueInputWrapper edge_partition_src_value_input, EdgePartitionDstValueInputWrapper edge_partition_dst_value_input, EdgePartitionEdgeValueInputWrapper edge_partition_e_value_input, + thrust::optional edge_partition_e_mask, ResultIterator result_iter /* size 1 */, EdgeOp e_op) { @@ -101,24 +103,31 @@ __global__ void transform_reduce_e_hypersparse( &edge_partition_src_value_input, &edge_partition_dst_value_input, &edge_partition_e_value_input, + &edge_partition_e_mask, &e_op, major, indices, edge_offset] __device__(auto i) { - auto major_offset = edge_partition.major_offset_from_major_nocheck(major); - auto minor = indices[i]; - auto minor_offset = edge_partition.minor_offset_from_minor_nocheck(minor); - auto src = GraphViewType::is_storage_transposed ? minor : major; - auto dst = GraphViewType::is_storage_transposed ? major : minor; - auto src_offset = - GraphViewType::is_storage_transposed ? minor_offset : static_cast(major_offset); - auto dst_offset = - GraphViewType::is_storage_transposed ? static_cast(major_offset) : minor_offset; - return e_op(src, - dst, - edge_partition_src_value_input.get(src_offset), - edge_partition_dst_value_input.get(dst_offset), - edge_partition_e_value_input.get(edge_offset + i)); + if (!edge_partition_e_mask || (*edge_partition_e_mask).get(edge_offset + i)) { + auto major_offset = edge_partition.major_offset_from_major_nocheck(major); + auto minor = indices[i]; + auto minor_offset = edge_partition.minor_offset_from_minor_nocheck(minor); + auto src = GraphViewType::is_storage_transposed ? minor : major; + auto dst = GraphViewType::is_storage_transposed ? major : minor; + auto src_offset = GraphViewType::is_storage_transposed + ? minor_offset + : static_cast(major_offset); + auto dst_offset = GraphViewType::is_storage_transposed + ? static_cast(major_offset) + : minor_offset; + return e_op(src, + dst, + edge_partition_src_value_input.get(src_offset), + edge_partition_dst_value_input.get(dst_offset), + edge_partition_e_value_input.get(edge_offset + i)); + } else { + return e_op_result_t{}; + } }, e_op_result_t{}, edge_property_add); @@ -135,6 +144,7 @@ template __global__ void transform_reduce_e_low_degree( @@ -146,6 +156,7 @@ __global__ void transform_reduce_e_low_degree( EdgePartitionSrcValueInputWrapper edge_partition_src_value_input, EdgePartitionDstValueInputWrapper edge_partition_dst_value_input, EdgePartitionEdgeValueInputWrapper edge_partition_e_value_input, + thrust::optional edge_partition_e_mask, ResultIterator result_iter /* size 1 */, EdgeOp e_op) { @@ -177,27 +188,34 @@ __global__ void transform_reduce_e_low_degree( &edge_partition_src_value_input, &edge_partition_dst_value_input, &edge_partition_e_value_input, + &edge_partition_e_mask, &e_op, major_offset, indices, edge_offset] __device__(auto i) { - auto minor = indices[i]; - auto minor_offset = edge_partition.minor_offset_from_minor_nocheck(minor); - auto src = GraphViewType::is_storage_transposed - ? minor - : edge_partition.major_from_major_offset_nocheck(major_offset); - auto dst = GraphViewType::is_storage_transposed - ? edge_partition.major_from_major_offset_nocheck(major_offset) - : minor; - auto src_offset = - GraphViewType::is_storage_transposed ? minor_offset : static_cast(major_offset); - auto dst_offset = - GraphViewType::is_storage_transposed ? static_cast(major_offset) : minor_offset; - return e_op(src, - dst, - edge_partition_src_value_input.get(src_offset), - edge_partition_dst_value_input.get(dst_offset), - edge_partition_e_value_input.get(edge_offset + i)); + if (!edge_partition_e_mask || (*edge_partition_e_mask).get(edge_offset + i)) { + auto minor = indices[i]; + auto minor_offset = edge_partition.minor_offset_from_minor_nocheck(minor); + auto src = GraphViewType::is_storage_transposed + ? minor + : edge_partition.major_from_major_offset_nocheck(major_offset); + auto dst = GraphViewType::is_storage_transposed + ? edge_partition.major_from_major_offset_nocheck(major_offset) + : minor; + auto src_offset = GraphViewType::is_storage_transposed + ? minor_offset + : static_cast(major_offset); + auto dst_offset = GraphViewType::is_storage_transposed + ? static_cast(major_offset) + : minor_offset; + return e_op(src, + dst, + edge_partition_src_value_input.get(src_offset), + edge_partition_dst_value_input.get(dst_offset), + edge_partition_e_value_input.get(edge_offset + i)); + } else { + return e_op_result_t{}; + } }, e_op_result_t{}, edge_property_add); @@ -214,6 +232,7 @@ template __global__ void transform_reduce_e_mid_degree( @@ -225,6 +244,7 @@ __global__ void transform_reduce_e_mid_degree( EdgePartitionSrcValueInputWrapper edge_partition_src_value_input, EdgePartitionDstValueInputWrapper edge_partition_dst_value_input, EdgePartitionEdgeValueInputWrapper edge_partition_e_value_input, + thrust::optional edge_partition_e_mask, ResultIterator result_iter /* size 1 */, EdgeOp e_op) { @@ -250,24 +270,26 @@ __global__ void transform_reduce_e_mid_degree( edge_t local_degree{}; thrust::tie(indices, edge_offset, local_degree) = edge_partition.local_edges(major_offset); for (edge_t i = lane_id; i < local_degree; i += raft::warp_size()) { - auto minor = indices[i]; - auto minor_offset = edge_partition.minor_offset_from_minor_nocheck(minor); - auto src = GraphViewType::is_storage_transposed - ? minor - : edge_partition.major_from_major_offset_nocheck(major_offset); - auto dst = GraphViewType::is_storage_transposed - ? edge_partition.major_from_major_offset_nocheck(major_offset) - : minor; - auto src_offset = - GraphViewType::is_storage_transposed ? minor_offset : static_cast(major_offset); - auto dst_offset = - GraphViewType::is_storage_transposed ? static_cast(major_offset) : minor_offset; - auto e_op_result = e_op(src, - dst, - edge_partition_src_value_input.get(src_offset), - edge_partition_dst_value_input.get(dst_offset), - edge_partition_e_value_input.get(edge_offset + i)); - e_op_result_sum = edge_property_add(e_op_result_sum, e_op_result); + if (!edge_partition_e_mask || (*edge_partition_e_mask).get(edge_offset + i)) { + auto minor = indices[i]; + auto minor_offset = edge_partition.minor_offset_from_minor_nocheck(minor); + auto src = GraphViewType::is_storage_transposed + ? minor + : edge_partition.major_from_major_offset_nocheck(major_offset); + auto dst = GraphViewType::is_storage_transposed + ? edge_partition.major_from_major_offset_nocheck(major_offset) + : minor; + auto src_offset = + GraphViewType::is_storage_transposed ? minor_offset : static_cast(major_offset); + auto dst_offset = + GraphViewType::is_storage_transposed ? static_cast(major_offset) : minor_offset; + auto e_op_result = e_op(src, + dst, + edge_partition_src_value_input.get(src_offset), + edge_partition_dst_value_input.get(dst_offset), + edge_partition_e_value_input.get(edge_offset + i)); + e_op_result_sum = edge_property_add(e_op_result_sum, e_op_result); + } } idx += gridDim.x * (blockDim.x / raft::warp_size()); } @@ -280,6 +302,7 @@ template __global__ void transform_reduce_e_high_degree( @@ -291,6 +314,7 @@ __global__ void transform_reduce_e_high_degree( EdgePartitionSrcValueInputWrapper edge_partition_src_value_input, EdgePartitionDstValueInputWrapper edge_partition_dst_value_input, EdgePartitionEdgeValueInputWrapper edge_partition_e_value_input, + thrust::optional edge_partition_e_mask, ResultIterator result_iter /* size 1 */, EdgeOp e_op) { @@ -313,24 +337,26 @@ __global__ void transform_reduce_e_high_degree( edge_t local_degree{}; thrust::tie(indices, edge_offset, local_degree) = edge_partition.local_edges(major_offset); for (edge_t i = threadIdx.x; i < local_degree; i += blockDim.x) { - auto minor = indices[i]; - auto minor_offset = edge_partition.minor_offset_from_minor_nocheck(minor); - auto src = GraphViewType::is_storage_transposed - ? minor - : edge_partition.major_from_major_offset_nocheck(major_offset); - auto dst = GraphViewType::is_storage_transposed - ? edge_partition.major_from_major_offset_nocheck(major_offset) - : minor; - auto src_offset = - GraphViewType::is_storage_transposed ? minor_offset : static_cast(major_offset); - auto dst_offset = - GraphViewType::is_storage_transposed ? static_cast(major_offset) : minor_offset; - auto e_op_result = e_op(src, - dst, - edge_partition_src_value_input.get(src_offset), - edge_partition_dst_value_input.get(dst_offset), - edge_partition_e_value_input.get(edge_offset + i)); - e_op_result_sum = edge_property_add(e_op_result_sum, e_op_result); + if (!edge_partition_e_mask || (*edge_partition_e_mask).get(edge_offset + i)) { + auto minor = indices[i]; + auto minor_offset = edge_partition.minor_offset_from_minor_nocheck(minor); + auto src = GraphViewType::is_storage_transposed + ? minor + : edge_partition.major_from_major_offset_nocheck(major_offset); + auto dst = GraphViewType::is_storage_transposed + ? edge_partition.major_from_major_offset_nocheck(major_offset) + : minor; + auto src_offset = + GraphViewType::is_storage_transposed ? minor_offset : static_cast(major_offset); + auto dst_offset = + GraphViewType::is_storage_transposed ? static_cast(major_offset) : minor_offset; + auto e_op_result = e_op(src, + dst, + edge_partition_src_value_input.get(src_offset), + edge_partition_dst_value_input.get(dst_offset), + edge_partition_e_value_input.get(edge_offset + i)); + e_op_result_sum = edge_property_add(e_op_result_sum, e_op_result); + } } idx += gridDim.x; } @@ -417,8 +443,6 @@ T transform_reduce_e(raft::handle_t const& handle, typename EdgeValueInputWrapper::value_iterator, typename EdgeValueInputWrapper::value_type>>; - CUGRAPH_EXPECTS(!graph_view.has_edge_mask(), "unimplemented."); - if (do_expensive_check) { // currently, nothing to do } @@ -431,10 +455,18 @@ T transform_reduce_e(raft::handle_t const& handle, get_dataframe_buffer_begin(result_buffer) + 1, T{}); + auto edge_mask_view = graph_view.edge_mask_view(); + for (size_t i = 0; i < graph_view.number_of_local_edge_partitions(); ++i) { auto edge_partition = edge_partition_device_view_t( graph_view.local_edge_partition_view(i)); + auto edge_partition_e_mask = + edge_mask_view + ? thrust::make_optional< + detail::edge_partition_edge_property_device_view_t>( + *edge_mask_view, i) + : thrust::nullopt; edge_partition_src_input_device_view_t edge_partition_src_value_input{}; edge_partition_dst_input_device_view_t edge_partition_dst_value_input{}; @@ -467,6 +499,7 @@ T transform_reduce_e(raft::handle_t const& handle, edge_partition_src_value_input, edge_partition_dst_value_input, edge_partition_e_value_input, + edge_partition_e_mask, get_dataframe_buffer_begin(result_buffer), e_op); } @@ -482,6 +515,7 @@ T transform_reduce_e(raft::handle_t const& handle, edge_partition_src_value_input, edge_partition_dst_value_input, edge_partition_e_value_input, + edge_partition_e_mask, get_dataframe_buffer_begin(result_buffer), e_op); } @@ -497,6 +531,7 @@ T transform_reduce_e(raft::handle_t const& handle, edge_partition_src_value_input, edge_partition_dst_value_input, edge_partition_e_value_input, + edge_partition_e_mask, get_dataframe_buffer_begin(result_buffer), e_op); } @@ -510,6 +545,7 @@ T transform_reduce_e(raft::handle_t const& handle, edge_partition_src_value_input, edge_partition_dst_value_input, edge_partition_e_value_input, + edge_partition_e_mask, get_dataframe_buffer_begin(result_buffer), e_op); } @@ -527,6 +563,7 @@ T transform_reduce_e(raft::handle_t const& handle, edge_partition_src_value_input, edge_partition_dst_value_input, edge_partition_e_value_input, + edge_partition_e_mask, get_dataframe_buffer_begin(result_buffer), e_op); } @@ -601,8 +638,6 @@ auto transform_reduce_e(raft::handle_t const& handle, edge_op_result_type::type; static_assert(!std::is_same_v); - CUGRAPH_EXPECTS(!graph_view.has_edge_mask(), "unimplemented."); - if (do_expensive_check) { // currently, nothing to do } diff --git a/cpp/src/prims/update_edge_src_dst_property.cuh b/cpp/src/prims/update_edge_src_dst_property.cuh index 2d72a075ca5..b8621e122c6 100644 --- a/cpp/src/prims/update_edge_src_dst_property.cuh +++ b/cpp/src/prims/update_edge_src_dst_property.cuh @@ -866,8 +866,6 @@ void update_edge_src_property( edge_src_property_output, bool do_expensive_check = false) { - CUGRAPH_EXPECTS(!graph_view.has_edge_mask(), "unimplemented."); - if (do_expensive_check) { // currently, nothing to do } @@ -917,8 +915,6 @@ void update_edge_src_property( edge_src_property_output, bool do_expensive_check = false) { - CUGRAPH_EXPECTS(!graph_view.has_edge_mask(), "unimplemented."); - if (do_expensive_check) { auto num_invalids = thrust::count_if( handle.get_thrust_policy(), @@ -985,8 +981,6 @@ void update_edge_dst_property( edge_dst_property_output, bool do_expensive_check = false) { - CUGRAPH_EXPECTS(!graph_view.has_edge_mask(), "unimplemented."); - if (do_expensive_check) { // currently, nothing to do } @@ -1036,8 +1030,6 @@ void update_edge_dst_property( edge_dst_property_output, bool do_expensive_check = false) { - CUGRAPH_EXPECTS(!graph_view.has_edge_mask(), "unimplemented."); - if (do_expensive_check) { auto num_invalids = thrust::count_if( handle.get_thrust_policy(), diff --git a/cpp/src/structure/graph_view_impl.cuh b/cpp/src/structure/graph_view_impl.cuh index 64a8a3212b3..37a553dcdbd 100644 --- a/cpp/src/structure/graph_view_impl.cuh +++ b/cpp/src/structure/graph_view_impl.cuh @@ -548,7 +548,7 @@ graph_view_tpartition_, this->edge_partition_segment_offsets_); } else { - CUGRAPH_EXPECTS(!has_edge_mask(), "unimplemented."); + CUGRAPH_EXPECTS(!(this->has_edge_mask()), "unimplemented."); return compute_minor_degrees(handle, *this); } } @@ -566,7 +566,7 @@ graph_view_tlocal_vertex_partition_range_size()); } else { - CUGRAPH_EXPECTS(!has_edge_mask(), "unimplemented."); + CUGRAPH_EXPECTS(!(this->has_edge_mask()), "unimplemented."); return compute_minor_degrees(handle, *this); } } @@ -577,7 +577,7 @@ graph_view_thas_edge_mask()), "unimplemented."); return compute_minor_degrees(handle, *this); } else { return compute_major_degrees(handle, @@ -598,7 +598,7 @@ graph_view_thas_edge_mask()), "unimplemented."); return compute_minor_degrees(handle, *this); } else { return compute_major_degrees( @@ -614,7 +614,7 @@ template >:: compute_max_in_degree(raft::handle_t const& handle) const { - CUGRAPH_EXPECTS(!has_edge_mask(), "unimplemented."); + CUGRAPH_EXPECTS(!(this->has_edge_mask()), "unimplemented."); auto in_degrees = compute_in_degrees(handle); auto it = thrust::max_element(handle.get_thrust_policy(), in_degrees.begin(), in_degrees.end()); @@ -632,7 +632,7 @@ template >:: compute_max_in_degree(raft::handle_t const& handle) const { - CUGRAPH_EXPECTS(!has_edge_mask(), "unimplemented."); + CUGRAPH_EXPECTS(!(this->has_edge_mask()), "unimplemented."); auto in_degrees = compute_in_degrees(handle); auto it = thrust::max_element(handle.get_thrust_policy(), in_degrees.begin(), in_degrees.end()); @@ -646,7 +646,7 @@ template >:: compute_max_out_degree(raft::handle_t const& handle) const { - CUGRAPH_EXPECTS(!has_edge_mask(), "unimplemented."); + CUGRAPH_EXPECTS(!(this->has_edge_mask()), "unimplemented."); auto out_degrees = compute_out_degrees(handle); auto it = thrust::max_element(handle.get_thrust_policy(), out_degrees.begin(), out_degrees.end()); @@ -664,7 +664,7 @@ template >:: compute_max_out_degree(raft::handle_t const& handle) const { - CUGRAPH_EXPECTS(!has_edge_mask(), "unimplemented."); + CUGRAPH_EXPECTS(!(this->has_edge_mask()), "unimplemented."); auto out_degrees = compute_out_degrees(handle); auto it = thrust::max_element(handle.get_thrust_policy(), out_degrees.begin(), out_degrees.end()); @@ -678,7 +678,7 @@ template >:: count_self_loops(raft::handle_t const& handle) const { - CUGRAPH_EXPECTS(!has_edge_mask(), "unimplemented."); + CUGRAPH_EXPECTS(!(this->has_edge_mask()), "unimplemented."); return count_if_e( handle, @@ -693,7 +693,7 @@ template >:: count_self_loops(raft::handle_t const& handle) const { - CUGRAPH_EXPECTS(!has_edge_mask(), "unimplemented."); + CUGRAPH_EXPECTS(!(this->has_edge_mask()), "unimplemented."); return count_if_e( handle, @@ -708,7 +708,7 @@ template >:: count_multi_edges(raft::handle_t const& handle) const { - CUGRAPH_EXPECTS(!has_edge_mask(), "unimplemented."); + CUGRAPH_EXPECTS(!(this->has_edge_mask()), "unimplemented."); if (!this->is_multigraph()) { return edge_t{0}; } @@ -728,7 +728,7 @@ template >:: count_multi_edges(raft::handle_t const& handle) const { - CUGRAPH_EXPECTS(!has_edge_mask(), "unimplemented."); + CUGRAPH_EXPECTS(!(this->has_edge_mask()), "unimplemented."); if (!this->is_multigraph()) { return edge_t{0}; } diff --git a/cpp/tests/prims/mg_count_if_e.cu b/cpp/tests/prims/mg_count_if_e.cu index 449aa728d87..03bf8ae0ae5 100644 --- a/cpp/tests/prims/mg_count_if_e.cu +++ b/cpp/tests/prims/mg_count_if_e.cu @@ -53,8 +53,9 @@ #include struct Prims_Usecase { - bool check_correctness{true}; bool test_weighted{false}; + bool edge_masking{false}; + bool check_correctness{true}; }; template @@ -102,6 +103,13 @@ class Tests_MGCountIfE auto mg_graph_view = mg_graph.view(); + std::optional> edge_mask{std::nullopt}; + if (prims_usecase.edge_masking) { + edge_mask = + cugraph::test::generate::edge_property(*handle_, mg_graph_view, 2); + mg_graph_view.attach_edge_mask((*edge_mask).view()); + } + // 2. run MG count_if_e const int hash_bin_count = 5; @@ -148,19 +156,19 @@ class Tests_MGCountIfE (*mg_renumber_map).size()), false); - auto sg_graph_view = sg_graph.view(); + if (handle_->get_comms().get_rank() == 0) { + auto sg_graph_view = sg_graph.view(); - auto sg_vertex_prop = cugraph::test::generate::vertex_property( - *handle_, - thrust::make_counting_iterator(sg_graph_view.local_vertex_partition_range_first()), - thrust::make_counting_iterator(sg_graph_view.local_vertex_partition_range_last()), - hash_bin_count); - auto sg_src_prop = cugraph::test::generate::src_property( - *handle_, sg_graph_view, sg_vertex_prop); - auto sg_dst_prop = cugraph::test::generate::dst_property( - *handle_, sg_graph_view, sg_vertex_prop); + auto sg_vertex_prop = cugraph::test::generate::vertex_property( + *handle_, + thrust::make_counting_iterator(sg_graph_view.local_vertex_partition_range_first()), + thrust::make_counting_iterator(sg_graph_view.local_vertex_partition_range_last()), + hash_bin_count); + auto sg_src_prop = cugraph::test::generate::src_property( + *handle_, sg_graph_view, sg_vertex_prop); + auto sg_dst_prop = cugraph::test::generate::dst_property( + *handle_, sg_graph_view, sg_vertex_prop); - if (handle_->get_comms().get_rank() == 0) { auto expected_result = count_if_e( *handle_, sg_graph_view, @@ -312,7 +320,10 @@ INSTANTIATE_TEST_SUITE_P( file_test, Tests_MGCountIfE_File, ::testing::Combine( - ::testing::Values(Prims_Usecase{true}), + ::testing::Values(Prims_Usecase{false, false, true}, + Prims_Usecase{false, true, true}, + Prims_Usecase{true, false, true}, + Prims_Usecase{true, true, true}), ::testing::Values(cugraph::test::File_Usecase("test/datasets/karate.mtx"), cugraph::test::File_Usecase("test/datasets/web-Google.mtx"), cugraph::test::File_Usecase("test/datasets/ljournal-2008.mtx"), @@ -320,7 +331,10 @@ INSTANTIATE_TEST_SUITE_P( INSTANTIATE_TEST_SUITE_P(rmat_small_test, Tests_MGCountIfE_Rmat, - ::testing::Combine(::testing::Values(Prims_Usecase{true}), + ::testing::Combine(::testing::Values(Prims_Usecase{false, false, true}, + Prims_Usecase{false, true, true}, + Prims_Usecase{true, false, true}, + Prims_Usecase{true, true, true}), ::testing::Values(cugraph::test::Rmat_Usecase( 10, 16, 0.57, 0.19, 0.19, 0, false, false)))); @@ -332,7 +346,10 @@ INSTANTIATE_TEST_SUITE_P( factor (to avoid running same benchmarks more than once) */ Tests_MGCountIfE_Rmat, ::testing::Combine( - ::testing::Values(Prims_Usecase{false}), + ::testing::Values(Prims_Usecase{false, false, false}, + Prims_Usecase{false, true, false}, + Prims_Usecase{true, false, false}, + Prims_Usecase{true, true, false}), ::testing::Values(cugraph::test::Rmat_Usecase(20, 32, 0.57, 0.19, 0.19, 0, false, false)))); CUGRAPH_MG_TEST_PROGRAM_MAIN() diff --git a/cpp/tests/prims/mg_per_v_pair_transform_dst_nbr_intersection.cu b/cpp/tests/prims/mg_per_v_pair_transform_dst_nbr_intersection.cu index a3edb1f6372..ac73c446d89 100644 --- a/cpp/tests/prims/mg_per_v_pair_transform_dst_nbr_intersection.cu +++ b/cpp/tests/prims/mg_per_v_pair_transform_dst_nbr_intersection.cu @@ -14,6 +14,8 @@ * limitations under the License. */ +#include "property_generator.cuh" + #include #include #include @@ -116,29 +118,8 @@ class Tests_MGPerVPairTransformDstNbrIntersection std::optional> edge_mask{std::nullopt}; if (prims_usecase.edge_masking) { - cugraph::edge_src_property_t edge_src_renumber_map( - *handle_, mg_graph_view); - cugraph::edge_dst_property_t edge_dst_renumber_map( - *handle_, mg_graph_view); - cugraph::update_edge_src_property( - *handle_, mg_graph_view, (*mg_renumber_map).begin(), edge_src_renumber_map); - cugraph::update_edge_dst_property( - *handle_, mg_graph_view, (*mg_renumber_map).begin(), edge_dst_renumber_map); - - edge_mask = cugraph::edge_property_t(*handle_, mg_graph_view); - - cugraph::transform_e( - *handle_, - mg_graph_view, - edge_src_renumber_map.view(), - edge_dst_renumber_map.view(), - cugraph::edge_dummy_property_t{}.view(), - [] __device__(auto src, auto dst, auto src_property, auto dst_property, thrust::nullopt_t) { - return ((src_property % 2 == 0) && (dst_property % 2 == 0)) - ? false - : true; // mask out the edges with even unrenumbered src & dst vertex IDs - }, - (*edge_mask).mutable_view()); + edge_mask = + cugraph::test::generate::edge_property(*handle_, mg_graph_view, 2); mg_graph_view.attach_edge_mask((*edge_mask).view()); } @@ -257,42 +238,6 @@ class Tests_MGPerVPairTransformDstNbrIntersection if (handle_->get_comms().get_rank() == 0) { auto sg_graph_view = sg_graph.view(); - if (prims_usecase.edge_masking) { - rmm::device_uvector srcs(0, handle_->get_stream()); - rmm::device_uvector dsts(0, handle_->get_stream()); - std::tie(srcs, dsts, std::ignore, std::ignore) = - cugraph::decompress_to_edgelist( - *handle_, sg_graph_view, std::nullopt, std::nullopt, std::nullopt); - auto edge_first = thrust::make_zip_iterator(srcs.begin(), dsts.begin()); - srcs.resize(thrust::distance(edge_first, - thrust::remove_if(handle_->get_thrust_policy(), - edge_first, - edge_first + srcs.size(), - [] __device__(auto pair) { - return (thrust::get<0>(pair) % 2 == 0) && - (thrust::get<1>(pair) % 2 == 0); - })), - handle_->get_stream()); - dsts.resize(srcs.size(), handle_->get_stream()); - rmm::device_uvector vertices(sg_graph_view.number_of_vertices(), - handle_->get_stream()); - thrust::sequence( - handle_->get_thrust_policy(), vertices.begin(), vertices.end(), vertex_t{0}); - std::tie(sg_graph, std::ignore, std::ignore, std::ignore, std::ignore) = cugraph:: - create_graph_from_edgelist( - *handle_, - std::move(vertices), - std::move(srcs), - std::move(dsts), - std::nullopt, - std::nullopt, - std::nullopt, - cugraph::graph_properties_t{sg_graph_view.is_symmetric(), - sg_graph_view.is_multigraph()}, - false); - sg_graph_view = sg_graph.view(); - } - auto sg_result_buffer = cugraph::allocate_dataframe_buffer>( cugraph::size_dataframe_buffer(mg_aggregate_vertex_pair_buffer), handle_->get_stream()); auto sg_out_degrees = sg_graph_view.compute_out_degrees(*handle_); diff --git a/cpp/tests/prims/mg_per_v_random_select_transform_outgoing_e.cu b/cpp/tests/prims/mg_per_v_random_select_transform_outgoing_e.cu index eb6a8fd5cb6..2b9e9aafa3f 100644 --- a/cpp/tests/prims/mg_per_v_random_select_transform_outgoing_e.cu +++ b/cpp/tests/prims/mg_per_v_random_select_transform_outgoing_e.cu @@ -324,8 +324,9 @@ class Tests_MGPerVRandomSelectTransformOutgoingE with_replacement = prims_usecase.with_replacement, invalid_value = invalid_value ? thrust::make_optional(*invalid_value) : thrust::nullopt, - property_transform = cugraph::test::detail::property_transform{ - hash_bin_count}] __device__(size_t i) { + property_transform = + cugraph::test::detail::vertex_property_transform{ + hash_bin_count}] __device__(size_t i) { auto v = *(frontier_vertex_first + i); // check sample_offsets diff --git a/cpp/tests/prims/mg_transform_e.cu b/cpp/tests/prims/mg_transform_e.cu index 24deaad810a..e9be80f1f7d 100644 --- a/cpp/tests/prims/mg_transform_e.cu +++ b/cpp/tests/prims/mg_transform_e.cu @@ -52,6 +52,7 @@ struct Prims_Usecase { bool use_edgelist{false}; + bool edge_masking{false}; bool check_correctness{true}; }; @@ -100,6 +101,13 @@ class Tests_MGTransformE auto mg_graph_view = mg_graph.view(); + std::optional> edge_mask{std::nullopt}; + if (prims_usecase.edge_masking) { + edge_mask = + cugraph::test::generate::edge_property(*handle_, mg_graph_view, 2); + mg_graph_view.attach_edge_mask((*edge_mask).view()); + } + // 2. run MG transform_e const int hash_bin_count = 5; @@ -439,7 +447,10 @@ INSTANTIATE_TEST_SUITE_P( file_test, Tests_MGTransformE_File, ::testing::Combine( - ::testing::Values(Prims_Usecase{false, true}, Prims_Usecase{true, true}), + ::testing::Values(Prims_Usecase{false, false, true}, + Prims_Usecase{false, true, true}, + Prims_Usecase{true, false, true}, + Prims_Usecase{true, true, true}), ::testing::Values(cugraph::test::File_Usecase("test/datasets/karate.mtx"), cugraph::test::File_Usecase("test/datasets/web-Google.mtx"), cugraph::test::File_Usecase("test/datasets/ljournal-2008.mtx"), @@ -447,8 +458,10 @@ INSTANTIATE_TEST_SUITE_P( INSTANTIATE_TEST_SUITE_P(rmat_small_test, Tests_MGTransformE_Rmat, - ::testing::Combine(::testing::Values(Prims_Usecase{false, true}, - Prims_Usecase{true, true}), + ::testing::Combine(::testing::Values(Prims_Usecase{false, false, true}, + Prims_Usecase{false, true, true}, + Prims_Usecase{true, false, true}, + Prims_Usecase{true, true, true}), ::testing::Values(cugraph::test::Rmat_Usecase( 10, 16, 0.57, 0.19, 0.19, 0, false, false)))); @@ -460,7 +473,10 @@ INSTANTIATE_TEST_SUITE_P( factor (to avoid running same benchmarks more than once) */ Tests_MGTransformE_Rmat, ::testing::Combine( - ::testing::Values(Prims_Usecase{false, false}, Prims_Usecase{true, false}), + ::testing::Values(Prims_Usecase{false, false, false}, + Prims_Usecase{false, true, false}, + Prims_Usecase{true, false, false}, + Prims_Usecase{true, true, false}), ::testing::Values(cugraph::test::Rmat_Usecase(20, 32, 0.57, 0.19, 0.19, 0, false, false)))); CUGRAPH_MG_TEST_PROGRAM_MAIN() diff --git a/cpp/tests/prims/mg_transform_reduce_e.cu b/cpp/tests/prims/mg_transform_reduce_e.cu index 79aa3da54df..c4ae11ab7c9 100644 --- a/cpp/tests/prims/mg_transform_reduce_e.cu +++ b/cpp/tests/prims/mg_transform_reduce_e.cu @@ -91,8 +91,9 @@ struct result_compare> { }; struct Prims_Usecase { - bool check_correctness{true}; bool test_weighted{false}; + bool edge_masking{false}; + bool check_correctness{true}; }; template @@ -141,6 +142,13 @@ class Tests_MGTransformReduceE auto mg_graph_view = mg_graph.view(); + std::optional> edge_mask{std::nullopt}; + if (prims_usecase.edge_masking) { + edge_mask = + cugraph::test::generate::edge_property(*handle_, mg_graph_view, 2); + mg_graph_view.attach_edge_mask((*edge_mask).view()); + } + // 2. run MG transform reduce const int hash_bin_count = 5; @@ -365,7 +373,10 @@ INSTANTIATE_TEST_SUITE_P( file_test, Tests_MGTransformReduceE_File, ::testing::Combine( - ::testing::Values(Prims_Usecase{true}), + ::testing::Values(Prims_Usecase{false, false, true}, + Prims_Usecase{false, true, true}, + Prims_Usecase{true, false, true}, + Prims_Usecase{true, true, true}), ::testing::Values(cugraph::test::File_Usecase("test/datasets/karate.mtx"), cugraph::test::File_Usecase("test/datasets/web-Google.mtx"), cugraph::test::File_Usecase("test/datasets/ljournal-2008.mtx"), @@ -373,7 +384,10 @@ INSTANTIATE_TEST_SUITE_P( INSTANTIATE_TEST_SUITE_P(rmat_small_test, Tests_MGTransformReduceE_Rmat, - ::testing::Combine(::testing::Values(Prims_Usecase{true}), + ::testing::Combine(::testing::Values(Prims_Usecase{false, false, true}, + Prims_Usecase{false, true, true}, + Prims_Usecase{true, false, true}, + Prims_Usecase{true, true, true}), ::testing::Values(cugraph::test::Rmat_Usecase( 10, 16, 0.57, 0.19, 0.19, 0, false, false)))); @@ -385,7 +399,10 @@ INSTANTIATE_TEST_SUITE_P( factor (to avoid running same benchmarks more than once) */ Tests_MGTransformReduceE_Rmat, ::testing::Combine( - ::testing::Values(Prims_Usecase{false}), + ::testing::Values(Prims_Usecase{false, false, false}, + Prims_Usecase{false, true, false}, + Prims_Usecase{true, false, false}, + Prims_Usecase{true, true, false}), ::testing::Values(cugraph::test::Rmat_Usecase(20, 32, 0.57, 0.19, 0.19, 0, false, false)))); CUGRAPH_MG_TEST_PROGRAM_MAIN() diff --git a/cpp/tests/prims/property_generator.cuh b/cpp/tests/prims/property_generator.cuh index e7264cd276f..680455eda79 100644 --- a/cpp/tests/prims/property_generator.cuh +++ b/cpp/tests/prims/property_generator.cuh @@ -15,6 +15,7 @@ */ #pragma once +#include #include #include @@ -61,7 +62,7 @@ __host__ __device__ auto make_property_value(T val) } template -struct property_transform { +struct vertex_property_transform { int32_t mod{}; constexpr __device__ property_t operator()(vertex_t v) const @@ -73,6 +74,20 @@ struct property_transform { } }; +template +struct edge_property_transform { + int32_t mod{}; + + constexpr __device__ property_t operator()( + vertex_t src, vertex_t dst, thrust::nullopt_t, thrust::nullopt_t, thrust::nullopt_t) const + { + static_assert(cugraph::is_thrust_tuple_of_arithmetic::value || + std::is_arithmetic_v); + cuco::detail::MurmurHash3_32 hash_func{}; + return make_property_value(hash_func(src + dst) % mod); + } +}; + } // namespace detail template @@ -96,7 +111,7 @@ struct generate { labels.begin(), labels.end(), cugraph::get_dataframe_buffer_begin(data), - detail::property_transform{hash_bin_count}); + detail::vertex_property_transform{hash_bin_count}); return data; } @@ -111,7 +126,7 @@ struct generate { begin, end, cugraph::get_dataframe_buffer_begin(data), - detail::property_transform{hash_bin_count}); + detail::vertex_property_transform{hash_bin_count}); return data; } @@ -138,6 +153,22 @@ struct generate { handle, graph_view, cugraph::get_dataframe_buffer_begin(property), output_property); return output_property; } + + template + static auto edge_property(raft::handle_t const& handle, + graph_view_type const& graph_view, + int32_t hash_bin_count) + { + auto output_property = cugraph::edge_property_t(handle, graph_view); + cugraph::transform_e(handle, + graph_view, + cugraph::edge_src_dummy_property_t{}.view(), + cugraph::edge_dst_dummy_property_t{}.view(), + cugraph::edge_dummy_property_t{}.view(), + detail::edge_property_transform{hash_bin_count}, + output_property.mutable_view()); + return output_property; + } }; } // namespace test From c6aa9814bff8902d3efae1574fb9d1de633f1b24 Mon Sep 17 00:00:00 2001 From: Chuck Hastings <45364586+ChuckHastings@users.noreply.github.com> Date: Fri, 1 Dec 2023 14:03:44 -0500 Subject: [PATCH 104/111] Mtmg updates for rmm (#4031) Discovered RAII capabilities in RMM while reviewing issues related to MTMG testing. This PR modifies the MTMG implementation to use the RAII capabilities for setting the device id temporarily to another device. Authors: - Chuck Hastings (https://github.com/ChuckHastings) Approvers: - Seunghwa Kang (https://github.com/seunghwak) URL: https://github.com/rapidsai/cugraph/pull/4031 --- .../mtmg/detail/device_shared_wrapper.hpp | 8 +++---- cpp/include/cugraph/mtmg/handle.hpp | 21 +++++++------------ cpp/include/cugraph/mtmg/instance_manager.hpp | 10 ++------- cpp/include/cugraph/mtmg/resource_manager.hpp | 11 +++------- cpp/src/structure/detail/structure_utils.cuh | 1 + cpp/tests/mtmg/threaded_test.cu | 21 ++++++++++++++++--- 6 files changed, 35 insertions(+), 37 deletions(-) diff --git a/cpp/include/cugraph/mtmg/detail/device_shared_wrapper.hpp b/cpp/include/cugraph/mtmg/detail/device_shared_wrapper.hpp index c4cacb401af..3e4b2513a8d 100644 --- a/cpp/include/cugraph/mtmg/detail/device_shared_wrapper.hpp +++ b/cpp/include/cugraph/mtmg/detail/device_shared_wrapper.hpp @@ -57,10 +57,10 @@ class device_shared_wrapper_t { { std::lock_guard lock(lock_); - auto pos = objects_.find(handle.get_local_rank()); + auto pos = objects_.find(handle.get_rank()); CUGRAPH_EXPECTS(pos == objects_.end(), "Cannot overwrite wrapped object"); - objects_.insert(std::make_pair(handle.get_local_rank(), std::move(obj))); + objects_.insert(std::make_pair(handle.get_rank(), std::move(obj))); } /** @@ -90,7 +90,7 @@ class device_shared_wrapper_t { { std::lock_guard lock(lock_); - auto pos = objects_.find(handle.get_local_rank()); + auto pos = objects_.find(handle.get_rank()); CUGRAPH_EXPECTS(pos != objects_.end(), "Uninitialized wrapped object"); return pos->second; @@ -106,7 +106,7 @@ class device_shared_wrapper_t { { std::lock_guard lock(lock_); - auto pos = objects_.find(handle.get_local_rank()); + auto pos = objects_.find(handle.get_rank()); CUGRAPH_EXPECTS(pos != objects_.end(), "Uninitialized wrapped object"); diff --git a/cpp/include/cugraph/mtmg/handle.hpp b/cpp/include/cugraph/mtmg/handle.hpp index 6223de1781d..0b02091a3cc 100644 --- a/cpp/include/cugraph/mtmg/handle.hpp +++ b/cpp/include/cugraph/mtmg/handle.hpp @@ -32,18 +32,19 @@ namespace mtmg { * */ class handle_t { + handle_t(handle_t const&) = delete; + handle_t operator=(handle_t const&) = delete; + public: /** * @brief Constructor * * @param raft_handle Raft handle for the resources * @param thread_rank Rank for this thread + * @param device_id Device id for the device this handle operates on */ - handle_t(raft::handle_t const& raft_handle, int thread_rank, size_t device_id) - : raft_handle_(raft_handle), - thread_rank_(thread_rank), - local_rank_(raft_handle.get_comms().get_rank()), // FIXME: update for multi-node - device_id_(device_id) + handle_t(raft::handle_t const& raft_handle, int thread_rank, rmm::cuda_device_id device_id) + : raft_handle_(raft_handle), thread_rank_(thread_rank), device_id_raii_(device_id) { } @@ -118,18 +119,10 @@ class handle_t { */ int get_rank() const { return raft_handle_.get_comms().get_rank(); } - /** - * @brief Get local gpu rank - * - * @return local gpu rank - */ - int get_local_rank() const { return local_rank_; } - private: raft::handle_t const& raft_handle_; int thread_rank_; - int local_rank_; - size_t device_id_; + rmm::cuda_set_device_raii device_id_raii_; }; } // namespace mtmg diff --git a/cpp/include/cugraph/mtmg/instance_manager.hpp b/cpp/include/cugraph/mtmg/instance_manager.hpp index f819a5a0abe..f60063c4101 100644 --- a/cpp/include/cugraph/mtmg/instance_manager.hpp +++ b/cpp/include/cugraph/mtmg/instance_manager.hpp @@ -47,15 +47,10 @@ class instance_manager_t { ~instance_manager_t() { - int current_device{}; - RAFT_CUDA_TRY(cudaGetDevice(¤t_device)); - for (size_t i = 0; i < nccl_comms_.size(); ++i) { - RAFT_CUDA_TRY(cudaSetDevice(device_ids_[i].value())); + rmm::cuda_set_device_raii local_set_device(device_ids_[i]); RAFT_NCCL_TRY(ncclCommDestroy(*nccl_comms_[i])); } - - RAFT_CUDA_TRY(cudaSetDevice(current_device)); } /** @@ -75,8 +70,7 @@ class instance_manager_t { int gpu_id = local_id % raft_handle_.size(); int thread_id = local_id / raft_handle_.size(); - RAFT_CUDA_TRY(cudaSetDevice(device_ids_[gpu_id].value())); - return handle_t(*raft_handle_[gpu_id], thread_id, static_cast(gpu_id)); + return handle_t(*raft_handle_[gpu_id], thread_id, device_ids_[gpu_id]); } /** diff --git a/cpp/include/cugraph/mtmg/resource_manager.hpp b/cpp/include/cugraph/mtmg/resource_manager.hpp index 127944cf7ba..bc312c9ae77 100644 --- a/cpp/include/cugraph/mtmg/resource_manager.hpp +++ b/cpp/include/cugraph/mtmg/resource_manager.hpp @@ -89,7 +89,7 @@ class resource_manager_t { local_rank_map_.insert(std::pair(global_rank, local_device_id)); - RAFT_CUDA_TRY(cudaSetDevice(local_device_id.value())); + rmm::cuda_set_device_raii local_set_device(local_device_id); // FIXME: There is a bug in the cuda_memory_resource that results in a Hang. // using the pool resource as a work-around. @@ -182,14 +182,12 @@ class resource_manager_t { --gpu_row_comm_size; } - int current_device{}; - RAFT_CUDA_TRY(cudaGetDevice(¤t_device)); RAFT_NCCL_TRY(ncclGroupStart()); for (size_t i = 0; i < local_ranks_to_include.size(); ++i) { int rank = local_ranks_to_include[i]; auto pos = local_rank_map_.find(rank); - RAFT_CUDA_TRY(cudaSetDevice(pos->second.value())); + rmm::cuda_set_device_raii local_set_device(pos->second); nccl_comms.push_back(std::make_unique()); handles.push_back( @@ -204,7 +202,6 @@ class resource_manager_t { handles[i].get(), *nccl_comms[i], ranks_to_include.size(), rank); } RAFT_NCCL_TRY(ncclGroupEnd()); - RAFT_CUDA_TRY(cudaSetDevice(current_device)); std::vector running_threads; @@ -217,9 +214,7 @@ class resource_manager_t { &device_ids, &nccl_comms, &handles]() { - int rank = local_ranks_to_include[idx]; - RAFT_CUDA_TRY(cudaSetDevice(device_ids[idx].value())); - + rmm::cuda_set_device_raii local_set_device(device_ids[idx]); cugraph::partition_manager::init_subcomm(*handles[idx], gpu_row_comm_size); }); } diff --git a/cpp/src/structure/detail/structure_utils.cuh b/cpp/src/structure/detail/structure_utils.cuh index c49b62e4543..7630d5855a0 100644 --- a/cpp/src/structure/detail/structure_utils.cuh +++ b/cpp/src/structure/detail/structure_utils.cuh @@ -524,6 +524,7 @@ std::tuple> mark_entries(raft::handle_t co return word; }); + // FIXME: use detail::count_set_bits size_t bit_count = thrust::transform_reduce( handle.get_thrust_policy(), marked_entries.begin(), diff --git a/cpp/tests/mtmg/threaded_test.cu b/cpp/tests/mtmg/threaded_test.cu index bc4d8cfef6a..1a6a17eaa18 100644 --- a/cpp/tests/mtmg/threaded_test.cu +++ b/cpp/tests/mtmg/threaded_test.cu @@ -155,10 +155,25 @@ class Tests_Multithreaded input_usecase.template construct_edgelist( handle, multithreaded_usecase.test_weighted, false, false); + rmm::device_uvector d_unique_vertices(2 * d_src_v.size(), handle.get_stream()); + thrust::copy( + handle.get_thrust_policy(), d_src_v.begin(), d_src_v.end(), d_unique_vertices.begin()); + thrust::copy(handle.get_thrust_policy(), + d_dst_v.begin(), + d_dst_v.end(), + d_unique_vertices.begin() + d_src_v.size()); + thrust::sort(handle.get_thrust_policy(), d_unique_vertices.begin(), d_unique_vertices.end()); + + d_unique_vertices.resize(thrust::distance(d_unique_vertices.begin(), + thrust::unique(handle.get_thrust_policy(), + d_unique_vertices.begin(), + d_unique_vertices.end())), + handle.get_stream()); + auto h_src_v = cugraph::test::to_host(handle, d_src_v); auto h_dst_v = cugraph::test::to_host(handle, d_dst_v); auto h_weights_v = cugraph::test::to_host(handle, d_weights_v); - auto unique_vertices = cugraph::test::to_host(handle, d_vertices_v); + auto unique_vertices = cugraph::test::to_host(handle, d_unique_vertices); // Load edgelist from different threads. We'll use more threads than GPUs here for (int i = 0; i < num_threads; ++i) { @@ -293,13 +308,13 @@ class Tests_Multithreaded num_threads]() { auto thread_handle = instance_manager->get_handle(); - auto number_of_vertices = unique_vertices->size(); + auto number_of_vertices = unique_vertices.size(); std::vector my_vertex_list; my_vertex_list.reserve((number_of_vertices + num_threads - 1) / num_threads); for (size_t j = i; j < number_of_vertices; j += num_threads) { - my_vertex_list.push_back((*unique_vertices)[j]); + my_vertex_list.push_back(unique_vertices[j]); } rmm::device_uvector d_my_vertex_list(my_vertex_list.size(), From 90fec7123eea3b8e04b6c1141d10e30a25acbe3e Mon Sep 17 00:00:00 2001 From: Seunghwa Kang <45857425+seunghwak@users.noreply.github.com> Date: Fri, 1 Dec 2023 18:43:58 -0800 Subject: [PATCH 105/111] Fix % 0 bug in MG_SELECT_RANDOM_VERTICES test (#4034) `% 0` throws an exception (`mg_graph_view.local_vertex_partition_range_size()` & `num_of_elements_in_given_set` can be 0). This PR fixes the bug. Authors: - Seunghwa Kang (https://github.com/seunghwak) Approvers: - Chuck Hastings (https://github.com/ChuckHastings) URL: https://github.com/rapidsai/cugraph/pull/4034 --- cpp/tests/structure/mg_select_random_vertices_test.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cpp/tests/structure/mg_select_random_vertices_test.cpp b/cpp/tests/structure/mg_select_random_vertices_test.cpp index 79c50301922..e49e1ebcb99 100644 --- a/cpp/tests/structure/mg_select_random_vertices_test.cpp +++ b/cpp/tests/structure/mg_select_random_vertices_test.cpp @@ -90,7 +90,7 @@ class Tests_MGSelectRandomVertices std::iota( h_given_set.begin(), h_given_set.end(), mg_graph_view.local_vertex_partition_range_first()); std::shuffle(h_given_set.begin(), h_given_set.end(), std::mt19937{std::random_device{}()}); - h_given_set.resize(std::rand() % mg_graph_view.local_vertex_partition_range_size() + 1); + h_given_set.resize(std::rand() % (mg_graph_view.local_vertex_partition_range_size() + 1)); // Compute size of the distributed vertex set int num_of_elements_in_given_set = static_cast(h_given_set.size()); @@ -105,7 +105,7 @@ class Tests_MGSelectRandomVertices size_t select_count = num_of_elements_in_given_set > select_random_vertices_usecase.select_count ? select_random_vertices_usecase.select_count - : std::rand() % num_of_elements_in_given_set + 1; + : std::rand() % (num_of_elements_in_given_set + 1); for (int idx = 0; idx < with_replacement_flags.size(); idx++) { bool with_replacement = with_replacement_flags[idx]; From 3fc3e98ba260083bdc5c0b52f41139c6180bb074 Mon Sep 17 00:00:00 2001 From: Ray Douglass <3107146+raydouglass@users.noreply.github.com> Date: Mon, 4 Dec 2023 14:09:23 -0500 Subject: [PATCH 106/111] Pin actions/labeler to v4 [skip ci] (#4038) RAPIDS repos are using the `main` branch of https://github.com/actions/labeler which recently introduced [breaking changes](https://github.com/actions/labeler/releases/tag/v5.0.0). This PR pins to the latest v4 release of the labeler action until we can evaluate the changes required for v5. This PR also moves the labeler workflow to the correct location in the repo. Authors: - Ray Douglass (https://github.com/raydouglass) Approvers: - AJ Schmidt (https://github.com/ajschmidt8) --- {github => .github}/workflows/labeler.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename {github => .github}/workflows/labeler.yml (83%) diff --git a/github/workflows/labeler.yml b/.github/workflows/labeler.yml similarity index 83% rename from github/workflows/labeler.yml rename to .github/workflows/labeler.yml index 23956a02fbd..31e78f82a62 100644 --- a/github/workflows/labeler.yml +++ b/.github/workflows/labeler.yml @@ -6,6 +6,6 @@ jobs: triage: runs-on: ubuntu-latest steps: - - uses: actions/labeler@main + - uses: actions/labeler@v4 with: repo-token: "${{ secrets.GITHUB_TOKEN }}" From 32eaa5e97e5c26ebf5c8ca04faee51316eee75e8 Mon Sep 17 00:00:00 2001 From: Chuck Hastings <45364586+ChuckHastings@users.noreply.github.com> Date: Tue, 5 Dec 2023 12:34:30 -0500 Subject: [PATCH 107/111] Clean up self-loop and multi-edge removal logic (#4032) There are mask utilities that perform some of the functions that were implemented to do this cleanup. Use the mask utilities instead of replicating functionality. Authors: - Chuck Hastings (https://github.com/ChuckHastings) Approvers: - Seunghwa Kang (https://github.com/seunghwak) URL: https://github.com/rapidsai/cugraph/pull/4032 --- cpp/src/structure/detail/structure_utils.cuh | 33 ++--- cpp/src/structure/remove_multi_edges_impl.cuh | 53 ++++---- cpp/src/structure/remove_self_loops_impl.cuh | 36 ++--- cpp/tests/community/triangle_count_test.cpp | 4 +- cpp/tests/utilities/test_graphs.hpp | 20 ++- cpp/tests/utilities/thrust_wrapper.cu | 126 ------------------ cpp/tests/utilities/thrust_wrapper.hpp | 13 -- 7 files changed, 73 insertions(+), 212 deletions(-) diff --git a/cpp/src/structure/detail/structure_utils.cuh b/cpp/src/structure/detail/structure_utils.cuh index 7630d5855a0..f0f729bce18 100644 --- a/cpp/src/structure/detail/structure_utils.cuh +++ b/cpp/src/structure/detail/structure_utils.cuh @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -524,35 +525,21 @@ std::tuple> mark_entries(raft::handle_t co return word; }); - // FIXME: use detail::count_set_bits - size_t bit_count = thrust::transform_reduce( - handle.get_thrust_policy(), - marked_entries.begin(), - marked_entries.end(), - [] __device__(auto word) { return __popc(word); }, - size_t{0}, - thrust::plus()); + size_t bit_count = detail::count_set_bits(handle, marked_entries.begin(), num_entries); return std::make_tuple(bit_count, std::move(marked_entries)); } template -rmm::device_uvector remove_flagged_elements(raft::handle_t const& handle, - rmm::device_uvector&& vector, - raft::device_span remove_flags, - size_t remove_count) +rmm::device_uvector keep_flagged_elements(raft::handle_t const& handle, + rmm::device_uvector&& vector, + raft::device_span keep_flags, + size_t keep_count) { - rmm::device_uvector result(vector.size() - remove_count, handle.get_stream()); - - thrust::copy_if( - handle.get_thrust_policy(), - thrust::make_counting_iterator(size_t{0}), - thrust::make_counting_iterator(vector.size()), - thrust::make_transform_output_iterator(result.begin(), - indirection_t{vector.data()}), - [remove_flags] __device__(size_t i) { - return !(remove_flags[cugraph::packed_bool_offset(i)] & cugraph::packed_bool_mask(i)); - }); + rmm::device_uvector result(keep_count, handle.get_stream()); + + detail::copy_if_mask_set( + handle, vector.begin(), vector.end(), keep_flags.begin(), result.begin()); return result; } diff --git a/cpp/src/structure/remove_multi_edges_impl.cuh b/cpp/src/structure/remove_multi_edges_impl.cuh index ab6b1fba8eb..fdd3059f874 100644 --- a/cpp/src/structure/remove_multi_edges_impl.cuh +++ b/cpp/src/structure/remove_multi_edges_impl.cuh @@ -254,50 +254,47 @@ remove_multi_edges(raft::handle_t const& handle, } } - auto [multi_edge_count, multi_edges_to_delete] = - detail::mark_entries(handle, - edgelist_srcs.size(), - [d_edgelist_srcs = edgelist_srcs.data(), - d_edgelist_dsts = edgelist_dsts.data()] __device__(auto idx) { - return (idx > 0) && (d_edgelist_srcs[idx - 1] == d_edgelist_srcs[idx]) && - (d_edgelist_dsts[idx - 1] == d_edgelist_dsts[idx]); - }); - - if (multi_edge_count > 0) { - edgelist_srcs = detail::remove_flagged_elements( + auto [keep_count, keep_flags] = detail::mark_entries( + handle, + edgelist_srcs.size(), + [d_edgelist_srcs = edgelist_srcs.data(), + d_edgelist_dsts = edgelist_dsts.data()] __device__(auto idx) { + return !((idx > 0) && (d_edgelist_srcs[idx - 1] == d_edgelist_srcs[idx]) && + (d_edgelist_dsts[idx - 1] == d_edgelist_dsts[idx])); + }); + + if (keep_count < edgelist_srcs.size()) { + edgelist_srcs = detail::keep_flagged_elements( handle, std::move(edgelist_srcs), - raft::device_span{multi_edges_to_delete.data(), multi_edges_to_delete.size()}, - multi_edge_count); - edgelist_dsts = detail::remove_flagged_elements( + raft::device_span{keep_flags.data(), keep_flags.size()}, + keep_count); + edgelist_dsts = detail::keep_flagged_elements( handle, std::move(edgelist_dsts), - raft::device_span{multi_edges_to_delete.data(), multi_edges_to_delete.size()}, - multi_edge_count); + raft::device_span{keep_flags.data(), keep_flags.size()}, + keep_count); if (edgelist_weights) - edgelist_weights = detail::remove_flagged_elements( + edgelist_weights = detail::keep_flagged_elements( handle, std::move(*edgelist_weights), - raft::device_span{multi_edges_to_delete.data(), - multi_edges_to_delete.size()}, - multi_edge_count); + raft::device_span{keep_flags.data(), keep_flags.size()}, + keep_count); if (edgelist_edge_ids) - edgelist_edge_ids = detail::remove_flagged_elements( + edgelist_edge_ids = detail::keep_flagged_elements( handle, std::move(*edgelist_edge_ids), - raft::device_span{multi_edges_to_delete.data(), - multi_edges_to_delete.size()}, - multi_edge_count); + raft::device_span{keep_flags.data(), keep_flags.size()}, + keep_count); if (edgelist_edge_types) - edgelist_edge_types = detail::remove_flagged_elements( + edgelist_edge_types = detail::keep_flagged_elements( handle, std::move(*edgelist_edge_types), - raft::device_span{multi_edges_to_delete.data(), - multi_edges_to_delete.size()}, - multi_edge_count); + raft::device_span{keep_flags.data(), keep_flags.size()}, + keep_count); } return std::make_tuple(std::move(edgelist_srcs), diff --git a/cpp/src/structure/remove_self_loops_impl.cuh b/cpp/src/structure/remove_self_loops_impl.cuh index 161ffeae28e..dafe26cd1c5 100644 --- a/cpp/src/structure/remove_self_loops_impl.cuh +++ b/cpp/src/structure/remove_self_loops_impl.cuh @@ -44,44 +44,44 @@ remove_self_loops(raft::handle_t const& handle, std::optional>&& edgelist_edge_ids, std::optional>&& edgelist_edge_types) { - auto [self_loop_count, self_loops_to_delete] = + auto [keep_count, keep_flags] = detail::mark_entries(handle, edgelist_srcs.size(), [d_srcs = edgelist_srcs.data(), d_dsts = edgelist_dsts.data()] __device__( - size_t i) { return d_srcs[i] == d_dsts[i]; }); + size_t i) { return d_srcs[i] != d_dsts[i]; }); - if (self_loop_count > 0) { - edgelist_srcs = detail::remove_flagged_elements( + if (keep_count < edgelist_srcs.size()) { + edgelist_srcs = detail::keep_flagged_elements( handle, std::move(edgelist_srcs), - raft::device_span{self_loops_to_delete.data(), self_loops_to_delete.size()}, - self_loop_count); - edgelist_dsts = detail::remove_flagged_elements( + raft::device_span{keep_flags.data(), keep_flags.size()}, + keep_count); + edgelist_dsts = detail::keep_flagged_elements( handle, std::move(edgelist_dsts), - raft::device_span{self_loops_to_delete.data(), self_loops_to_delete.size()}, - self_loop_count); + raft::device_span{keep_flags.data(), keep_flags.size()}, + keep_count); if (edgelist_weights) - edgelist_weights = detail::remove_flagged_elements( + edgelist_weights = detail::keep_flagged_elements( handle, std::move(*edgelist_weights), - raft::device_span{self_loops_to_delete.data(), self_loops_to_delete.size()}, - self_loop_count); + raft::device_span{keep_flags.data(), keep_flags.size()}, + keep_count); if (edgelist_edge_ids) - edgelist_edge_ids = detail::remove_flagged_elements( + edgelist_edge_ids = detail::keep_flagged_elements( handle, std::move(*edgelist_edge_ids), - raft::device_span{self_loops_to_delete.data(), self_loops_to_delete.size()}, - self_loop_count); + raft::device_span{keep_flags.data(), keep_flags.size()}, + keep_count); if (edgelist_edge_types) - edgelist_edge_types = detail::remove_flagged_elements( + edgelist_edge_types = detail::keep_flagged_elements( handle, std::move(*edgelist_edge_types), - raft::device_span{self_loops_to_delete.data(), self_loops_to_delete.size()}, - self_loop_count); + raft::device_span{keep_flags.data(), keep_flags.size()}, + keep_count); } return std::make_tuple(std::move(edgelist_srcs), diff --git a/cpp/tests/community/triangle_count_test.cpp b/cpp/tests/community/triangle_count_test.cpp index 836bab59457..592924c3c47 100644 --- a/cpp/tests/community/triangle_count_test.cpp +++ b/cpp/tests/community/triangle_count_test.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, NVIDIA CORPORATION. + * Copyright (c) 2022-2023, NVIDIA CORPORATION. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -232,7 +232,7 @@ class Tests_TriangleCount for (size_t i = 0; i < h_cugraph_vertices.size(); ++i) { auto v = h_cugraph_vertices[i]; auto count = h_cugraph_triangle_counts[i]; - ASSERT_TRUE(count == h_reference_triangle_counts[v]) + ASSERT_EQ(count, h_reference_triangle_counts[v]) << "Triangle count values do not match with the reference values."; } } diff --git a/cpp/tests/utilities/test_graphs.hpp b/cpp/tests/utilities/test_graphs.hpp index 16c9d3ed145..8cc87b26f1d 100644 --- a/cpp/tests/utilities/test_graphs.hpp +++ b/cpp/tests/utilities/test_graphs.hpp @@ -621,9 +621,25 @@ construct_graph(raft::handle_t const& handle, CUGRAPH_EXPECTS(d_src_v.size() <= static_cast(std::numeric_limits::max()), "Invalid template parameter: edge_t overflow."); - if (drop_self_loops) { remove_self_loops(handle, d_src_v, d_dst_v, d_weights_v); } + if (drop_self_loops) { + std::tie(d_src_v, d_dst_v, d_weights_v, std::ignore, std::ignore) = + cugraph::remove_self_loops(handle, + std::move(d_src_v), + std::move(d_dst_v), + std::move(d_weights_v), + std::nullopt, + std::nullopt); + } - if (drop_multi_edges) { sort_and_remove_multi_edges(handle, d_src_v, d_dst_v, d_weights_v); } + if (drop_multi_edges) { + std::tie(d_src_v, d_dst_v, d_weights_v, std::ignore, std::ignore) = + cugraph::remove_multi_edges(handle, + std::move(d_src_v), + std::move(d_dst_v), + std::move(d_weights_v), + std::nullopt, + std::nullopt); + } graph_t graph(handle); std::optional< diff --git a/cpp/tests/utilities/thrust_wrapper.cu b/cpp/tests/utilities/thrust_wrapper.cu index cb7e6f1bd66..2daf250b4a2 100644 --- a/cpp/tests/utilities/thrust_wrapper.cu +++ b/cpp/tests/utilities/thrust_wrapper.cu @@ -206,131 +206,5 @@ template void populate_vertex_ids(raft::handle_t const& handle, rmm::device_uvector& d_vertices_v, int64_t vertex_id_offset); -template -void remove_self_loops(raft::handle_t const& handle, - rmm::device_uvector& d_src_v /* [INOUT] */, - rmm::device_uvector& d_dst_v /* [INOUT] */, - std::optional>& d_weight_v /* [INOUT] */) -{ - if (d_weight_v) { - auto edge_first = thrust::make_zip_iterator( - thrust::make_tuple(d_src_v.begin(), d_dst_v.begin(), (*d_weight_v).begin())); - d_src_v.resize( - thrust::distance(edge_first, - thrust::remove_if( - handle.get_thrust_policy(), - edge_first, - edge_first + d_src_v.size(), - [] __device__(auto e) { return thrust::get<0>(e) == thrust::get<1>(e); })), - handle.get_stream()); - d_dst_v.resize(d_src_v.size(), handle.get_stream()); - (*d_weight_v).resize(d_src_v.size(), handle.get_stream()); - } else { - auto edge_first = - thrust::make_zip_iterator(thrust::make_tuple(d_src_v.begin(), d_dst_v.begin())); - d_src_v.resize( - thrust::distance(edge_first, - thrust::remove_if( - handle.get_thrust_policy(), - edge_first, - edge_first + d_src_v.size(), - [] __device__(auto e) { return thrust::get<0>(e) == thrust::get<1>(e); })), - handle.get_stream()); - d_dst_v.resize(d_src_v.size(), handle.get_stream()); - } - - d_src_v.shrink_to_fit(handle.get_stream()); - d_dst_v.shrink_to_fit(handle.get_stream()); - if (d_weight_v) { (*d_weight_v).shrink_to_fit(handle.get_stream()); } -} - -template void remove_self_loops( - raft::handle_t const& handle, - rmm::device_uvector& d_src_v /* [INOUT] */, - rmm::device_uvector& d_dst_v /* [INOUT] */, - std::optional>& d_weight_v /* [INOUT] */); - -template void remove_self_loops( - raft::handle_t const& handle, - rmm::device_uvector& d_src_v /* [INOUT] */, - rmm::device_uvector& d_dst_v /* [INOUT] */, - std::optional>& d_weight_v /* [INOUT] */); - -template void remove_self_loops( - raft::handle_t const& handle, - rmm::device_uvector& d_src_v /* [INOUT] */, - rmm::device_uvector& d_dst_v /* [INOUT] */, - std::optional>& d_weight_v /* [INOUT] */); - -template void remove_self_loops( - raft::handle_t const& handle, - rmm::device_uvector& d_src_v /* [INOUT] */, - rmm::device_uvector& d_dst_v /* [INOUT] */, - std::optional>& d_weight_v /* [INOUT] */); - -template -void sort_and_remove_multi_edges( - raft::handle_t const& handle, - rmm::device_uvector& d_src_v /* [INOUT] */, - rmm::device_uvector& d_dst_v /* [INOUT] */, - std::optional>& d_weight_v /* [INOUT] */) -{ - if (d_weight_v) { - auto edge_first = thrust::make_zip_iterator( - thrust::make_tuple(d_src_v.begin(), d_dst_v.begin(), (*d_weight_v).begin())); - thrust::sort(handle.get_thrust_policy(), edge_first, edge_first + d_src_v.size()); - d_src_v.resize( - thrust::distance(edge_first, - thrust::unique(handle.get_thrust_policy(), - edge_first, - edge_first + d_src_v.size(), - [] __device__(auto lhs, auto rhs) { - return (thrust::get<0>(lhs) == thrust::get<0>(rhs)) && - (thrust::get<1>(lhs) == thrust::get<1>(rhs)); - })), - handle.get_stream()); - d_dst_v.resize(d_src_v.size(), handle.get_stream()); - (*d_weight_v).resize(d_src_v.size(), handle.get_stream()); - } else { - auto edge_first = - thrust::make_zip_iterator(thrust::make_tuple(d_src_v.begin(), d_dst_v.begin())); - thrust::sort(handle.get_thrust_policy(), edge_first, edge_first + d_src_v.size()); - d_src_v.resize( - thrust::distance( - edge_first, - thrust::unique(handle.get_thrust_policy(), edge_first, edge_first + d_src_v.size())), - handle.get_stream()); - d_dst_v.resize(d_src_v.size(), handle.get_stream()); - } - - d_src_v.shrink_to_fit(handle.get_stream()); - d_dst_v.shrink_to_fit(handle.get_stream()); - if (d_weight_v) { (*d_weight_v).shrink_to_fit(handle.get_stream()); } -} - -template void sort_and_remove_multi_edges( - raft::handle_t const& handle, - rmm::device_uvector& d_src_v /* [INOUT] */, - rmm::device_uvector& d_dst_v /* [INOUT] */, - std::optional>& d_weight_v /* [INOUT] */); - -template void sort_and_remove_multi_edges( - raft::handle_t const& handle, - rmm::device_uvector& d_src_v /* [INOUT] */, - rmm::device_uvector& d_dst_v /* [INOUT] */, - std::optional>& d_weight_v /* [INOUT] */); - -template void sort_and_remove_multi_edges( - raft::handle_t const& handle, - rmm::device_uvector& d_src_v /* [INOUT] */, - rmm::device_uvector& d_dst_v /* [INOUT] */, - std::optional>& d_weight_v /* [INOUT] */); - -template void sort_and_remove_multi_edges( - raft::handle_t const& handle, - rmm::device_uvector& d_src_v /* [INOUT] */, - rmm::device_uvector& d_dst_v /* [INOUT] */, - std::optional>& d_weight_v /* [INOUT] */); - } // namespace test } // namespace cugraph diff --git a/cpp/tests/utilities/thrust_wrapper.hpp b/cpp/tests/utilities/thrust_wrapper.hpp index eead4dc268f..fb82d781198 100644 --- a/cpp/tests/utilities/thrust_wrapper.hpp +++ b/cpp/tests/utilities/thrust_wrapper.hpp @@ -46,18 +46,5 @@ void populate_vertex_ids(raft::handle_t const& handle, rmm::device_uvector& d_vertices_v /* [INOUT] */, vertex_t vertex_id_offset); -template -void remove_self_loops(raft::handle_t const& handle, - rmm::device_uvector& d_src_v /* [INOUT] */, - rmm::device_uvector& d_dst_v /* [INOUT] */, - std::optional>& d_weight_v /* [INOUT] */); - -template -void sort_and_remove_multi_edges( - raft::handle_t const& handle, - rmm::device_uvector& d_src_v /* [INOUT] */, - rmm::device_uvector& d_dst_v /* [INOUT] */, - std::optional>& d_weight_v /* [INOUT] */); - } // namespace test } // namespace cugraph From 2ec94b310ae854678e3c39a687b6cf34bd702c1b Mon Sep 17 00:00:00 2001 From: Ray Douglass Date: Wed, 6 Dec 2023 09:58:26 -0500 Subject: [PATCH 108/111] Update Changelog [skip ci] --- CHANGELOG.md | 81 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 33a5b2bc5e7..d165cd7efc4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,84 @@ +# cuGraph 23.12.00 (6 Dec 2023) + +## 🚨 Breaking Changes + +- [BUG] Restore the original default order of CSR, which does not reverse edges in cuGraph-PyG ([#3980](https://github.com/rapidsai/cugraph/pull/3980)) [@alexbarghi-nv](https://github.com/alexbarghi-nv) +- `Resultset` and `Dataset` Refactors ([#3957](https://github.com/rapidsai/cugraph/pull/3957)) [@nv-rliu](https://github.com/nv-rliu) +- Moves more MG graph ETL to libcugraph and re-enables MG tests in CI ([#3941](https://github.com/rapidsai/cugraph/pull/3941)) [@jnke2016](https://github.com/jnke2016) + +## 🐛 Bug Fixes + +- Pin actions/labeler to v4 ([#4038](https://github.com/rapidsai/cugraph/pull/4038)) [@raydouglass](https://github.com/raydouglass) +- Find rmm before cuco ([#4011](https://github.com/rapidsai/cugraph/pull/4011)) [@vyasr](https://github.com/vyasr) +- Pin to minor versions of packages outside the cuGraph repository. ([#4004](https://github.com/rapidsai/cugraph/pull/4004)) [@bdice](https://github.com/bdice) +- Move MTMG_TEST to MG tests block ([#3993](https://github.com/rapidsai/cugraph/pull/3993)) [@naimnv](https://github.com/naimnv) +- Fix Leiden refinement phase ([#3990](https://github.com/rapidsai/cugraph/pull/3990)) [@naimnv](https://github.com/naimnv) +- [BUG] Fix Graph Construction From Pandas in cuGraph-PyG ([#3985](https://github.com/rapidsai/cugraph/pull/3985)) [@alexbarghi-nv](https://github.com/alexbarghi-nv) +- [BUG] Restore the original default order of CSR, which does not reverse edges in cuGraph-PyG ([#3980](https://github.com/rapidsai/cugraph/pull/3980)) [@alexbarghi-nv](https://github.com/alexbarghi-nv) +- Fix eigenvector testing and HITS testing discrepancies ([#3979](https://github.com/rapidsai/cugraph/pull/3979)) [@ChuckHastings](https://github.com/ChuckHastings) +- [BUG] Fix Incorrect Edge Index, Directory Selection in cuGraph-PyG Loader ([#3978](https://github.com/rapidsai/cugraph/pull/3978)) [@alexbarghi-nv](https://github.com/alexbarghi-nv) +- [BUG] Check if Dask has quit to avoid throwing an exception and triggering a segfault on ddp exit ([#3961](https://github.com/rapidsai/cugraph/pull/3961)) [@alexbarghi-nv](https://github.com/alexbarghi-nv) +- nx-cugraph: xfail test_louvain.py:test_threshold in Python 3.9 ([#3944](https://github.com/rapidsai/cugraph/pull/3944)) [@eriknw](https://github.com/eriknw) + +## 📖 Documentation + +- [DOC]: Fix invalid links and add materials to notebooks ([#4002](https://github.com/rapidsai/cugraph/pull/4002)) [@huiyuxie](https://github.com/huiyuxie) +- Update Broken Links in README.md ([#3924](https://github.com/rapidsai/cugraph/pull/3924)) [@nv-rliu](https://github.com/nv-rliu) + +## 🚀 New Features + +- Implement the transform_e primitive (to update property values for all edges) ([#3917](https://github.com/rapidsai/cugraph/pull/3917)) [@seunghwak](https://github.com/seunghwak) +- Update the neighbor intersection primitive to support edge masking. ([#3550](https://github.com/rapidsai/cugraph/pull/3550)) [@seunghwak](https://github.com/seunghwak) + +## 🛠️ Improvements + +- Correct defect found in DLFW testing ([#4021](https://github.com/rapidsai/cugraph/pull/4021)) [@ChuckHastings](https://github.com/ChuckHastings) +- `nx-cugraph` README update: adds missing `connected_components` algo to table ([#4019](https://github.com/rapidsai/cugraph/pull/4019)) [@rlratzel](https://github.com/rlratzel) +- Build concurrency for nightly and merge triggers ([#4009](https://github.com/rapidsai/cugraph/pull/4009)) [@bdice](https://github.com/bdice) +- Support `drop_last` Argument in cuGraph-PyG Loader ([#3995](https://github.com/rapidsai/cugraph/pull/3995)) [@alexbarghi-nv](https://github.com/alexbarghi-nv) +- Adds `update-version.sh` support for recently added files containing RAPIDS versions ([#3994](https://github.com/rapidsai/cugraph/pull/3994)) [@rlratzel](https://github.com/rlratzel) +- Use new `rapids-dask-dependency` metapackage for managing `dask` versions ([#3991](https://github.com/rapidsai/cugraph/pull/3991)) [@galipremsagar](https://github.com/galipremsagar) +- Fixes to nx-cugraph README: fixes typos, updates link to NX backend docs ([#3989](https://github.com/rapidsai/cugraph/pull/3989)) [@rlratzel](https://github.com/rlratzel) +- Address FIXMEs ([#3988](https://github.com/rapidsai/cugraph/pull/3988)) [@seunghwak](https://github.com/seunghwak) +- Updates README file to include nx-cugraph user documentation, adds nx-cugraph to main README ([#3984](https://github.com/rapidsai/cugraph/pull/3984)) [@rlratzel](https://github.com/rlratzel) +- Update C API graph creation function signatures ([#3982](https://github.com/rapidsai/cugraph/pull/3982)) [@ChuckHastings](https://github.com/ChuckHastings) +- [REVIEW]Optimize cugraph-DGL csc codepath ([#3977](https://github.com/rapidsai/cugraph/pull/3977)) [@VibhuJawa](https://github.com/VibhuJawa) +- nx-cugraph: add SSSP (unweighted) ([#3976](https://github.com/rapidsai/cugraph/pull/3976)) [@eriknw](https://github.com/eriknw) +- CuGraph compatibility fixes ([#3973](https://github.com/rapidsai/cugraph/pull/3973)) [@brandon-b-miller](https://github.com/brandon-b-miller) +- Skip certain `cugraph-pyg` tests when torch-sparse is not available ([#3970](https://github.com/rapidsai/cugraph/pull/3970)) [@tingyu66](https://github.com/tingyu66) +- nx-cugraph: add `eigenvector_centrality`, `katz_centrality`, `hits`, `pagerank` ([#3968](https://github.com/rapidsai/cugraph/pull/3968)) [@eriknw](https://github.com/eriknw) +- Cut peak memory footprint in graph creation ([#3966](https://github.com/rapidsai/cugraph/pull/3966)) [@seunghwak](https://github.com/seunghwak) +- nx-cugraph: add CC for undirected graphs to fix k-truss ([#3965](https://github.com/rapidsai/cugraph/pull/3965)) [@eriknw](https://github.com/eriknw) +- Skip certain `cugraph-pyg` tests when `torch_sparse` is not available ([#3962](https://github.com/rapidsai/cugraph/pull/3962)) [@tingyu66](https://github.com/tingyu66) +- `Resultset` and `Dataset` Refactors ([#3957](https://github.com/rapidsai/cugraph/pull/3957)) [@nv-rliu](https://github.com/nv-rliu) +- Download `xml` docs artifact through CloudFront endpoint ([#3955](https://github.com/rapidsai/cugraph/pull/3955)) [@AyodeAwe](https://github.com/AyodeAwe) +- Add many graph generators to nx-cugraph ([#3954](https://github.com/rapidsai/cugraph/pull/3954)) [@eriknw](https://github.com/eriknw) +- Unpin `dask` and `distributed` for `23.12` development ([#3953](https://github.com/rapidsai/cugraph/pull/3953)) [@galipremsagar](https://github.com/galipremsagar) +- Errors compiling for DLFW on CUDA 12.3 ([#3952](https://github.com/rapidsai/cugraph/pull/3952)) [@ChuckHastings](https://github.com/ChuckHastings) +- nx-cugraph: add k_truss and degree centralities ([#3945](https://github.com/rapidsai/cugraph/pull/3945)) [@eriknw](https://github.com/eriknw) +- nx-cugraph: handle seed argument in edge_betweenness_centrality ([#3943](https://github.com/rapidsai/cugraph/pull/3943)) [@eriknw](https://github.com/eriknw) +- Moves more MG graph ETL to libcugraph and re-enables MG tests in CI ([#3941](https://github.com/rapidsai/cugraph/pull/3941)) [@jnke2016](https://github.com/jnke2016) +- Temporarily disable mg testing ([#3940](https://github.com/rapidsai/cugraph/pull/3940)) [@jnke2016](https://github.com/jnke2016) +- adding C/C++ API docs ([#3938](https://github.com/rapidsai/cugraph/pull/3938)) [@BradReesWork](https://github.com/BradReesWork) +- Add multigraph support to nx-cugraph ([#3934](https://github.com/rapidsai/cugraph/pull/3934)) [@eriknw](https://github.com/eriknw) +- Setup Consistent Nightly Versions for Pip and Conda ([#3933](https://github.com/rapidsai/cugraph/pull/3933)) [@divyegala](https://github.com/divyegala) +- MTMG multi node ([#3932](https://github.com/rapidsai/cugraph/pull/3932)) [@ChuckHastings](https://github.com/ChuckHastings) +- Use branch-23.12 workflows. ([#3928](https://github.com/rapidsai/cugraph/pull/3928)) [@bdice](https://github.com/bdice) +- Fix an issue occurring in the cuGraph-DGL example for "mixed" mode. ([#3927](https://github.com/rapidsai/cugraph/pull/3927)) [@drivanov](https://github.com/drivanov) +- Updating Docs ([#3923](https://github.com/rapidsai/cugraph/pull/3923)) [@BradReesWork](https://github.com/BradReesWork) +- Forward-merge branch-23.10 to branch-23.12 ([#3919](https://github.com/rapidsai/cugraph/pull/3919)) [@nv-rliu](https://github.com/nv-rliu) +- new build all option ([#3916](https://github.com/rapidsai/cugraph/pull/3916)) [@BradReesWork](https://github.com/BradReesWork) +- Silence spurious compiler warnings ([#3913](https://github.com/rapidsai/cugraph/pull/3913)) [@seunghwak](https://github.com/seunghwak) +- Link wholegrah and cugraphops XML docs ([#3906](https://github.com/rapidsai/cugraph/pull/3906)) [@AyodeAwe](https://github.com/AyodeAwe) +- Updates to 23.12 ([#3905](https://github.com/rapidsai/cugraph/pull/3905)) [@raydouglass](https://github.com/raydouglass) +- Forward-merge branch-23.10 to branch-23.12 ([#3904](https://github.com/rapidsai/cugraph/pull/3904)) [@GPUtester](https://github.com/GPUtester) +- Build CUDA 12.0 ARM conda packages. ([#3903](https://github.com/rapidsai/cugraph/pull/3903)) [@bdice](https://github.com/bdice) +- Merge branch-23.10 into branch-23.12 ([#3898](https://github.com/rapidsai/cugraph/pull/3898)) [@rlratzel](https://github.com/rlratzel) +- Some MTMG code cleanup and small optimizations ([#3894](https://github.com/rapidsai/cugraph/pull/3894)) [@ChuckHastings](https://github.com/ChuckHastings) +- Enable parallel mode ([#3875](https://github.com/rapidsai/cugraph/pull/3875)) [@jnke2016](https://github.com/jnke2016) +- Adds benchmarks for `nx-cugraph` ([#3854](https://github.com/rapidsai/cugraph/pull/3854)) [@rlratzel](https://github.com/rlratzel) +- Add nx-cugraph notebook for showing accelerated networkX APIs ([#3830](https://github.com/rapidsai/cugraph/pull/3830)) [@betochimas](https://github.com/betochimas) + # cuGraph 23.10.00 (11 Oct 2023) ## 🚨 Breaking Changes From 783e4dc0d4ae06f16e469cb63883c66a224aab73 Mon Sep 17 00:00:00 2001 From: AJ Schmidt Date: Wed, 6 Dec 2023 10:06:20 -0500 Subject: [PATCH 109/111] Prevent automatic labeler from adding `Label Checker` labels (#4048) This PR prevents the `doc` label from being automatically added to PRs since it can interfere with the [Label Checker](https://docs.rapids.ai/resources/label-checker/) check. [skip ci] --- .github/labeler.yml | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/.github/labeler.yml b/.github/labeler.yml index c589fda6099..368bf328b99 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -9,17 +9,6 @@ python: benchmarks: - 'benchmarks/**' -doc: - - 'docs/**' - - '**/*.md' - - 'datasets/**' - - 'notebooks/**' - - '**/*.txt' - - '**/*.rst' - - '**/*.ipynb' - - '**/*.pdf' - - '**/*.png' - datasets: - 'datasets/**' From f80480dae7aca9c419a7cdf9c6e90a77e056695f Mon Sep 17 00:00:00 2001 From: Chuck Hastings <45364586+ChuckHastings@users.noreply.github.com> Date: Wed, 6 Dec 2023 11:29:47 -0500 Subject: [PATCH 110/111] Add support for Louvain to MTMG (#4033) Added vertex result instantiation for `vertex_t` which is necessary to handle Louvain results. Added an MTMG test for Louvain to demonstrate how to use Louvain in MTMG. Closes https://github.com/rapidsai/graph_dl/issues/330 Authors: - Chuck Hastings (https://github.com/ChuckHastings) Approvers: - Seunghwa Kang (https://github.com/seunghwak) - Joseph Nke (https://github.com/jnke2016) - Naim (https://github.com/naimnv) URL: https://github.com/rapidsai/cugraph/pull/4033 --- .../mtmg/detail/device_shared_wrapper.hpp | 1 - cpp/include/cugraph/mtmg/graph_view.hpp | 23 +- .../cugraph/mtmg/vertex_result_view.hpp | 5 +- cpp/src/mtmg/vertex_result.cu | 89 ++- cpp/tests/CMakeLists.txt | 13 +- cpp/tests/mtmg/multi_node_threaded_test.cu | 3 +- cpp/tests/mtmg/threaded_test.cu | 3 +- cpp/tests/mtmg/threaded_test_louvain.cu | 511 ++++++++++++++++++ 8 files changed, 617 insertions(+), 31 deletions(-) create mode 100644 cpp/tests/mtmg/threaded_test_louvain.cu diff --git a/cpp/include/cugraph/mtmg/detail/device_shared_wrapper.hpp b/cpp/include/cugraph/mtmg/detail/device_shared_wrapper.hpp index 3e4b2513a8d..5fbe7bc9f01 100644 --- a/cpp/include/cugraph/mtmg/detail/device_shared_wrapper.hpp +++ b/cpp/include/cugraph/mtmg/detail/device_shared_wrapper.hpp @@ -79,7 +79,6 @@ class device_shared_wrapper_t { objects_.insert(std::make_pair(local_rank, std::move(obj))); } - public: /** * @brief Get reference to an object for a particular thread * diff --git a/cpp/include/cugraph/mtmg/graph_view.hpp b/cpp/include/cugraph/mtmg/graph_view.hpp index 94347e016ea..8e202ab4904 100644 --- a/cpp/include/cugraph/mtmg/graph_view.hpp +++ b/cpp/include/cugraph/mtmg/graph_view.hpp @@ -27,8 +27,27 @@ namespace mtmg { * @brief Graph view for each GPU */ template -using graph_view_t = detail::device_shared_wrapper_t< - cugraph::graph_view_t>; +class graph_view_t : public detail::device_shared_wrapper_t< + cugraph::graph_view_t> { + public: + /** + * @brief Get the vertex_partition_view for this graph + */ + vertex_partition_view_t get_vertex_partition_view( + cugraph::mtmg::handle_t const& handle) const + { + return this->get(handle).local_vertex_partition_view(); + } + + /** + * @brief Get the vertex_partition_view for this graph + */ + std::vector get_vertex_partition_range_lasts( + cugraph::mtmg::handle_t const& handle) const + { + return this->get(handle).vertex_partition_range_lasts(); + } +}; } // namespace mtmg } // namespace cugraph diff --git a/cpp/include/cugraph/mtmg/vertex_result_view.hpp b/cpp/include/cugraph/mtmg/vertex_result_view.hpp index a349bb95333..42b80cea62f 100644 --- a/cpp/include/cugraph/mtmg/vertex_result_view.hpp +++ b/cpp/include/cugraph/mtmg/vertex_result_view.hpp @@ -39,11 +39,12 @@ class vertex_result_view_t : public detail::device_shared_device_span_t + template rmm::device_uvector gather( handle_t const& handle, raft::device_span vertices, - cugraph::mtmg::graph_view_t const& graph_view, + std::vector const& vertex_partition_range_lasts, + cugraph::vertex_partition_view_t vertex_partition_view, std::optional>& renumber_map_view); }; diff --git a/cpp/src/mtmg/vertex_result.cu b/cpp/src/mtmg/vertex_result.cu index 97fcd291c87..5b1825656ff 100644 --- a/cpp/src/mtmg/vertex_result.cu +++ b/cpp/src/mtmg/vertex_result.cu @@ -27,15 +27,14 @@ namespace cugraph { namespace mtmg { template -template +template rmm::device_uvector vertex_result_view_t::gather( handle_t const& handle, raft::device_span vertices, - cugraph::mtmg::graph_view_t const& graph_view, + std::vector const& vertex_partition_range_lasts, + vertex_partition_view_t vertex_partition_view, std::optional>& renumber_map_view) { - auto this_gpu_graph_view = graph_view.get(handle); - rmm::device_uvector local_vertices(vertices.size(), handle.get_stream()); rmm::device_uvector vertex_gpu_ids(vertices.size(), handle.get_stream()); rmm::device_uvector vertex_pos(vertices.size(), handle.get_stream()); @@ -47,11 +46,11 @@ rmm::device_uvector vertex_result_view_t::gather( cugraph::detail::sequence_fill( handle.get_stream(), vertex_pos.data(), vertex_pos.size(), size_t{0}); - rmm::device_uvector d_vertex_partition_range_lasts( - this_gpu_graph_view.vertex_partition_range_lasts().size(), handle.get_stream()); + rmm::device_uvector d_vertex_partition_range_lasts(vertex_partition_range_lasts.size(), + handle.get_stream()); raft::update_device(d_vertex_partition_range_lasts.data(), - this_gpu_graph_view.vertex_partition_range_lasts().data(), - this_gpu_graph_view.vertex_partition_range_lasts().size(), + vertex_partition_range_lasts.data(), + vertex_partition_range_lasts.size(), handle.get_stream()); if (renumber_map_view) { @@ -60,8 +59,8 @@ rmm::device_uvector vertex_result_view_t::gather( local_vertices.data(), local_vertices.size(), renumber_map_view->get(handle).data(), - this_gpu_graph_view.local_vertex_partition_range_first(), - this_gpu_graph_view.local_vertex_partition_range_last()); + vertex_partition_view.local_vertex_partition_range_first(), + vertex_partition_view.local_vertex_partition_range_last()); } auto const major_comm_size = @@ -89,8 +88,8 @@ rmm::device_uvector vertex_result_view_t::gather( auto& wrapped = this->get(handle); - auto vertex_partition = vertex_partition_device_view_t( - this_gpu_graph_view.local_vertex_partition_view()); + auto vertex_partition = + vertex_partition_device_view_t(vertex_partition_view); auto iter = thrust::make_transform_iterator(local_vertices.begin(), [vertex_partition] __device__(auto v) { @@ -130,37 +129,85 @@ rmm::device_uvector vertex_result_view_t::gather( template rmm::device_uvector vertex_result_view_t::gather( handle_t const& handle, raft::device_span vertices, - cugraph::mtmg::graph_view_t const& graph_view, + std::vector const& vertex_partition_range_lasts, + vertex_partition_view_t vertex_partition_view, std::optional>& renumber_map_view); +template rmm::device_uvector vertex_result_view_t::gather( + handle_t const& handle, + raft::device_span vertices, + std::vector const& vertex_partition_range_lasts, + vertex_partition_view_t vertex_partition_view, + std::optional>& renumber_map_view); + template rmm::device_uvector vertex_result_view_t::gather( handle_t const& handle, raft::device_span vertices, - cugraph::mtmg::graph_view_t const& graph_view, + std::vector const& vertex_partition_range_lasts, + vertex_partition_view_t vertex_partition_view, std::optional>& renumber_map_view); template rmm::device_uvector vertex_result_view_t::gather( handle_t const& handle, raft::device_span vertices, - cugraph::mtmg::graph_view_t const& graph_view, + std::vector const& vertex_partition_range_lasts, + vertex_partition_view_t vertex_partition_view, std::optional>& renumber_map_view); -template rmm::device_uvector vertex_result_view_t::gather( +template rmm::device_uvector vertex_result_view_t::gather( handle_t const& handle, raft::device_span vertices, - cugraph::mtmg::graph_view_t const& graph_view, + std::vector const& vertex_partition_range_lasts, + vertex_partition_view_t vertex_partition_view, std::optional>& renumber_map_view); -template rmm::device_uvector vertex_result_view_t::gather( +template rmm::device_uvector vertex_result_view_t::gather( + handle_t const& handle, + raft::device_span vertices, + std::vector const& vertex_partition_range_lasts, + vertex_partition_view_t vertex_partition_view, + std::optional>& renumber_map_view); + +template rmm::device_uvector vertex_result_view_t::gather( handle_t const& handle, raft::device_span vertices, - cugraph::mtmg::graph_view_t const& graph_view, + std::vector const& vertex_partition_range_lasts, + vertex_partition_view_t vertex_partition_view, std::optional>& renumber_map_view); -template rmm::device_uvector vertex_result_view_t::gather( +template rmm::device_uvector vertex_result_view_t::gather( + handle_t const& handle, + raft::device_span vertices, + std::vector const& vertex_partition_range_lasts, + vertex_partition_view_t vertex_partition_view, + std::optional>& renumber_map_view); + +template rmm::device_uvector vertex_result_view_t::gather( + handle_t const& handle, + raft::device_span vertices, + std::vector const& vertex_partition_range_lasts, + vertex_partition_view_t vertex_partition_view, + std::optional>& renumber_map_view); + +template rmm::device_uvector vertex_result_view_t::gather( + handle_t const& handle, + raft::device_span vertices, + std::vector const& vertex_partition_range_lasts, + vertex_partition_view_t vertex_partition_view, + std::optional>& renumber_map_view); + +template rmm::device_uvector vertex_result_view_t::gather( + handle_t const& handle, + raft::device_span vertices, + std::vector const& vertex_partition_range_lasts, + vertex_partition_view_t vertex_partition_view, + std::optional>& renumber_map_view); + +template rmm::device_uvector vertex_result_view_t::gather( handle_t const& handle, raft::device_span vertices, - cugraph::mtmg::graph_view_t const& graph_view, + std::vector const& vertex_partition_range_lasts, + vertex_partition_view_t vertex_partition_view, std::optional>& renumber_map_view); } // namespace mtmg diff --git a/cpp/tests/CMakeLists.txt b/cpp/tests/CMakeLists.txt index 6530a25d178..d9c88bc179e 100644 --- a/cpp/tests/CMakeLists.txt +++ b/cpp/tests/CMakeLists.txt @@ -738,9 +738,16 @@ if (BUILD_CUGRAPH_MTMG_TESTS) # - MTMG tests ------------------------------------------------------------------------- ConfigureTest(MTMG_TEST mtmg/threaded_test.cu) target_link_libraries(MTMG_TEST - PRIVATE - UCP::UCP - ) + PRIVATE + UCP::UCP + ) + + ConfigureTest(MTMG_LOUVAIN_TEST mtmg/threaded_test_louvain.cu) + target_link_libraries(MTMG_LOUVAIN_TEST + PRIVATE + cugraphmgtestutil + UCP::UCP + ) if(BUILD_CUGRAPH_MG_TESTS) ############################################################################################### diff --git a/cpp/tests/mtmg/multi_node_threaded_test.cu b/cpp/tests/mtmg/multi_node_threaded_test.cu index e5a7de07781..17aed4fdecf 100644 --- a/cpp/tests/mtmg/multi_node_threaded_test.cu +++ b/cpp/tests/mtmg/multi_node_threaded_test.cu @@ -311,7 +311,8 @@ class Tests_Multithreaded auto d_my_pageranks = pageranks_view.gather( thread_handle, raft::device_span{d_my_vertex_list.data(), d_my_vertex_list.size()}, - graph_view, + graph_view.get_vertex_partition_range_lasts(thread_handle), + graph_view.get_vertex_partition_view(thread_handle), renumber_map_view); std::vector my_pageranks(d_my_pageranks.size()); diff --git a/cpp/tests/mtmg/threaded_test.cu b/cpp/tests/mtmg/threaded_test.cu index 1a6a17eaa18..a5df0199cac 100644 --- a/cpp/tests/mtmg/threaded_test.cu +++ b/cpp/tests/mtmg/threaded_test.cu @@ -327,7 +327,8 @@ class Tests_Multithreaded auto d_my_pageranks = pageranks_view.gather( thread_handle, raft::device_span{d_my_vertex_list.data(), d_my_vertex_list.size()}, - graph_view, + graph_view.get_vertex_partition_range_lasts(thread_handle), + graph_view.get_vertex_partition_view(thread_handle), renumber_map_view); std::vector my_pageranks(d_my_pageranks.size()); diff --git a/cpp/tests/mtmg/threaded_test_louvain.cu b/cpp/tests/mtmg/threaded_test_louvain.cu new file mode 100644 index 00000000000..c1395037646 --- /dev/null +++ b/cpp/tests/mtmg/threaded_test_louvain.cu @@ -0,0 +1,511 @@ +/* + * Copyright (c) 2023, NVIDIA CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include + +#include + +#include + +#include +#include + +struct Multithreaded_Usecase { + bool test_weighted{false}; + bool check_correctness{true}; +}; + +template +class Tests_Multithreaded + : public ::testing::TestWithParam> { + public: + Tests_Multithreaded() {} + + static void SetUpTestCase() {} + static void TearDownTestCase() {} + + virtual void SetUp() {} + virtual void TearDown() {} + + std::vector get_gpu_list() + { + int num_gpus_per_node{1}; + RAFT_CUDA_TRY(cudaGetDeviceCount(&num_gpus_per_node)); + + std::vector gpu_list(num_gpus_per_node); + std::iota(gpu_list.begin(), gpu_list.end(), 0); + + return gpu_list; + } + + template + void run_current_test( + std::tuple const& param, + std::vector gpu_list) + { + using edge_type_t = int32_t; + + constexpr bool renumber = true; + constexpr bool do_expensive_check = false; + + auto [multithreaded_usecase, input_usecase] = param; + + raft::handle_t handle{}; + + size_t max_level{1}; // Louvain is non-deterministic in MG if max_leve > 1 + weight_t threshold{1e-6}; + weight_t resolution{1}; + + size_t device_buffer_size{64 * 1024 * 1024}; + size_t thread_buffer_size{4 * 1024 * 1024}; + + int num_gpus = gpu_list.size(); + int num_threads = num_gpus * 4; + + cugraph::mtmg::resource_manager_t resource_manager; + + std::for_each(gpu_list.begin(), gpu_list.end(), [&resource_manager](int gpu_id) { + resource_manager.register_local_gpu(gpu_id, rmm::cuda_device_id{gpu_id}); + }); + + ncclUniqueId instance_manager_id; + ncclGetUniqueId(&instance_manager_id); + + auto instance_manager = resource_manager.create_instance_manager( + resource_manager.registered_ranks(), instance_manager_id); + + cugraph::mtmg::edgelist_t edgelist; + cugraph::mtmg::graph_t graph; + cugraph::mtmg::graph_view_t graph_view; + cugraph::mtmg::vertex_result_t louvain_clusters; + std::optional> renumber_map = + std::make_optional>(); + + auto edge_weights = multithreaded_usecase.test_weighted + ? std::make_optional, + weight_t>>() + : std::nullopt; + + // + // Simulate graph creation by spawning threads to walk through the + // local COO and add edges + // + std::vector running_threads; + + // Initialize shared edgelist object, one per GPU + for (int i = 0; i < num_gpus; ++i) { + running_threads.emplace_back([&instance_manager, + &edgelist, + device_buffer_size, + use_weight = true, + use_edge_id = false, + use_edge_type = false]() { + auto thread_handle = instance_manager->get_handle(); + + edgelist.set(thread_handle, device_buffer_size, use_weight, use_edge_id, use_edge_type); + }); + } + + // Wait for CPU threads to complete + std::for_each(running_threads.begin(), running_threads.end(), [](auto& t) { t.join(); }); + running_threads.resize(0); + instance_manager->reset_threads(); + + // Load SG edge list + auto [d_src_v, d_dst_v, d_weights_v, d_vertices_v, is_symmetric] = + input_usecase.template construct_edgelist( + handle, multithreaded_usecase.test_weighted, false, false); + + rmm::device_uvector d_unique_vertices(2 * d_src_v.size(), handle.get_stream()); + thrust::copy( + handle.get_thrust_policy(), d_src_v.begin(), d_src_v.end(), d_unique_vertices.begin()); + thrust::copy(handle.get_thrust_policy(), + d_dst_v.begin(), + d_dst_v.end(), + d_unique_vertices.begin() + d_src_v.size()); + thrust::sort(handle.get_thrust_policy(), d_unique_vertices.begin(), d_unique_vertices.end()); + + d_unique_vertices.resize(thrust::distance(d_unique_vertices.begin(), + thrust::unique(handle.get_thrust_policy(), + d_unique_vertices.begin(), + d_unique_vertices.end())), + handle.get_stream()); + + auto h_src_v = cugraph::test::to_host(handle, d_src_v); + auto h_dst_v = cugraph::test::to_host(handle, d_dst_v); + auto h_weights_v = cugraph::test::to_host(handle, d_weights_v); + auto unique_vertices = cugraph::test::to_host(handle, d_unique_vertices); + + // Load edgelist from different threads. We'll use more threads than GPUs here + for (int i = 0; i < num_threads; ++i) { + running_threads.emplace_back([&instance_manager, + thread_buffer_size, + &edgelist, + &h_src_v, + &h_dst_v, + &h_weights_v, + i, + num_threads]() { + auto thread_handle = instance_manager->get_handle(); + cugraph::mtmg::per_thread_edgelist_t + per_thread_edgelist(edgelist.get(thread_handle), thread_buffer_size); + + for (size_t j = i; j < h_src_v.size(); j += num_threads) { + per_thread_edgelist.append( + thread_handle, + h_src_v[j], + h_dst_v[j], + h_weights_v ? std::make_optional((*h_weights_v)[j]) : std::nullopt, + std::nullopt, + std::nullopt); + } + + per_thread_edgelist.flush(thread_handle); + }); + } + + // Wait for CPU threads to complete + std::for_each(running_threads.begin(), running_threads.end(), [](auto& t) { t.join(); }); + running_threads.resize(0); + instance_manager->reset_threads(); + + for (int i = 0; i < num_gpus; ++i) { + running_threads.emplace_back([&instance_manager, + &graph, + &edge_weights, + &edgelist, + &renumber_map, + is_symmetric = is_symmetric, + renumber, + do_expensive_check]() { + auto thread_handle = instance_manager->get_handle(); + + if (thread_handle.get_thread_rank() > 0) return; + + std::optional, + edge_t>> + edge_ids{std::nullopt}; + std::optional, + int32_t>> + edge_types{std::nullopt}; + + edgelist.finalize_buffer(thread_handle); + edgelist.consolidate_and_shuffle(thread_handle, false); + + cugraph::mtmg:: + create_graph_from_edgelist( + thread_handle, + edgelist, + cugraph::graph_properties_t{is_symmetric, true}, + renumber, + graph, + edge_weights, + edge_ids, + edge_types, + renumber_map, + do_expensive_check); + }); + } + + // Wait for CPU threads to complete + std::for_each(running_threads.begin(), running_threads.end(), [](auto& t) { t.join(); }); + running_threads.resize(0); + instance_manager->reset_threads(); + + graph_view = graph.view(); + auto renumber_map_view = renumber_map ? std::make_optional(renumber_map->view()) : std::nullopt; + + weight_t modularity{0}; + + for (int i = 0; i < num_threads; ++i) { + running_threads.emplace_back([&instance_manager, + &graph_view, + &edge_weights, + &louvain_clusters, + &modularity, + &renumber_map, + max_level, + threshold, + resolution]() { + auto thread_handle = instance_manager->get_handle(); + + if (thread_handle.get_thread_rank() > 0) return; + + rmm::device_uvector local_louvain_clusters( + graph_view.get(thread_handle).local_vertex_partition_range_size(), + thread_handle.get_stream()); + + std::tie(std::ignore, modularity) = cugraph::louvain( + thread_handle.raft_handle(), + graph_view.get(thread_handle), + edge_weights ? std::make_optional(edge_weights->get(thread_handle).view()) : std::nullopt, + local_louvain_clusters.data(), + max_level, + threshold, + resolution); + + louvain_clusters.set(thread_handle, std::move(local_louvain_clusters)); + }); + } + + // Wait for CPU threads to complete + std::for_each(running_threads.begin(), running_threads.end(), [](auto& t) { t.join(); }); + running_threads.resize(0); + instance_manager->reset_threads(); + + std::vector, std::vector>> computed_clusters_v; + std::mutex computed_clusters_lock{}; + + auto louvain_clusters_view = louvain_clusters.view(); + std::vector h_renumber_map; + + // Load computed_clusters_v from different threads. + for (int i = 0; i < num_gpus; ++i) { + running_threads.emplace_back([&instance_manager, + &graph_view, + &renumber_map_view, + &louvain_clusters_view, + &computed_clusters_lock, + &computed_clusters_v, + &h_src_v, + &h_dst_v, + &h_weights_v, + &h_renumber_map, + &unique_vertices, + i, + num_threads]() { + auto thread_handle = instance_manager->get_handle(); + + auto number_of_vertices = unique_vertices.size(); + + std::vector my_vertex_list; + my_vertex_list.reserve((number_of_vertices + num_threads - 1) / num_threads); + + for (size_t j = i; j < number_of_vertices; j += num_threads) { + my_vertex_list.push_back(unique_vertices[j]); + } + + rmm::device_uvector d_my_vertex_list(my_vertex_list.size(), + thread_handle.raft_handle().get_stream()); + raft::update_device(d_my_vertex_list.data(), + my_vertex_list.data(), + my_vertex_list.size(), + thread_handle.raft_handle().get_stream()); + + auto d_my_clusters = louvain_clusters_view.gather( + thread_handle, + raft::device_span{d_my_vertex_list.data(), d_my_vertex_list.size()}, + graph_view.get_vertex_partition_range_lasts(thread_handle), + graph_view.get_vertex_partition_view(thread_handle), + renumber_map_view); + + std::vector my_clusters(d_my_clusters.size()); + raft::update_host(my_clusters.data(), + d_my_clusters.data(), + d_my_clusters.size(), + thread_handle.raft_handle().get_stream()); + + { + std::lock_guard lock(computed_clusters_lock); + computed_clusters_v.push_back( + std::make_tuple(std::move(my_vertex_list), std::move(my_clusters))); + } + + h_renumber_map = cugraph::test::to_host( + thread_handle.raft_handle(), + cugraph::test::device_allgatherv(thread_handle.raft_handle(), + renumber_map_view->get(thread_handle))); + }); + } + + // Wait for CPU threads to complete + std::for_each(running_threads.begin(), running_threads.end(), [](auto& t) { t.join(); }); + running_threads.resize(0); + instance_manager->reset_threads(); + + if (multithreaded_usecase.check_correctness) { + // Want to compare the results in computed_clusters_v with SG results + cugraph::graph_t sg_graph(handle); + std::optional< + cugraph::edge_property_t, weight_t>> + sg_edge_weights{std::nullopt}; + + for (int i = 0; i < num_gpus; ++i) { + running_threads.emplace_back( + [&instance_manager, &graph_view, &edge_weights, &sg_graph, &sg_edge_weights]() { + auto thread_handle = instance_manager->get_handle(); + + if (thread_handle.get_rank() == 0) { + std::tie(sg_graph, sg_edge_weights, std::ignore) = + cugraph::test::mg_graph_to_sg_graph( + thread_handle.raft_handle(), + graph_view.get(thread_handle), + edge_weights ? std::make_optional(edge_weights->get(thread_handle).view()) + : std::nullopt, + std::optional>{std::nullopt}, + false); // create an SG graph with MG graph vertex IDs + } else { + cugraph::test::mg_graph_to_sg_graph( + thread_handle.raft_handle(), + graph_view.get(thread_handle), + edge_weights ? std::make_optional(edge_weights->get(thread_handle).view()) + : std::nullopt, + std::optional>{std::nullopt}, + false); // create an SG graph with MG graph vertex IDs + } + }); + } + + // Wait for CPU threads to complete + std::for_each(running_threads.begin(), running_threads.end(), [](auto& t) { t.join(); }); + running_threads.resize(0); + instance_manager->reset_threads(); + + rmm::device_uvector sg_clusters(sg_graph.number_of_vertices(), handle.get_stream()); + weight_t modularity; + + std::tie(std::ignore, modularity) = cugraph::louvain( + handle, + sg_graph.view(), + sg_edge_weights ? std::make_optional(sg_edge_weights->view()) : std::nullopt, + sg_clusters.data(), + max_level, + threshold, + resolution); + + auto h_sg_clusters = cugraph::test::to_host(handle, sg_clusters); + std::map h_cluster_map; + std::map h_cluster_reverse_map; + + std::for_each( + computed_clusters_v.begin(), + computed_clusters_v.end(), + [&h_sg_clusters, &h_cluster_map, &h_renumber_map, &h_cluster_reverse_map](auto t1) { + std::for_each( + thrust::make_zip_iterator(std::get<0>(t1).begin(), std::get<1>(t1).begin()), + thrust::make_zip_iterator(std::get<0>(t1).end(), std::get<1>(t1).end()), + [&h_sg_clusters, &h_cluster_map, &h_renumber_map, &h_cluster_reverse_map](auto t2) { + vertex_t v = thrust::get<0>(t2); + vertex_t c = thrust::get<1>(t2); + + auto pos = std::find(h_renumber_map.begin(), h_renumber_map.end(), v); + auto offset = std::distance(h_renumber_map.begin(), pos); + + auto cluster_pos = h_cluster_map.find(c); + if (cluster_pos == h_cluster_map.end()) { + auto reverse_pos = h_cluster_reverse_map.find(h_sg_clusters[offset]); + + ASSERT_TRUE(reverse_pos != h_cluster_map.end()) << "two different cluster mappings"; + + h_cluster_map.insert(std::make_pair(c, h_sg_clusters[offset])); + h_cluster_reverse_map.insert(std::make_pair(h_sg_clusters[offset], c)); + } else { + ASSERT_EQ(cluster_pos->second, h_sg_clusters[offset]) + << "vertex " << v << ", offset = " << offset + << ", SG cluster = " << h_sg_clusters[offset] << ", mtmg cluster = " << c + << ", mapped value = " << cluster_pos->second; + } + }); + }); + } + } +}; + +using Tests_Multithreaded_File = Tests_Multithreaded; +using Tests_Multithreaded_Rmat = Tests_Multithreaded; + +// FIXME: add tests for type combinations +TEST_P(Tests_Multithreaded_File, CheckInt32Int32FloatFloat) +{ + run_current_test( + override_File_Usecase_with_cmd_line_arguments(GetParam()), std::vector{{0, 1}}); +} + +TEST_P(Tests_Multithreaded_Rmat, CheckInt32Int32FloatFloat) +{ + run_current_test( + override_Rmat_Usecase_with_cmd_line_arguments(GetParam()), std::vector{{0, 1}}); +} + +INSTANTIATE_TEST_SUITE_P(file_test, + Tests_Multithreaded_File, + ::testing::Combine( + // enable correctness checks + ::testing::Values(Multithreaded_Usecase{true, true}), + ::testing::Values(cugraph::test::File_Usecase("karate.csv"), + cugraph::test::File_Usecase("dolphins.csv")))); + +INSTANTIATE_TEST_SUITE_P( + rmat_small_test, + Tests_Multithreaded_Rmat, + ::testing::Combine( + // enable correctness checks + ::testing::Values(Multithreaded_Usecase{true, true}), + //::testing::Values(cugraph::test::Rmat_Usecase(10, 16, 0.57, 0.19, 0.19, 0, false, false)))); + ::testing::Values(cugraph::test::Rmat_Usecase(5, 8, 0.57, 0.19, 0.19, 0, false, false)))); + +INSTANTIATE_TEST_SUITE_P( + file_benchmark_test, /* note that the test filename can be overridden in benchmarking (with + --gtest_filter to select only the file_benchmark_test with a specific + vertex & edge type combination) by command line arguments and do not + include more than one File_Usecase that differ only in filename + (to avoid running same benchmarks more than once) */ + Tests_Multithreaded_File, + ::testing::Combine( + // disable correctness checks + ::testing::Values(Multithreaded_Usecase{true, false}), + ::testing::Values(cugraph::test::File_Usecase("test/datasets/karate.mtx")))); + +INSTANTIATE_TEST_SUITE_P( + rmat_benchmark_test, /* note that scale & edge factor can be overridden in benchmarking (with + --gtest_filter to select only the rmat_benchmark_test with a specific + vertex & edge type combination) by command line arguments and do not + include more than one Rmat_Usecase that differ only in scale or edge + factor (to avoid running same benchmarks more than once) */ + Tests_Multithreaded_Rmat, + ::testing::Combine( + // disable correctness checks for large graphs + ::testing::Values(Multithreaded_Usecase{true, false}), + ::testing::Values(cugraph::test::Rmat_Usecase(10, 16, 0.57, 0.19, 0.19, 0, false, false)))); + +CUGRAPH_TEST_PROGRAM_MAIN() From a5718c66aa5e72a7d91b4b3a073736f195352736 Mon Sep 17 00:00:00 2001 From: Vibhu Jawa Date: Wed, 6 Dec 2023 08:52:39 -0800 Subject: [PATCH 111/111] Add a barrier before cugraph Graph creation (#4046) This PR introduces a short term fix for https://github.com/rapidsai/cugraph/issues/4037 . CC: @jnke2016 , @rlratzel Authors: - Vibhu Jawa (https://github.com/VibhuJawa) Approvers: - Rick Ratzel (https://github.com/rlratzel) - Joseph Nke (https://github.com/jnke2016) URL: https://github.com/rapidsai/cugraph/pull/4046 --- .../cugraph/cugraph/dask/common/mg_utils.py | 7 +++++- .../simpleDistributedGraph.py | 22 +++++++++---------- 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/python/cugraph/cugraph/dask/common/mg_utils.py b/python/cugraph/cugraph/dask/common/mg_utils.py index 6acda48c9da..b04f293dc0e 100644 --- a/python/cugraph/cugraph/dask/common/mg_utils.py +++ b/python/cugraph/cugraph/dask/common/mg_utils.py @@ -12,7 +12,7 @@ # limitations under the License. import os - +import gc import numba.cuda @@ -68,3 +68,8 @@ def get_visible_devices(): else: visible_devices = _visible_devices.strip().split(",") return visible_devices + + +def run_gc_on_dask_cluster(client): + gc.collect() + client.run(gc.collect) diff --git a/python/cugraph/cugraph/structure/graph_implementation/simpleDistributedGraph.py b/python/cugraph/cugraph/structure/graph_implementation/simpleDistributedGraph.py index f666900b226..319435575cc 100644 --- a/python/cugraph/cugraph/structure/graph_implementation/simpleDistributedGraph.py +++ b/python/cugraph/cugraph/structure/graph_implementation/simpleDistributedGraph.py @@ -37,8 +37,8 @@ from cugraph.dask.common.part_utils import ( get_persisted_df_worker_map, persist_dask_df_equal_parts_per_worker, - _chunk_lst, ) +from cugraph.dask.common.mg_utils import run_gc_on_dask_cluster from cugraph.dask import get_n_workers import cugraph.dask.comms.comms as Comms @@ -171,7 +171,6 @@ def __from_edgelist( store_transposed=False, legacy_renum_only=False, ): - if not isinstance(input_ddf, dask_cudf.DataFrame): raise TypeError("input should be a dask_cudf dataFrame") @@ -275,7 +274,6 @@ def __from_edgelist( ) value_col = None else: - source_col, dest_col, value_col = symmetrize( input_ddf, source, @@ -350,9 +348,11 @@ def __from_edgelist( is_symmetric=not self.properties.directed, ) ddf = ddf.repartition(npartitions=len(workers) * 2) - ddf_keys = ddf.to_delayed() workers = _client.scheduler_info()["workers"].keys() - ddf_keys_ls = _chunk_lst(ddf_keys, len(workers)) + persisted_keys_d = persist_dask_df_equal_parts_per_worker( + ddf, _client, return_type="dict" + ) + del ddf delayed_tasks_d = { w: delayed(simpleDistributedGraphImpl._make_plc_graph)( @@ -367,19 +367,19 @@ def __from_edgelist( self.edge_id_type, self.edge_type_id_type, ) - for w, edata in zip(workers, ddf_keys_ls) + for w, edata in persisted_keys_d.items() } + del persisted_keys_d self._plc_graph = { w: _client.compute( delayed_task, workers=w, allow_other_workers=False, pure=False ) for w, delayed_task in delayed_tasks_d.items() } - wait(list(self._plc_graph.values())) - del ddf_keys del delayed_tasks_d - gc.collect() - _client.run(gc.collect) + run_gc_on_dask_cluster(_client) + wait(list(self._plc_graph.values())) + run_gc_on_dask_cluster(_client) @property def renumbered(self): @@ -945,7 +945,6 @@ def convert_to_cudf(cp_arrays: cp.ndarray) -> cudf.Series: def _call_plc_select_random_vertices( mg_graph_x, sID: bytes, random_state: int, num_vertices: int ) -> cudf.Series: - cp_arrays = pylibcugraph_select_random_vertices( graph=mg_graph_x, resource_handle=ResourceHandle(Comms.get_handle(sID).getHandle()), @@ -961,7 +960,6 @@ def _mg_call_plc_select_random_vertices( random_state: int, num_vertices: int, ) -> dask_cudf.Series: - result = [ client.submit( _call_plc_select_random_vertices,

M^CzZYJ{U(;hM&;D2BzE7^$fiZ5egA%28D)UN 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ʦPmp&t*1A3u+?i%1ql*L)?( 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 z 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�b&jHY5u;I3RV8)krg61OMIj!fSY1{((Gymy7 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