diff --git a/dgf/src/io/dataset_loader_traffic.py b/dgf/src/io/dataset_loader_traffic.py index a1fdc93..49ee119 100644 --- a/dgf/src/io/dataset_loader_traffic.py +++ b/dgf/src/io/dataset_loader_traffic.py @@ -137,8 +137,8 @@ def traffic_cns_name(dataset: str, forecast_horizon_seconds: int) -> str: if forecast_horizon_seconds not in CACHED_FORECAST_HORIZONS_SECONDS: raise ValueError( f"Forecast horizon {forecast_horizon_seconds}s is not cached on CNS for" - f" dataset {dataset!r}. Cached horizons are: " - f"{sorted(CACHED_FORECAST_HORIZONS_SECONDS)} (seconds, corresponding" + f" dataset {dataset!r}. Cached horizons are:" + f" {sorted(CACHED_FORECAST_HORIZONS_SECONDS)} (seconds, corresponding" f" to {[CACHED_FORECAST_HORIZONS_SECONDS[h] for h in sorted(CACHED_FORECAST_HORIZONS_SECONDS)]})." " To build a graph with an arbitrary horizon, pass" " repo=dgf.io.Repo.WEB." @@ -278,17 +278,13 @@ def download_traffic_sensor_graph( ["sensor_id", "latitude", "longitude"], spec.locations_have_header, ) - distances = distances.assign( - **{ - "from": [_decode_sensor_id(value) for value in distances["from"]], - "to": [_decode_sensor_id(value) for value in distances["to"]], - "cost": distances["cost"].astype(np.float32), - } - ) + distances = distances.assign(**{ + "from": [_decode_sensor_id(value) for value in distances["from"]], + "to": [_decode_sensor_id(value) for value in distances["to"]], + "cost": distances["cost"].astype(np.float32), + }) locations = locations.assign( - sensor_id=[ - _decode_sensor_id(value) for value in locations["sensor_id"] - ], + sensor_id=[_decode_sensor_id(value) for value in locations["sensor_id"]], latitude=locations["latitude"].astype(np.float32), longitude=locations["longitude"].astype(np.float32), ) @@ -465,12 +461,12 @@ def build_traffic_graph( ), "time": sensor_times, "speed": sensor_speeds, - "latitude": locations["latitude"].to_numpy(dtype=np.float32)[ - location_positions - ], - "longitude": locations["longitude"].to_numpy(dtype=np.float32)[ - location_positions - ], + "latitude": ( + locations["latitude"].to_numpy(dtype=np.float32)[location_positions] + ), + "longitude": ( + locations["longitude"].to_numpy(dtype=np.float32)[location_positions] + ), } sensor_schema_features: dict[str, schema_lib.FeatureSchema] = { "#id": schema_lib.FeatureSchema( @@ -541,8 +537,9 @@ def build_traffic_graph( ) query_times = np.repeat(timestamps[query_time_indices], num_sensors) - query_sensors = np.tile(np.arange(num_sensors, dtype=np.int64), - num_query_times) + query_sensors = np.tile( + np.arange(num_sensors, dtype=np.int64), num_query_times + ) target_speeds = values[target_time_indices].reshape(-1) # Chronological 70/10/20 split, matching the canonical METR-LA and PEMS-BAY @@ -677,7 +674,7 @@ def fetch_traffic_graph( adjacency_threshold: float = 0.1, repo: Repo | str = Repo.AUTO, ) -> tuple[in_memory_graph_lib.InMemoryGraph, schema_lib.GraphSchema]: - """Downloads and loads a traffic speed forecasting benchmark into memory. + """Gets the METR-LA and PEMS-BAY traffic speed forecasting datasets. Both supported datasets record the speed of highway loop detectors every five minutes: METR-LA covers 207 detectors of the Los Angeles county highways over diff --git a/dgf/src/transform/merge.py b/dgf/src/transform/merge.py index 99a12dd..82cd657 100644 --- a/dgf/src/transform/merge.py +++ b/dgf/src/transform/merge.py @@ -50,6 +50,8 @@ class GraphMerger: (e.g., the padding has space for 100 nodes but 120 nodes are provided), raises an `InsufficientPaddingError` exception. + If `padding` is provided, sentinel nodes will addeds. + Attributes: schema: The original input GraphSchema. padding: The padding configuration to apply. diff --git a/doc/docs/api.md b/doc/docs/api.md index aba9845..a568a81 100644 --- a/doc/docs/api.md +++ b/doc/docs/api.md @@ -30,8 +30,12 @@ Utilities to analyze graphs, e.g., feature and graph statistics. * [`dgf.analyse.feature_statistics`](api/dgf-analyse.md#section-feature-statistics): Computes the feature stats from a single graph. * [`dgf.analyse.feature_statistics_from_graphs`](api/dgf-analyse.md#section-feature-statistics-from-graphs): Computes the feature stats from multiple graphs. +* [`dgf.analyse.infer_schema_semantic`](api/dgf-analyse.md#section-infer-schema-semantic): Automatically detects the semantic of features with UNKNOWN semantic. +* [`dgf.analyse.make_histogram`](api/dgf-analyse.md#section-make-histogram): Helper to create a Histogram from a numpy array of values. * [`dgf.analyse.padding_from_graph_generator`](api/dgf-analyse.md#section-padding-from-graph-generator): Creates a padding configuration from a set of in-memory graphs. * [`dgf.analyse.print_schema`](api/dgf-analyse.md#section-print-schema): Generates a human-readable string representation of a graph schema. +* [`dgf.analyse.topology_statistics`](api/dgf-analyse.md#section-topology-statistics): Computes topology statistics for a single InMemoryGraph. +* [`dgf.analyse.topology_statistics_from_graphs`](api/dgf-analyse.md#section-topology-statistics-from-graphs): Computes topology statistics for a set of InMemoryGraphs. @@ -43,6 +47,7 @@ Converts object formats, e.g., a graph to a Sparse Deferred struct. * [`dgf.convert.graph_dict_to_graph`](api/dgf-convert.md#section-graph-dict-to-graph): Converts a TF GNN Graph Sample Dict to an InMemoryGraph. * [`dgf.convert.graph_to_jax_graph`](api/dgf-convert.md#section-graph-to-jax-graph): Converts a (NumPy) in-memory graph into a JAX in-memory graph. * [`dgf.convert.graph_to_networkx`](api/dgf-convert.md#section-graph-to-networkx): Converts an InMemoryGraph into a NetworkX MultiDiGraph. +* [`dgf.convert.graph_to_pyg_data`](api/dgf-convert.md#section-graph-to-pyg-data): Converts a normalized DGF InMemoryGraph to a PyG HeteroData object. * [`dgf.convert.graph_to_serialized_tfgnn_graph`](api/dgf-convert.md#section-graph-to-serialized-tfgnn-graph): Converts an InMemoryGraph into a serialized TF-GNN graph sample proto. * [`dgf.convert.graph_to_sparse_deferred_struct`](api/dgf-convert.md#section-graph-to-sparse-deferred-struct): Converts an in-memory graph into a Sparse Deferred struct. * [`dgf.convert.graph_to_tf_graph`](api/dgf-convert.md#section-graph-to-tf-graph): Converts a graph to a TF in-memory graph. @@ -52,10 +57,13 @@ Converts object formats, e.g., a graph to a Sparse Deferred struct. * [`dgf.convert.networkx_to_graph`](api/dgf-convert.md#section-networkx-to-graph): Converts a NetworkX graph into an InMemoryGraph and its schema. * [`dgf.convert.schema_to_spanner_ddl`](api/dgf-convert.md#section-schema-to-spanner-ddl): Converts a GraphSchema to a string of CREATE statements for Spanner. * [`dgf.convert.schema_to_sparse_deferred_schema`](api/dgf-convert.md#section-schema-to-sparse-deferred-schema): Converts a DGF `GraphSchema` into a Sparse Deferred schema. +* [`dgf.convert.schema_to_tfgnn_graph_parsing_spec`](api/dgf-convert.md#section-schema-to-tfgnn-graph-parsing-spec): Builds the parsing spec of a TF GNN Graph Sample from a graph schema. * [`dgf.convert.schema_to_tfgnn_schema`](api/dgf-convert.md#section-schema-to-tfgnn-schema): Converts a GraphSchema object into a TF-GNN schema proto. +* [`dgf.convert.serialized_tfgnn_graph_to_tf_graph`](api/dgf-convert.md#section-serialized-tfgnn-graph-to-tf-graph): Converts a serialized TF GNN Graph Sample into a `TFInMemoryGraph`. * [`dgf.convert.sparse_deferred_struct_to_graph`](api/dgf-convert.md#section-sparse-deferred-struct-to-graph): Converts a Sparse Deferred struct into an in-memory graph. * [`dgf.convert.tf_graph_dict_to_tf_graph`](api/dgf-convert.md#section-tf-graph-dict-to-tf-graph): Converts a flattened TFInMemoryGraphDict back into a TFInMemoryGraph. * [`dgf.convert.tf_graph_to_tf_graph_dict`](api/dgf-convert.md#section-tf-graph-to-tf-graph-dict): Converts a TFInMemoryGraph into a flattened TFInMemoryGraphDict. +* [`dgf.convert.tfgnn_graph_dict_to_tf_graph`](api/dgf-convert.md#section-tfgnn-graph-dict-to-tf-graph): Converts a parsed TF GNN Graph Sample into a `TFInMemoryGraph`. * [`dgf.convert.tfgnn_graph_to_graph`](api/dgf-convert.md#section-tfgnn-graph-to-graph): Converts a TF GNN Graph Sample to an InMemoryGraph. * [`dgf.convert.tfgnn_schema_to_schema`](api/dgf-convert.md#section-tfgnn-schema-to-schema): Converts a TF-GNN schema proto into a GraphSchema object. @@ -67,8 +75,9 @@ Converts object formats, e.g., a graph to a Sparse Deferred struct. Classes that represent graph data. Contains no functions or algorithms. * [`dgf.data.EdgeSchema`](api/dgf-data.md#section-edgeschema): EdgeSchema(source: str, target: str, features: dict[str, dgf.src.data.schema.FeatureSchema] = ) -* [`dgf.data.EdgeSetPadding`](api/dgf-data.md#section-edgesetpadding): EdgeSetPadding(num_edges: int) +* [`dgf.data.EdgeSetPadding`](api/dgf-data.md#section-edgesetpadding): EdgeSetPadding(num_edges: int | None = None, features: dict[str, dgf.src.data.padding.FeaturePadding] = ) * [`dgf.data.FeatureFormat`](api/dgf-data.md#section-featureformat): How a value is represented / stored. +* [`dgf.data.FeaturePadding`](api/dgf-data.md#section-featurepadding): FeaturePadding(max_timeseries_len: int | None = None) * [`dgf.data.FeatureSchema`](api/dgf-data.md#section-featureschema): Schema for a single feature. * [`dgf.data.FeatureSemantic`](api/dgf-data.md#section-featuresemantic): How a value should be interpreted. * [`dgf.data.FeatureSetStatistics`](api/dgf-data.md#section-featuresetstatistics): Statistics for a set of features. @@ -77,6 +86,10 @@ Classes that represent graph data. Contains no functions or algorithms. * [`dgf.data.GraphSchema`](api/dgf-data.md#section-graphschema): GraphSchema(node_sets: dict[str, dgf.src.data.schema.NodeSchema], edge_sets: dict[str, dgf.src.data.schema.EdgeSchema]) * [`dgf.data.GraphSchemaFilter`](api/dgf-data.md#section-graphschemafilter): Filters a GraphSchema to sub-select node sets, edge sets, and features. * [`dgf.data.GraphSchemaV2`](api/dgf-data.md#section-graphschemav2): GraphSchema(node_sets: dict[str, dgf.src.data.schema.NodeSchema], edge_sets: dict[str, dgf.src.data.schema.EdgeSchema]) +* [`dgf.data.GraphSnapshots`](api/dgf-data.md#section-graphsnapshots): A chronologically ordered sequence of graph snapshots. +* [`dgf.data.GraphSnapshotsFormat`](api/dgf-data.md#section-graphsnapshotsformat): Format options for snapshots datasets. +* [`dgf.data.GraphSnapshotsMetadata`](api/dgf-data.md#section-graphsnapshotsmetadata): Root metadata for a DGF Graph Snapshots dataset. +* [`dgf.data.Histogram`](api/dgf-data.md#section-histogram): A dataclass representing a histogram. * [`dgf.data.InMemoryEdgeSet`](api/dgf-data.md#section-inmemoryedgeset): An Edge Set. * [`dgf.data.InMemoryGraph`](api/dgf-data.md#section-inmemorygraph): An in-memory generic graph. * [`dgf.data.InMemoryNodeSet`](api/dgf-data.md#section-inmemorynodeset): A Node Set. @@ -84,7 +97,7 @@ Classes that represent graph data. Contains no functions or algorithms. * [`dgf.data.JaxInMemoryGraph`](api/dgf-data.md#section-jaxinmemorygraph): An in-memory generic graph. * [`dgf.data.JaxInMemoryNodeSet`](api/dgf-data.md#section-jaxinmemorynodeset): A Node Set. * [`dgf.data.NodeSchema`](api/dgf-data.md#section-nodeschema): NodeSchema(features: dict[str, dgf.src.data.schema.FeatureSchema] = ) -* [`dgf.data.NodeSetPadding`](api/dgf-data.md#section-nodesetpadding): NodeSetPadding(num_nodes: int) +* [`dgf.data.NodeSetPadding`](api/dgf-data.md#section-nodesetpadding): NodeSetPadding(num_nodes: int | None = None, features: dict[str, dgf.src.data.padding.FeaturePadding] = ) * [`dgf.data.Padding`](api/dgf-data.md#section-padding): Information to pad a graph. * [`dgf.data.TFInMemoryEdgeSet`](api/dgf-data.md#section-tfinmemoryedgeset): An Edge Set. * [`dgf.data.TFInMemoryGraph`](api/dgf-data.md#section-tfinmemorygraph): An in-memory generic graph. @@ -107,11 +120,12 @@ DGF-specific exceptions. GraphFlow unified filesystem API. * [`dgf.filesystem.create_gcs_bucket`](api/dgf-filesystem.md#section-create-gcs-bucket): Creates a GCS bucket. -* [`dgf.filesystem.exists`](api/dgf-filesystem.md#section-exists): Returns True if the path exists. +* [`dgf.filesystem.exists`](api/dgf-filesystem.md#section-exists): Returns True if the file or directory exists. * [`dgf.filesystem.glob`](api/dgf-filesystem.md#section-glob): Returns a list of files and directories matching a pattern. * [`dgf.filesystem.is_gcs_path`](api/dgf-filesystem.md#section-is-gcs-path): Returns True if the path is a Google Cloud Storage (GCS) path. * [`dgf.filesystem.makedirs`](api/dgf-filesystem.md#section-makedirs): Creates directories if it does not exist. * [`dgf.filesystem.open_read`](api/dgf-filesystem.md#section-open-read): Opens a file for reading and return a python file handle. +* [`dgf.filesystem.open_write`](api/dgf-filesystem.md#section-open-write): Opens a file for writing and returns a python file handle. * [`dgf.filesystem.remove_paths`](api/dgf-filesystem.md#section-remove-paths): Removes all the files in parallel. * [`dgf.filesystem.rename`](api/dgf-filesystem.md#section-rename): Renames (moves) a file or directory from old_path to new_path. * [`dgf.filesystem.rmtree`](api/dgf-filesystem.md#section-rmtree): Recursively removes a directory and its contents. @@ -141,22 +155,30 @@ Functions to read and write graphs, schemas, and related data. * [`dgf.io.create_spanner_tables_from_graph_schema`](api/dgf-io.md#section-create-spanner-tables-from-graph-schema): Creates Spanner tables for a graph schema. * [`dgf.io.export_bigquery_to_disk`](api/dgf-io.md#section-export-bigquery-to-disk): Reads a BigQuery Graph in-process and returns a GraphFlow in-memory graph. * [`dgf.io.fetch_graphland_graph`](api/dgf-io.md#section-fetch-graphland-graph): Downloads and loads a Graphland dataset into memory. +* [`dgf.io.fetch_jena_climate_graph`](api/dgf-io.md#section-fetch-jena-climate-graph): Downloads and loads the Jena Climate time series benchmark into memory. * [`dgf.io.fetch_ogb_graph`](api/dgf-io.md#section-fetch-ogb-graph): Downloads and loads an OGB node property prediction dataset into memory. +* [`dgf.io.fetch_traffic_graph`](api/dgf-io.md#section-fetch-traffic-graph): Gets the METR-LA and PEMS-BAY traffic speed forecasting datasets. * [`dgf.io.read_bigquery_graph`](api/dgf-io.md#section-read-bigquery-graph): Reads a BigQuery Graph in-process and returns a GraphFlow in-memory graph. * [`dgf.io.read_bigquery_graph_schema`](api/dgf-io.md#section-read-bigquery-graph-schema): Reads the schema of a BigQuery graph into a GF schema. * [`dgf.io.read_feature_statistics`](api/dgf-io.md#section-read-feature-statistics): Reads feature statistics from disk in a JSON format. * [`dgf.io.read_graph`](api/dgf-io.md#section-read-graph): Reads a GF graph from a directory to an in-memory graph. +* [`dgf.io.read_graph_snapshots`](api/dgf-io.md#section-read-graph-snapshots): Reads a DGF Graph Snapshots dataset from disk into memory. +* [`dgf.io.read_graph_snapshots_as_temporal_graph`](api/dgf-io.md#section-read-graph-snapshots-as-temporal-graph): Reads a DGF Graph Snapshots dataset and combines it into a Temporal Graph. * [`dgf.io.read_graphai_hgraph`](api/dgf-io.md#section-read-graphai-hgraph): Reads an on-disk HGraph into an in-memory representation. * [`dgf.io.read_schema`](api/dgf-io.md#section-read-schema): Loads graph schema from disk in a json format. -* [`dgf.io.read_spanner_graph`](api/dgf-io.md#section-read-spanner-graph): Reads a Spanner Graph in-process and returns a GraphFlow in-memory graph. +* [`dgf.io.read_snapshot_metadata`](api/dgf-io.md#section-read-snapshot-metadata): Reads and parses GraphSnapshotsMetadata from a JSON file. +* [`dgf.io.read_spanner_graph`](api/dgf-io.md#section-read-spanner-graph): Reads a Spanner Graph sequentially in-process using direct SQL queries on base tables. * [`dgf.io.read_spanner_graph_schema`](api/dgf-io.md#section-read-spanner-graph-schema): Reads the schema of a Spanner Graph. * [`dgf.io.read_text_proto`](api/dgf-io.md#section-read-text-proto): Read a proto from disk in text format. * [`dgf.io.read_tfgnn_graphs`](api/dgf-io.md#section-read-tfgnn-graphs): Reads a set of in-memory graphs from disk stored as TF Examples. +* [`dgf.io.read_topology_statistics`](api/dgf-io.md#section-read-topology-statistics): Reads topology statistics from disk in a JSON format. * [`dgf.io.write_feature_statistics`](api/dgf-io.md#section-write-feature-statistics): Saves feature statistics to disk in a json format. * [`dgf.io.write_graph`](api/dgf-io.md#section-write-graph): Writes an in-memory graph and schema to a GF Graph directory. * [`dgf.io.write_schema`](api/dgf-io.md#section-write-schema): Saves graph schema to disk in a json format. +* [`dgf.io.write_snapshot_metadata`](api/dgf-io.md#section-write-snapshot-metadata): Serializes and writes GraphSnapshotsMetadata to a JSON file. * [`dgf.io.write_text_proto`](api/dgf-io.md#section-write-text-proto): Writes a proto to disk in text format. * [`dgf.io.write_tfgnn_graphs`](api/dgf-io.md#section-write-tfgnn-graphs): Writes a set of in-memory graphs to disk as TF Examples. +* [`dgf.io.write_topology_statistics`](api/dgf-io.md#section-write-topology-statistics): Saves topology statistics to disk in a json format. @@ -165,10 +187,6 @@ Functions to read and write graphs, schemas, and related data. Machine Learning and Graph Neural Networks using JAX. -* [`dgf.jax.JaxBaseConfig`](api/dgf-jax.md#section-jaxbaseconfig): Base class for a GNN implemented in JAX. -* [`dgf.jax.get_activation`](api/dgf-jax.md#section-get-activation): Get an activation function by (string) name. -* [`dgf.jax.jnp_dtype_from_string`](api/dgf-jax.md#section-jnp-dtype-from-string): Return a JAX numpy type from a string name. -* [`dgf.jax.jnp_name_from_dtype`](api/dgf-jax.md#section-jnp-name-from-dtype): Return a string name for a jnp.dtype object. * [`dgf.jax.train`](api/dgf-jax.md#section-train): Trains a Flax module with a flexible and feature-rich training loop. @@ -178,34 +196,37 @@ Flax modules implementing low level GNN operations. * [`dgf.jax.layers.ClassificationHead`](api/dgf-jax-layers.md#section-classificationhead): Simple classification head. * [`dgf.jax.layers.ClassificationHeadConfig`](api/dgf-jax-layers.md#section-classificationheadconfig): Configuration for a classification head. -* [`dgf.jax.layers.ConditionalGIN`](api/dgf-jax-layers.md#section-conditionalgin): Conditional GIN with a labeling trick: https://arxiv.org/abs/2106.06935. +* [`dgf.jax.layers.ConditionalGIN`](api/dgf-jax-layers.md#section-conditionalgin): Homogeneous Conditional GIN with a labeling trick. * [`dgf.jax.layers.EmbedAndHomogenizeGraph`](api/dgf-jax-layers.md#section-embedandhomogenizegraph): Convert a heterogeneous graph into a homogeneous one. * [`dgf.jax.layers.EmbedAndHomogenizeGraphConfig`](api/dgf-jax-layers.md#section-embedandhomogenizegraphconfig): Config for EmbedAndHomogenizeGraph. -* [`dgf.jax.layers.EmbedFeatureSet`](api/dgf-jax-layers.md#section-embedfeatureset): Computes a fixed sized dense embedding for a set of feature values. +* [`dgf.jax.layers.EmbedFeatureGroups`](api/dgf-jax-layers.md#section-embedfeaturegroups): Computes embeddings for static features and timeseries sequence groups. +* [`dgf.jax.layers.EmbedFeatureGroupsConfig`](api/dgf-jax-layers.md#section-embedfeaturegroupsconfig): Configuration for the EmbedFeatureGroups layer. +* [`dgf.jax.layers.EmbedFeatureSet`](api/dgf-jax-layers.md#section-embedfeatureset): Computes a fixed sized dense embedding for static and timeseries feature values. * [`dgf.jax.layers.EmbedFeatureSetConfig`](api/dgf-jax-layers.md#section-embedfeaturesetconfig): Configuration for the EmbedFeatureSet layer. * [`dgf.jax.layers.EmbedGraph`](api/dgf-jax-layers.md#section-embedgraph): Compute a fixed sized dense embedding for all the features in a graph. * [`dgf.jax.layers.EmbedGraphConfig`](api/dgf-jax-layers.md#section-embedgraphconfig): Configuration for "EmbedGraph". -* [`dgf.jax.layers.GCN`](api/dgf-jax-layers.md#section-gcn): Graph convolutional network: https://arxiv.org/pdf/1609.02907.pdf. -* [`dgf.jax.layers.GCNConfig`](api/dgf-jax-layers.md#section-gcnconfig): Makeable GCN config class with sensible defaults. -* [`dgf.jax.layers.GIN`](api/dgf-jax-layers.md#section-gin): Graph isomorphism network: https://arxiv.org/pdf/1810.00826.pdf. -* [`dgf.jax.layers.GINConfig`](api/dgf-jax-layers.md#section-ginconfig): Makeable GIN config class with sensible defaults. +* [`dgf.jax.layers.GCN`](api/dgf-jax-layers.md#section-gcn): Homogeneous Graph Convolutional Network. +* [`dgf.jax.layers.GCNConfig`](api/dgf-jax-layers.md#section-gcnconfig): Config for GCN. +* [`dgf.jax.layers.GIN`](api/dgf-jax-layers.md#section-gin): Homogeneous Graph Isomorphism Network. +* [`dgf.jax.layers.GINConfig`](api/dgf-jax-layers.md#section-ginconfig): Config for GIN. * [`dgf.jax.layers.GenericBlock`](api/dgf-jax-layers.md#section-genericblock): A generic configurable neural network block. * [`dgf.jax.layers.GenericBlockConfig`](api/dgf-jax-layers.md#section-genericblockconfig): Configuration for a generic block parsed from a string. +* [`dgf.jax.layers.GnnPlus`](api/dgf-jax-layers.md#section-gnnplus): Generic module for GnnPlus. * [`dgf.jax.layers.HeterogeneousGraphAttentionNetwork`](api/dgf-jax-layers.md#section-heterogeneousgraphattentionnetwork): A single layer of heterogeneous Graph Attention Network. * [`dgf.jax.layers.HeterogeneousGraphAttentionNetworkConfig`](api/dgf-jax-layers.md#section-heterogeneousgraphattentionnetworkconfig): Configuration for HeterogeneousGraphAttentionNetwork. * [`dgf.jax.layers.HeterogeneousGraphConvolution`](api/dgf-jax-layers.md#section-heterogeneousgraphconvolution): A single layer of heterogeneous Graph Neural Network message passing. * [`dgf.jax.layers.HeterogeneousGraphConvolutionConfig`](api/dgf-jax-layers.md#section-heterogeneousgraphconvolutionconfig): Configuration for HeterogeneousGraphConvolution. * [`dgf.jax.layers.MLP`](api/dgf-jax-layers.md#section-mlp): A generic MLP followed by a linear layer. -* [`dgf.jax.layers.MPNN`](api/dgf-jax-layers.md#section-mpnn): Message-Passing Neural Network: https://arxiv.org/abs/1704.01212. -* [`dgf.jax.layers.MPNNConfig`](api/dgf-jax-layers.md#section-mpnnconfig): Makeable MPNN config class with sensible defaults. -* [`dgf.jax.layers.Projector`](api/dgf-jax-layers.md#section-projector): Simple wrapper around the generic MLP layer for graph input/output. -* [`dgf.jax.layers.ProjectorConfig`](api/dgf-jax-layers.md#section-projectorconfig): Makeable Projector config class with sensible defaults. -* [`dgf.jax.layers.ResidualMLPV2`](api/dgf-jax-layers.md#section-residualmlpv2): A residual MLP layer. See ResidualMLPV2Config. -* [`dgf.jax.layers.ResidualMLPV2Config`](api/dgf-jax-layers.md#section-residualmlpv2config): A residual MLP layer. -* [`dgf.jax.layers.identity`](api/dgf-jax-layers.md#section-identity): Returns a GenericBlockConfig that acts as an identity block. -* [`dgf.jax.layers.ingest_feature`](api/dgf-jax-layers.md#section-ingest-feature): Returns a GenericBlockConfig for feature ingestion. -* [`dgf.jax.layers.modern_residual_mlp`](api/dgf-jax-layers.md#section-modern-residual-mlp): Returns a GenericBlockConfig for a modern residual MLP. -* [`dgf.jax.layers.sequential_mlp`](api/dgf-jax-layers.md#section-sequential-mlp): Returns a GenericBlockConfig for a sequential MLP. +* [`dgf.jax.layers.MPNN`](api/dgf-jax-layers.md#section-mpnn): Homogeneous Message-Passing Neural Network. +* [`dgf.jax.layers.MPNNConfig`](api/dgf-jax-layers.md#section-mpnnconfig): Config for MPNN. +* [`dgf.jax.layers.Projector`](api/dgf-jax-layers.md#section-projector): Homogeneous Graph Feature Projector. +* [`dgf.jax.layers.ProjectorConfig`](api/dgf-jax-layers.md#section-projectorconfig): Config for Projector. +* [`dgf.jax.layers.ResidualMLPV2`](api/dgf-jax-layers.md#section-residualmlpv2): A [dense + norm + activation + drop-out + residual] * num_layers MLP layer. +* [`dgf.jax.layers.ResidualMLPV2Config`](api/dgf-jax-layers.md#section-residualmlpv2config): A [dense + norm + activation + drop-out + residual] * num_layers MLP layer. +* [`dgf.jax.layers.identity`](api/dgf-jax-layers.md#section-identity): Preconfigured GenericBlockConfig that acts as an identity block. +* [`dgf.jax.layers.ingest_feature`](api/dgf-jax-layers.md#section-ingest-feature): Preconfigured GenericBlockConfig for feature ingestion. +* [`dgf.jax.layers.modern_residual_mlp`](api/dgf-jax-layers.md#section-modern-residual-mlp): Preconfigured GenericBlockConfig for a modern residual MLP. +* [`dgf.jax.layers.sequential_mlp`](api/dgf-jax-layers.md#section-sequential-mlp): Preconfigured GenericBlockConfig for a sequential MLP. @@ -219,6 +240,7 @@ Top-level learning module. * [`dgf.learning.LinkPredictionModel`](api/dgf-learning.md#section-linkpredictionmodel): The user-visible returned model object for edge prediction. * [`dgf.learning.Model`](api/dgf-learning.md#section-model): A generic model from the 10-lines of code API. * [`dgf.learning.NodePredictionModel`](api/dgf-learning.md#section-nodepredictionmodel): The user-visible returned model object. +* [`dgf.learning.TFFunctionInputFormat`](api/dgf-learning.md#section-tffunctioninputformat): Input format of a model exported with `to_tensorflow_function`. * [`dgf.learning.load_model`](api/dgf-learning.md#section-load-model): Loads a model previously saved with `model.save()`. * [`dgf.learning.train_link_model`](api/dgf-learning.md#section-train-link-model): Trains a supervised Graph Neural Network model for edge prediction. * [`dgf.learning.train_node_model`](api/dgf-learning.md#section-train-node-model): Trains a supervised Graph Neural Network model for node-level prediction. @@ -259,6 +281,7 @@ Functions and classes to extract subsets of graphs for GNN training. * [`dgf.sampling.create_graph_spanner_sampler`](api/dgf-sampling.md#section-create-graph-spanner-sampler): Creates a SpannerGraphSampler instance. * [`dgf.sampling.create_sampler`](api/dgf-sampling.md#section-create-sampler): Creates an in-memory sampler. * [`dgf.sampling.extract_beam_nodes_ids`](api/dgf-sampling.md#section-extract-beam-nodes-ids): Extracts all the node ids of a given nodeset. +* [`dgf.sampling.offline_distributed_sampler_gcp`](api/dgf-sampling.md#section-offline-distributed-sampler-gcp): Runs the offline distributed graph sampler on GCP. * [`dgf.sampling.sample_with_beam_semi_distributed_sampler`](api/dgf-sampling.md#section-sample-with-beam-semi-distributed-sampler): Samples subgraphs from a distributed graph using a semi-distributed algo. * [`dgf.sampling.sample_with_beam_semi_distributed_sampler_v2`](api/dgf-sampling.md#section-sample-with-beam-semi-distributed-sampler-v2): Samples subgraphs from a distributed graph using a semi-distributed algo. * [`dgf.sampling.simple_sampling_config_to_sampling_plan`](api/dgf-sampling.md#section-simple-sampling-config-to-sampling-plan): Converts a SimpleSamplingConfig to a more general SamplingPlan. @@ -280,18 +303,24 @@ Functions and classes to train core GNN models. Transforms graph data into other graph structures or formats. * [`dgf.transform.AutoNormalizeConfig`](api/dgf-transform.md#section-autonormalizeconfig): Configuration for automatic feature normalization for GNNs. +* [`dgf.transform.CalendarFeature`](api/dgf-transform.md#section-calendarfeature): Supported calendar features to extract from timestamps. +* [`dgf.transform.CalendarNormalizer`](api/dgf-transform.md#section-calendarnormalizer): Extracts normalized UTC calendar components from a TIMESTAMP feature. * [`dgf.transform.ContainsLabelPredicate`](api/dgf-transform.md#section-containslabelpredicate): Predicate for filtering subgraphs if they have a positive label. * [`dgf.transform.DictionaryIndexNormalizer`](api/dgf-transform.md#section-dictionaryindexnormalizer): Normalizes features by mapping dictionary keys to their integer indices. * [`dgf.transform.GNNDatasetPreparator`](api/dgf-transform.md#section-gnndatasetpreparator): Generates graph samples to train node prediction models. +* [`dgf.transform.GraphMerger`](api/dgf-transform.md#section-graphmerger): Merges multiple `InMemoryGraph` instances into a single graph. * [`dgf.transform.GraphNormalizer`](api/dgf-transform.md#section-graphnormalizer): Applies a collection of individual AbstractFeatureNormalizer on a graph. * [`dgf.transform.GraphNormalizerConfig`](api/dgf-transform.md#section-graphnormalizerconfig): Raw information of a GraphNormalizer for easy serialization. * [`dgf.transform.IdentityNormalizer`](api/dgf-transform.md#section-identitynormalizer): A normalizer that simply pass a feature without changing it. * [`dgf.transform.NumNodesPredicate`](api/dgf-transform.md#section-numnodespredicate): Predicate for filtering by number of nodes. +* [`dgf.transform.SequentialNormalizer`](api/dgf-transform.md#section-sequentialnormalizer): Applies multiple AbstractFeatureNormalizers in sequence. * [`dgf.transform.SinusoidTimedeltaNormalizer`](api/dgf-transform.md#section-sinusoidtimedeltanormalizer): Normalizes a time delta feature by applying sinusoidal embeddings. * [`dgf.transform.SoftQuantileNormalizer`](api/dgf-transform.md#section-softquantilenormalizer): Normalizes a numerical feature by replacing it with its soft quantile -0.5. +* [`dgf.transform.TimedeltaNormalizer`](api/dgf-transform.md#section-timedeltanormalizer): Normalizes a TIMESTAMP feature into a TIMEDELTA feature relative to seed timestamps. * [`dgf.transform.apply_feature`](api/dgf-transform.md#section-apply-feature): Applies feature processors to the node and edge sets of a graph. * [`dgf.transform.auto_normalize`](api/dgf-transform.md#section-auto-normalize): Create a generally good GraphNormalizer from feature statistics. * [`dgf.transform.batch_indices_generator`](api/dgf-transform.md#section-batch-indices-generator): Generates batches of indices. +* [`dgf.transform.combine_graph_snapshots`](api/dgf-transform.md#section-combine-graph-snapshots): Combines chronologically ordered graph snapshots into a Temporal Graph. * [`dgf.transform.drop_edge_features`](api/dgf-transform.md#section-drop-edge-features): Drops all edge features from a graph and its schema. * [`dgf.transform.drop_edge_features_from_schema`](api/dgf-transform.md#section-drop-edge-features-from-schema): Drops all edge features from a schema. * [`dgf.transform.filter_graph`](api/dgf-transform.md#section-filter-graph): Creates an in-memory graph with a subset of nodesets/edgesets/features. @@ -299,9 +328,8 @@ Transforms graph data into other graph structures or formats. * [`dgf.transform.filter_schema`](api/dgf-transform.md#section-filter-schema): Extracts a subset of the nodesets/edgesets/features from a schema. * [`dgf.transform.homogeneous_graph_piece_to_nx`](api/dgf-transform.md#section-homogeneous-graph-piece-to-nx): Convert InMemoryGraph to an nx.Graph object. * [`dgf.transform.homogenize`](api/dgf-transform.md#section-homogenize): Homogenizes a heterogeneous graph into a homogeneous one. -* [`dgf.transform.merge_graphs`](api/dgf-transform.md#section-merge-graphs): Merges multiple `InMemoryGraph` instances into a single graph. * [`dgf.transform.propagate_timestamp_to_edges`](api/dgf-transform.md#section-propagate-timestamp-to-edges): Propagates timestamps from nodes to edges. -* [`dgf.transform.remove_padding_sentinels`](api/dgf-transform.md#section-remove-padding-sentinels): Removes the sentinel nodes and edges added by `merge_graphs`. +* [`dgf.transform.remove_padding_sentinels`](api/dgf-transform.md#section-remove-padding-sentinels): Removes the sentinel nodes and edges added by `GraphMerger`. * [`dgf.transform.table2graph`](api/dgf-transform.md#section-table2graph): Converts a table (dict of arrays or DataFrame) into an InMemoryGraph and Schema. @@ -311,7 +339,9 @@ Transforms graph data into other graph structures or formats. Functions to validate graph data. +* [`dgf.validate.fix_schema`](api/dgf-validate.md#section-fix-schema): Tries to fix broken/invalid schemas by inferring and setting primary keys. * [`dgf.validate.validate_graph`](api/dgf-validate.md#section-validate-graph): Validates an in memory graph object. +* [`dgf.validate.validate_snapshots`](api/dgf-validate.md#section-validate-snapshots): Validates the storage layout and contents of DGF Graph Snapshots. @@ -344,7 +374,7 @@ Classes that represent graph data. Contains no functions or algorithms. * [`dgf.beam.data.HeterogeniousGraph`](api/dgf-beam-data.md#section-heterogeniousgraph): A (potentially distributed) heterogeneous graph. * [`dgf.beam.data.HomogeneousGraph`](api/dgf-beam-data.md#section-homogeneousgraph): A (potentially distributed) homogeneous graph. * [`dgf.beam.data.KeyedInMemoryGraph`](api/dgf-beam-data.md#section-keyedinmemorygraph): KeyedInMemoryGraph(key, graph) -* [`dgf.beam.data.Node`](api/dgf-beam-data.md#section-node): Node(id: bytes | int, features: dict[str, numpy.ndarray] | None = None) +* [`dgf.beam.data.Node`](api/dgf-beam-data.md#section-node): Node(id: 'NodeId', features: 'Features | None' = None) @@ -362,10 +392,11 @@ Functions to read and write graphs, schemas, and related data using Beam. * [`dgf.beam.io.write_edge_set_to_spanner`](api/dgf-beam-io.md#section-write-edge-set-to-spanner): Writes an edge set to a Spanner table using SpannerInsertOrUpdate. * [`dgf.beam.io.write_feature_statistics`](api/dgf-beam-io.md#section-write-feature-statistics): Writes a beam pcollection of feature statistics to disk in json format. * [`dgf.beam.io.write_graph`](api/dgf-beam-io.md#section-write-graph): Writes a GF Graph from a distributed graph (beam). -* [`dgf.beam.io.write_graphai_hgraph`](api/dgf-beam-io.md#section-write-graphai-hgraph): Initializes the WriteToHGraph PTransform. +* [`dgf.beam.io.write_graphai_hgraph`](api/dgf-beam-io.md#section-write-graphai-hgraph): Writes a distributed HGraph using Beam. * [`dgf.beam.io.write_node_set_to_spanner`](api/dgf-beam-io.md#section-write-node-set-to-spanner): Writes a node set to a Spanner table using SpannerInsertOrUpdate. * [`dgf.beam.io.write_spanner`](api/dgf-beam-io.md#section-write-spanner): Writes a heterogeneous graph to Spanner. * [`dgf.beam.io.write_tfgnn_graphs`](api/dgf-beam-io.md#section-write-tfgnn-graphs): Writes a collection of TF Graph Samples on disk. +* [`dgf.beam.io.write_topology_statistics`](api/dgf-beam-io.md#section-write-topology-statistics): Writes a beam pcollection of topology statistics to disk in json format. diff --git a/doc/docs/api/dgf-analyse.md b/doc/docs/api/dgf-analyse.md index d7d232b..51956a4 100644 --- a/doc/docs/api/dgf-analyse.md +++ b/doc/docs/api/dgf-analyse.md @@ -11,9 +11,21 @@ hide: ## dgf.analyse.feature_statistics_from_graphs # {: #section-feature-statistics-from-graphs} ::: dgf.analyse.feature_statistics_from_graphs +## dgf.analyse.infer_schema_semantic # {: #section-infer-schema-semantic} +::: dgf.analyse.infer_schema_semantic + +## dgf.analyse.make_histogram # {: #section-make-histogram} +::: dgf.analyse.make_histogram + ## dgf.analyse.padding_from_graph_generator # {: #section-padding-from-graph-generator} ::: dgf.analyse.padding_from_graph_generator ## dgf.analyse.print_schema # {: #section-print-schema} ::: dgf.analyse.print_schema +## dgf.analyse.topology_statistics # {: #section-topology-statistics} +::: dgf.analyse.topology_statistics + +## dgf.analyse.topology_statistics_from_graphs # {: #section-topology-statistics-from-graphs} +::: dgf.analyse.topology_statistics_from_graphs + diff --git a/doc/docs/api/dgf-beam-io.md b/doc/docs/api/dgf-beam-io.md index 4de53ba..3a02eac 100644 --- a/doc/docs/api/dgf-beam-io.md +++ b/doc/docs/api/dgf-beam-io.md @@ -44,3 +44,6 @@ hide: ## dgf.beam.io.write_tfgnn_graphs # {: #section-write-tfgnn-graphs} ::: dgf.beam.io.write_tfgnn_graphs +## dgf.beam.io.write_topology_statistics # {: #section-write-topology-statistics} +::: dgf.beam.io.write_topology_statistics + diff --git a/doc/docs/api/dgf-convert.md b/doc/docs/api/dgf-convert.md index 9b471b8..ead1921 100644 --- a/doc/docs/api/dgf-convert.md +++ b/doc/docs/api/dgf-convert.md @@ -14,6 +14,9 @@ hide: ## dgf.convert.graph_to_networkx # {: #section-graph-to-networkx} ::: dgf.convert.graph_to_networkx +## dgf.convert.graph_to_pyg_data # {: #section-graph-to-pyg-data} +::: dgf.convert.graph_to_pyg_data + ## dgf.convert.graph_to_serialized_tfgnn_graph # {: #section-graph-to-serialized-tfgnn-graph} ::: dgf.convert.graph_to_serialized_tfgnn_graph @@ -41,9 +44,15 @@ hide: ## dgf.convert.schema_to_sparse_deferred_schema # {: #section-schema-to-sparse-deferred-schema} ::: dgf.convert.schema_to_sparse_deferred_schema +## dgf.convert.schema_to_tfgnn_graph_parsing_spec # {: #section-schema-to-tfgnn-graph-parsing-spec} +::: dgf.convert.schema_to_tfgnn_graph_parsing_spec + ## dgf.convert.schema_to_tfgnn_schema # {: #section-schema-to-tfgnn-schema} ::: dgf.convert.schema_to_tfgnn_schema +## dgf.convert.serialized_tfgnn_graph_to_tf_graph # {: #section-serialized-tfgnn-graph-to-tf-graph} +::: dgf.convert.serialized_tfgnn_graph_to_tf_graph + ## dgf.convert.sparse_deferred_struct_to_graph # {: #section-sparse-deferred-struct-to-graph} ::: dgf.convert.sparse_deferred_struct_to_graph @@ -53,6 +62,9 @@ hide: ## dgf.convert.tf_graph_to_tf_graph_dict # {: #section-tf-graph-to-tf-graph-dict} ::: dgf.convert.tf_graph_to_tf_graph_dict +## dgf.convert.tfgnn_graph_dict_to_tf_graph # {: #section-tfgnn-graph-dict-to-tf-graph} +::: dgf.convert.tfgnn_graph_dict_to_tf_graph + ## dgf.convert.tfgnn_graph_to_graph # {: #section-tfgnn-graph-to-graph} ::: dgf.convert.tfgnn_graph_to_graph diff --git a/doc/docs/api/dgf-data.md b/doc/docs/api/dgf-data.md index 0e16b4d..2cd97eb 100644 --- a/doc/docs/api/dgf-data.md +++ b/doc/docs/api/dgf-data.md @@ -14,6 +14,9 @@ hide: ## dgf.data.FeatureFormat # {: #section-featureformat} ::: dgf.data.FeatureFormat +## dgf.data.FeaturePadding # {: #section-featurepadding} +::: dgf.data.FeaturePadding + ## dgf.data.FeatureSchema # {: #section-featureschema} ::: dgf.data.FeatureSchema @@ -38,6 +41,18 @@ hide: ## dgf.data.GraphSchemaV2 # {: #section-graphschemav2} ::: dgf.data.GraphSchemaV2 +## dgf.data.GraphSnapshots # {: #section-graphsnapshots} +::: dgf.data.GraphSnapshots + +## dgf.data.GraphSnapshotsFormat # {: #section-graphsnapshotsformat} +::: dgf.data.GraphSnapshotsFormat + +## dgf.data.GraphSnapshotsMetadata # {: #section-graphsnapshotsmetadata} +::: dgf.data.GraphSnapshotsMetadata + +## dgf.data.Histogram # {: #section-histogram} +::: dgf.data.Histogram + ## dgf.data.InMemoryEdgeSet # {: #section-inmemoryedgeset} ::: dgf.data.InMemoryEdgeSet diff --git a/doc/docs/api/dgf-filesystem.md b/doc/docs/api/dgf-filesystem.md index dbb2c33..0212ad1 100644 --- a/doc/docs/api/dgf-filesystem.md +++ b/doc/docs/api/dgf-filesystem.md @@ -23,6 +23,9 @@ hide: ## dgf.filesystem.open_read # {: #section-open-read} ::: dgf.filesystem.open_read +## dgf.filesystem.open_write # {: #section-open-write} +::: dgf.filesystem.open_write + ## dgf.filesystem.remove_paths # {: #section-remove-paths} ::: dgf.filesystem.remove_paths diff --git a/doc/docs/api/dgf-io.md b/doc/docs/api/dgf-io.md index de3a150..2195ffa 100644 --- a/doc/docs/api/dgf-io.md +++ b/doc/docs/api/dgf-io.md @@ -17,9 +17,15 @@ hide: ## dgf.io.fetch_graphland_graph # {: #section-fetch-graphland-graph} ::: dgf.io.fetch_graphland_graph +## dgf.io.fetch_jena_climate_graph # {: #section-fetch-jena-climate-graph} +::: dgf.io.fetch_jena_climate_graph + ## dgf.io.fetch_ogb_graph # {: #section-fetch-ogb-graph} ::: dgf.io.fetch_ogb_graph +## dgf.io.fetch_traffic_graph # {: #section-fetch-traffic-graph} +::: dgf.io.fetch_traffic_graph + ## dgf.io.read_bigquery_graph # {: #section-read-bigquery-graph} ::: dgf.io.read_bigquery_graph @@ -32,12 +38,21 @@ hide: ## dgf.io.read_graph # {: #section-read-graph} ::: dgf.io.read_graph +## dgf.io.read_graph_snapshots # {: #section-read-graph-snapshots} +::: dgf.io.read_graph_snapshots + +## dgf.io.read_graph_snapshots_as_temporal_graph # {: #section-read-graph-snapshots-as-temporal-graph} +::: dgf.io.read_graph_snapshots_as_temporal_graph + ## dgf.io.read_graphai_hgraph # {: #section-read-graphai-hgraph} ::: dgf.io.read_graphai_hgraph ## dgf.io.read_schema # {: #section-read-schema} ::: dgf.io.read_schema +## dgf.io.read_snapshot_metadata # {: #section-read-snapshot-metadata} +::: dgf.io.read_snapshot_metadata + ## dgf.io.read_spanner_graph # {: #section-read-spanner-graph} ::: dgf.io.read_spanner_graph @@ -50,6 +65,9 @@ hide: ## dgf.io.read_tfgnn_graphs # {: #section-read-tfgnn-graphs} ::: dgf.io.read_tfgnn_graphs +## dgf.io.read_topology_statistics # {: #section-read-topology-statistics} +::: dgf.io.read_topology_statistics + ## dgf.io.write_feature_statistics # {: #section-write-feature-statistics} ::: dgf.io.write_feature_statistics @@ -59,9 +77,15 @@ hide: ## dgf.io.write_schema # {: #section-write-schema} ::: dgf.io.write_schema +## dgf.io.write_snapshot_metadata # {: #section-write-snapshot-metadata} +::: dgf.io.write_snapshot_metadata + ## dgf.io.write_text_proto # {: #section-write-text-proto} ::: dgf.io.write_text_proto ## dgf.io.write_tfgnn_graphs # {: #section-write-tfgnn-graphs} ::: dgf.io.write_tfgnn_graphs +## dgf.io.write_topology_statistics # {: #section-write-topology-statistics} +::: dgf.io.write_topology_statistics + diff --git a/doc/docs/api/dgf-jax-layers.md b/doc/docs/api/dgf-jax-layers.md index 75f810b..eb00df7 100644 --- a/doc/docs/api/dgf-jax-layers.md +++ b/doc/docs/api/dgf-jax-layers.md @@ -20,6 +20,12 @@ hide: ## dgf.jax.layers.EmbedAndHomogenizeGraphConfig # {: #section-embedandhomogenizegraphconfig} ::: dgf.jax.layers.EmbedAndHomogenizeGraphConfig +## dgf.jax.layers.EmbedFeatureGroups # {: #section-embedfeaturegroups} +::: dgf.jax.layers.EmbedFeatureGroups + +## dgf.jax.layers.EmbedFeatureGroupsConfig # {: #section-embedfeaturegroupsconfig} +::: dgf.jax.layers.EmbedFeatureGroupsConfig + ## dgf.jax.layers.EmbedFeatureSet # {: #section-embedfeatureset} ::: dgf.jax.layers.EmbedFeatureSet @@ -50,6 +56,9 @@ hide: ## dgf.jax.layers.GenericBlockConfig # {: #section-genericblockconfig} ::: dgf.jax.layers.GenericBlockConfig +## dgf.jax.layers.GnnPlus # {: #section-gnnplus} +::: dgf.jax.layers.GnnPlus + ## dgf.jax.layers.HeterogeneousGraphAttentionNetwork # {: #section-heterogeneousgraphattentionnetwork} ::: dgf.jax.layers.HeterogeneousGraphAttentionNetwork diff --git a/doc/docs/api/dgf-jax.md b/doc/docs/api/dgf-jax.md index ef0614f..e53ae23 100644 --- a/doc/docs/api/dgf-jax.md +++ b/doc/docs/api/dgf-jax.md @@ -5,18 +5,6 @@ hide: # dgf.jax -## dgf.jax.JaxBaseConfig # {: #section-jaxbaseconfig} -::: dgf.jax.JaxBaseConfig - -## dgf.jax.get_activation # {: #section-get-activation} -::: dgf.jax.get_activation - -## dgf.jax.jnp_dtype_from_string # {: #section-jnp-dtype-from-string} -::: dgf.jax.jnp_dtype_from_string - -## dgf.jax.jnp_name_from_dtype # {: #section-jnp-name-from-dtype} -::: dgf.jax.jnp_name_from_dtype - ## dgf.jax.train # {: #section-train} ::: dgf.jax.train diff --git a/doc/docs/api/dgf-learning.md b/doc/docs/api/dgf-learning.md index d20b009..8b0b762 100644 --- a/doc/docs/api/dgf-learning.md +++ b/doc/docs/api/dgf-learning.md @@ -14,6 +14,9 @@ hide: ## dgf.learning.NodePredictionModel # {: #section-nodepredictionmodel} ::: dgf.learning.NodePredictionModel +## dgf.learning.TFFunctionInputFormat # {: #section-tffunctioninputformat} +::: dgf.learning.TFFunctionInputFormat + ## dgf.learning.load_model # {: #section-load-model} ::: dgf.learning.load_model diff --git a/doc/docs/api/dgf-sampling.md b/doc/docs/api/dgf-sampling.md index a5c9ce0..a85b9b0 100644 --- a/doc/docs/api/dgf-sampling.md +++ b/doc/docs/api/dgf-sampling.md @@ -26,6 +26,9 @@ hide: ## dgf.sampling.extract_beam_nodes_ids # {: #section-extract-beam-nodes-ids} ::: dgf.sampling.extract_beam_nodes_ids +## dgf.sampling.offline_distributed_sampler_gcp # {: #section-offline-distributed-sampler-gcp} +::: dgf.sampling.offline_distributed_sampler_gcp + ## dgf.sampling.sample_with_beam_semi_distributed_sampler # {: #section-sample-with-beam-semi-distributed-sampler} ::: dgf.sampling.sample_with_beam_semi_distributed_sampler diff --git a/doc/docs/api/dgf-transform.md b/doc/docs/api/dgf-transform.md index dfff1c8..bce2f57 100644 --- a/doc/docs/api/dgf-transform.md +++ b/doc/docs/api/dgf-transform.md @@ -8,6 +8,12 @@ hide: ## dgf.transform.AutoNormalizeConfig # {: #section-autonormalizeconfig} ::: dgf.transform.AutoNormalizeConfig +## dgf.transform.CalendarFeature # {: #section-calendarfeature} +::: dgf.transform.CalendarFeature + +## dgf.transform.CalendarNormalizer # {: #section-calendarnormalizer} +::: dgf.transform.CalendarNormalizer + ## dgf.transform.ContainsLabelPredicate # {: #section-containslabelpredicate} ::: dgf.transform.ContainsLabelPredicate @@ -17,6 +23,9 @@ hide: ## dgf.transform.GNNDatasetPreparator # {: #section-gnndatasetpreparator} ::: dgf.transform.GNNDatasetPreparator +## dgf.transform.GraphMerger # {: #section-graphmerger} +::: dgf.transform.GraphMerger + ## dgf.transform.GraphNormalizer # {: #section-graphnormalizer} ::: dgf.transform.GraphNormalizer @@ -29,12 +38,18 @@ hide: ## dgf.transform.NumNodesPredicate # {: #section-numnodespredicate} ::: dgf.transform.NumNodesPredicate +## dgf.transform.SequentialNormalizer # {: #section-sequentialnormalizer} +::: dgf.transform.SequentialNormalizer + ## dgf.transform.SinusoidTimedeltaNormalizer # {: #section-sinusoidtimedeltanormalizer} ::: dgf.transform.SinusoidTimedeltaNormalizer ## dgf.transform.SoftQuantileNormalizer # {: #section-softquantilenormalizer} ::: dgf.transform.SoftQuantileNormalizer +## dgf.transform.TimedeltaNormalizer # {: #section-timedeltanormalizer} +::: dgf.transform.TimedeltaNormalizer + ## dgf.transform.apply_feature # {: #section-apply-feature} ::: dgf.transform.apply_feature @@ -44,6 +59,9 @@ hide: ## dgf.transform.batch_indices_generator # {: #section-batch-indices-generator} ::: dgf.transform.batch_indices_generator +## dgf.transform.combine_graph_snapshots # {: #section-combine-graph-snapshots} +::: dgf.transform.combine_graph_snapshots + ## dgf.transform.drop_edge_features # {: #section-drop-edge-features} ::: dgf.transform.drop_edge_features @@ -65,9 +83,6 @@ hide: ## dgf.transform.homogenize # {: #section-homogenize} ::: dgf.transform.homogenize -## dgf.transform.merge_graphs # {: #section-merge-graphs} -::: dgf.transform.merge_graphs - ## dgf.transform.propagate_timestamp_to_edges # {: #section-propagate-timestamp-to-edges} ::: dgf.transform.propagate_timestamp_to_edges diff --git a/doc/docs/api/dgf-validate.md b/doc/docs/api/dgf-validate.md index dd8e3a2..0e5cb58 100644 --- a/doc/docs/api/dgf-validate.md +++ b/doc/docs/api/dgf-validate.md @@ -5,6 +5,12 @@ hide: # dgf.validate +## dgf.validate.fix_schema # {: #section-fix-schema} +::: dgf.validate.fix_schema + ## dgf.validate.validate_graph # {: #section-validate-graph} ::: dgf.validate.validate_graph +## dgf.validate.validate_snapshots # {: #section-validate-snapshots} +::: dgf.validate.validate_snapshots + diff --git a/doc/docs/tutorial/getting_started_advanced_api.ipynb b/doc/docs/tutorial/getting_started_advanced_api.ipynb index c2055ac..d1c7dfe 100644 --- a/doc/docs/tutorial/getting_started_advanced_api.ipynb +++ b/doc/docs/tutorial/getting_started_advanced_api.ipynb @@ -1,2035 +1,2728 @@ { - "nbformat": 4, - "nbformat_minor": 0, - "metadata": { - "colab": { - "provenance": [ - { - "file_id": "1gCGQGADi7XgLfBUxVhY9lStsRb-ix1MR", - "timestamp": 1772703029637 - } - ], - "last_runtime": { - "build_target": "", - "kind": "local" - } + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "4QgiF5D7XIoY" + }, + "source": [ + "## 🔥 Advanced API\n", + "\n", + "[![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/google/distributed_graph_flow/blob/main/doc/docs/tutorial/getting_started_advanced_api.ipynb)\n", + "\n", + "The GF Advanced API is a modular, composable collection of low-level primitives\n", + "engineered for GNN experts and ML engineers. GF also works as a pick-and-choose\n", + "library. You can combine GF primitives with primitives from other ML libraries,\n", + "as well as integrate your own custom code.\n", + "\n", + "This tutorial provides an end-to-end implementation of an **in-process** GNN\n", + "pipeline using the GF Advanced API. You will learn to ingest graph data, compute\n", + "graph samples, normalize features, define a GNN JAX/FLAX model architecture\n", + "using low-level primitives, and train, evaluate and export this model for\n", + "production.\n", + "\n", + "This tutorial covers essentially the same functionality as the Simple API\n", + "tutorial, but with a more detailed and verbose path.\n", + "\n", + "**Prerequisites:**\n", + "\n", + "- If you are new to GF, read the [Getting Started (Simple API) tutorial](getting_started_simple_api.ipynb) first.\n", + "- You are familiar with JAX, GNN, and Neural Networks in general.\n", + "\n", + "**Notes**\n", + "\n", + "- Check the 🐜 [API](/api) for more details / examples about specific functions.\n", + "" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "xuLmBxj0Czyl" + }, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "AoYoqfcmio5m" + }, + "source": [ + "## Installing GF\n", + "\n", + "Make sure your machine has a GPU or TPU, otherwise training is going to take forever.\n", + "If you are using Google Colab, you can get one for free. Just go to Edit > Notebook settings and select your hardware accelerator as TPU or GPU." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-23T18:08:57.017580Z", + "iopub.status.busy": "2026-09-23T18:08:57.017392Z", + "iopub.status.idle": "2026-09-23T18:08:57.262362Z", + "shell.execute_reply": "2026-09-23T18:08:57.261602Z" }, - "kernelspec": { - "name": "python3", - "display_name": "Python 3" + "executionInfo": { + "elapsed": 287, + "status": "ok", + "timestamp": 1790186937304.5542, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 }, - "language_info": { - "name": "python" - } + "id": "vZnTGFe5Hqe3" + }, + "outputs": [], + "source": [ + "# Install DGF (Distributed Graph Flow) and OGB (for the toy dataset).\n", + "!pip install dgf ogb -U" + ] }, - "cells": [ - { - "cell_type": "markdown", - "source": [ - "## 🔥 Advanced API\n", - "\n", - "[![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/google/distributed_graph_flow/blob/main/doc/docs/tutorial/getting_started_advanced_api.ipynb)\n", - "\n", - "The GF Advanced API is a modular, composable collection of low-level primitives\n", - "engineered for GNN experts and ML engineers. GF also works as a pick-and-choose\n", - "library. You can combine GF primitives with primitives from other ML libraries,\n", - "as well as integrate your own custom code.\n", - "\n", - "This tutorial provides an end-to-end implementation of an **in-process** GNN\n", - "pipeline using the GF Advanced API. You will learn to ingest graph data, compute\n", - "graph samples, normalize features, define a GNN JAX/FLAX model architecture\n", - "using low-level primitives, and train, evaluate and export this model for\n", - "production.\n", - "\n", - "This tutorial covers essentially the same functionality as the Simple API\n", - "tutorial, but with a more detailed and verbose path.\n", - "\n", - "**Prerequisites:**\n", - "\n", - "- If you are new to GF, read the\n", - " [Getting Started (Simple API) tutorial](tutorial/getting_started_simple_api.md)\n", - " first.\n", - "- You are familiar with JAX, GNN, and Neural Networks in general.\n", - "\n", - "**Notes**\n", - "\n", - "- Check the 🐜\n", - " [API](/api) for\n", - " more details / examples about specific functions." - ], - "metadata": { - "id": "4QgiF5D7XIoY" - } + { + "cell_type": "markdown", + "metadata": { + "id": "at9KPOnoafKs" + }, + "source": [ + "## Importing libraries" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-23T18:08:57.306090Z", + "iopub.status.busy": "2026-09-23T18:08:57.305850Z", + "iopub.status.idle": "2026-09-23T18:09:01.426465Z", + "shell.execute_reply": "2026-09-23T18:09:01.425884Z" }, - { - "cell_type": "markdown", - "source": [ - "## Installing GF\n", - "\n", - "DGF is installed with `pip install dgf -U`." - ], - "metadata": { - "id": "2oAu8Wrractx" - } + "executionInfo": { + "elapsed": 4123, + "status": "ok", + "timestamp": 1790186941427.9507, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 }, - { - "cell_type": "markdown", - "source": [ - "## Importing libraries" - ], - "metadata": { - "id": "at9KPOnoafKs" - } + "id": "u4bluL_XhtzR" + }, + "outputs": [], + "source": [ + "from itertools import islice\n", + "import random\n", + "import tempfile\n", + "import dgf\n", + "import flax.linen as nn\n", + "import jax\n", + "import jax.numpy as jnp\n", + "import jaxtyping\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import optax" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "oEaYcGfaahbW" + }, + "source": [ + "## Download a graph\n", + "\n", + "The core of GF is the `dgf.data.InMemoryGraph` python dataclass. It is\n", + "logic-less dataclass containing Numpy arrays. It is generally paired with a\n", + "`dgf.data.GraphSchema`, another dataclass that defines the graph's structure.\n", + "\n", + "GF can ingest data directly from graph repositories like OGB. This tutorial uses\n", + "the OGB Arxiv graph:" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-23T18:09:01.429082Z", + "iopub.status.busy": "2026-09-23T18:09:01.428681Z", + "iopub.status.idle": "2026-09-23T18:09:01.501952Z", + "shell.execute_reply": "2026-09-23T18:09:01.501397Z" + }, + "executionInfo": { + "elapsed": 75, + "status": "ok", + "timestamp": 1790186941503.3076, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 }, + "id": "cGefsacZif5_", + "outputId": "2990c1e5-22d0-4c7a-fea8-33768da47f97" + }, + "outputs": [ { - "cell_type": "code", - "execution_count": 1, - "metadata": { - "id": "u4bluL_XhtzR", - "executionInfo": { - "status": "ok", - "timestamp": 1779800257579, - "user_tz": -120, - "elapsed": 5700, - "user": { - "displayName": "", - "userId": "" - } - } - }, - "outputs": [], - "source": [ - "import os\n", - "\n", - "os.environ[\"TF_USE_LEGACY_KERAS\"] = \"1\"\n", - "\n", - "from itertools import islice\n", - "import random\n", - "import tempfile\n", - "import dgf\n", - "import flax.linen as nn\n", - "import jax\n", - "import jax.numpy as jnp\n", - "import jaxtyping\n", - "import matplotlib.pyplot as plt\n", - "import numpy as np\n", - "import optax" - ] + "name": "stdout", + "output_type": "stream", + "text": [ + "Caching arxiv graph at /tmp/gf_fetch/arxiv.cache\n", + "OGB dependency not available. Downloading graph from CNS.\n" + ] + } + ], + "source": [ + "# Download the arxiv graph from the OGB repo.\n", + "graph, schema = dgf.io.fetch_ogb_graph(\"arxiv\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "OwB5TY8BcW2h" + }, + "source": [ + "**Note:** You can replace \"arxiv\" with \"mag\" to make this tutorial more\n", + "interesting (larger dataset, more nodesets and edgesets).\n", + "\n", + "GF primary way to store graphs on disk is the\n", + "[GF Graph format](https://g3doc.corp.google.com/third_party/py/dgf/g3doc/file_formats.md?cl=head).\n", + "It is a highly IO efficient, open-sourced, distributable format.\n", + "\n", + "You can load the Arxiv dataset from the GF Graph format with (which is what the\n", + "`fetch_ogb_graph` above likely did):\n", + "```python\n", + "graph, schema = dgf.io.read_graph(\"/.../ogb_arxiv\")\n", + "```\n", + "GF also has importers and exporters to popular on-disk / remote graph formats such\n", + "as Graph AI, TF-GNN, Spanner Graph, BigQuery Graph. You can find the full list\n", + "in the `dgf.io.*` and `dgf.beam.io.*` modules.\n", + "\n", + "Finally, GF also has converters to different python-object graph representations\n", + "such as other graph libraries representations (e.g., Sparse Deferred, TF-GNN) or\n", + "format with different properties (e.g., `dgf.data.JaxInMemoryGraph`). You can\n", + "find the full list in the `dgf.convert.*` module." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "6WJ2I8_pc22o" + }, + "source": [ + "--------------------------------------------------------------------------------\n", + "\n", + "**For Graph AI users:**\n", + "\n", + "The GF Grpah format is an evolution of the\n", + "[Graph AI HGraph format](https://g3doc.corp.google.com/third_party/py/dgf/g3doc/file_formats.md#graph-ai-hgraph).\n", + "Conversion from Graph AI Format to GF format can be done in process as simply\n", + "as:\n", + "\n", + "```python\n", + "graph, schema = dgf.io.read_graphai_hgraph(...)\n", + "dgf.io.write_graph(graph, schema)\n", + "```\n", + "\n", + "Or, using the distributed CLI\n", + "[convert_hgraph_to_gf_graph](https://source.corp.google.com/piper///depot/google3/third_party/py/dgf/src/bin/import_graph_ai_graph.py)\n", + "for large graphs.\n", + "\n", + "--------------------------------------------------------------------------------\n", + "\n", + "**For users with large graphs:**\n", + "\n", + "This tutorial only cover in-process operations. For large graphs, GF support\n", + "equivalent distributed representations e.g., `dgf.beam.data.Graph` and\n", + "`dgf.beam.io.read_graph`.\n", + "\n", + "--------------------------------------------------------------------------------" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "27gzPvMLeam-" + }, + "source": [ + "Before any operations, looking / validating your data is critical." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-23T18:09:31.581398Z", + "iopub.status.busy": "2026-09-23T18:09:31.581212Z", + "iopub.status.idle": "2026-09-23T18:09:31.586678Z", + "shell.execute_reply": "2026-09-23T18:09:31.586153Z" + }, + "executionInfo": { + "elapsed": 7, + "status": "ok", + "timestamp": 1790186971587.9304, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 + }, + "id": "nBAj2O-TDsww" + }, + "outputs": [], + "source": [ + "# Validate the schema and the graph\n", + "dgf.validate.validate_graph(graph, schema)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "A8cimhfqDxFH" + }, + "source": [ + "**Remark:** Almost all GF objects are pure dataclasses without checking logic. If\n", + "you modify / create them yourself, don't forget to validate them." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-23T18:09:31.588811Z", + "iopub.status.busy": "2026-09-23T18:09:31.588626Z", + "iopub.status.idle": "2026-09-23T18:09:31.593094Z", + "shell.execute_reply": "2026-09-23T18:09:31.592692Z" }, + "executionInfo": { + "elapsed": 6, + "status": "ok", + "timestamp": 1790186971594.028, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 + }, + "id": "VYelwcaofUbL", + "outputId": "0c903e8b-4284-4628-b038-0e17a64115dd" + }, + "outputs": [ { - "cell_type": "markdown", - "source": [ - "## Download a graph\n", - "\n", - "The core of GF is the `dgf.data.InMemoryGraph` python dataclass. It is\n", - "logic-less dataclass containing Numpy arrays. It is generally paired with a\n", - "`dgf.data.GraphSchema`, another dataclass that defines the graph's structure.\n", - "\n", - "GF can ingest data directly from graph repositories like OGB. This tutorial uses\n", - "the OGB Arxiv graph:" - ], - "metadata": { - "id": "oEaYcGfaahbW" - } + "name": "stdout", + "output_type": "stream", + "text": [ + "Graph Schema:\n", + "\n", + "Node Sets:\n", + " nodes:\n", + " | Feature | Format | Semantic | Shape | Detail |\n", + " |-----------|------------|-------------|---------|-------------|\n", + " | #id | BYTES | PRIMARY_ID | None | |\n", + " | #split | BYTES | CATEGORICAL | None | |\n", + " | feat | FLOAT_32 | EMBEDDING | (128,) | |\n", + " | labels | INTEGER_64 | CATEGORICAL | None | #num.cat:40 |\n", + " | year | INTEGER_64 | NUMERICAL | None | |\n", + "\n", + "\n", + "Edge Sets:\n", + " edges: (Source: nodes, Target: nodes)\n", + " (No features)\n", + "\n" + ] + } + ], + "source": [ + "# Show the schema\n", + "dgf.analyse.print_schema(schema)" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "colab": { + "height": 155 + }, + "execution": { + "iopub.execute_input": "2026-09-23T18:09:31.594860Z", + "iopub.status.busy": "2026-09-23T18:09:31.594580Z", + "iopub.status.idle": "2026-09-23T18:09:31.636174Z", + "shell.execute_reply": "2026-09-23T18:09:31.635511Z" }, + "executionInfo": { + "elapsed": 43, + "status": "ok", + "timestamp": 1790186971637.2273, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 + }, + "id": "C7teiqzofVow", + "outputId": "b0604a3d-7f90-4d1c-e7f9-15a92e3fe613" + }, + "outputs": [ { - "cell_type": "code", - "source": [ - "# Download the arxiv graph from the OGB repo.\n", - "graph, schema = dgf.io.fetch_ogb_graph(\"arxiv\")" + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "nodes\n", + "\n", + "nodes\n", + "#id\n", + "#split\n", + "feat\n", + "labels\n", + "year\n", + "\n", + "\n", + "\n", + "nodes->nodes\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n" ], - "metadata": { - "id": "cGefsacZif5_", - "executionInfo": { - "status": "ok", - "timestamp": 1779800257714, - "user_tz": -120, - "elapsed": 93, - "user": { - "displayName": "", - "userId": "" - } - }, - "outputId": "6aa1da8b-d084-4d12-e609-4a28ee20e627" - }, - "execution_count": 2, - "outputs": [ - { - "output_type": "stream", - "name": "stdout", - "text": [ - "Caching arxiv graph at /tmp/gf_fetch/arxiv.cache\n", - "OGB dependency not available. Downloading graph from CNS.\n" - ] - } + "text/plain": [ + "" ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Plot the schema\n", + "dgf.plot.plot_schema(schema)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "p5bcFindgyV7" + }, + "source": [ + "**Note:** The Arxiv graph is too large to be plotted with `dgf.plot.plot_graph`.\n", + "However, we will later plot graph samples." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Ni9JJ2Luo70y" + }, + "source": [ + "## More remarks about graph schemas in GF\n", + "\n", + "1. Many functions take a `dgf.data.GraphSchema` as input. In most of your code,\n", + " you will handle a graph and its related schema.\n", + "\n", + "2. When a GF function consumes a graph+schema, only the nodesets / edgesets /\n", + " features in the schema are considered. For example, if you only want to\n", + " process / import / export some nodesets, you'll generally make a copy of the\n", + " schema (cheap), remove the unused nodsets, and call GF on this modified\n", + " schema.\n", + "\n", + "3. The `dgf.data.GraphSchemaFilter` is an alternative to modifying schema\n", + " manually. It is espetially useful for readers / importers functions.\n", + "\n", + "4. Each feature in a schema has two importance fields:\n", + "\n", + "- The `format` defines how the feature is stored e.g. int32, float64, bytes.\n", + "- The `semantic` defines how the feature should be interpreted e.g.\n", + " categorical, numerical, embedding, date, primary key.\n", + "\n", + "While only a subset of GF methods use the semantic feature, configuring it\n", + "correctly from the start can save you a lot of time. For instance, it is used by\n", + "mid-level tools to automatize data normalization." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "2IsWBT-Qg-pj" + }, + "source": [ + "## Graph Sampling\n", + "\n", + "While the arXiv dataset does not need it, our next step is to generate graph\n", + "samples." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-23T18:09:31.638111Z", + "iopub.status.busy": "2026-09-23T18:09:31.637920Z", + "iopub.status.idle": "2026-09-23T18:09:31.820452Z", + "shell.execute_reply": "2026-09-23T18:09:31.820069Z" }, - { - "cell_type": "markdown", - "source": [ - "**Note:** You can replace \"arxiv\" with \"mag\" to make this tutorial more\n", - "interesting (larger dataset, more nodesets and edgesets).\n", - "\n", - "GF primary way to store graphs on disk is the\n", - "[GF Graph format](https://g3doc.corp.google.com/third_party/py/dgf/g3doc/file_formats.md?cl=head).\n", - "It is a highly IO efficient, open-sourced, distributable format.\n", - "\n", - "You can load the Arxiv dataset from the GF Graph format with (which is what the\n", - "`fetch_ogb_graph` above likely did):\n", - "\n", - "```python\n", - "graph, schema = dgf.io.read_graph(\"/.../ogb_arxiv\")\n", - "```" - ], - "metadata": { - "id": "OwB5TY8BcW2h" - } + "executionInfo": { + "elapsed": 184, + "status": "ok", + "timestamp": 1790186971821.4756, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 }, - { - "cell_type": "markdown", - "source": [ - "GF also has importers and exporters to popular on-disk / remote graph formats such\n", - "as Graph AI, TF-GNN, Spanner Graph, BigQuery Graph. You can find the full list\n", - "in the `dgf.io.*` and `dgf.beam.io.*` modules.\n", - "\n", - "Finally, GF also has converters to different python-object graph representations\n", - "such as other graph libraries representations (e.g., Sparse Deferred, TF-GNN) or\n", - "format with different properties (e.g., `dgf.data.JaxInMemoryGraph`). You can\n", - "find the full list in the `dgf.convert.*` module." - ], - "metadata": { - "id": "mgTO5T26DRKU" - } + "id": "r_ca-ojxi7PM" + }, + "outputs": [], + "source": [ + "# Create a graph sampling configuration.\n", + "plan = dgf.sampling.SimpleSamplingConfig(\n", + " seed_nodeset=\"nodes\",\n", + " # Maximum distances to consider.\n", + " num_hops=1,\n", + " # How many neighbors we consider at each hop.\n", + " hop_width=5,\n", + " # Follow the edges on both directions.\n", + " reverse=True,\n", + ")\n", + "\n", + "# Create a graph sampler / index the graph.\n", + "sampler = dgf.sampling.create_sampler(\n", + " graph=graph,\n", + " plan=plan,\n", + " schema=schema,\n", + " batch_size=32,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "W5PFWST8jJpa" + }, + "source": [ + "**Remarks:**\n", + "\n", + "- For more control, specify a `dgf.sampling.SamplingPlan` instead of a\n", + " `dgf.sampling.SimpleSamplingConfig`.\n", + "- GF has functions to convert SamplingPlan from/to TFGNN Sampling specs." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "7RHtpI7-jfH-" + }, + "source": [ + "We can then generate a single sample as:" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": { + "colab": { + "height": 368 }, - { - "cell_type": "markdown", - "source": [ - "--------------------------------------------------------------------------------\n", - "\n", - "**For Graph AI users:**\n", - "\n", - "The GF Grpah format is an evolution of the\n", - "[Graph AI HGraph format](https://g3doc.corp.google.com/third_party/py/dgf/g3doc/file_formats.md#graph-ai-hgraph).\n", - "Conversion from Graph AI Format to GF format can be done in process as simply\n", - "as:\n", - "\n", - "```python\n", - "graph, schema = dgf.io.read_graphai_hgraph(...)\n", - "dgf.io.write_graph(graph, schema)\n", - "```\n", - "\n", - "Or, using the distributed CLI\n", - "[convert_hgraph_to_gf_graph](https://source.corp.google.com/piper///depot/google3/third_party/py/dgf/src/bin/import_graph_ai_graph.py)\n", - "for large graphs.\n", - "\n", - "--------------------------------------------------------------------------------\n", - "\n", - "**For users with large graphs:**\n", - "\n", - "This tutorial only cover in-process operations. For large graphs, GF support\n", - "equivalent distributed representations e.g., `dgf.beam.data.Graph` and\n", - "`dgf.beam.io.read_graph`.\n", - "\n", - "--------------------------------------------------------------------------------" - ], - "metadata": { - "id": "6WJ2I8_pc22o" - } + "execution": { + "iopub.execute_input": "2026-09-23T18:09:31.822315Z", + "iopub.status.busy": "2026-09-23T18:09:31.822126Z", + "iopub.status.idle": "2026-09-23T18:09:31.861850Z", + "shell.execute_reply": "2026-09-23T18:09:31.861331Z" }, - { - "cell_type": "markdown", - "source": [ - "Before any operations, looking / validating your data is critical." - ], - "metadata": { - "id": "27gzPvMLeam-" - } + "executionInfo": { + "elapsed": 41, + "status": "ok", + "timestamp": 1790186971862.8142, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 }, + "id": "IWxlbK_wjeLk", + "outputId": "e3fdfa67-32e7-4eb2-9be4-532b3c8339ee" + }, + "outputs": [ { - "cell_type": "code", - "source": [ - "# Validate the schema and the graph\n", - "dgf.validate.validate_graph(graph, schema)" + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "nodes_0\n", + "\n", + "nodes_0\n", + "\n", + "\n", + "\n", + "nodes_1\n", + "\n", + "nodes_1\n", + "\n", + "\n", + "\n", + "nodes_0->nodes_1\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_2\n", + "\n", + "nodes_2\n", + "\n", + "\n", + "\n", + "nodes_0->nodes_2\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_3\n", + "\n", + "nodes_3\n", + "\n", + "\n", + "\n", + "nodes_3->nodes_0\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_4\n", + "\n", + "nodes_4\n", + "\n", + "\n", + "\n", + "nodes_4->nodes_0\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_5\n", + "\n", + "nodes_5\n", + "\n", + "\n", + "\n", + "nodes_5->nodes_0\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_6\n", + "\n", + "nodes_6\n", + "\n", + "\n", + "\n", + "nodes_6->nodes_0\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_7\n", + "\n", + "nodes_7\n", + "\n", + "\n", + "\n", + "nodes_7->nodes_0\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n" ], - "metadata": { - "id": "nBAj2O-TDsww", - "executionInfo": { - "status": "ok", - "timestamp": 1779800257778, - "user_tz": -120, - "elapsed": 20, - "user": { - "displayName": "", - "userId": "" - } - } - }, - "execution_count": 3, - "outputs": [] + "text/plain": [ + "" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Generate a graph\n", + "sample = sampler.sample(seed_node_idxs=0)\n", + "\n", + "# Plot the sample\n", + "dgf.plot.plot_graph(sample, schema, features=False)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "YvE-3H1MjpIP" + }, + "source": [ + "You can also generate a set of samples (this is multi-threaded)." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-23T18:09:31.863768Z", + "iopub.status.busy": "2026-09-23T18:09:31.863561Z", + "iopub.status.idle": "2026-09-23T18:09:31.866779Z", + "shell.execute_reply": "2026-09-23T18:09:31.866322Z" + }, + "executionInfo": { + "elapsed": 4, + "status": "ok", + "timestamp": 1790186971867.7458, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 }, + "id": "nvyfFD5Vj5Wn", + "outputId": "c333c40b-7a0c-4a83-8a75-50b6554b0093" + }, + "outputs": [ { - "cell_type": "markdown", - "source": [ - "**Remark:** Almost all GF objects are pure dataclasses without checking logic. If\n", - "you modify / create them yourself, don't forget to validate them." - ], - "metadata": { - "id": "A8cimhfqDxFH" - } + "name": "stdout", + "output_type": "stream", + "text": [ + "Generated 3 samples\n" + ] + } + ], + "source": [ + "samples = sampler.sample(seed_node_idxs=[0, 1, 2])\n", + "print(f\"Generated {len(samples)} samples\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "yZh_oTpEk9g7" + }, + "source": [ + "The `merge` method can be used to merge multiple graph samples together\n", + "(optionally with padding):" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-23T18:09:31.868590Z", + "iopub.status.busy": "2026-09-23T18:09:31.868270Z", + "iopub.status.idle": "2026-09-23T18:09:31.871249Z", + "shell.execute_reply": "2026-09-23T18:09:31.870907Z" }, + "executionInfo": { + "elapsed": 4, + "status": "ok", + "timestamp": 1790186971872.2334, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 + }, + "id": "40upqIN5lEPf" + }, + "outputs": [], + "source": [ + "merger = dgf.transform.GraphMerger(\n", + " schema=schema, padding=None, sentinel_offset=False\n", + ")\n", + "merged_samples, merge_offsets = merger(samples)" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": { + "colab": { + "height": 800 + }, + "execution": { + "iopub.execute_input": "2026-09-23T18:09:31.873039Z", + "iopub.status.busy": "2026-09-23T18:09:31.872828Z", + "iopub.status.idle": "2026-09-23T18:09:31.915050Z", + "shell.execute_reply": "2026-09-23T18:09:31.914528Z" + }, + "executionInfo": { + "elapsed": 43, + "status": "ok", + "timestamp": 1790186971916.127, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 + }, + "id": "Ib9_Nz-plH6N", + "outputId": "f9816bd3-e5e7-451d-d326-eb69a7acb7b5" + }, + "outputs": [ { - "cell_type": "code", - "source": [ - "# Show the schema\n", - "dgf.analyse.print_schema(schema)" + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "nodes_0\n", + "\n", + "nodes_0\n", + "\n", + "\n", + "\n", + "nodes_1\n", + "\n", + "nodes_1\n", + "\n", + "\n", + "\n", + "nodes_0->nodes_1\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_2\n", + "\n", + "nodes_2\n", + "\n", + "\n", + "\n", + "nodes_0->nodes_2\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_3\n", + "\n", + "nodes_3\n", + "\n", + "\n", + "\n", + "nodes_3->nodes_0\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_4\n", + "\n", + "nodes_4\n", + "\n", + "\n", + "\n", + "nodes_4->nodes_0\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_5\n", + "\n", + "nodes_5\n", + "\n", + "\n", + "\n", + "nodes_5->nodes_0\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_6\n", + "\n", + "nodes_6\n", + "\n", + "\n", + "\n", + "nodes_6->nodes_0\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_7\n", + "\n", + "nodes_7\n", + "\n", + "\n", + "\n", + "nodes_7->nodes_0\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_8\n", + "\n", + "nodes_8\n", + "\n", + "\n", + "\n", + "nodes_9\n", + "\n", + "nodes_9\n", + "\n", + "\n", + "\n", + "nodes_8->nodes_9\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_10\n", + "\n", + "nodes_10\n", + "\n", + "\n", + "\n", + "nodes_10->nodes_8\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_11\n", + "\n", + "nodes_11\n", + "\n", + "\n", + "\n", + "nodes_12\n", + "\n", + "nodes_12\n", + "\n", + "\n", + "\n", + "nodes_11->nodes_12\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_13\n", + "\n", + "nodes_13\n", + "\n", + "\n", + "\n", + "nodes_11->nodes_13\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_14\n", + "\n", + "nodes_14\n", + "\n", + "\n", + "\n", + "nodes_11->nodes_14\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_15\n", + "\n", + "nodes_15\n", + "\n", + "\n", + "\n", + "nodes_11->nodes_15\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_16\n", + "\n", + "nodes_16\n", + "\n", + "\n", + "\n", + "nodes_11->nodes_16\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_17\n", + "\n", + "nodes_17\n", + "\n", + "\n", + "\n", + "nodes_17->nodes_11\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_18\n", + "\n", + "nodes_18\n", + "\n", + "\n", + "\n", + "nodes_18->nodes_11\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_19\n", + "\n", + "nodes_19\n", + "\n", + "\n", + "\n", + "nodes_19->nodes_11\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_20\n", + "\n", + "nodes_20\n", + "\n", + "\n", + "\n", + "nodes_20->nodes_11\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_21\n", + "\n", + "nodes_21\n", + "\n", + "\n", + "\n", + "nodes_21->nodes_11\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n" ], - "metadata": { - "id": "VYelwcaofUbL", - "executionInfo": { - "status": "ok", - "timestamp": 1779800257839, - "user_tz": -120, - "elapsed": 21, - "user": { - "displayName": "", - "userId": "" - } - }, - "outputId": "91363bf1-d4a9-44f9-8930-e052a55ec6c8" - }, - "execution_count": 4, - "outputs": [ - { - "output_type": "stream", - "name": "stdout", - "text": [ - "Graph Schema:\n", - "\n", - "Node Sets:\n", - " nodes:\n", - " | Feature | Format | Semantic | Shape | Num cat. vals |\n", - " |-----------|------------|-------------|---------|-----------------|\n", - " | #id | BYTES | PRIMARY_ID | None | None |\n", - " | #split | BYTES | CATEGORICAL | None | None |\n", - " | feat | FLOAT_32 | EMBEDDING | (128,) | None |\n", - " | labels | INTEGER_64 | CATEGORICAL | None | 40 |\n", - " | year | INTEGER_64 | NUMERICAL | None | None |\n", - "\n", - "\n", - "Edge Sets:\n", - " edges: (Source: nodes, Target: nodes)\n", - " (No features)\n", - "\n" - ] - } + "text/plain": [ + "" ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "dgf.plot.plot_graph(merged_samples, schema, features=False)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "z8aZKvxFlRJ9" + }, + "source": [ + "The `merge_offsets` contains the starting indices of each nodeset and each\n", + "original graph in the merge graph. Since we call it with\n", + "`sentinel_offset=False`, `merge_offsets` does not include the offset of the\n", + "sentinel nodes." + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-23T18:09:31.917217Z", + "iopub.status.busy": "2026-09-23T18:09:31.916843Z", + "iopub.status.idle": "2026-09-23T18:09:31.920620Z", + "shell.execute_reply": "2026-09-23T18:09:31.920224Z" }, + "executionInfo": { + "elapsed": 5, + "status": "ok", + "timestamp": 1790186971921.654, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 + }, + "id": "I4mUMmcXlQjO", + "outputId": "cf3a0a44-e68a-4556-d8a8-3de094cf6c80" + }, + "outputs": [ { - "cell_type": "code", - "source": [ - "# Plot the schema\n", - "dgf.plot.plot_schema(schema)" - ], - "metadata": { - "colab": { - "height": 155 - }, - "id": "C7teiqzofVow", - "executionInfo": { - "status": "ok", - "timestamp": 1779800257944, - "user_tz": -120, - "elapsed": 64, - "user": { - "displayName": "", - "userId": "" - } - }, - "outputId": "255bc9a0-583e-4c01-b839-a476ad26fd2a" - }, - "execution_count": 5, - "outputs": [ - { - "output_type": "execute_result", - "data": { - "image/svg+xml": "\n\n\n\n\n\n\n\n\nnodes\n\nnodes\n#id\n#split\nfeat\nlabels\nyear\n\n\n\nnodes->nodes\n\n\nedges\n\n\n\n", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "execution_count": 5 - } + "data": { + "text/plain": [ + "{'nodes': array([ 0, 8, 11])}" ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "merge_offsets" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "06wgX3hNkHwc" + }, + "source": [ + "Let's split the nodes into a training and validation set." + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-23T18:09:31.922460Z", + "iopub.status.busy": "2026-09-23T18:09:31.922286Z", + "iopub.status.idle": "2026-09-23T18:09:31.929565Z", + "shell.execute_reply": "2026-09-23T18:09:31.929259Z" }, - { - "cell_type": "markdown", - "source": [ - "**Note:** The Arxiv graph is too large to be plotted with `dgf.plot.plot_graph`.\n", - "However, we will later plot graph samples." - ], - "metadata": { - "id": "p5bcFindgyV7" - } + "executionInfo": { + "elapsed": 8, + "status": "ok", + "timestamp": 1790186971930.4934, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 }, + "id": "rRCO4m2WkLIT", + "outputId": "4d0dc96b-b45f-46c5-9129-e6fdbe224ad6" + }, + "outputs": [ { - "cell_type": "markdown", - "source": [ - "## More remarks about graph schemas in GF\n", - "\n", - "1. Many functions take a `dgf.data.GraphSchema` as input. In most of your code,\n", - " you will handle a graph and its related schema.\n", - "\n", - "2. When a GF function consumes a graph+schema, only the nodesets / edgesets /\n", - " features in the schema are considered. For example, if you only want to\n", - " process / import / export some nodesets, you'll generally make a copy of the\n", - " schema (cheap), remove the unused nodsets, and call GF on this modified\n", - " schema.\n", - "\n", - "3. The `dgf.data.GraphSchemaFilter` is an alternative to modifying schema\n", - " manually. It is espetially useful for readers / importers functions.\n", - "\n", - "4. Each feature in a schema has two importance fields:\n", - "\n", - "- The `format` defines how the feature is stored e.g. int32, float64, bytes.\n", - "- The `semantic` defines how the feature should be interpreted e.g.\n", - " categorical, numerical, embedding, date, primary key.\n", - "\n", - "While only a subset of GF methods use the semantic feature, configuring it\n", - "correctly from the start can save you a lot of time. For instance, it is used by\n", - "mid-level tools to automatize data normalization." - ], - "metadata": { - "id": "Ni9JJ2Luo70y" - } + "name": "stdout", + "output_type": "stream", + "text": [ + "Num train nodes: 135475\n", + "Num valid nodes: 33868\n" + ] + } + ], + "source": [ + "num_nodes = graph.node_sets[\"nodes\"].num_nodes\n", + "all_seed_node_idxs = np.arange(num_nodes)\n", + "np.random.seed(42)\n", + "np.random.shuffle(all_seed_node_idxs)\n", + "num_valid = int(num_nodes * 0.2) # Keep 20% of the nodes for validation\n", + "valid_seed_node_idxs = all_seed_node_idxs[:num_valid]\n", + "train_seed_node_idxs = all_seed_node_idxs[num_valid:]\n", + "print(\"Num train nodes:\", len(train_seed_node_idxs))\n", + "print(\"Num valid nodes:\", len(valid_seed_node_idxs))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Ipraa9jYkWfR" + }, + "source": [ + "The `dgf.transform.batch_indices_generator` function is useful to generate seed\n", + "indices:" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-23T18:09:31.931258Z", + "iopub.status.busy": "2026-09-23T18:09:31.931064Z", + "iopub.status.idle": "2026-09-23T18:09:31.934093Z", + "shell.execute_reply": "2026-09-23T18:09:31.933715Z" }, - { - "cell_type": "markdown", - "source": [ - "## Graph Sampling\n", - "\n", - "While the arXiv dataset does not need it, our next step is to generate graph\n", - "samples." - ], - "metadata": { - "id": "2IsWBT-Qg-pj" - } + "executionInfo": { + "elapsed": 4, + "status": "ok", + "timestamp": 1790186971935.002, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 }, + "id": "bh1-MIFo709W", + "outputId": "9b825e2b-2fd3-4f18-87aa-0397c0db37d9" + }, + "outputs": [ { - "cell_type": "code", - "source": [ - "# Create a graph sampling configuration.\n", - "plan = dgf.sampling.SimpleSamplingConfig(\n", - " seed_nodeset=\"nodes\",\n", - " # Maximum distances to consider.\n", - " num_hops=1,\n", - " # How many neighbors we consider at each hop.\n", - " hop_width=5,\n", - " # Follow the edges on both directions.\n", - " reverse=True,\n", - ")\n", - "\n", - "# Create a graph sampler / index the graph.\n", - "sampler = dgf.sampling.create_sampler(\n", - " graph=graph,\n", - " plan=plan,\n", - " schema=schema,\n", - " batch_size=32,\n", - ")" - ], - "metadata": { - "id": "r_ca-ojxi7PM", - "executionInfo": { - "status": "ok", - "timestamp": 1779800258287, - "user_tz": -120, - "elapsed": 230, - "user": { - "displayName": "", - "userId": "" - } - } - }, - "execution_count": 6, - "outputs": [] + "name": "stdout", + "output_type": "stream", + "text": [ + "[ 1895 44513 24828 122605 140271]\n", + "[ 19815 8619 107350 93409 12795]\n", + "[ 69167 69552 143796 129899 59454]\n" + ] + } + ], + "source": [ + "# Generate 3 batches, each containing 5 seed-node indices.\n", + "for seed_node_idxs in islice(\n", + " dgf.transform.batch_indices_generator(\n", + " train_seed_node_idxs, batch_size=5, drop_remainder=True, shuffle=False\n", + " ),\n", + " 3,\n", + "):\n", + " print(seed_node_idxs)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "O19qUi6Lk2hS" + }, + "source": [ + "We can combine all of this together to define a graph sample python generator." + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-23T18:09:31.936008Z", + "iopub.status.busy": "2026-09-23T18:09:31.935657Z", + "iopub.status.idle": "2026-09-23T18:09:31.944068Z", + "shell.execute_reply": "2026-09-23T18:09:31.943700Z" }, - { - "cell_type": "markdown", - "source": [ - "**Remarks:**\n", - "\n", - "- For more control, specify a `dgf.sampling.SamplingPlan` instead of a\n", - " `dgf.sampling.SimpleSamplingConfig`.\n", - "- GF has functions to convert SamplingPlan from/to TFGNN Sampling specs." - ], - "metadata": { - "id": "W5PFWST8jJpa" - } + "executionInfo": { + "elapsed": 9, + "status": "ok", + "timestamp": 1790186971945.0232, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 }, + "id": "XChnfUL5tjRU", + "outputId": "fce050a6-d86a-432d-d78a-e7c91850a8c9" + }, + "outputs": [ { - "cell_type": "markdown", - "source": [ - "We can then generate a single sample as:" - ], - "metadata": { - "id": "7RHtpI7-jfH-" - } + "name": "stdout", + "output_type": "stream", + "text": [ + "Number of nodes and edges in the sample: 206 175\n", + "Number of nodes and edges in the sample: 192 163\n", + "Number of nodes and edges in the sample: 198 169\n", + "Number of nodes and edges in the sample: 217 185\n", + "Number of nodes and edges in the sample: 178 146\n" + ] + } + ], + "source": [ + "def batch_generator(\n", + " seed_node_idxs, padding=None, also_return_merge_offsets=False, batch_size=32\n", + "):\n", + " \"\"\"Generates batched merged graphs samples, and (optional) merging offsets.\"\"\"\n", + " merger = dgf.transform.GraphMerger(\n", + " schema=schema, padding=padding, sentinel_offset=False\n", + " )\n", + " for seed_node_idxs in dgf.transform.batch_indices_generator(\n", + " seed_node_idxs, batch_size=batch_size, drop_remainder=True, shuffle=False\n", + " ):\n", + " # Sample the graphs\n", + " samples = sampler.sample(seed_node_idxs.tolist())\n", + "\n", + " try:\n", + " # Merge the graph samples into a single graph.\n", + " merged_samples, merge_offsets = merger(samples)\n", + " except dgf.exception.InsufficientPaddingError:\n", + " # Skip if the number of nodes is too large for the padding.\n", + " continue\n", + "\n", + " if also_return_merge_offsets:\n", + " yield merged_samples, merge_offsets\n", + " else:\n", + " yield merged_samples\n", + "\n", + "\n", + "# Generate 5 batches of graphs.\n", + "for graph_sample in islice(batch_generator(train_seed_node_idxs), 5):\n", + " print(\n", + " \"Number of nodes and edges in the sample:\",\n", + " graph_sample.node_sets[\"nodes\"].num_nodes,\n", + " graph_sample.edge_sets[\"edges\"].num_edges(),\n", + " )" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "kamAFS51mZXy" + }, + "source": [ + "**Remark:**\n", + "\n", + "- Many GF functions consume / return graph generators." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "ndbwi_ZUmjfQ" + }, + "source": [ + "We can use our graph generator to determine the optimal padding (since JAX does\n", + "not support variable length arrays)." + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-23T18:09:31.945846Z", + "iopub.status.busy": "2026-09-23T18:09:31.945631Z", + "iopub.status.idle": "2026-09-23T18:09:32.088494Z", + "shell.execute_reply": "2026-09-23T18:09:32.088063Z" + }, + "executionInfo": { + "elapsed": 144, + "status": "ok", + "timestamp": 1790186972089.5522, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 }, + "id": "paI3RaXOkHGk", + "outputId": "c2e14597-35d1-4085-b5c1-24fbb9d429eb" + }, + "outputs": [ { - "cell_type": "code", - "source": [ - "# Generate a graph\n", - "sample = sampler.sample(seed_node_idxs=0)\n", - "\n", - "# Plot the sample\n", - "dgf.plot.plot_graph(sample, schema, features=False)" - ], - "metadata": { - "colab": { - "height": 368 - }, - "id": "IWxlbK_wjeLk", - "executionInfo": { - "status": "ok", - "timestamp": 1779800258383, - "user_tz": -120, - "elapsed": 56, - "user": { - "displayName": "", - "userId": "" - } - }, - "outputId": "96091ef8-bcf2-4d77-ebfb-ba4319622b2b" - }, - "execution_count": 7, - "outputs": [ - { - "output_type": "execute_result", - "data": { - "image/svg+xml": "\n\n\n\n\n\n\n\n\nnodes_0\n\nnodes_0\n\n\n\nnodes_1\n\nnodes_1\n\n\n\nnodes_0->nodes_1\n\n\nedges\n\n\n\nnodes_2\n\nnodes_2\n\n\n\nnodes_0->nodes_2\n\n\nedges\n\n\n\nnodes_3\n\nnodes_3\n\n\n\nnodes_3->nodes_0\n\n\nedges\n\n\n\nnodes_4\n\nnodes_4\n\n\n\nnodes_4->nodes_0\n\n\nedges\n\n\n\nnodes_5\n\nnodes_5\n\n\n\nnodes_5->nodes_0\n\n\nedges\n\n\n\nnodes_6\n\nnodes_6\n\n\n\nnodes_6->nodes_0\n\n\nedges\n\n\n\nnodes_7\n\nnodes_7\n\n\n\nnodes_7->nodes_0\n\n\nedges\n\n\n\n", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "execution_count": 7 - } + "data": { + "text/plain": [ + "Padding(node_sets={'nodes': NodeSetPadding(num_nodes=261, features={})}, edge_sets={'edges': EdgeSetPadding(num_edges=226, features={})})" ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "padding = dgf.analyse.padding_from_graph_generator(\n", + " schema=schema, graphs=islice(batch_generator(train_seed_node_idxs), 200)\n", + ")\n", + "padding" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "dLq8zeAumvwv" + }, + "source": [ + "If we apply the padding, all the batches have the same number of nodes and\n", + "edges. Sentinel nodes and edges are added." + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-23T18:09:32.090443Z", + "iopub.status.busy": "2026-09-23T18:09:32.090212Z", + "iopub.status.idle": "2026-09-23T18:09:32.098570Z", + "shell.execute_reply": "2026-09-23T18:09:32.098007Z" + }, + "executionInfo": { + "elapsed": 10, + "status": "ok", + "timestamp": 1790186972099.6445, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 }, + "id": "b4iIIEhKcGdz", + "outputId": "bad460f9-dbbf-4fa7-dbdc-4d06b0b0e4ef" + }, + "outputs": [ { - "cell_type": "markdown", - "source": [ - "You can also generate a set of samples (this is multi-threaded)." - ], - "metadata": { - "id": "YvE-3H1MjpIP" - } + "name": "stdout", + "output_type": "stream", + "text": [ + "Number of nodes and edges in the sample: 261 226\n", + "Number of nodes and edges in the sample: 261 226\n", + "Number of nodes and edges in the sample: 261 226\n", + "Number of nodes and edges in the sample: 261 226\n", + "Number of nodes and edges in the sample: 261 226\n" + ] + } + ], + "source": [ + "# Now, all the batches of graph samples have the same number of nodes.\n", + "for graph_sample in islice(\n", + " batch_generator(train_seed_node_idxs, padding=padding), 5\n", + "):\n", + " print(\n", + " \"Number of nodes and edges in the sample:\",\n", + " graph_sample.node_sets[\"nodes\"].num_nodes,\n", + " graph_sample.edge_sets[\"edges\"].num_edges(),\n", + " )" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "7RnCFqXjm_6v" + }, + "source": [ + "## Data normalization\n", + "\n", + "To be processed by neural networks, features must be normalized (unless they are\n", + "pre-normalized, like embeddings). GF provides several normalization utility\n", + "primitives to help manage data with varying levels of control.\n", + "\n", + "The primary goal of normalization is to make the data compatible with neural\n", + "nets. For instance, numerical features are generally scaled (e.g., using soft\n", + "quantiles), while categorical string features are mapped to integers using a\n", + "dictionary.\n", + "\n", + "We will begin by computing statistics for all features" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-23T18:09:32.100533Z", + "iopub.status.busy": "2026-09-23T18:09:32.100325Z", + "iopub.status.idle": "2026-09-23T18:09:32.296545Z", + "shell.execute_reply": "2026-09-23T18:09:32.296145Z" + }, + "executionInfo": { + "elapsed": 197, + "status": "ok", + "timestamp": 1790186972297.5088, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 }, + "id": "VhSmaRvXnsdG", + "outputId": "3a418137-01d2-4603-dd8b-1d9c5472688d" + }, + "outputs": [ { - "cell_type": "code", - "source": [ - "samples = sampler.sample(seed_node_idxs=[0, 1, 2])\n", - "print(f\"Generated {len(samples)} samples\")" - ], - "metadata": { - "id": "nvyfFD5Vj5Wn", - "executionInfo": { - "status": "ok", - "timestamp": 1779800258496, - "user_tz": -120, - "elapsed": 18, - "user": { - "displayName": "", - "userId": "" - } - }, - "outputId": "f9800673-bd93-451d-82f6-e08b1f08b3dd" - }, - "execution_count": 8, - "outputs": [ - { - "output_type": "stream", - "name": "stdout", - "text": [ - "Generated 3 samples\n" - ] - } + "data": { + "text/plain": [ + "GraphFeatureStatistics:\n", + " Node Sets (1):\n", + " 'nodes':\n", + " '#id': count=38760, min=nan, max=nan\n", + " '#split': count=38760, min=nan, max=nan, dictionary=(3)['train': 25179, 'test': 8189, 'valid': 5392]\n", + " 'feat': count=38760, min=nan, max=nan\n", + " 'labels': count=38760, min=0.0000, max=39.0000\n", + " 'year': count=38760, min=1971.0000, max=2020.0000, quantiles=(100)[1971.0000, 2002.0000, 2006.0000, ..., 2020.0000, 2020.0000, 2020.0000]" ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "feature_stats = dgf.analyse.feature_statistics_from_graphs(\n", + " graphs=islice(batch_generator(train_seed_node_idxs), 200),\n", + " schema=schema,\n", + ")\n", + "feature_stats" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "6i3k1YjMoLRw" + }, + "source": [ + "**Remarks:**\n", + "\n", + "- We compute statistics in-process. For large datasets, distributed\n", + " computation of feature statistics might be better.\n", + "- Don't use padding for the feature statistics.\n", + "- GF allows you both to compute statistics on graph samples or on the original\n", + " graph.\n", + "- The statistics are computed according to the `semantic` and `format` of the\n", + " schema. For example, quantile statistics are not computed on the `feat`\n", + " numerical column because it is has a `semantic=EMBEDDING`." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "P1SdL8EEHKcZ" + }, + "source": [ + "The statistics can be used to instantiate normalization blocks." + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-23T18:09:32.298405Z", + "iopub.status.busy": "2026-09-23T18:09:32.298170Z", + "iopub.status.idle": "2026-09-23T18:09:32.302745Z", + "shell.execute_reply": "2026-09-23T18:09:32.302386Z" }, - { - "cell_type": "markdown", - "source": [ - "The `merge` method can be used to merge multiple graph samples together\n", - "(optionally with padding):" - ], - "metadata": { - "id": "yZh_oTpEk9g7" - } + "executionInfo": { + "elapsed": 6, + "status": "ok", + "timestamp": 1790186972303.7637, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 }, + "id": "E1bU8xwfHVHr", + "outputId": "fac4e007-efc7-4de8-8983-57b120625c3c" + }, + "outputs": [ { - "cell_type": "code", - "source": [ - "merged_samples, merge_offsets = dgf.transform.merge_graphs(\n", - " graphs=samples, schema=schema, padding=None, sentinel_offset=False\n", - ")" - ], - "metadata": { - "id": "40upqIN5lEPf", - "executionInfo": { - "status": "ok", - "timestamp": 1779800258559, - "user_tz": -120, - "elapsed": 17, - "user": { - "displayName": "", - "userId": "" - } - } - }, - "execution_count": 9, - "outputs": [] + "name": "stderr", + "output_type": "stream", + "text": [ + "[Warning] No normalizer created for node set 'nodes', feature '#id'.\n" + ] }, { - "cell_type": "code", - "source": [ - "dgf.plot.plot_graph(merged_samples, schema, features=False)" - ], - "metadata": { - "colab": { - "height": 800 - }, - "id": "Ib9_Nz-plH6N", - "executionInfo": { - "status": "ok", - "timestamp": 1779800258665, - "user_tz": -120, - "elapsed": 61, - "user": { - "displayName": "", - "userId": "" - } - }, - "outputId": "c163829f-2438-4ba3-e181-470e029a33cf" - }, - "execution_count": 10, - "outputs": [ - { - "output_type": "execute_result", - "data": { - "image/svg+xml": "\n\n\n\n\n\n\n\n\nnodes_0\n\nnodes_0\n\n\n\nnodes_1\n\nnodes_1\n\n\n\nnodes_0->nodes_1\n\n\nedges\n\n\n\nnodes_2\n\nnodes_2\n\n\n\nnodes_0->nodes_2\n\n\nedges\n\n\n\nnodes_3\n\nnodes_3\n\n\n\nnodes_3->nodes_0\n\n\nedges\n\n\n\nnodes_4\n\nnodes_4\n\n\n\nnodes_4->nodes_0\n\n\nedges\n\n\n\nnodes_5\n\nnodes_5\n\n\n\nnodes_5->nodes_0\n\n\nedges\n\n\n\nnodes_6\n\nnodes_6\n\n\n\nnodes_6->nodes_0\n\n\nedges\n\n\n\nnodes_7\n\nnodes_7\n\n\n\nnodes_7->nodes_0\n\n\nedges\n\n\n\nnodes_8\n\nnodes_8\n\n\n\nnodes_9\n\nnodes_9\n\n\n\nnodes_8->nodes_9\n\n\nedges\n\n\n\nnodes_10\n\nnodes_10\n\n\n\nnodes_10->nodes_8\n\n\nedges\n\n\n\nnodes_11\n\nnodes_11\n\n\n\nnodes_12\n\nnodes_12\n\n\n\nnodes_11->nodes_12\n\n\nedges\n\n\n\nnodes_13\n\nnodes_13\n\n\n\nnodes_11->nodes_13\n\n\nedges\n\n\n\nnodes_14\n\nnodes_14\n\n\n\nnodes_11->nodes_14\n\n\nedges\n\n\n\nnodes_15\n\nnodes_15\n\n\n\nnodes_11->nodes_15\n\n\nedges\n\n\n\nnodes_16\n\nnodes_16\n\n\n\nnodes_11->nodes_16\n\n\nedges\n\n\n\nnodes_17\n\nnodes_17\n\n\n\nnodes_17->nodes_11\n\n\nedges\n\n\n\nnodes_18\n\nnodes_18\n\n\n\nnodes_18->nodes_11\n\n\nedges\n\n\n\nnodes_19\n\nnodes_19\n\n\n\nnodes_19->nodes_11\n\n\nedges\n\n\n\nnodes_20\n\nnodes_20\n\n\n\nnodes_20->nodes_11\n\n\nedges\n\n\n\nnodes_21\n\nnodes_21\n\n\n\nnodes_21->nodes_11\n\n\nedges\n\n\n\n", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "execution_count": 10 - } + "data": { + "text/plain": [ + "GraphNormalizer(config=GraphNormalizerConfig(nodesets={'nodes': NodeSetNormalizerConfig(normalizers=[DictionaryIndexNormalizer(input_feature='#split', type='DictionaryIndexNormalizer', dictionary_map={'train': 0, 'test': 1, 'valid': 2}, out_of_vocab_value=3, output_shape=(), output_feature_name='#split_INDEX', tf_table=None), IdentityNormalizer(input_feature='labels', type='IdentityNormalizer', input_schema=FeatureSchema(format=, semantic=, shape=None, num_categorical_values=40, is_utf8_string=False, is_timeseries=False, is_creation_time=False, group=None)), SoftQuantileNormalizer(input_feature='year', type='SoftQuantileNormalizer', output_feature_name='year_SOFT_QUANTILE', output_shape=(), quantiles=array([1971., 2002., 2006., 2007., 2008., 2009., 2010., 2011., 2012.,\n", + " 2013., 2014., 2015., 2016., 2017., 2018., 2019., 2020.],\n", + " dtype=float32), is_timeseries=False, group=None), IdentityNormalizer(input_feature='feat', type='IdentityNormalizer', input_schema=FeatureSchema(format=, semantic=, shape=(128,), num_categorical_values=None, is_utf8_string=False, is_timeseries=False, is_creation_time=False, group=None))])}, edgesets={'edges': EdgeSetNormalizerConfig(source='nodes', target='nodes', normalizers=[])}), _nodeset_kwargs={'nodes': [frozenset(), frozenset(), frozenset(), frozenset()]}, _edgeset_kwargs={'edges': []}, _all_accepted_kwargs=frozenset())" ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "normalizer = dgf.transform.auto_normalize(schema=schema, stats=feature_stats)\n", + "normalizer" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "NsnAdalHHRD3" + }, + "source": [ + "**Remarks:**\n", + "\n", + "- Instead of the auto-normalization, you can define the normalization\n", + " operations manually e.g. `dgf.transform.SoftQuantileNormalizer`." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "P-EbWtnIH05W" + }, + "source": [ + "You can check individual normalization blocks:" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-23T18:09:32.304681Z", + "iopub.status.busy": "2026-09-23T18:09:32.304321Z", + "iopub.status.idle": "2026-09-23T18:09:32.307824Z", + "shell.execute_reply": "2026-09-23T18:09:32.307388Z" }, - { - "cell_type": "markdown", - "source": [ - "The `merge_offsets` contains the starting indices of each nodeset and each\n", - "original graph in the merge graph. Since we call it with\n", - "`sentinel_offset=False`, `merge_offsets` does not include the offset of the\n", - "sentinel nodes." - ], - "metadata": { - "id": "z8aZKvxFlRJ9" - } + "executionInfo": { + "elapsed": 5, + "status": "ok", + "timestamp": 1790186972308.8079, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 }, + "id": "vG618kYeHzQQ", + "outputId": "2493acbc-0327-4bd0-be28-83acb3db33e4" + }, + "outputs": [ { - "cell_type": "code", - "source": [ - "merge_offsets" - ], - "metadata": { - "id": "I4mUMmcXlQjO", - "executionInfo": { - "status": "ok", - "timestamp": 1779800258777, - "user_tz": -120, - "elapsed": 17, - "user": { - "displayName": "", - "userId": "" - } - }, - "outputId": "d7bab44f-7892-4526-8d54-67cbe727ad41" - }, - "execution_count": 11, - "outputs": [ - { - "output_type": "execute_result", - "data": { - "text/plain": [ - "{'nodes': array([ 0, 8, 11])}" - ] - }, - "metadata": {}, - "execution_count": 11 - } + "data": { + "text/plain": [ + "SoftQuantileNormalizer(input_feature='year', type='SoftQuantileNormalizer', output_feature_name='year_SOFT_QUANTILE', output_shape=(), quantiles=array([1971., 2002., 2006., 2007., 2008., 2009., 2010., 2011., 2012.,\n", + " 2013., 2014., 2015., 2016., 2017., 2018., 2019., 2020.],\n", + " dtype=float32), is_timeseries=False, group=None)" ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# The \"year\" feature is normalized with the SoftQuantileNormalizer.\n", + "normalizer.config.nodesets[\"nodes\"].normalizers[2]" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-23T18:09:32.309676Z", + "iopub.status.busy": "2026-09-23T18:09:32.309328Z", + "iopub.status.idle": "2026-09-23T18:09:32.312630Z", + "shell.execute_reply": "2026-09-23T18:09:32.312193Z" }, - { - "cell_type": "markdown", - "source": [ - "Let's split the nodes into a training and validation set." - ], - "metadata": { - "id": "06wgX3hNkHwc" - } + "executionInfo": { + "elapsed": 4, + "status": "ok", + "timestamp": 1790186972313.6562, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 }, + "id": "_HtDHI4CINXZ", + "outputId": "ae59c764-dead-4666-a7ed-0c764f0304b7" + }, + "outputs": [ { - "cell_type": "code", - "source": [ - "num_nodes = graph.node_sets[\"nodes\"].num_nodes\n", - "all_seed_node_idxs = np.arange(num_nodes)\n", - "np.random.seed(42)\n", - "np.random.shuffle(all_seed_node_idxs)\n", - "num_valid = int(num_nodes * 0.2) # Keep 20% of the nodes for validation\n", - "valid_seed_node_idxs = all_seed_node_idxs[:num_valid]\n", - "train_seed_node_idxs = all_seed_node_idxs[num_valid:]\n", - "print(\"Num train nodes:\", len(train_seed_node_idxs))\n", - "print(\"Num valid nodes:\", len(valid_seed_node_idxs))" - ], - "metadata": { - "id": "rRCO4m2WkLIT", - "executionInfo": { - "status": "ok", - "timestamp": 1779800258843, - "user_tz": -120, - "elapsed": 21, - "user": { - "displayName": "", - "userId": "" - } - }, - "outputId": "00dbe8b7-dc06-4893-f806-48887d096ed6" - }, - "execution_count": 12, - "outputs": [ - { - "output_type": "stream", - "name": "stdout", - "text": [ - "Num train nodes: 135475\n", - "Num valid nodes: 33868\n" - ] - } + "data": { + "text/plain": [ + "IdentityNormalizer(input_feature='feat', type='IdentityNormalizer', input_schema=FeatureSchema(format=, semantic=, shape=(128,), num_categorical_values=None, is_utf8_string=False, is_timeseries=False, is_creation_time=False, group=None))" ] + }, + "execution_count": 23, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# The \"feat\" feature is an embedding, so it is transmitted without modification (identity normalizer).\n", + "normalizer.config.nodesets[\"nodes\"].normalizers[3]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "pOSxkBgmIdvb" + }, + "source": [ + "You can obtain the output schema of the normalizer:" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-23T18:09:32.315175Z", + "iopub.status.busy": "2026-09-23T18:09:32.314637Z", + "iopub.status.idle": "2026-09-23T18:09:32.318330Z", + "shell.execute_reply": "2026-09-23T18:09:32.317864Z" }, + "executionInfo": { + "elapsed": 5, + "status": "ok", + "timestamp": 1790186972319.3284, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 + }, + "id": "bDjt0X2VIj9L", + "outputId": "fb24fa1b-d783-4507-e1ca-d01cdd3bcdf3" + }, + "outputs": [ { - "cell_type": "markdown", - "source": [ - "The `dgf.transform.batch_indices_generator` function is useful to generate seed\n", - "indices:" - ], - "metadata": { - "id": "Ipraa9jYkWfR" - } + "name": "stdout", + "output_type": "stream", + "text": [ + "Graph Schema:\n", + "\n", + "Node Sets:\n", + " nodes:\n", + " | Feature | Format | Semantic | Shape | Detail |\n", + " |--------------------|------------|-------------|---------|-------------|\n", + " | #split_INDEX | INTEGER_64 | CATEGORICAL | () | #num.cat:4 |\n", + " | feat | FLOAT_32 | EMBEDDING | (128,) | |\n", + " | labels | INTEGER_64 | CATEGORICAL | None | #num.cat:40 |\n", + " | year_SOFT_QUANTILE | FLOAT_32 | EMBEDDING | () | |\n", + "\n", + "\n", + "Edge Sets:\n", + " edges: (Source: nodes, Target: nodes)\n", + " (No features)\n", + "\n" + ] + } + ], + "source": [ + "dgf.analyse.print_schema(normalizer.output_schema())" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "bl5FYdFoWiAd" + }, + "source": [ + "You can run the normalizer:" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-23T18:09:32.320125Z", + "iopub.status.busy": "2026-09-23T18:09:32.319916Z", + "iopub.status.idle": "2026-09-23T18:09:32.324692Z", + "shell.execute_reply": "2026-09-23T18:09:32.324259Z" + }, + "executionInfo": { + "elapsed": 6, + "status": "ok", + "timestamp": 1790186972325.6587, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 }, + "id": "oG2rH38WJ3ho", + "outputId": "f06b3859-2f84-434e-8355-9da579e07f3a" + }, + "outputs": [ { - "cell_type": "code", - "source": [ - "# Generate 3 batches, each containing 5 seed-node indices.\n", - "for seed_node_idxs in islice(\n", - " dgf.transform.batch_indices_generator(\n", - " train_seed_node_idxs, batch_size=5, drop_remainder=True, shuffle=False\n", - " ),\n", - " 3,\n", - "):\n", - " print(seed_node_idxs)" - ], - "metadata": { - "id": "bh1-MIFo709W", - "executionInfo": { - "status": "ok", - "timestamp": 1779800258904, - "user_tz": -120, - "elapsed": 18, - "user": { - "displayName": "", - "userId": "" - } - }, - "outputId": "017937af-07b4-414b-973d-6cfd8c4ab652" - }, - "execution_count": 13, - "outputs": [ - { - "output_type": "stream", - "name": "stdout", - "text": [ - "[ 1895 44513 24828 122605 140271]\n", - "[ 19815 8619 107350 93409 12795]\n", - "[ 69167 69552 143796 129899 59454]\n" - ] - } + "data": { + "text/plain": [ + "InMemoryGraph(node_sets={'nodes': InMemoryNodeSet(num_nodes=np.int64(13), features={'#split_INDEX': array([2, 0, 0, 0, 0, 0, 1, 0, 0, 0, 2, 2, 1]), 'labels': array([16, 16, 16, 16, 16, 16, 16, 16, 24, 16, 16, 16, 16]), 'year_SOFT_QUANTILE': array([0.375 , 0.1875, 0.25 , 0.3125, 0.3125, 0.25 , 0.4375, 0.1875,\n", + " 0.1875, 0.3125, 0.375 , 0.375 , 0.4375], dtype=float32), 'feat': array([[-0.153313, -0.020952, -0.165293, ..., 0.096392, 0.019828,\n", + " -0.138239],\n", + " [-0.118739, -0.01598 , -0.270465, ..., 0.046328, -0.012872,\n", + " -0.166937],\n", + " [-0.291505, 0.34393 , -0.184386, ..., -0.202508, -0.037857,\n", + " -0.21467 ],\n", + " ...,\n", + " [-0.17497 , 0.24994 , -0.276248, ..., -0.209126, 0.121964,\n", + " -0.082123],\n", + " [ 0.050382, 0.087316, -0.167011, ..., 0.0449 , -0.05214 ,\n", + " -0.36406 ],\n", + " [ 0.118253, 0.025628, -0.094837, ..., 0.169792, 0.053808,\n", + " -0.243891]], shape=(13, 128), dtype=float32)})}, edge_sets={'edges': InMemoryEdgeSet(adjacency=array([[ 0, 0, 0, 0, 0, 6, 6, 6, 6, 6, 12],\n", + " [ 1, 2, 3, 4, 5, 7, 8, 9, 10, 11, 6]], dtype=int64), features={})}, timestamp=None)" ] + }, + "execution_count": 25, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "raw_batch = next(batch_generator(train_seed_node_idxs, batch_size=2))\n", + "normalized_batch = normalizer.normalize_numpy(raw_batch)\n", + "normalized_batch" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "KE1X1ZBAInGe" + }, + "source": [ + "In the next section, we use JAX and Flax to define and train our model. Our\n", + "`normalized_batch` object (`dgf.data.InMemoryGraph`) is made of NumPy arrays. We\n", + "can convert it into an equivalent representation with JAX arrays instead:" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-23T18:09:32.326409Z", + "iopub.status.busy": "2026-09-23T18:09:32.326181Z", + "iopub.status.idle": "2026-09-23T18:09:32.329248Z", + "shell.execute_reply": "2026-09-23T18:09:32.328917Z" }, - { - "cell_type": "markdown", - "source": [ - "We can combine all of this together to define a graph sample python generator." - ], - "metadata": { - "id": "O19qUi6Lk2hS" - } + "executionInfo": { + "elapsed": 4, + "status": "ok", + "timestamp": 1790186972330.158, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 }, + "id": "d32_5m25HJ_A", + "outputId": "3ef2cafe-6e83-49ae-d166-cb57cfd9af44" + }, + "outputs": [ { - "cell_type": "code", - "source": [ - "def batch_generator(\n", - " seed_node_idxs, padding=None, also_return_merge_offsets=False, batch_size=32\n", - "):\n", - " \"\"\"Generates batched merged graphs samples, and (optional) merging offsets.\"\"\"\n", - " for seed_node_idxs in dgf.transform.batch_indices_generator(\n", - " seed_node_idxs, batch_size=batch_size, drop_remainder=True, shuffle=False\n", - " ):\n", - " # Sample the graphs\n", - " samples = sampler.sample(seed_node_idxs.tolist())\n", - "\n", - " try:\n", - " # Merge the graph samples into a single graph.\n", - " merged_samples, merge_offsets = dgf.transform.merge_graphs(\n", - " graphs=samples,\n", - " schema=schema,\n", - " padding=padding,\n", - " sentinel_offset=False,\n", - " )\n", - " except dgf.exception.InsufficientPaddingError:\n", - " # Skip if the number of nodes is too large for the padding.\n", - " continue\n", - "\n", - " if also_return_merge_offsets:\n", - " yield merged_samples, merge_offsets\n", - " else:\n", - " yield merged_samples\n", - "\n", - "\n", - "# Generate 5 batches of graphs.\n", - "for graph_sample in islice(batch_generator(train_seed_node_idxs), 5):\n", - " print(\n", - " \"Number of nodes and edges in the sample:\",\n", - " graph_sample.node_sets[\"nodes\"].num_nodes,\n", - " graph_sample.edge_sets[\"edges\"].num_edges(),\n", - " )" - ], - "metadata": { - "id": "XChnfUL5tjRU", - "executionInfo": { - "status": "ok", - "timestamp": 1779800258966, - "user_tz": -120, - "elapsed": 21, - "user": { - "displayName": "", - "userId": "" - } - }, - "outputId": "9b1169a2-dbd5-4284-9548-ed65900a5f65" - }, - "execution_count": 14, - "outputs": [ - { - "output_type": "stream", - "name": "stdout", - "text": [ - "Number of nodes and edges in the sample: 205 175\n", - "Number of nodes and edges in the sample: 192 163\n", - "Number of nodes and edges in the sample: 199 169\n", - "Number of nodes and edges in the sample: 217 185\n", - "Number of nodes and edges in the sample: 178 146\n" - ] - } + "data": { + "text/plain": [ + "numpy.ndarray" ] + }, + "execution_count": 26, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "type(normalized_batch.edge_sets[\"edges\"].adjacency)" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-23T18:09:32.331001Z", + "iopub.status.busy": "2026-09-23T18:09:32.330680Z", + "iopub.status.idle": "2026-09-23T18:09:33.045903Z", + "shell.execute_reply": "2026-09-23T18:09:33.045528Z" }, + "executionInfo": { + "elapsed": 716, + "status": "ok", + "timestamp": 1790186973046.876, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 + }, + "id": "ACLtqDLWK7NO", + "outputId": "10fa420e-2e1f-4431-88ef-ebb82520a773" + }, + "outputs": [ { - "cell_type": "markdown", - "source": [ - "**Remark:**\n", - "\n", - "- Many GF functions consume / return graph generators." - ], - "metadata": { - "id": "kamAFS51mZXy" - } + "data": { + "text/plain": [ + "jax.jaxlib._jax.ArrayImpl" + ] + }, + "execution_count": 27, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "jax_normalized_batch = dgf.convert.graph_to_jax_graph(normalized_batch)\n", + "type(jax_normalized_batch.edge_sets[\"edges\"].adjacency)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "WU3ap6RRLEpr" + }, + "source": [ + "## Core model definition and training\n", + "\n", + "Let's define and train a FLAX module for our graph.\n", + "\n", + "**Note:** We use the `dgf.jax.train` simple FLAX training loop:" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-23T18:09:33.047922Z", + "iopub.status.busy": "2026-09-23T18:09:33.047675Z", + "iopub.status.idle": "2026-09-23T18:10:45.773122Z", + "shell.execute_reply": "2026-09-23T18:10:45.772506Z" }, + "executionInfo": { + "elapsed": 72727, + "status": "ok", + "timestamp": 1790187045774.1333, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 + }, + "id": "KOGYXiyMkwc7", + "outputId": "36221189-14e5-45bc-80de-6c49fd50605f" + }, + "outputs": [ { - "cell_type": "markdown", - "source": [ - "We can use our graph generator to determine the optimal padding (since JAX does\n", - "not support variable length arrays)." - ], - "metadata": { - "id": "ndbwi_ZUmjfQ" - } + "name": "stdout", + "output_type": "stream", + "text": [ + "Generate first batch to initialize model\n", + "Create model variables\n", + "...Tracing model\n", + "Create model variables finished in 8.11 seconds\n", + "Will validate model every 1000 step(s)\n", + "Will checkpoint model every 1000 step(s)\n", + "Start training. The first two steps are generally slow.\n" + ] }, { - "cell_type": "code", - "source": [ - "padding = dgf.analyse.padding_from_graph_generator(\n", - " schema=schema, graphs=islice(batch_generator(train_seed_node_idxs), 200)\n", - ")\n", - "padding" - ], - "metadata": { - "id": "paI3RaXOkHGk", - "executionInfo": { - "status": "ok", - "timestamp": 1779800259190, - "user_tz": -120, - "elapsed": 182, - "user": { - "displayName": "", - "userId": "" - } - }, - "outputId": "451376c8-286c-45f9-af21-d28766ddb604" - }, - "execution_count": 15, - "outputs": [ - { - "output_type": "execute_result", - "data": { - "text/plain": [ - "Padding(node_sets={'nodes': NodeSetPadding(num_nodes=260)}, edge_sets={'edges': EdgeSetPadding(num_edges=226)})" - ] - }, - "metadata": {}, - "execution_count": 15 - } - ] + "name": "stderr", + "output_type": "stream", + "text": [ + "Training: 0%| | 0/10000 [00:00, semantic=, shape=None, num_categorical_values=40, is_utf8_string=False)), SoftQuantileNormalizer(input_feature='year', type='SoftQuantileNormalizer', output_feature_name='year_SOFT_QUANTILE', output_shape=None, quantiles=array([1971., 2002., 2006., 2007., 2008., 2009., 2010., 2011., 2012.,\n", - " 2013., 2014., 2015., 2016., 2017., 2018., 2019., 2020.],\n", - " dtype=float32)), IdentityNormalizer(input_feature='feat', type='IdentityNormalizer', input_schema=FeatureSchema(format=, semantic=, shape=(128,), num_categorical_values=None, is_utf8_string=False))])}, edgesets={'edges': EdgeSetNormalizerConfig(source='nodes', target='nodes', normalizers=[])}))" - ] - }, - "metadata": {}, - "execution_count": 18 - } - ] + "name": "stdout", + "output_type": "stream", + "text": [ + "step:2000 train-accuracy:0.5978 train-loss:1.3734 valid-accuracy:0.6043 valid-loss:1.3254\n" + ] }, { - "cell_type": "markdown", - "source": [ - "**Remarks:**\n", - "\n", - "- Instead of the auto-normalization, you can define the normalization\n", - " operations manually e.g. `dgf.transform.SoftQuantileNormalizer`." - ], - "metadata": { - "id": "NsnAdalHHRD3" - } + "name": "stderr", + "output_type": "stream", + "text": [ + "Training: 31%|███ | 3057/10000 [00:24<02:20, 49.43it/s, step=3000, train-accuracy=0.5925, train-loss=1.3298, valid-accuracy=0.6043, valid-loss=1.3254]" + ] }, { - "cell_type": "markdown", - "source": [ - "You can check individual normalization blocks:" - ], - "metadata": { - "id": "P-EbWtnIH05W" - } + "name": "stdout", + "output_type": "stream", + "text": [ + "step:3000 train-accuracy:0.5925 train-loss:1.3298 valid-accuracy:0.6217 valid-loss:1.2541\n" + ] }, { - "cell_type": "code", - "source": [ - "# The \"year\" feature is normalized with the SoftQuantileNormalizer.\n", - "normalizer.config.nodesets[\"nodes\"].normalizers[2]" - ], - "metadata": { - "id": "vG618kYeHzQQ", - "executionInfo": { - "status": "ok", - "timestamp": 1779800259685, - "user_tz": -120, - "elapsed": 20, - "user": { - "displayName": "", - "userId": "" - } - }, - "outputId": "4359ffe0-d185-4fd0-fdaf-56868b7f004e" - }, - "execution_count": 19, - "outputs": [ - { - "output_type": "execute_result", - "data": { - "text/plain": [ - "SoftQuantileNormalizer(input_feature='year', type='SoftQuantileNormalizer', output_feature_name='year_SOFT_QUANTILE', output_shape=None, quantiles=array([1971., 2002., 2006., 2007., 2008., 2009., 2010., 2011., 2012.,\n", - " 2013., 2014., 2015., 2016., 2017., 2018., 2019., 2020.],\n", - " dtype=float32))" - ] - }, - "metadata": {}, - "execution_count": 19 - } - ] + "name": "stderr", + "output_type": "stream", + "text": [ + "Training: 40%|████ | 4031/10000 [00:30<01:52, 53.13it/s, step=4000, train-accuracy=0.6191, train-loss=1.2458, valid-accuracy=0.6217, valid-loss=1.2541]" + ] }, { - "cell_type": "code", - "source": [ - "# The \"feat\" feature is an embedding, so it is transmitted without modification (identity normalizer).\n", - "normalizer.config.nodesets[\"nodes\"].normalizers[3]" - ], - "metadata": { - "id": "_HtDHI4CINXZ", - "executionInfo": { - "status": "ok", - "timestamp": 1779800259747, - "user_tz": -120, - "elapsed": 20, - "user": { - "displayName": "", - "userId": "" - } - }, - "outputId": "363f2706-4bfa-4804-bc14-afd35183bdef" - }, - "execution_count": 20, - "outputs": [ - { - "output_type": "execute_result", - "data": { - "text/plain": [ - "IdentityNormalizer(input_feature='feat', type='IdentityNormalizer', input_schema=FeatureSchema(format=, semantic=, shape=(128,), num_categorical_values=None, is_utf8_string=False))" - ] - }, - "metadata": {}, - "execution_count": 20 - } - ] + "name": "stdout", + "output_type": "stream", + "text": [ + "step:4000 train-accuracy:0.6191 train-loss:1.2458 valid-accuracy:0.6310 valid-loss:1.2059\n" + ] }, { - "cell_type": "markdown", - "source": [ - "You can obtain the output schema of the normalizer:" - ], - "metadata": { - "id": "pOSxkBgmIdvb" - } + "name": "stderr", + "output_type": "stream", + "text": [ + "Training: 50%|█████ | 5045/10000 [00:35<01:35, 51.68it/s, step=5000, train-accuracy=0.6428, train-loss=1.1623, valid-accuracy=0.6310, valid-loss=1.2059]" + ] }, { - "cell_type": "code", - "source": [ - "dgf.analyse.print_schema(normalizer.output_schema())" - ], - "metadata": { - "id": "bDjt0X2VIj9L", - "executionInfo": { - "status": "ok", - "timestamp": 1779800259811, - "user_tz": -120, - "elapsed": 21, - "user": { - "displayName": "", - "userId": "" - } - }, - "outputId": "a8ac57ba-fd88-4b0c-d31e-0bf842cacecc" - }, - "execution_count": 21, - "outputs": [ - { - "output_type": "stream", - "name": "stdout", - "text": [ - "Graph Schema:\n", - "\n", - "Node Sets:\n", - " nodes:\n", - " | Feature | Format | Semantic | Shape | Num cat. vals |\n", - " |--------------------|------------|-------------|---------|-----------------|\n", - " | #split_INDEX | INTEGER_64 | CATEGORICAL | None | 4 |\n", - " | feat | FLOAT_32 | EMBEDDING | (128,) | None |\n", - " | labels | INTEGER_64 | CATEGORICAL | None | 40 |\n", - " | year_SOFT_QUANTILE | FLOAT_32 | EMBEDDING | None | None |\n", - "\n", - "\n", - "Edge Sets:\n", - " edges: (Source: nodes, Target: nodes)\n", - " (No features)\n", - "\n" - ] - } - ] + "name": "stdout", + "output_type": "stream", + "text": [ + "step:5000 train-accuracy:0.6428 train-loss:1.1623 valid-accuracy:0.6420 valid-loss:1.1714\n" + ] }, { - "cell_type": "markdown", - "source": [ - "You can run the normalizer:" - ], - "metadata": { - "id": "bl5FYdFoWiAd" - } + "name": "stderr", + "output_type": "stream", + "text": [ + "Training: 61%|██████ | 6051/10000 [00:41<01:14, 53.08it/s, step=6000, train-accuracy=0.6381, train-loss=1.1894, valid-accuracy=0.6420, valid-loss=1.1714]" + ] }, { - "cell_type": "code", - "source": [ - "raw_batch = next(batch_generator(train_seed_node_idxs, batch_size=2))\n", - "normalized_batch = normalizer.normalize_numpy(raw_batch)\n", - "normalized_batch" - ], - "metadata": { - "id": "oG2rH38WJ3ho", - "executionInfo": { - "status": "ok", - "timestamp": 1779800259875, - "user_tz": -120, - "elapsed": 22, - "user": { - "displayName": "", - "userId": "" - } - }, - "outputId": "a2de97a2-5bd2-4c9f-fbfa-9695340ce24f" - }, - "execution_count": 22, - "outputs": [ - { - "output_type": "execute_result", - "data": { - "text/plain": [ - "InMemoryGraph(node_sets={'nodes': InMemoryNodeSet(num_nodes=260, features={'#split_INDEX': array([0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1,\n", - " 0, 2, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 2, 0, 2, 0,\n", - " 0, 1, 0, 2, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 2, 2, 2, 2,\n", - " 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 2, 0, 2, 0, 2, 0, 0, 0, 0,\n", - " 0, 2, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 2, 2, 0, 2, 2, 0, 2, 2, 2,\n", - " 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 0, 0, 0, 2, 2, 0, 0, 1, 0, 0, 0, 0,\n", - " 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 2,\n", - " 0, 0, 2, 0, 0, 0, 0, 2, 0, 0, 2, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1,\n", - " 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,\n", - " 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,\n", - " 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,\n", - " 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3]), 'labels': array([29, 34, 29, 35, 8, 34, 8, 30, 26, 31, 30, 30, 30, 30, 30, 2, 22,\n", - " 22, 22, 5, 37, 24, 24, 24, 24, 24, 24, 24, 24, 16, 16, 16, 16, 16,\n", - " 16, 23, 23, 23, 23, 24, 24, 13, 16, 16, 16, 16, 17, 16, 10, 10, 10,\n", - " 10, 10, 10, 16, 16, 16, 24, 30, 37, 10, 30, 30, 30, 30, 30, 30, 30,\n", - " 24, 24, 24, 30, 30, 30, 30, 30, 30, 28, 28, 28, 28, 28, 28, 28, 2,\n", - " 2, 2, 2, 2, 2, 2, 2, 34, 34, 34, 34, 34, 34, 34, 16, 16, 16,\n", - " 16, 24, 16, 6, 26, 26, 21, 24, 24, 24, 16, 24, 30, 24, 24, 30, 10,\n", - " 3, 37, 28, 3, 4, 24, 10, 27, 24, 30, 30, 28, 28, 28, 32, 12, 23,\n", - " 23, 23, 7, 29, 23, 24, 5, 16, 24, 16, 24, 10, 9, 10, 10, 5, 5,\n", - " 5, 5, 10, 24, 2, 19, 24, 2, 2, 24, 10, 10, 9, 34, 39, 34, 34,\n", - " 34, 5, 34, 34, 9, 34, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n", - " 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n", - " 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n", - " 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n", - " 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n", - " 0, 0, 0, 0, 0]), 'year_SOFT_QUANTILE': array([ 0.3125 , -0.25 , 0.125 , 0.1875 , 0. ,\n", - " -0.44153225, 0.375 , 0.25 , 0.1875 , 0.1875 ,\n", - " 0.25 , 0.3125 , 0.3125 , 0.3125 , 0.25 ,\n", - " 0.4375 , 0.4375 , 0.4375 , -0.3125 , -0.421875 ,\n", - " -0.421875 , 0.4375 , 0.1875 , 0.375 , 0.3125 ,\n", - " 0.4375 , 0.5 , 0.4375 , 0.5 , 0.4375 ,\n", - " 0.125 , 0.125 , 0.25 , 0.25 , 0.1875 ,\n", - " 0.5 , 0.25 , 0.4375 , 0.3125 , 0.4375 ,\n", - " 0.375 , 0.25 , 0.375 , 0.3125 , 0.25 ,\n", - " 0.4375 , 0.1875 , 0.375 , 0.25 , -0.43951613,\n", - " -0.375 , -0.421875 , 0. , -0.125 , 0.5 ,\n", - " 0.1875 , 0.25 , 0.5 , 0.1875 , 0. ,\n", - " 0.0625 , 0.4375 , 0.375 , 0.375 , 0.375 ,\n", - " 0.375 , 0.25 , 0.1875 , 0. , 0.1875 ,\n", - " 0.1875 , 0.125 , 0.4375 , 0.3125 , 0.25 ,\n", - " 0.5 , 0.4375 , 0.3125 , 0.3125 , 0.375 ,\n", - " -0.125 , 0.375 , -0.1875 , 0.375 , 0.25 ,\n", - " 0.1875 , 0.125 , 0.125 , 0.125 , 0.375 ,\n", - " 0.3125 , 0.4375 , 0.4375 , -0.125 , 0.0625 ,\n", - " -0.125 , 0.1875 , 0.4375 , 0.5 , 0.4375 ,\n", - " 0.25 , 0.375 , 0.375 , 0.1875 , 0.375 ,\n", - " 0.375 , 0.25 , 0.375 , 0.375 , 0.375 ,\n", - " 0.3125 , 0.125 , 0.1875 , 0.25 , 0.3125 ,\n", - " 0.4375 , 0.5 , 0.4375 , 0.4375 , 0.375 ,\n", - " 0. , 0.125 , 0.3125 , 0.375 , 0.375 ,\n", - " 0.0625 , 0.3125 , 0.5 , 0.1875 , 0. ,\n", - " 0.1875 , 0. , -0.0625 , 0.125 , 0.0625 ,\n", - " -0.1875 , 0.125 , 0.25 , 0.1875 , 0.3125 ,\n", - " 0.3125 , 0.3125 , 0.25 , 0.3125 , 0.3125 ,\n", - " 0.3125 , 0.25 , 0.5 , 0.125 , 0.0625 ,\n", - " 0.3125 , 0.4375 , 0.25 , 0.375 , 0.3125 ,\n", - " 0.3125 , 0.375 , 0.0625 , 0.1875 , 0. ,\n", - " 0.125 , 0.375 , 0.25 , 0.125 , 0.375 ,\n", - " 0.1875 , 0.125 , 0.3125 , 0.125 , -0.25 ,\n", - " -0.25 , 0.4375 , -0.0625 , -0.390625 , 0.25 ,\n", - " 0.4375 , -4.47379 , -4.47379 , -4.47379 , -4.47379 ,\n", - " -4.47379 , -4.47379 , -4.47379 , -4.47379 , -4.47379 ,\n", - " -4.47379 , -4.47379 , -4.47379 , -4.47379 , -4.47379 ,\n", - " -4.47379 , -4.47379 , -4.47379 , -4.47379 , -4.47379 ,\n", - " -4.47379 , -4.47379 , -4.47379 , -4.47379 , -4.47379 ,\n", - " -4.47379 , -4.47379 , -4.47379 , -4.47379 , -4.47379 ,\n", - " -4.47379 , -4.47379 , -4.47379 , -4.47379 , -4.47379 ,\n", - " -4.47379 , -4.47379 , -4.47379 , -4.47379 , -4.47379 ,\n", - " -4.47379 , -4.47379 , -4.47379 , -4.47379 , -4.47379 ,\n", - " -4.47379 , -4.47379 , -4.47379 , -4.47379 , -4.47379 ,\n", - " -4.47379 , -4.47379 , -4.47379 , -4.47379 , -4.47379 ,\n", - " -4.47379 , -4.47379 , -4.47379 , -4.47379 , -4.47379 ,\n", - " -4.47379 , -4.47379 , -4.47379 , -4.47379 , -4.47379 ,\n", - " -4.47379 , -4.47379 , -4.47379 , -4.47379 , -4.47379 ,\n", - " -4.47379 , -4.47379 , -4.47379 , -4.47379 , -4.47379 ,\n", - " -4.47379 , -4.47379 , -4.47379 , -4.47379 , -4.47379 ,\n", - " -4.47379 , -4.47379 , -4.47379 , -4.47379 , -4.47379 ],\n", - " dtype=float32), 'feat': array([[ 0.00295 , 0.094847, -0.209811, ..., 0.020839, -0.097785,\n", - " -0.032948],\n", - " [-0.108829, 0.114401, -0.395645, ..., 0.175675, -0.121629,\n", - " -0.243047],\n", - " [-0.025696, 0.006006, -0.179712, ..., 0.219011, -0.042685,\n", - " -0.098881],\n", - " ...,\n", - " [ 0. , 0. , 0. , ..., 0. , 0. ,\n", - " 0. ],\n", - " [ 0. , 0. , 0. , ..., 0. , 0. ,\n", - " 0. ],\n", - " [ 0. , 0. , 0. , ..., 0. , 0. ,\n", - " 0. ]], shape=(260, 128), dtype=float32)})}, edge_sets={'edges': InMemoryEdgeSet(adjacency=array([[ 0, 0, 0, 0, 0, 6, 7, 7, 7, 11, 12, 13, 14,\n", - " 16, 17, 18, 18, 21, 21, 21, 25, 26, 27, 28, 29, 29,\n", - " 29, 29, 29, 35, 35, 35, 39, 39, 39, 39, 39, 45, 45,\n", - " 48, 48, 48, 48, 48, 54, 54, 57, 58, 58, 61, 62, 63,\n", - " 64, 65, 66, 66, 66, 66, 66, 72, 73, 74, 75, 76, 77,\n", - " 77, 77, 77, 77, 79, 83, 84, 84, 84, 84, 89, 90, 91,\n", - " 92, 92, 92, 92, 92, 97, 98, 99, 99, 99, 99, 99, 105,\n", - " 105, 105, 109, 109, 109, 109, 109, 115, 116, 118, 119, 119, 122,\n", - " 123, 124, 124, 124, 128, 130, 130, 133, 133, 136, 137, 138, 139,\n", - " 140, 141, 141, 141, 141, 141, 147, 147, 147, 151, 151, 151, 151,\n", - " 151, 157, 157, 160, 161, 162, 164, 165, 167, 168, 168, 168, 168,\n", - " 168, 174, 175, 259, 259, 259, 259, 259, 259, 259, 259, 259, 259,\n", - " 259, 259, 259, 259, 259, 259, 259, 259, 259, 259, 259, 259, 259,\n", - " 259, 259, 259, 259, 259, 259, 259, 259, 259, 259, 259, 259, 259,\n", - " 259, 259, 259, 259, 259, 259, 259, 259, 259, 259, 259, 259, 259,\n", - " 259, 259, 259, 259, 259, 259, 259, 259, 259, 259, 259, 259, 259,\n", - " 259, 259, 259, 259, 259, 259, 259, 259, 259, 259, 259, 259, 259,\n", - " 259, 259, 259, 259, 259],\n", - " [ 1, 2, 3, 4, 5, 0, 8, 9, 10, 7, 7, 7, 7,\n", - " 15, 15, 19, 20, 22, 23, 24, 21, 21, 21, 21, 30, 31,\n", - " 32, 33, 34, 36, 37, 38, 40, 41, 42, 43, 44, 46, 47,\n", - " 49, 50, 51, 52, 53, 55, 56, 54, 59, 60, 58, 58, 58,\n", - " 58, 58, 67, 68, 69, 70, 71, 66, 66, 66, 66, 66, 78,\n", - " 79, 80, 81, 82, 77, 77, 85, 86, 87, 88, 84, 84, 84,\n", - " 93, 94, 95, 96, 97, 92, 92, 100, 101, 102, 103, 104, 106,\n", - " 107, 108, 110, 111, 112, 113, 114, 109, 109, 117, 120, 121, 119,\n", - " 119, 125, 126, 127, 129, 131, 132, 134, 135, 133, 133, 133, 133,\n", - " 133, 142, 143, 144, 145, 146, 148, 149, 150, 152, 153, 154, 155,\n", - " 156, 158, 159, 157, 157, 157, 163, 166, 165, 169, 170, 171, 172,\n", - " 173, 168, 168, 259, 259, 259, 259, 259, 259, 259, 259, 259, 259,\n", - " 259, 259, 259, 259, 259, 259, 259, 259, 259, 259, 259, 259, 259,\n", - " 259, 259, 259, 259, 259, 259, 259, 259, 259, 259, 259, 259, 259,\n", - " 259, 259, 259, 259, 259, 259, 259, 259, 259, 259, 259, 259, 259,\n", - " 259, 259, 259, 259, 259, 259, 259, 259, 259, 259, 259, 259, 259,\n", - " 259, 259, 259, 259, 259, 259, 259, 259, 259, 259, 259, 259, 259,\n", - " 259, 259, 259, 259, 259]], dtype=int64), features={})})" - ] - }, - "metadata": {}, - "execution_count": 22 - } - ] + "name": "stdout", + "output_type": "stream", + "text": [ + "step:6000 train-accuracy:0.6381 train-loss:1.1894 valid-accuracy:0.6473 valid-loss:1.1503\n" + ] }, { - "cell_type": "markdown", - "source": [ - "In the next section, we use JAX and Flax to define and train our model. Our\n", - "`normalized_batch` object (`dgf.data.InMemoryGraph`) is made of NumPy arrays. We\n", - "can convert it into an equivalent representation with JAX arrays instead:" - ], - "metadata": { - "id": "KE1X1ZBAInGe" - } + "name": "stderr", + "output_type": "stream", + "text": [ + "Training: 71%|███████ | 7059/10000 [00:47<00:56, 52.31it/s, step=7000, train-accuracy=0.6397, train-loss=1.1660, valid-accuracy=0.6473, valid-loss=1.1503]" + ] }, { - "cell_type": "code", - "source": [ - "type(normalized_batch.edge_sets[\"edges\"].adjacency)" - ], - "metadata": { - "id": "d32_5m25HJ_A", - "executionInfo": { - "status": "ok", - "timestamp": 1779800259940, - "user_tz": -120, - "elapsed": 21, - "user": { - "displayName": "", - "userId": "" - } - }, - "outputId": "57d3513b-be93-4986-87a2-73fd49d9ce17" - }, - "execution_count": 23, - "outputs": [ - { - "output_type": "execute_result", - "data": { - "text/plain": [ - "numpy.ndarray" - ] - }, - "metadata": {}, - "execution_count": 23 - } - ] + "name": "stdout", + "output_type": "stream", + "text": [ + "step:7000 train-accuracy:0.6397 train-loss:1.1660 valid-accuracy:0.6507 valid-loss:1.1452\n" + ] }, { - "cell_type": "code", - "source": [ - "jax_normalized_batch = dgf.convert.graph_to_jax_graph(normalized_batch)\n", - "type(jax_normalized_batch.edge_sets[\"edges\"].adjacency)" - ], - "metadata": { - "id": "ACLtqDLWK7NO", - "executionInfo": { - "status": "ok", - "timestamp": 1779800262145, - "user_tz": -120, - "elapsed": 2162, - "user": { - "displayName": "", - "userId": "" - } - }, - "outputId": "2e7887ad-bae0-44f1-af06-2040329c280f" - }, - "execution_count": 24, - "outputs": [ - { - "output_type": "execute_result", - "data": { - "text/plain": [ - "jax.jaxlib._jax.ArrayImpl" - ] - }, - "metadata": {}, - "execution_count": 24 - } - ] + "name": "stderr", + "output_type": "stream", + "text": [ + "Training: 80%|████████ | 8040/10000 [00:52<00:37, 52.46it/s, step=8000, train-accuracy=0.6516, train-loss=1.1437, valid-accuracy=0.6507, valid-loss=1.1452]" + ] }, { - "cell_type": "markdown", - "source": [ - "## Core model definition and training\n", - "\n", - "Let's define and train a FLAX module for our graph.\n", - "\n", - "**Note:** We use the `dgf.jax.train` simple FLAX training loop:" - ], - "metadata": { - "id": "WU3ap6RRLEpr" - } + "name": "stdout", + "output_type": "stream", + "text": [ + "step:8000 train-accuracy:0.6516 train-loss:1.1437 valid-accuracy:0.6560 valid-loss:1.1298\n" + ] }, { - "cell_type": "code", - "source": [ - "class Model(nn.Module):\n", - " schema: dgf.data.GraphSchema\n", - " num_label_classes: int\n", - "\n", - " @nn.compact\n", - " def __call__(\n", - " self, batch: tuple[dgf.data.JaxInMemoryGraph, jnp.ndarray], training: bool\n", - " ):\n", - " print(\"...Tracing model\")\n", - " graph, seed_node_idxs = batch\n", - "\n", - " # Embed features into a single embedding per nodeset.\n", - " embedder_config = dgf.jax.layers.EmbedGraphConfig()\n", - " embedder = embedder_config.make(schema=self.schema)\n", - " embedded_output_schema = embedder_config.output_schema(self.schema)\n", - " graph = embedder(graph, training=training)\n", - "\n", - " # A MLP layer on each nodeset independently. This also ensure that all the\n", - " # nodeset embeddings have the same size.\n", - " for _, nodeset_value in graph.node_sets.items():\n", - " mlp = dgf.jax.layers.ResidualMLPV2Config(\n", - " dims=128, norm=\"layer_norm\", residual=False\n", - " ).make()\n", - " nodeset_value.features[\"embedding\"] = mlp(\n", - " nodeset_value.features[\"embedding\"]\n", - " )\n", - "\n", - " # Message passing between nodes\n", - " for _ in range(2):\n", - " message_passer = dgf.jax.layers.HeterogeneousGraphConvolutionConfig(\n", - " dims=128\n", - " ).make(embedded_output_schema)\n", - " graph = message_passer(graph, training=training)\n", - "\n", - " # Extract embedding of seed nodes\n", - " node_embedding = graph.node_sets[\"nodes\"].features[\"embedding\"][\n", - " seed_node_idxs\n", - " ]\n", - "\n", - " logits = nn.Dense(num_label_classes)(node_embedding)\n", - " return logits\n", - "\n", - "\n", - "# The model received the normalized schema, without the label column.\n", - "model_schema = normalizer.output_schema()\n", - "num_label_classes = (\n", - " model_schema.node_sets[\"nodes\"].features[\"labels\"].num_categorical_values\n", - ")\n", - "del model_schema.node_sets[\"nodes\"].features[\"labels\"]\n", - "\n", - "# Instantiate the model\n", - "model = Model(schema=model_schema, num_label_classes=num_label_classes)\n", - "\n", - "\n", - "# The model loss\n", - "def loss_fn(\n", - " params: jaxtyping.PyTree,\n", - " batch: jaxtyping.PyTree,\n", - " labels: jnp.ndarray,\n", - " rng_key: jnp.ndarray | None,\n", - " training: bool,\n", - ") -> jnp.ndarray | jaxtyping.PyTree:\n", - " if rng_key is not None:\n", - " rngs = {\"dropout\": rng_key}\n", - " else:\n", - " rngs = None\n", - " logits = model.apply(params, batch, training=training, rngs=rngs)\n", - " loss = optax.softmax_cross_entropy_with_integer_labels(logits, labels)\n", - " accuracy = jnp.argmax(logits, axis=-1) == labels\n", - " return jnp.mean(loss), {\"accuracy\": accuracy.mean()}\n", - "\n", - "\n", - "# The model training step\n", - "@jax.jit\n", - "def train_step(params, opt_state, batch, rng_key):\n", - " graph, seed_node_idxs = batch\n", - " labels = graph.node_sets[\"nodes\"].features[\"labels\"][seed_node_idxs]\n", - " (loss, aux_data), grads = jax.value_and_grad(loss_fn, has_aux=True)(\n", - " params, batch, labels, rng_key, True\n", - " )\n", - " updates, opt_state = opt.update(grads, opt_state, params)\n", - " params = optax.apply_updates(params, updates)\n", - " return params, opt_state, {\"loss\": loss, **aux_data}\n", - "\n", - "\n", - "# The model validation step\n", - "@jax.jit\n", - "def valid_step(params, opt_state, batch):\n", - " graph, seed_node_idxs = batch\n", - " labels = graph.node_sets[\"nodes\"].features[\"labels\"][seed_node_idxs]\n", - " loss, aux = loss_fn(params, batch, labels, None, False)\n", - " return {\"loss\": loss, **aux}\n", - "\n", - "\n", - "# Process a batch of data before sending it to the model.\n", - "def process_batch(\n", - " graph: dgf.data.InMemoryGraph,\n", - " merge_offsets: dict[str, np.ndarray],\n", - "):\n", - " normalized_graph = normalizer.normalize_numpy(graph)\n", - " jax_normalized_graph = dgf.convert.graph_to_jax_graph(normalized_graph)\n", - " seed_node_idxs = jnp.asarray(merge_offsets[\"nodes\"])\n", - " return jax_normalized_graph, seed_node_idxs\n", - "\n", - "\n", - "# Generate the training data\n", - "def infinite_train_dataset_iterator():\n", - " while True:\n", - " for raw_batch, merge_offsets in batch_generator(\n", - " train_seed_node_idxs,\n", - " batch_size=32,\n", - " padding=padding,\n", - " also_return_merge_offsets=True,\n", - " ):\n", - " yield process_batch(raw_batch, merge_offsets)\n", - "\n", - "\n", - "# Generate the validation data\n", - "def finite_valid_dataset_iterator():\n", - " for raw_batch, merge_offsets in batch_generator(\n", - " valid_seed_node_idxs,\n", - " batch_size=32,\n", - " padding=padding,\n", - " also_return_merge_offsets=True,\n", - " ):\n", - " yield process_batch(raw_batch, merge_offsets)\n", - "\n", - "\n", - "# A basic optimizer\n", - "opt = optax.chain(\n", - " optax.clip_by_global_norm(1.0),\n", - " optax.adamw(learning_rate=0.0001),\n", - ")\n", - "\n", - "# Train the model\n", - "training_output = dgf.jax.train(\n", - " model=model,\n", - " opt=opt,\n", - " train_step=train_step,\n", - " valid_step=valid_step,\n", - " dataset_iterator=infinite_train_dataset_iterator(),\n", - " valid_dataset_iterator_fn=finite_valid_dataset_iterator,\n", - " num_train_steps=10_000,\n", - " valid_every_n_steps=1000,\n", - " train_log_every_n_steps=100,\n", - " rng_key=jax.random.PRNGKey(42),\n", - " print_logs=True,\n", - ")" - ], - "metadata": { - "id": "KOGYXiyMkwc7", - "executionInfo": { - "status": "ok", - "timestamp": 1779800424922, - "user_tz": -120, - "elapsed": 162727, - "user": { - "displayName": "", - "userId": "" - } - }, - "outputId": "118a0f1e-abd3-46ed-a108-c6f7d9434bc5" - }, - "execution_count": 25, - "outputs": [ - { - "output_type": "stream", - "name": "stdout", - "text": [ - "Generate first batch to initialize model\n", - "Create model variables\n", - "...Tracing model\n", - "Create model variables finished in 9.07 seconds\n", - "Will validate model every 1000 step(s)\n", - "Will checkpoint model every 1000 step(s)\n", - "Start training. The first two steps are generally slow.\n" - ] - }, - { - "output_type": "stream", - "name": "stderr", - "text": [ - "\rTraining: 0%| | 0/10000 [00:00" - ], - "image/png": "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c1B4xygoq9ScjL281o1EgCdIBCbYzGw5AAAAAAAAAAAAAACApvL2km06939zlLm32L/tism9deWUPv71mSt38II3EgGeIBHBCC0AAAAAAAAAAAAAANDEylxu3fn2St3++nKVubyFItHhoXrgklH60znDdM6o7v59P1uzSy43pSONQYAnCBt4zEw5AAAAAAAAAAAAAACAI5GdW6ILHp2rVxZs9W9LT4nV+zdP1Tmjejjro3omqXtilLOcV+LSvIx9vOiNQIAnSITTwAMAAAAAAAAAAAAAQJvi8dh6dk6W7vt8g/JKKtRaVLo9emfpNp3x4HdalVPg337miG567+Zp6t8l3r/NsiydNrybf33mKsZoNQYBniBs4KGOCgAAAAAAAAAAAACAhodp7vlwjc56aLa+Wb/7qLx8z83brLs/WKMHv9yo0x/4TguyctWSSioq9dzczTruX9/otteWq6Cs0tkeHmrpj2cP1UOXjlZcZNhB9zt9eFf/8qerdzkBIDQMAZ4gYf5hqVLOCC0AAAAAAAAAAAAAABrk3WU5enJ2llbm5OuGFxZrWXZes76CpnHn/i82+te355fpksfn6YEvNsrtsXU05RZX6D+fb9DUv32lP7y/Wtv2l/pvM+OxXrthsn48pY/TtlOb0T07qGtClP9Y37dwEKktIsATJCIYoQUAAAAAAAAAAAAAQKMUl1fqbzPX1SjOuO75RdqeVx1kaWomvJNf6qqxzeR2/vPFBv3wifnakd98j10lO7dEf3hvlab87Us98OVG7S+pPp+kmHD97MT++viWYzSmV4dDHickxNKpw6pbeD5eyRithiLAE4QjtCqoogIAAAAAAAAAAAAAtAO2beutxdv0xLeZKnO5G32ch7/ZpN2F5TW27Sks17XPLXLCPU0tY0+RXpy/xb9uxlNN7JvsXzcNNqc98J0+X7NLzWFVTr7+75WlOvafX+u5eVtU5qoeedUjKVp3nzVEc399gm4/aYCSYiLqdczTh3fzL3+6eudRbxFq6wjwBInwwAaeSv4hAAAAAAAAAAAAAAAEN4/H1m/eWaU73liuez9eqzteX+4Eehpq674SPfFdln/98km9FRbiHRW1ZkeBbnttmfNYTemvH69Vpe+YE/om64rJvfXydZN024wB8j208kpcTguQacg5knBSoL1F5br9tWU686HZ+mD5dqfxp8qQ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\n" - }, - "metadata": { - "image/png": { - "width": 1144, - "height": 423 - }, - "needs_background": "light" - } - } - ] + "name": "stdout", + "output_type": "stream", + "text": [ + "step:10000 train-accuracy:0.6750 train-loss:1.0697 valid-accuracy:0.6596 valid-loss:1.0987\n", + "Final metrics: {'step': '10000', 'train-accuracy': '0.6750', 'train-loss': '1.0697', 'valid-accuracy': '0.6560', 'valid-loss': '1.1138'}\n" + ] }, { - "cell_type": "markdown", - "source": [ - "## Generating Predictions\n", - "\n", - "We can now use our model to make predictions:" - ], - "metadata": { - "id": "o67kTlkYg0SJ" - } + "name": "stderr", + "output_type": "stream", + "text": [ + "\n" + ] + } + ], + "source": [ + "class Model(nn.Module):\n", + " schema: dgf.data.GraphSchema\n", + " num_label_classes: int\n", + "\n", + " @nn.compact\n", + " def __call__(\n", + " self, batch: tuple[dgf.data.JaxInMemoryGraph, jnp.ndarray], training: bool\n", + " ):\n", + " print(\"...Tracing model\")\n", + " graph, seed_node_idxs = batch\n", + "\n", + " # Embed features into a single embedding per nodeset.\n", + " embedder_config = dgf.jax.layers.EmbedGraphConfig()\n", + " embedder = embedder_config.make(schema=self.schema)\n", + " embedded_output_schema = embedder_config.output_schema(self.schema)\n", + " graph = embedder(graph, training=training)\n", + "\n", + " # A MLP layer on each nodeset independently. This also ensure that all the\n", + " # nodeset embeddings have the same size.\n", + " for _, nodeset_value in graph.node_sets.items():\n", + " mlp = dgf.jax.layers.ResidualMLPV2Config(\n", + " dims=128, norm=\"layer_norm\", residual=False\n", + " ).make()\n", + " nodeset_value.features[\"embedding\"] = mlp(\n", + " nodeset_value.features[\"embedding\"]\n", + " )\n", + "\n", + " # Message passing between nodes\n", + " for _ in range(2):\n", + " message_passer = dgf.jax.layers.HeterogeneousGraphConvolutionConfig(\n", + " dims=128\n", + " ).make(embedded_output_schema)\n", + " graph = message_passer(graph, training=training)\n", + "\n", + " # Extract embedding of seed nodes\n", + " node_embedding = graph.node_sets[\"nodes\"].features[\"embedding\"][\n", + " seed_node_idxs\n", + " ]\n", + "\n", + " logits = nn.Dense(num_label_classes)(node_embedding)\n", + " return logits\n", + "\n", + "\n", + "# The model received the normalized schema, without the label column.\n", + "model_schema = normalizer.output_schema()\n", + "num_label_classes = (\n", + " model_schema.node_sets[\"nodes\"].features[\"labels\"].num_categorical_values\n", + ")\n", + "del model_schema.node_sets[\"nodes\"].features[\"labels\"]\n", + "\n", + "# Instantiate the model\n", + "model = Model(schema=model_schema, num_label_classes=num_label_classes)\n", + "\n", + "\n", + "# The model loss\n", + "def loss_fn(\n", + " params: jaxtyping.PyTree,\n", + " batch: jaxtyping.PyTree,\n", + " labels: jnp.ndarray,\n", + " rng_key: jnp.ndarray | None,\n", + " training: bool,\n", + ") -> jnp.ndarray | jaxtyping.PyTree:\n", + " if rng_key is not None:\n", + " rngs = {\"dropout\": rng_key}\n", + " else:\n", + " rngs = None\n", + " logits = model.apply(params, batch, training=training, rngs=rngs)\n", + " loss = optax.softmax_cross_entropy_with_integer_labels(logits, labels)\n", + " accuracy = jnp.argmax(logits, axis=-1) == labels\n", + " return jnp.mean(loss), {\"accuracy\": accuracy.mean()}\n", + "\n", + "\n", + "# The model training step\n", + "@jax.jit\n", + "def train_step(params, opt_state, batch, rng_key):\n", + " graph, seed_node_idxs = batch\n", + " labels = graph.node_sets[\"nodes\"].features[\"labels\"][seed_node_idxs]\n", + " (loss, aux_data), grads = jax.value_and_grad(loss_fn, has_aux=True)(\n", + " params, batch, labels, rng_key, True\n", + " )\n", + " updates, opt_state = opt.update(grads, opt_state, params)\n", + " params = optax.apply_updates(params, updates)\n", + " return params, opt_state, {\"loss\": loss, **aux_data}\n", + "\n", + "\n", + "# The model validation step\n", + "@jax.jit\n", + "def valid_step(params, opt_state, batch):\n", + " graph, seed_node_idxs = batch\n", + " labels = graph.node_sets[\"nodes\"].features[\"labels\"][seed_node_idxs]\n", + " loss, aux = loss_fn(params, batch, labels, None, False)\n", + " return {\"loss\": loss, **aux}\n", + "\n", + "\n", + "# Process a batch of data before sending it to the model.\n", + "def process_batch(\n", + " graph: dgf.data.InMemoryGraph,\n", + " merge_offsets: dict[str, np.ndarray],\n", + "):\n", + " normalized_graph = normalizer.normalize_numpy(graph)\n", + " jax_normalized_graph = dgf.convert.graph_to_jax_graph(normalized_graph)\n", + " seed_node_idxs = jnp.asarray(merge_offsets[\"nodes\"])\n", + " return jax_normalized_graph, seed_node_idxs\n", + "\n", + "\n", + "# Generate the training data\n", + "def infinite_train_dataset_iterator():\n", + " while True:\n", + " for raw_batch, merge_offsets in batch_generator(\n", + " train_seed_node_idxs,\n", + " batch_size=32,\n", + " padding=padding,\n", + " also_return_merge_offsets=True,\n", + " ):\n", + " yield process_batch(raw_batch, merge_offsets)\n", + "\n", + "\n", + "# Generate the validation data\n", + "def finite_valid_dataset_iterator():\n", + " for raw_batch, merge_offsets in batch_generator(\n", + " valid_seed_node_idxs,\n", + " batch_size=32,\n", + " padding=padding,\n", + " also_return_merge_offsets=True,\n", + " ):\n", + " yield process_batch(raw_batch, merge_offsets)\n", + "\n", + "\n", + "# A basic optimizer\n", + "opt = optax.chain(\n", + " optax.clip_by_global_norm(1.0),\n", + " optax.adamw(learning_rate=0.0001),\n", + ")\n", + "\n", + "# Train the model\n", + "training_output = dgf.jax.train(\n", + " model=model,\n", + " opt=opt,\n", + " train_step=train_step,\n", + " valid_step=valid_step,\n", + " dataset_iterator=infinite_train_dataset_iterator(),\n", + " valid_dataset_iterator_fn=finite_valid_dataset_iterator,\n", + " num_train_steps=10_000,\n", + " valid_every_n_steps=1000,\n", + " train_log_every_n_steps=100,\n", + " rng_key=jax.random.PRNGKey(42),\n", + " print_logs=True,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "XZNG6EI9fU7Y" + }, + "source": [ + "**Remark:**\n", + "\n", + "- We use several of the FLAX layers defined in the `dgf.jax.layers.*` module\n", + " (embedder, hetero graph conf, mlp). Advanced users are encouraged to try\n", + " other layers and/or implement they own.\n", + "- This example shows a complex setup with feature normalization and\n", + " multiple-nodesets. A model trained on a single nodeset and with only\n", + " embedding features will be much simpler.\n", + "- Layers are implemented as a config dataclass and a flax module e.g.:\n", + "\n", + "```python\n", + "config = dgf.jax.layers.ResidualMLPV2Config()\n", + "module : dgf.jax.layers.ResidualMLPV2 = config.make()\n", + "x = module(x)\n", + "```\n", + "\n", + "- Sometime the `make` method will require a schema." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "dI7yZRRXgt2r" + }, + "source": [ + "The `train` method collects training and validation logs that we can plot:" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "metadata": { + "colab": { + "height": 440 + }, + "execution": { + "iopub.execute_input": "2026-09-23T18:10:45.775278Z", + "iopub.status.busy": "2026-09-23T18:10:45.775070Z", + "iopub.status.idle": "2026-09-23T18:10:46.434880Z", + "shell.execute_reply": "2026-09-23T18:10:46.434351Z" }, + "executionInfo": { + "elapsed": 662, + "status": "ok", + "timestamp": 1790187046436.4983, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 + }, + "id": "lV6XhyxWapT1", + "outputId": "708b441f-5823-474b-b1fc-b5e9485a6970" + }, + "outputs": [ { - "cell_type": "code", - "source": [ - "# Generate some predictions and compare them to the labels.\n", - "@jax.jit\n", - "def predict(jax_merged_samples, seed_node_idxs):\n", - " logits = model.apply(\n", - " variables=training_output.model_params,\n", - " batch=(jax_merged_samples, seed_node_idxs),\n", - " training=False,\n", - " )\n", - " probabilities = nn.softmax(logits)\n", - " predicted_class = jnp.argmax(probabilities, axis=-1)\n", - " return predicted_class\n", - "\n", - "\n", - "for jax_merged_samples, seed_node_idxs in islice(\n", - " finite_valid_dataset_iterator(), 3\n", - "):\n", - " predicted_class = predict(jax_merged_samples, seed_node_idxs)\n", - " print(\"preds:\", predicted_class)\n", - "\n", - " labels = jax_merged_samples.node_sets[\"nodes\"].features[\"labels\"][\n", - " seed_node_idxs\n", - " ]\n", - " print(\"label:\", labels)\n", - " print(\"==============\")" - ], - "metadata": { - "id": "-ofpuaa9ccka", - "executionInfo": { - "status": "ok", - "timestamp": 1779800427198, - "user_tz": -120, - "elapsed": 1378, - "user": { - "displayName": "", - "userId": "" - } - }, - "outputId": "13bad6ab-d989-4435-bd20-ae528741ea49" - }, - "execution_count": 27, - "outputs": [ - { - "output_type": "stream", - "name": "stdout", - "text": [ - "...Tracing model\n", - "preds: [24 30 13 26 37 16 30 16 28 28 16 8 9 2 2 28 30 26 28 16 16 16 24 5\n", - " 16 24 16 28 24 5 16 8]\n", - "label: [24 30 13 3 37 30 6 24 18 19 16 10 28 9 2 8 30 26 28 16 16 16 24 28\n", - " 16 24 16 28 10 5 16 8]\n", - "==============\n", - "preds: [24 16 28 27 28 8 38 16 27 28 25 16 28 26 2 10 24 28 28 4 16 24 30 10\n", - " 20 24 5 10 28 23 16 30]\n", - "label: [16 16 28 27 25 28 38 16 10 28 25 3 28 26 2 10 24 28 28 36 16 10 30 36\n", - " 20 5 5 27 28 23 16 30]\n", - "==============\n", - "preds: [30 24 16 4 28 16 14 19 28 16 16 28 26 8 30 16 30 30 4 16 24 28 16 24\n", - " 27 24 16 24 16 24 34 2]\n", - "label: [30 13 16 4 28 16 9 19 8 16 16 28 26 34 30 24 30 30 3 30 28 28 16 24\n", - " 6 16 16 30 16 19 34 22]\n", - "==============\n" - ] - } + "data": { + "image/png": "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\n", + "text/plain": [ + "
" ] + }, + "metadata": { + "image/png": { + "height": 423, + "width": 1144 + }, + "needs_background": "light" + }, + "output_type": "display_data" } - ] + ], + "source": [ + "# Plot the training logs.\n", + "plt.figure(figsize=(16, 6))\n", + "\n", + "\n", + "def extract_steps(logs):\n", + " return [log.step for log in logs]\n", + "\n", + "\n", + "def extract_metric(metric, logs):\n", + " return [log.metrics[metric] for log in logs]\n", + "\n", + "\n", + "metrics = [\"loss\", \"accuracy\"]\n", + "for metrix_idx, metric in enumerate(metrics):\n", + " plt.subplot(1, len(metrics), metrix_idx + 1)\n", + " plt.plot(\n", + " extract_steps(training_output.train_logs),\n", + " extract_metric(metric, training_output.train_logs),\n", + " label=\"train\",\n", + " )\n", + " plt.plot(\n", + " extract_steps(training_output.valid_logs),\n", + " extract_metric(metric, training_output.valid_logs),\n", + " label=\"valid\",\n", + " )\n", + " plt.xlabel(\"step\")\n", + " plt.ylabel(metric)\n", + "\n", + "\n", + "plt.tight_layout()\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "o67kTlkYg0SJ" + }, + "source": [ + "## Generating Predictions\n", + "\n", + "We can now use our model to make predictions:" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-23T18:10:46.437595Z", + "iopub.status.busy": "2026-09-23T18:10:46.437388Z", + "iopub.status.idle": "2026-09-23T18:10:47.677609Z", + "shell.execute_reply": "2026-09-23T18:10:47.677039Z" + }, + "executionInfo": { + "elapsed": 1242, + "status": "ok", + "timestamp": 1790187047678.598, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 + }, + "id": "-ofpuaa9ccka", + "outputId": "2e623eeb-585c-414c-ed0d-c4795ee5dd8f" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "...Tracing model\n", + "preds: [24 30 13 26 37 16 30 16 28 28 16 8 9 2 2 28 30 26 28 16 16 16 24 5\n", + " 16 24 30 28 24 5 16 8]\n", + "label: [24 30 13 3 37 30 6 24 18 19 16 10 28 9 2 8 30 26 28 16 16 16 24 28\n", + " 16 24 16 28 10 5 16 8]\n", + "==============\n", + "preds: [24 16 28 27 28 8 38 16 27 28 25 16 28 26 2 10 24 28 28 4 16 24 30 10\n", + " 20 24 5 10 28 23 16 30]\n", + "label: [16 16 28 27 25 28 38 16 10 28 25 3 28 26 2 10 24 28 28 36 16 10 30 36\n", + " 20 5 5 27 28 23 16 30]\n", + "==============\n", + "preds: [30 24 16 4 28 16 14 19 8 16 16 28 26 8 30 16 30 30 4 16 26 28 16 24\n", + " 10 24 16 24 16 24 34 2]\n", + "label: [30 13 16 4 28 16 9 19 8 16 16 28 26 34 30 24 30 30 3 30 28 28 16 24\n", + " 6 16 16 30 16 19 34 22]\n", + "==============\n" + ] + } + ], + "source": [ + "# Generate some predictions and compare them to the labels.\n", + "@jax.jit\n", + "def predict(jax_merged_samples, seed_node_idxs):\n", + " logits = model.apply(\n", + " variables=training_output.model_params,\n", + " batch=(jax_merged_samples, seed_node_idxs),\n", + " training=False,\n", + " )\n", + " probabilities = nn.softmax(logits)\n", + " predicted_class = jnp.argmax(probabilities, axis=-1)\n", + " return predicted_class\n", + "\n", + "\n", + "for jax_merged_samples, seed_node_idxs in islice(\n", + " finite_valid_dataset_iterator(), 3\n", + "):\n", + " predicted_class = predict(jax_merged_samples, seed_node_idxs)\n", + " print(\"preds:\", predicted_class)\n", + "\n", + " labels = jax_merged_samples.node_sets[\"nodes\"].features[\"labels\"][\n", + " seed_node_idxs\n", + " ]\n", + " print(\"label:\", labels)\n", + " print(\"==============\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "mHBVIh63iGwd" + }, + "source": [ + "" + ] + } + ], + "metadata": { + "colab": { + "last_runtime": { + "build_target": "", + "kind": "local" + }, + "provenance": [ + { + "file_id": "1gCGQGADi7XgLfBUxVhY9lStsRb-ix1MR", + "timestamp": 1772703029637 + } + ], + "views": { + "output_only": { + "cells": [ + { + "id": "4QgiF5D7XIoY" + }, + { + "id": "xuLmBxj0Czyl" + }, + { + "id": "AoYoqfcmio5m" + }, + { + "id": "vZnTGFe5Hqe3" + }, + { + "id": "at9KPOnoafKs" + }, + { + "id": "u4bluL_XhtzR" + }, + { + "id": "oEaYcGfaahbW" + }, + { + "id": "cGefsacZif5_" + }, + { + "id": "OwB5TY8BcW2h" + }, + { + "id": "9NRnROoHcYup" + }, + { + "id": "mgTO5T26DRKU" + }, + { + "id": "6WJ2I8_pc22o" + }, + { + "id": "27gzPvMLeam-" + }, + { + "id": "nBAj2O-TDsww" + }, + { + "id": "A8cimhfqDxFH" + }, + { + "id": "VYelwcaofUbL" + }, + { + "id": "C7teiqzofVow" + }, + { + "id": "p5bcFindgyV7" + }, + { + "id": "Ni9JJ2Luo70y" + }, + { + "id": "2IsWBT-Qg-pj" + }, + { + "id": "r_ca-ojxi7PM" + }, + { + "id": "W5PFWST8jJpa" + }, + { + "id": "7RHtpI7-jfH-" + }, + { + "id": "IWxlbK_wjeLk" + }, + { + "id": "YvE-3H1MjpIP" + }, + { + "id": "nvyfFD5Vj5Wn" + }, + { + "id": "yZh_oTpEk9g7" + }, + { + "id": "40upqIN5lEPf" + }, + { + "id": "Ib9_Nz-plH6N" + }, + { + "id": "z8aZKvxFlRJ9" + }, + { + "id": "I4mUMmcXlQjO" + }, + { + "id": "06wgX3hNkHwc" + }, + { + "id": "rRCO4m2WkLIT" + }, + { + "id": "Ipraa9jYkWfR" + }, + { + "id": "bh1-MIFo709W" + }, + { + "id": "O19qUi6Lk2hS" + }, + { + "id": "XChnfUL5tjRU" + }, + { + "id": "kamAFS51mZXy" + }, + { + "id": "ndbwi_ZUmjfQ" + }, + { + "id": "paI3RaXOkHGk" + }, + { + "id": "dLq8zeAumvwv" + }, + { + "id": "b4iIIEhKcGdz" + }, + { + "id": "7RnCFqXjm_6v" + }, + { + "id": "VhSmaRvXnsdG" + }, + { + "id": "6i3k1YjMoLRw" + }, + { + "id": "P1SdL8EEHKcZ" + }, + { + "id": "E1bU8xwfHVHr" + }, + { + "id": "NsnAdalHHRD3" + }, + { + "id": "P-EbWtnIH05W" + }, + { + "id": "vG618kYeHzQQ" + }, + { + "id": "_HtDHI4CINXZ" + }, + { + "id": "pOSxkBgmIdvb" + }, + { + "id": "bDjt0X2VIj9L" + }, + { + "id": "bl5FYdFoWiAd" + }, + { + "id": "oG2rH38WJ3ho" + }, + { + "id": "KE1X1ZBAInGe" + }, + { + "id": "d32_5m25HJ_A" + }, + { + "id": "ACLtqDLWK7NO" + }, + { + "id": "WU3ap6RRLEpr" + }, + { + "id": "KOGYXiyMkwc7" + }, + { + "id": "XZNG6EI9fU7Y" + }, + { + "id": "dI7yZRRXgt2r" + }, + { + "id": "lV6XhyxWapT1" + }, + { + "id": "o67kTlkYg0SJ" + }, + { + "id": "-ofpuaa9ccka" + }, + { + "id": "mHBVIh63iGwd" + } + ], + "hide_code": true + } + } + }, + "kernelspec": { + "display_name": "Python 3", + "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.14.5" + } + }, + "nbformat": 4, + "nbformat_minor": 0 } diff --git a/doc/docs/tutorial/getting_started_simple_api.ipynb b/doc/docs/tutorial/getting_started_simple_api.ipynb index 58a2500..2a13712 100644 --- a/doc/docs/tutorial/getting_started_simple_api.ipynb +++ b/doc/docs/tutorial/getting_started_simple_api.ipynb @@ -1,1086 +1,1982 @@ { - "nbformat": 4, - "nbformat_minor": 0, - "metadata": { - "colab": { - "provenance": [ - { - "file_id": "1hMr9gK0hhKcCtIs1Pro3_ksOnYcdAL2P", - "timestamp": 1771583153488 - } - ], - "last_runtime": { - "build_target": "", - "kind": "local" - } + "cells": [ + { + "cell_type": "markdown", + "id": "woDLT7o2leXI", + "metadata": {}, + "source": [ + "## 🧭 Getting Started\n", + "\n", + "[![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/google/distributed_graph_flow/blob/main/doc/docs/tutorial/getting_started_simple_api.ipynb)\n", + "\n", + "**Distributed Graph Flow** (DGF or GF) is a python toolbox for training,\n", + "evaluating, and deploying Graph Neural Networks (GNNs) models and other\n", + "relational data ML models. GF contains two APIs:\n", + "\n", + "- **Advanced API:** A library of low-level, modular, framework agnostic functions designed for GNN experts and ML engineers. It is great\n", + " for those who like freedom, need custom pipelines, or simply want to augment\n", + " existing custom pipelines.\n", + "\n", + "- **Simple API (this tutorial):** Built on top of the Advanced API, it\n", + " allows you to train and deploy a GNN on any data in 10 lines of code.\n", + "\n", + "**This document shows the Simple API**. You'll learn how\n", + "to train, analyse, evaluate, and productionze a node prediction GNN model.\n", + "\n", + "The Advanced API guide is available [here](/tutorial/getting_started_advanced_api.ipynb). We recommend starting with the Simple API even if you are an expert; you'll learn about GF general basic concepts, and see how the Simple API can be customized using Advanced components.\n", + "\n", + "**Remember:** See the [API page](https://dgf.readthedocs.io/en/latest/api.html) for all the available methods. And in Colab, use the `?` operator for method details, e.g., `?dgf.io.fetch_ogb_graph`." + ] + }, + { + "cell_type": "markdown", + "id": "e_tVcMnEC7iO", + "metadata": {}, + "source": [ + "## A word about GNN task selection\n", + "\n", + "Graph Neural Networks (GNNs) are **versatile**, but their application **must\n", + "match the underlying problem**. To guide this selection, the GF high-level API\n", + "defines **three distinct tasks**. Given a training graph, you can train a GNN\n", + "for:" + ] + }, + { + "cell_type": "markdown", + "id": "GrVvb1oCLi5O", + "metadata": {}, + "source": [ + "![tasks.png](data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAyAAAAGkCAYAAADJzkGPAAAACXBIWXMAABEUAAARFAGAwd9BAAAAGXRFWHRTb2Z0d2FyZQB3d3cuaW5rc2NhcGUub3Jnm+48GgAAIABJREFUeJzs3XV01fUfx/HnzXX3xpLB6O6QkO4OFUTpkpAGBZEGkVAQSQvp7u7BqFGDAQvYWHff3fr9MZzuRziQMdg+j3N+5/y49xvvzz1zu6/vpyR6vZ530GxgSmEXIQhv0YfAicIuQhAEQRAEoaBJC7sAQRAEQRAEQRCKDxFABEEQBEEQBEF4a0QAEQRBEARBEAThrREBRBAEQRAEQRCEt0Ze2AUIgiDkh0ajqa/X638u7DoE4S1aqlAoxM+8IAhFjgggxZAePaHRQQQ+CSApPQGFXEkJGzcquFfBxMC0sMsThOeSy+XmQLnCrkMQ3iK7wi5AEAShIIgAUoxka1RsPfc7m87+wuOYkGfeVyoMaFyxOQNajMTHRXzPE4TCoFJnceuRP5EJ4chkcko6lqa0cxkkEjFiVhAEQSgaRAApJu5H3GXi+uEERz2gsld1PmkygDIlyvP97jm0rtYRZ5sSnAs4yT6/HRz3P8hnLYYxvPWXSKWywi5dEIoFlTqLNUd+YNOZDSjkSko5l0GtyeZBxD2sTG0Y2mYsrat3LOwyBUEQBOE/EwGkGLgZeo2hKz7BUGnEssHraVShGdmabCZsGEbnOj3pWLsHAA3LNaV+2cZM+200aw4t50lcGHP6LBEhRBAKWEJaHMNW9EWjU1PCzp0lA9bgYOlEhiqdedu/ppJHNRbvmo3fgwt81XMuUtEbIgiCILzHRAAp4mKSoxm9uj+WJtas+WIzTlYuqNQqJv4ynCYVW+SGD8gJKqsPL2f7lOPs9P2TH/ctwtXOneFtxhViCwTh3ZOelUbgkwCSM5KwMrGmtEtZjA1MXutaWp2WsWsGYWthj7mROWM6TsXOwoH0rDTm75jO0NZjcbJyoUG5JnSe1RR7C0eGth7zhlskCIIgCG+PCCBF3MIdM0jLTOPnEZtwsnIhW5PNxF+G06hCszzhIzz+MbO3TGXpwLXYmdszqOUoQqODWHvkR5pXaUtp57KvdN/M7EyO3TiA793TBEU9IC0zFUMDIzzsvKhVuj4tq7XH0sTqTTdXEApUZOITfti3gKPX92Njboe1qS3xqbEkpSXQqkYnhrf5EjsLh1e65l6/bTyKDaFmqbqM7TQNW3N70rJSWbB9BsPajsPR0gmNVsP6YyuZ2nMOszZPpkPtbrhYuxZQKwVBEAShYIl+/CIsKOo+x64f4JOmA/B28skddtW4Ygs61+mVe1xY3CMmbhjOnL5LcbRyzn3d3d4LnVbL2iM/5PueOr2OP8+sp9X02kz7dTT7L+9Ep9NS0aMqzlYluPP4JnO2TKXl17VZvm8BKrXqjbZZEAqK34ML9JjfCr/7FxjQciQHZ/jyx7i9TOs1j16NPkOtyab7vJbcCLn6Stfdev537Mztmdh1Jrbm9qSr0pi/fToj2k3A0dIJrU7L/B3T6Vq3N+1qdqFRhWbsvrilgFopCIIgCAVP9IAUYYeu7kEqldKr4ad5wkenf/R8hMU9YtIvI/jmo+8o6Vg69/VfTqwiPP4xXer3Zv/lnWSo0v91iElmdgbj1w/j7O3j1CxdD3d7TyITnrB4wGoMFYYAZKmz6L+sB+ZG5qw98gPnA06xfMh67Mxf7amxILxNwVEPGLt6IB3qdEcqkTKo5SgAAp8EcPbOcSZ3+xaAFQe+Y+Sqfvw54UC+eihUGhV3w26zbdIRbMxs0Wg1LN0zj34fDsHewgGtTst3u76lU+0elH66Mp25sSX+wVcKrrGCIAiCUMBED0gRdi3Ij1IuZbE0sf7X8OHtlDd8PIwIZEbvhXxQoRlZ2ZkEhN166b00Wg2jVw/kQsApJnSbQbf6HxOVGJknfKjUWXy5djCfNO7PymG/s3jAz4TGBDFoeW9SM5ML5kMQhDdg3rav8XEtD8CYjlMBuBd+mx2+fzK+83QAHkYGEp8aS7Mqbfhu57f5um5KRhIyqQxXO3c0WjXzt0+na72PKOlYGq1Oy6KdM2lTvRPl3SoDsPbojyDJmbQuCIIgCO8rEUCKsCcJYThbl2DihuGvHj4+WohMKsPDvmTOteLDXnqv1UeWc/HeGab0nI2NmT17/bbxXf9VecPHusG0q9kldynRppVaMaHrDIKjHjBr85Q33XxBeCNCY4Lwu38eD3svxnX+GplUxp2wm+y6tIUJXWYgl8m5H3GXzWd/ZVK3bxnWdhzn7pwkOinyX69tYmiGRqshKT2Redun07NhX3xcyqHRaViwYwYdanengnsVAH4+vJSSTqWp5lULMyOLgm62IAiCIBQYEUCKsGxNNmduH6dRhWZ5wsfj2NDnho+Np9fzIOJebvgAUMoVOdfSZr/wPtFJkWw4toIW1dphbGDCXr+tecKHVqfly3VDaFOjc559DIKjH7Lt/B90qtuTQ1f34B8ihpUI755rQX5U8arB1B5zkEqk3Hp0nX1+2xnfeToyqYzAJwFsO/87E7vOQCFToNGoMVQacT348r9e21hpjKeDN5M2jKB3w354O/mg1qpZsH0Gnev2omyJCuj1elYeXEzZEhVpXKE5lx9coIJ75bfQckEQBEEoGCKAFGHWpjZU8apBpzo9c18Lj3/M5F9HMqP3wjzh49iNA5y9c5yves3LDR8qjYo5W6blXutFdvj+SbYmm1LOZdnnt51Fn/+EocIIyJmUPmvLFKqVrEWb6p1yzwmNCWbab6OZ3WcpE7t+g7mxBRtPr3+j7ReENyE2JQZPB2+kEin3I+6y+9JWxj/tCQmOfsCfZzYwses3yGUKwuIeseLAd5R3r0x8Smy+rt+xTnfUWjWejt5odVoW75pFh9rdKeNSHp1ex5I9c6nqVZOG5ZsSkxzFoat7aF+rWwG3WhAEQRAKjgggRZiPSzkeRtxDo1UDOeFj4obhfPPRIko5l8k97tiNA2w7v5HFA37GQG4AQLZGxYR1Q0lMT8i91oucvn0MhUyBf8gVFg/4GSOlMZATPr7dPBln6xJ83mxY7vGhMcFM+fUL5vRdhqdDSYwNTGhauRXn7pxAp9e98c9BEP4LUwNTUjNTuBd+m23n/2BS15lIpTIeRgay8fQ6pnSfhUwqJzQmmHVHf2Rqj9nodFqMDU3zdf2eDfuSkpHM8n0LWLRzJu1qdqGCW2V0eh3f755N44rNqePTkMzsDCZuGEHr6h1feVlsQRAEQXiXiABShDWq2JzEtATO3T2Zu9TuNx8twtvJJ/eYo/4H2H5+I9//Izio1CrGrxtKsypt0Go1eDuXeeGKPhqthqDI+5R3r8Li/j/n6fn4dtMkXGxcGdhiZO7xoTFBTPn1C+b2XYaHvVfu639tvBYe/7ggPgpBeG1l3Spy5YEvOy9uZmLXnDkfDyLvsensL0zs+g1KuZKQ6CB+PbGKSd1mgkRCwOOblHOtkK/rGyqMWDJwDfv9dhAaE4yDpRM6vY7Fu2bRrHIbqnrVJCDsJp8t7YZSrmRC1xkF22BBEARBKGBiGd4irHGF5jjblGDZnvkYGRg/N3zsuLAxT6+FSq1iwvqhNKvSFqVCyd2w23zVax4SieS590jJTEatUdOkYovcOR86vY5ZmyfjYuvGgOYjco/N6fkYxdy+y3D/R/jYf2Un+y/vACAhNQ43W483/VEIwmur7FEdUyMzKrpXRSaVcy/8Ntt9/2RS15nIZXKCou6z8fQ6JnX7FqVcyZZzv+Fk7UJp5xf3Gv4/D/uSbBy/n77fd6LNjHq42LphaWJNelYac7dO43FsKJ9+OJjPmw9HIVMUYGsFQRAEoeCJAFKEKeVKxnScyoR1w+hUtycl/zHn49/CR2XPavRZ3JEyruXpWKfHi26BQq5AIpGQrcnZUPCv8OFkXeKZ8DH1BeHj4NXdjGw3kcm/jEQpV77pj0EQ/hOZVMbojlOYtWkK5sYWnLx5mKk95iCXyQmLe8SG46v4uudcFHIlDyLusXzvfBZ8tuKFof1FUjKTSUiNY/UXW3gYGUhkfDhymZy6ZT+gXplGmBqaFVALBUEQBOHtEgGkiGtRtR1XG11i0+kNGBmYMLbTVDaf/YXzAafyhI9sjYqxawfSsmp7vJxKMWB5L9DD/H4/vvSJq5mhORYmloTGBKNSZzFhw3AquFfJM+wqJCaIieuHs+CzFXnCx7pjK7ge5Md3n//E76fWAuCcj83bBOFta1a5DQ8i7jHq5/7M67ccmVTGo9gQNhxbydTus5DLFBy6toe5W6cxoOVI6pb54JXvsebwMjrW6UFlj2pU9qhWAK0QBEEQhHeDCCDFwMQu3wCw8dQ6Tt06gqWJFetGbcNImTNfI1ujYvz6YVRwr8q9JwHM3DQRa1NbVo34M3cfkJep6lWTi/fOMnbtYNrU6ETbGp1z3wuNCWbar6NZ8NmPeeZ8rD6ynIiEcJYMXItMKuNcwEk8Hb2xNLF6w60XijuVOotTt4/iH3yFhNQ4zIwsqOBemaaVWmFunP/9NIa2Hou7vRcLt3/DygOLUamzaFmtA4t2fculwHOoNdl83XsBH1Zq9co1hsYEceLmYXZNO/XK5wqCIAjC+0YEkGJAKpUyudu31ChVl+93ziLg8U2af1WTsq4VsDW351FMEPfCAzh96yhSiZS2NbswttNUrF6y9O4/tanRiRM3DtGuVtdnwsfUX0cxu+/S54aPr3rORSqRcjf8FteD/BjW9ss33naheDviv5+F22dgYWxJvbKNKO9emd0XtxAQdpNFO2YypM0YPm7UP9/DpdpU78SHlVpz/u4pboVeIyUjGQtTK9zsPPFyLPVa4QNgzZEfaFuzM46WTq91viAIgiC8TyR6vb6wa3ie2YDYGrsAaLRqzgac5PTtY9x/EkBSWgIqjYrk9ET6NB1It3of42Lj9krX1Oq09JzfipTMFLZMPISliRWhMUFM/XU0c/ouzTPs6ufDy4hKjGBaz5xN3dRaNZ8t6crj2FD2fX0Gc2PLN93k98WHwInCLuId1xo4kN+DfzmximV75jGt11w61e6JRCLhxM3DRCaG83Gj/viHXGHC+uE0KNeEr3rOfeU5G/905eFFJm0YwaFvLiKXvdpznbC4R3Sf14Ltk4/hYiOGIAp5TCPn76EgCEKRIpbhLWbkMgVNKrZgRu8FbBy3jwMzLjCn71LUGjU1S9V/5fABOZN0p3+0gITUOMasGcidsJvPDR+rjyzPEz60Og3TN47jdqg/k3t8W5zDh/CGXX5wgeV7F/Btn+/pXKcXEomEYzcOEBLzkI8b9QcgNSOF1jU64nvvDFvP//6f7letZC0UciUXA8++8rlrj/5A6+odRfgQBEEQig0RQAS8HEshlUh5GBn42teo6F6Vbz5exI3gK3y8sB0fNxnwTPiITAjPDR/h8Y8ZsuIT9vvtYEjbMbSu1vFNNEUQAFiyZy4j20+gTfVOABzx30d4/GP6NxsOwKlbR7kbfosxHabwVa+5rNj/HSp11mvfTyqR0qZGJ/Zd3v5K50UkhHPo6h4+bz78te8tCIIgCO8bMQdEwNbMHktTK4L+QwABaFujMxbGlny5djBTf/2CQ1d306JqOzJU6fjeO8PXveZx4uYhTt48wpHr+5BJZXz90QK61u39hloiCPA4LpSgyPv0GNkXgIPXdhOXHEO/pkMAOH7zEKHRQQxqOQoAMyNzMlRp+AaepXGF5q9933Y1u9B7YVvSs9Iwyecu6OuOraBZlTa42rq/9n0FQRAE4X0jAoiARCLBy7H0f+oB+YtUKiVbk03tMg25/eg6p28dzX2v47eNATA1MqND7e70bz4cZ+sS//megvBPgeF3KO1SFiOlEdsvbESlzqJPk4EAnL97msAnAQxrPRaAKw99OXnrCK2qd+Re+J3/FEA8Hbwp6eTDsRsH6Fj7xXvn/CUqKYJ9ftvZNOHga99TEARBEN5HIoAIAHg7+7DPbzsareaVJ9H+RaPV8N3Ob3GydmbpgDXIZQruPblDcOR9UjNTMTIwxsPei3KulTBQGLzhFghCjkxVBiYGpmw59xtKuZKPGn0OwNk7J7j92D83fFy6f55L988xrtPXLNo1k0xV+n++d/uaXdjrt52OtXuQlpWKVCLF2MAk932VOgsDhSEA64+tpHHFFnlWiBMEQRCE4kAEEAEAbycf0jJTiUqKoMRrTEQH2HbhDx5GBLKw/0+5X7LKu1aivGulN1mqILyUnYUD/sFXaFOjE+1rdQPg1O2jPIi4y9Cn4ePCvdNcC/JjZLsJSJAQmfCE6iVr/+d7t6zegcW7ZhOZ+IT1x1YwocuMPO+HxgSTlJ6Ip4M3ey5u5fdxe/7zPd9VwVEPuBp0ibiUWEwMTCjrWoGqJWshl77anx09eu4/ucv14MskpsZjZmxOebfKVPasjlQipjEKb8THQNXCLkIQ3qJFQFRhFiACiADkBBCAh5GBrxVAUjKSWXlgMdVL1aF55TZvujxByLeKHlXR6XVU8szZTfzErcMERz1gYIsvADgXcJJbj64zvO04JEgIfBLAxXtn+aBCs9e+p1an5diNA5R08qFOmYbM3TqNe+F3mNI97wqqEomElQe+o7xbZRqUb0pJx9Kv39B3VFDUfeZt/Qq/+xeoVboebnaehGSmsObID5gYmjK6w2RaVmufr2vdeXyDedu+JiTqIaVcyhKbHI1PiXKsP7oSUyMzxnX+ioblPyzgFgnFQFtATEYUipNfKOQAIh4fCUDO+HWpVPba80BWHV5CSkYS4zp//Z/2UxCE/8rU0Ix2tbqwfO8C9vht43FMCAOajwBylue9GnSJoa3HIEHCjZCrDFzeE1sLe5bsnkPvhW3Zf2Unam32K91TJpVR2bMG49cN4eK9M5y+dQyH52wqKJVIuR50mU1nNmBpYsmcrVO5Fuz3Rtr9LrgYeJa+izsTlRzJ4NajWT1yE1/1msdHjT+nQ53ujOk4hQXbp7N0z9x/vdZR/wMMXN6LJpVasmXSYZytXVg/ejvffb6KPV+fwcrUmqm/jWHD8Z/eQssEQRCEN0n0gAgAWJpYYWtu91orYYXGBLH5zK90qN2dcq4VC6A6QXg1I9tNpOeC1mRmZ/DD4F8AuBh4Dr8H5/mi3URAwvXgy4xbPxSNVsPi/j9TwsaN/Vd2svbIDyzZPZeeH/SlW72PsTSxytc9HS2dWPvFVoat+IS7YbdxtHJ+5pi/hgxptBrO3jnJ/M9+oLJH9TfW7sL0JP4x49cNpUap2tQsVY9PGg8A4FqQHydvHWFsx6k57ZfAlF++wMOh5Asn69+PuMv0P8axqP9P+LiUY8meuYzvMgNLEyuy1Jks3DGDmR8vJluj4vOl3fFwKPmfFhAQhPzSZSejyYxFaeFd2KUIwntN9IAIuUo6leZhxKsHkMW7ZqGUKxnRbnwBVCUIr87SxIqVw34nJOohw3/qy9bzv3PloS8j205AIpHgG3iG4Sv7olarWDJoLd5OPhgqjeha7yO2Tz7G9I/mc+WBL62n12X2lqmExAQ9c4/kjMRnXrM2teHnkZuo7FUdR8sXB5AG5ZuwacL+IhM+AJbvW4BUKqN26Qa54ePKw4ucuXMsN3zcDL3GjeCrLB7wM0t2zyErO/O51/p+12w+avwZpZ3LsmT3XMZ1/hpLEysyszOYt+1r+jcfgZudB652HpR3r8SiHTPR6jRvs7lCMaSK8yfq5FBizo4l9cEW0OsLuyRBeG+JHhAhl7eTD9eDLpOtyUYpV6LVaVFr1Rg+nVD+PL73znDm1nGGtx+PnbnDW6xWEF7Oy8GbzRMPse7YCn7cvwiNRs3R6/tJTk8iW6OiRbX2DG095pmhUhKJhAZlm9CgbBOCou7z+8k1fLSgDdW96/BJ04HULlUfiUTC2YCTuFi7UtWrZp7zzY0sWDl4PTevriHh2iK0mbEgVaC08ERn4M6I9uPp33xEkZpAnZaVyvEbhxjTcSofNfoMeLrKWOA5RrWfjESS0+N04uZhvuz8FRKJBFtze07fOUbLqnnng0QlRXLl4UXGdJrK0r3zmNB1xtO9WtKZt306g1uOwsXGlSx1JvO3TWdClxkM/vFjrjy8RO3S9Quj+UJRp9eSfO830kL2YFG2HwozVxJv/EBWnD/W1cYjM8hfL6kgCH8rOn8Bhdei/8cTHG8nH7KyMwmPf4Rer2fZvvnPfEmKS43J/f8arYZFO2fibFMid58FQXiXmBmZM6r9JI59e4UNY3Yyteccfhr+OyfnXGdG7wXPnafxTyUdSzO99wL2TT9PRY+qTPnlC3ouaM3uS1vwdvLhy7WDiUgIz3NOVsxVUs6NxFMThNzYAVVCAFKFMVpVMgb3fqSLVTJo/vuSv++SwCcB2Fs45IYP/5CrnAs4mbPKmERCePwj9vhtY0zHKUglUqITI4lPjeV2qP8z17rz6AYlbN1Yd3QFE7t8g5mROSqNirnbvmZo6zG42Lii1+tZsP0b+jUbiqeDNzW8a3P70bPXeh6dTodKrcrzu08QXkSTEU3MuXFkRfthV/dbjJzqIjctgW2dmcgMLIk+OZSsmCuFXaYgvHdED0gxF5UUwV6/7fRo8MnfK2FFBLLTdxPRSZEo5co8x+/1206HWt2xMbNlu+9GHkYEsuDzlS/tJRGEwiaXyfF2Kg283qpTNma2DG41ms+aDeXg1d38cWodMclRJKUlMHp1fzaM3omxgTEZT86Q6P89lpWGY+xUH4ncEEP7GsjN3ZBI5FiW+5zEmz8Qc3Ys9g2+Q6o0f7MNLSQpGclYmdoAPO3pyOkNkUgkPI4NZd2xH5nUdSZSiZTIhHBWHvqejrV7EJ8a95xrJZGakcLIdhMwNTLL7ekY0GIETlYu6PQ6luyZS7uaXXC380SPngxVBikZSS+sL1ujYvuFP9l/eQeBTwLIVqswMzanqlctejbsS4NyTQrssxHeXxkRZ0j0X4ZxicaYl+oGElnuexKZEotyn2FgU5H4K3MxcW+FRdl+SKSKQqxYEN4fogekmHOycsHOwp523zTk4LXdSCVSlu2dz6/Hf6bRcyZ1JqTGs/3CH6RkJrNy/2KqedemRZW2hVC5ILx9SrkBHWv34Lv+q7A2s0Wv1xMYHsC030eTnRZOov9ibGpOw8S1GdGnR5L+6DBK67JI5SbEnB1LxpPT2NSYgsLMnQT/JYXdnDfGytSa2JQYrjy8yOnbRxnzdM5HaEwQ64+vZHK3bzFUGhEe/5ifDi1hUrdvSVelY/00tOS5lpkN1ma2uNi4kpmdybytX/N58+G423mi02n5fvdsmlZqSbWStdDrdSzbM4+HkYG5Aej/hcQE0XN+a/b5bad3o34MbPUF47pO55cxO6nuXYtpv43my3WDyczOKOiPSXhP6LVZJPh/T9LNH7GqPBzz0j3zhI9/MnSogV292ajibxFzZgya9CdvuVpBeD+JACLQqU5PGpRvyu8n1qDT6wmLDUUuU1C/bONnjk3LSmXrud/5cf8ikjMSxbK7QrFz89F1NhxfSQ3v2nzWfBjD2n5JNa9aBF9egrFLYwztc/Yfsak5BSOXD4CcoT7W1cZj7PohSCRYVf4CVaw/2Ymvt+z1u8bHpRzJ6Ykcv3GQUe0nIZVICYl+yK8nVjOp60wMFIaExT1izZHlTOr2LUYKIy4FnqOy17OT8Cu6VyUk+iGPYkOYt+0rBrb8Aldbd3R6HYt2fUuzKm2p4lkjpydk7zxq+zQgPSuNyp7PXisqKYJBy3vxYZXW/DZ2N1qdDnMjC/o0HkBJx9KUc6tMh9rdSc1IYezawej0urfxcQnvuAT/paiTg7GrNwcDmwr/erzM0AbbmlNRWpUi+sxo0GvfQpWC8H4TQ7AEJEiY1nMOtx/5ExYbCkBlr+rPXX40PTOVmKQoNp/5hUqe1XgUE4zvvdMo5Eq6N+iDsdL4LVcvCG9XJfeqVHLPu2myXq8h4kAPTKsNJeJIH6wrf4GhQy0AIo99jkW5fhg7fwBA3KXpKK3KYOzyARkRZ1Ba+bz1NrxpRkpj2tToREJaPBKJlICwW2y/8AeTus1EKVcSlRjBqkNLmNZzLoYKQw5d20NWdiYNyjV95lo2ZrZ8UP5Dhq74hLUjN+NkXQK1Vs387dPpXKcn5d0qA3q+3z2bJhVbcj8iABtzu+cGkFmbp9CwfFNGtB3PvsvbSc9KpfcHOfNUTt8+xq1H1xnTaSpZ2Zn0XtiWLed+o1fDTwv64xLecQbW5dCpEl5tiKREiszYAQMrnxf2lgiC8DfRAyIAOZu3zf10OYqncz4avWBX6HRVGpAzef1G8FWm/TaaxPREejToK8KHUGxpM2LR6zUozD2xrvolyn88NbWuMgpDu78Di7nPJ5i4fojSqgzq5JDCKLdADG87jiv3fVm48xv2+m1jSvfZKOVKIhLCWXHwOyZ3n4WhwpB74XeYs2Uak7rPRCF7/nj5MZ2mkpqRwtUgP7I1KuZt+5reH/SjvFtl9Ho9y/ctpEG5JujRs3zvAiZ3//aZBTMeRgZy7eElRnWYzMGru0lMT8wNH8dvHORhZCAj2o5HgoSgyPs4W5fgl+M/odOJp9fFnZFTPVTxAeg0rzYsLyvKDyPnhgVUlSAULSKACLkquldhZPsJSJA8d/4HQGpmau7/d3fwYs2oLYzv/DVGSqO3VaYgvHP0umz02myykx9iaFcVqdyYmHPjyYg4g4FtZaQKM2J9p5IedgylZSlkRvZkJ95Fr3u1HdffZTZmdiwbvI79l3eSnpVGbEo0T+LDWHVoCZO6zkQmkbLx9DoGLOvBoFZf0KRiyxdeq4SNG98NWMX8bV/z8aL2tKzWnpKOpZ+GjwWUd6/MrVB/Rv7Uj4ndvqF6ydrPXONcwEnqlW3EuYCTJKTF0+fp3iRH/fcTFveI/s2HA3Az9BoHr+5mycC1pGak8DDqfsF8QMJ7Q2Zog9LSG1Xs9Xyfo1OnokoMxNCxbgFWJghFR7EagqXVabkWdInLDy4SFhdKVnYmFiaWeDv5UL9sYzwdxM6mfZoMJC4lGldLG7RZsUiy0/gQAAAgAElEQVQVZkhkBkDOPI+MrDRkUhm9G3/OyLbjMRTBQxCQGdoAEqRKs9zXzLy75Nkt2cyrI3Izt9x/SxQmyI3s32aZBa68W2U2TzjEkt2zaT/zAxQyBRXcqzD8p0+5F3YbBysnjAyM6VSn579eq1apemwcv5/vdn3L8JV98Xb2QSlTEhoTxB+n1lLevTI/j/iTCu5Vnnv+k/jHPIoNITUzhY8bfQ7AoWt7iEmOot+HQwDwD77C8ZsH+bLzV0glUtzsPXkS/5jSzmXf3IcivJeMnBuSGXUZI6f87S2TFX2V+Cwrjm/cgYODA46Ojjg6OmJnZ4dcXqy+aglCvhSL/yr0eh17L29n1cElhMc9RiaTY6Q0QqfTolQYsst3M4uYSc3S9RjdYfLTMcbFjF5LRuQFMsKO0Ut5h4gD3XNel0iRmzhh5FALE4+22FjYM6XnbKp51SrcegXhHSJVmKK0KkVWzHUyn3yPeZm+GD19EhrrOxUz7665c0ISb/6AwtwTVaw/piU7F2bZBcLB0pG5ny5nfFo8N0KuEp8Sg7GBKWVdK+BhX5JhK/vw08HvGdf563+9lqutO0sGrCE2JZpv/pyIQqagd6PPKO9WGVdb95eeK5XIKOdaKXdOx1H//UQlRuSGj4uBZ7lw7wxjO05FIpES+CSA0OggpGL8vgAYOdUn+e4G9FrV04dwL5cVfRnvmj1xt2xAdHQ0kZGR3Lt3j7S0NOzs7HIDiYODA0ZG4sGdIBT5AJKWlcqUX0dx+tZRyrlVZO6ny/B2LsO3myYxufssyrpWIDz+MUeu7WXN4R/ou7gTI9pNoF+zIUgoHqs7ZSc9IOH6YtTJQciMbDFyqovCzB2JzBCtKoHsxEDSQveTGryHb+s0x86tYmGXLAjvHLOSXUi6vRpTz3bIjR1zXzd2qo/cxDn334Z2VVHF3UCrSs6dmF4UWZva0KRii2den9R9Jr0WtKFD7e757mmwM3dAIZNTp0xDWlXrkK9zPB29OXvnOAAnbh7mcWwI/ZuPAOBcwAluPvJnTMcpSJBwP+Iuf5xeS7ZGhaej6AkXQG7sgMLMHVXcjdyHBy+i02SgSgjAusYkTA2ssLKyokyZMgBkZmYSHR1NVFQU/v7+xMbGYmZmlqeXxNLSUqwmKRQ7RTqAZKkzGf7Tp9wOvc6XXb7ik8YDiEmOYvy6oUzuPotyrjlfpF1t3DE3tqB+ucbo9XqW7J5DpjqTYa3HFnILCl5m5Dnir8xHqjDFpvpEjEo0Ijv+DimBG7GqOib3i5Q2K4HYC5NQPT5ETMpDbOvORmbw7CpZglBcGbs0IjPiLFkxVzH1bE+s7xRMPdpi4tEGgLhLMzB2aYRUaU7646PY1JyWryerRY27nRefNB7A3C3TWDdqW76/eMWnxmFjZpvv+zSu0Jzvd83it1Nr0GjVueHj9O1jBD65k/v7/W7YLXZd2kIlj2rcDbv9rz0rQvFh7NyQzCi/fw0gqpjrJGRZkHj/CRUrWud5z8jICA8PDzw8PADQarXExsYSFRXFo0ePuHTpEoAYtvUe+2bGdFLT0gu7jGf4+PgwcODAwi7jhYr0T/iC7TO4GXyVOZ8uo3X1jkQnRT4TPgC2nv8Nv/u+zP10OTKJlK82fsnPB5dQzrUijV8wGbsoUMXfIv7yXBTmHtjWnZUbKLKTg8mKu4E2IyY3gMgMrZEZWqPNikedGkbcxa+wb7AYiUz5slsIQjEiwbraROKvziPq5BDkJk5P54boQa9DYe5B2qMDqJMeYFVlDIb2zy4bW1z0bz6c/Zd3sPfyNjrU6p6vcxJS47B+hQDiaOVMm5pd2OW7iS0TDwNw8tYRHkYGMqjlKADuhN1kr982Pv1wMH0WdWRKj1nFpudb+HdGzg1ICdxIRvgpXvZjkRF+GuuSzTlz6xaJiYk0aNAAqfT5a/zIZLLcoAE5K0omJycTFRVFVFQUgYGBpKWlYWNjk3uco6Pjmx+2pdcTeeYmj/ecJ/FOKJr0TIzsrbCrWQbPXk0x83D892sIAKxc+SOtGnhgZfHuDK27FxTDrVu33ukAItHr9YVdw/PMBqb8lwvcfuzPJ4s68HHj/ozvMp3opEjGrRvywvAxp+/S3CUh07JSafl1bcyNLdk97SRKedF7SqnXZBJ1YhBIZDg0WvZ/653r0aqSkRlY5j1Hl41eqyYr9hrxfrMwL90Di3L9327hRdeHwInCLuId1xo4UNhF/Ds9GRFnSQvaSXZiIBKZAXqdBvQ6ZMb22NWdg9zEqbCLLHSnbh1h5qZJ7Jp6EnNji5ceq9PraDixIhvH78Xdzivf94hKjKDDrEY0LN+UNjU68zDiHoNbjQYg8EkAO3030aVeb8avG0ptn/pM6T77P7WpAEwj5++hULA2Ar2f90bijWVoVYnPvK7OVhMRGYG7mztIwKricLIx4ciRI8hkMpo3b46Bwet9d8jMzMwNJFFRUcTHx2NqaponkFhYWLz2sK2s2CQujV1BakgEbh0aYFXRC4WpEZnRicRcuM2TY1co/Vlryo/qiuQFQUr4m6ODLbtX9KSku01hl5Jr26Fb7DybweHDh190SCXg1lss6RlFtgfk1xOrMTMyZ0ibsUQmPmH8+mFM+b/wseviFi7/X/hQa9XM2DiexhVbsM9vO0f9D9C2RtGbKJoWshdNRgz2DRYgVZqjSggg4ep8rKqMwtCuWm74SHmwmYywE9g3WIRUaYZEqsTYuQFJxnakBu3E1KvT06e8768nCWEcvb6f68GXiUgIR63JxsbMjjIlytOoYnNqeNd5Zo8BQXgxCcbOH2Ds/AF6TSbarHgkMgV6rZqoU8OQKk0Lu8B3QuOKLdjpu4kf9i9kSvdZLz1Wpc4iPSv1lXpAMlTpTNgwnHplG6HRahi3ZjAfN+nP6dvHAFi6Zy7eTj70XdyJfs2G5PaKCMI/WVX+4rmva7VaDv36KyV9OmNpmfP30gho164dZ86cYefOnbRu3RoLi5eH6+cxMjLC09MTT09PADQaDXFxcURFRREaGsrFixeB1xu2pUpI5USvmdhWK0312QOQKv/ei8e8VAkcGlSk5MfNuTJ1NVmxydSYM+CV6xeE/CiSASRbo+Ls7eO0qdmJ9KxUxq8fxqRuM/OEj7MBJ9h3eTvLBq3LEz6m/DqKmqXq0a3+x1x+4MvxGweLYADRk/boIAY2FTCwzVnxS6/NRpedgl6TlfdIdXrO6/p/bM6l1yNTWqDNiCE97Bjmpf59Sc13UUJaPEt2z2H/5R3odDpK2LnjbueJQq7E7/4Frgdf5veTa/ApUY5xXaZTq1S9wi5ZeM9I5EbITUvk/ltp4UXGkzOYerQtxKreHeO7zqDHvJZ0qtMzz+/n/5eYloBCrsDMMH87U2dlZ/LFz59jZmTOgn4/opQbcPnBBfZf3smP+xeRnpWGjbkdLjZubJ10RMz7EF6ZTCbD3d2dkJAQqlatmuf1Jk2acP36dXbt2kWzZs1wcXH5T/eSy+VvbNjWlWlrsKrgRYUvX/x329jFljpLv+DcgPk83nMBtw7ib5/w5hXJABIc9ZAMVTolbD0Yt27oMz0fZwNO8MvxVXw/YDXGBiZATviY+usoapauS4/6fQCoWrIm14MuF0obCpImIwpNWgSmXh3QZEQhVZhhaFcFlzbbQSJFr9eiSQtDYeaBRbnPsSjbDyRSdNmp6LSZyI3scWj0A1EnB6OKuQrvYQC58/gmo1f3JzEtgZ4ffErPhp/ibpfztGnLud9QyBVM7TGHkzcP8/OhpQz+oTdD24xlYMsvxBhx4bUZuzYjI+y4CCBPlbBxo1+zIczZMpVfx+56YU9jfGoc1qb56/1QqVWMWTMQqUTK4v6rcofQ1ixVj5riIYLwBnl6enLt2rU8AeQvVatWxdLSkiNHjlCnTh3Kln1ze8tIJBIsLS2xtLR87mpbN27c4Pjx45iYmOQJJPonScReDKDp1pn/eg+lhQnlv+jG7aXbcG1fV6zSJbxxRXJcSWxyNACbTq9/afgwM8p5mvZX+KhR6u/wAeBk5UJSWgJqrfrtNqCAaVLDAD1yIweijg8g/sqcnDee/vFPfbiVqOODyIq5kuf1uEvTiTo+EL1WBRIJSktv1Gk513qfPIwMZMiPHyGRSPh17E4mdJmRGz62nv+NKw99md1nKRbGlnSq05M/JxzAytSGH/ct4ufDywq5euF9Zuz8AdlJD9BkRBZ2Ke+MT5sOITkjiZ2+mwDI1mSTpc4EwD/kCvcj7uaZgB4SE8SL5i6qtWrGrRtCljqTpYPWYqAwfDuNEIolV1dXkpKSSEtLe+77np6etG/fnqtXr+Lr6/vCn9s34a/VturUqUOnTp3o168fjRs3xtLSktDQUHbv3s2BBatxaloNuUn+/ruwr18BdWoGyffCCqxuofgqkgHkrwfU47vOyBM+zgWcfHn4aPB3+IhMfMLR6/ufXq5oJX+dOme5OLmJIyYebTAu0TTP+4Z21TB2bYrCPO9kT2O3Zph6tstd+UqqMEevzgS97u0U/gaoNCrGrx+KXKZg9cjNlHOtlPveXwsSzO6Td07QrM1TGNRqFK1rdOKnA4vxu3++sMoX3nNSpTlGDrXICMvZn4J/Dm0spgwUBkzqNpNle+eTmJ7A8RsHiE7KCWjl3Sozbu0Qjvrvx0BhyNxtX3Hh7qncp7Gafzwc0mg1TNowgqT0BJYP3oCR0rhQ2iMUH3K5HFdXV4KDg194jK2tLZ07dyYyMpLDhw+jVr+dB5pyuRwnJyeqVq1K69at+fTTT3EzsMKyTP6HG0qkUixKu5J8XwQQ4c0rkgHEwTJnhRnV06dokBM+Nhz/KU/40LwkfIxfPwwDhQHW5rbIZUVrpJpEnjMuVKfJxKriMExcP0Sv05AZfQm9VoXSsjQ21Sc9XXY3AVWcPwCm7m2wLD8QkJCd/BB16iMkcsPcHpL3wZZzvxEc+ZAZHy3M7fWA56+G9s9heb0afsrXvebhZF2ChTu+QfcehS7h3WLs1oz0sBOAnrTQ92BRr7egftnG1ChVl2V75rH13B8kpiUAoJApaFypBXsvbeN6kB/+QZfp2eDT3PNO3z5GelYaWp2Wab+PISIhjBVDf8PU0KywmiIUM15eXoSEhLz0GBMTEzp06IBMJmPXrl2kpqa+per+JpFIkGh1eSad54dUKUenKlqjQIR3w/vzzfEVeDp4Y2pkxqWnT6q3nv/9mfCh1qqZuGEE1b3r5AkfAWE3+WLVZ0zqNpPkjCQquT87tvN9p3g6KVad/PdTm4zwk8T5fkXao4N5jk26tYKYcxPRpD/5+0W9lthz48iKuYrc1IWXLpD+DtHpdfx5ej3VvGvRqEIzIGcy39K987jy4GKe8JGamcKwlX3yDMszNjChR4M+3H9ylysPfQutHcL7Sa/LRqtKwtC+JrrsFNIfHSb98dHCLqvQPYwK5LeTqynvVoldFzdzLehSbgAB6FynF1KJFKlEytSec/I8EDrqf4CQmCBm/Dmeh5GBrBz2R+7veEF4G9zc3IiNjSUjI+Olx8nlcpo1a4aHhwc7d+4kOjr6LVX4N2NnWzIiYl/pnIwncRi75H/1ueIqO0WGKvHd+Z8m/d3/el+0Hu0/pZApaFKpBceuH8Ddzot74XdYMfQ3lPKcoUN/Pdmu5VOfng365p4XEHaLuVu/YvngDdx6dJ245BiaVy16k0XlJs7IjR3IjDyPqWd7AAwdamJeujfGTvXzHGtasgsKi5K5GxICIJFh5t2d5Lu/YGj3/gS0h5GBPIl7zIAWOTsi6/V6luyZix49cz9dnjsBNjUzhbFrBtKn6SA+KP9h7vkBYTc56r8fQ6URp24dpVap+s+9jyA8j0SqJCN8H5kRZ5AqTEnwX4KRQ83CLqvQeTv6kJyexKxNk9HpcnoWk/4RQDwdSlLNuzYeDl5U8qiW+3q2RsW5gBMEPrkDSFgzchOWJlZvu3yhmFMqlbi4uBAaGkq5cuVeeqxEIqFmzZpYWlpy4MABGjRoQKlSpd5SpTlzOgKW7aBUv9b5Oj49PJaMyHhsqnoXcGXvN71GQtg+MzC3/PeD35KYYBMwfP7cpHfFux+RXlPfpoPIzM5kr9825vRd+kz4qFGq7nPCxzQWfr4Sc2MLlu2dj7uDF40rtiisJhQciRRjtxZkxfqTfPcXVAl3kBlYYlHuM2RGdmgzY0h9sAW9JgsD63KYl+4NEhmquBukh+Wsoa/NikcilWPs2qyQG5N/98JvA1DFs0Zu+NDpdYzpMCU3fKRlpjJ2zUD6fjj4mfAxd+tXfNd/FaWcy3Av/E6htEF4v5mV7IKxa3M0GVE5GxMa2Rd2Se+E6iVrs3niQYa0HoNCriQxPSeA6HVq1KmP6VOrJSNaDs9zzuUHvqRmpBAc+QAbM1t+P7WWa8F+hVG+UMx5enq+dB7I/ytVqhStW7fG19eXy5cvF+jk9L9otVpibWQkRsUQcfxavs65t2oPnt0aITcWizkIb16RDSClncvxUaPPCI0OYvelLcCL53zkho/PVmJrZse038fwJO4xU7rPyh2SU9SYleyEzMCClMA/SA7YkOe9tEeHSLqzhqzYvL+kkm7/TOL1xWRGXSAt9AAmHm3y9oy84xJTE5BKpFiZ2eSGj7Edp+ZOaE3LTGXM2pzw0bDc3xPz/wofCz9fiaOlM3aWDiSmxhdWM4T3nKlHW2yqT0AilSMztivsct4ZSrkBQ9uMZcukwzgodcRfnsWT/V2IOj6AUgkHST89iOjTI8kIPwnoOX7jEADONiWo4F6FVtU6UM2rVuE2QiiWPDw8iIqKIisr698PfsrR0ZHOnTsTGhrK8ePH0Wg0BVZfSEgImzdvJjwqgqqz+nNr0Z8k3npJYNLD/bX7SX34hAqjuxdYXULxViSHYP1ldIfJBEU9YPamKUQlRvA4NvSl4UMmlTFiVT98757BzMSiyE0+/yepwgyrqmOJuzgd9Bp0mnSk8pw9Ucy8OqEwLYGhQ94/5tbVviQj4hwJVxehMC2BRbnPC6P01yaXydGj5/vds7Ewtnp++Ggy6P/Cx6084QNArVEjL6LBVHg7jEs0RSI3JDsxlSdHr5IeFo1Oo8PYyRqbaqUxKcZjru0zAzFIOYbMpi32DRaQGrQLqyqjQK8jM+IcyQHryIg4i725NRvG7KCyZ/UX7h8iCG+DoaEhjo6OhIaG5u7LkR9mZmZ07NiR48ePs2fPHlq1aoWx8ZtbvS0hIYELFy6QnJxM3bp18fLKWdnSSC/Db/wK3Dt/gEeXRhjaPd2tXa8nKeAR99ftJzMmkQ9+mYzCXKwmJxSMovsNG1DIlSwZuIaZmyay+tByLEwsqF6yFpGJT7C3cOR+xF1mbprI4Naj2XR2A1vP/Y5ao2ZMp6ns9N3MiJ/68f3A1dT1+aCwm1IgjBxqY11lFIn+y4g+NRJDuyqYeXdDbuKcuzRvVuw1smKuYl6qJ5kR50m9vxmZkR22dWchlb9fv5icbEqg1+sxVBjmCR8pmcl8uXZwTvgo/3f4uPP4JvO25Q0fer2eRzHBeDm+vXG7QtGTERnP7e9uEHbgIiau9pi6OyCRyQg7EE/KhJ+wrVGGiuN6Yl25ZGGX+lZlPDlNcsA6rKtPQJ0chNLSB5sak9HrskkJ3IhZqe4YOTcg7tJ0utvLsfGqwfuyCIZQtHl6ehISEvJKAQRy5pC0atUKX19fduzYgd61OQ+SX30hhQbeclqUy3kwlpWVxZUrV7h//z5VqlShVatWyOV/f91za1cXq3Ie3F6ylZO9pmNgZY7c1IismESkSjklezfDZ2A75MYGr1yHIORXkQ4gAIYKQ+b0WUrzKm356eBi5mz5ijlbpmFubEG2RkW2Rs3oVf2Ry+Q0qticke0m4OngTbuaXRiy4hNGrerPws9X5q6aVNSYuLdGZuxEov9i0kL2kRVzDSPnBijM3JHIDEi5vxF1cjBpwXvQa7MxdmmIVeWRSJUWhV36K6vsUQ2ZVIa5sWWeno+xawbRp+nAPOEjIOwmc7ZO5bvPV+WGD4A/z6zncUwIXet99NbrF4qGqNM3uDh6OY4fVKHR719h7GyT5311SgahO85wqs9syo3oQplB7Qqp0rdLp04n8eYPWFebgMLUmbTQ/ej1WiQSKXqtmuzEe+g1mcgMbbGtNZ2oE4PIjLyAkZNYDEIofJ6envj6+pKdnY1SqXylcyUSCfXq1cPS0pIlxyMwczajqpss3+f7hWgIjtWi08kICAjgypUruLq60qNHD0xNTZ97jpmXE3WXfYEmI4uwKwFcPHOetr1GYebphEQmehSFglfkA8hfmlRsQeMKzQmMCODKg4uExYWSmZ2JpbEl3s4+1PFpiL3F3/MZbM3tWT1iE0NXfMK4tYOZ8+lymldpU4gtKDiGdlVwbLqa9LCjpD8+SurDrfCPSXFSpTmGDjUw9WyPgXX5Qqz0v7Exs6OWT312+W6if/PhaLVaxqwZyKcvmPPxXf//Dx8bWLj9G+QyeZFcHU0oeDEXA7gwcimVp/TBqXGV5x6jMDemVL9WODSoiN+XPyKRSvAZUPR/3jLCjyNTWqCK88fQvhp2dWejU6eRHLAWi7KfYVdvHui1JN1ZjVnJzpiX7k1q0E4RQIR3grGxMXZ2djx+/Bhv79dbNapcuXKUjcjC0IDc3oz8iE3VExsXx7ZtF5DL5bRq1QpHx/zNz5QbG2JWzg19+D3MvV1eq25BeB3FJoBAzlOGMi7lKeOSvy/RVqbWrBqxkeE/9WXShhGoP1lMmxqdCrjKwiGRGWDq0Q6luRfRZ8diVXkESuuyyJTmyAxt36vNBl9mQIuRDFjWg+92zeJRdDB9m/77nA/ICR9/ntkAEgltanbGxdq1EKoX3mea9Cz8xq2g3IiuLwwf/2Tu7ULNBUPxHfE9Dg0qYlnG7S1UWXiyYv0xtK+OJj3y6Q7xCvTabDTpkeh1GiQyA/R6Xc6/NVkYOdUj6fbP6LUqJDIxVEQofH+thvW6AeS/SExMpHLDypQuXTq3h18Q3mVF41tlAbIwtuSnYX9QybMa034bza6nK2oVVdkpoaDXYWhfDaWFd84yoUUkfADU8K5Dl3ofsfXsb3g6ev/fsKu/l2L+//Cx/8oOMrLSsDW3Y0zHKYVRuvCee/jHMYwcbXDrUC/f51j4uOLRpRF3lm4rsLoyMjK4ceMGN27cID09vcDu82+0mXEY2FXBtvYM9FoVqQ+2IDOwxLb2N0hkSlLu/wk6Dba1vkZu4kR62FFAj1aVVGg1C8I/eXl5ERYWVqArWr1I6VKl8PHxEeFDeG8Uqx6Q12VqaMaPQ35h9OoBfLNxAtlqVZ6VtIoSdUoIUrkxMiOHwi6lwEzs9g0h0Q/ZcvZXlHIlw9qMIzQmKHc1tH+Gj01nf2HlwcWg16PX6/lp+B9YmxbfFYqE1/d49zlKfvzq+wq5d/mAU71moE7JeOMr0mzbto0ffviBTz75hKioKBYsWMD8+fMZOnToG71PfkhkSvSanGVMtaokMqMvYVqyExKJEr02i6yoS5i4NkMmN0KvzSYz0he9TotE9mrj7QWhoJiammJpaUlYWBienp6FXY7wluikMq5XaUiYo0dhl5LrgSwbwqILu4yXEgEkn4wNTFg2aB1frhvMnC3TyNao+KTxgNz3fQPPUtenYSFW+GaoU0KQm7kikRbdH434lBgexQRjZWbD7yfXcPDqbrQ6LRO6zkAhV5KamUJ0chRHr+9j1cGl6PU6SjqVZsFnK/B28ins8oX3kCZDRfKDcGyqvfrqaUYOVhg6WJF4JxT7ui/faflVLV68mJCQEAYMGIBer2fx4sUsWrSoUAKI0sKLrNjr6LWZmLi1xr7Bd+i12aSFHsDUvSX2HywB9KQ/PoyRUwMsKwwi/vJsZAbvzu7DguDl5UVISIgIIMWITiIh1s4JjeO7M0w2ydIWwgq7ipcrct8y9ejJVqvQ6nQo5HIUb/DpmKHSiMUDVjNxw3AW7ZhJtlrF581zduf9/dQaqnrWwFBp9Mbu9/bpUaeEYuRUt7ALKTCZ2RmMWTMIlTqLX8fuIjEtgTVHluN3/wKTN4x85vgStm70avQZPep/goFC7AYrvB5VfAoypQKF2ev1YBjaWZEZk/iGq4KFCxeSnJwMwKFDh0hKSiqU8AFg5NKYmDNjyIq9hpFLI6RyE7SqBFKDtmPsXB+p0gK9NpvUh9tRWJQkLXgPxi6NEMvwCu8SLy8vduzYgVarRSbL/0pWglDcFIkAokfP9eDL7Lm0lSsPLhKdGIFaq8ZQaYSbnSf1yzWmc52euNn99ycSBnIDFn62kqm/jWbZnvlkqbMY0noMgeF3OHv3JM0rv78rZWkz49CpU1GYF80nN3q9nukbx3H/yV2WDFxNScfSQM68kPjUWG49uk5EfDhqrRobM1t8XMpT0qm02ORM+M+kSjk6rRa9TvdaS1zqstXIDN785pf16+esIJWUlMSQIUPYsmUL3bp1e+P3yQ8D63IYOdVBIjNAgpTMKF+MHOvi9OHa/7F3ntFRVV0YfqZPZtJ7rwQSeieEXqRZQIqoFAuCUhSxgfqJSlFUEMEGiIiIIFIEFQQRpPfQAgFCSCONlEnP9Lnfj4HBSAsQCMF51mIRZu4599wB7tz37L3fDYKANnsXSt8YfLsuoCJzO7q8I/h2nV8ja7Vj51q4uLigVqvJysoiKMhuVmLHzrWo9QIkU3OeaSveYk/CNtRKR1rVjaVn84cprihi85H1OCqdWLJlAUu2LGBg+yGMf/hNVIrby6OWSWR8MHwOcqmC+X98Rom2mMLSAv44tLZWCxBjaSoIwn0rQL7d/CWb4n7jxYcn0qnhA5Xe83DyonPDm8/Pt2OnKii9XJHIZZSfz8Mp3O+mxgpmC2WpOTiF3dy4m2HNmjWMGTOmxsTHJdybvULuzte4sH0cZn0R/j2XIZIosBiK0Rz5DO+Os9HlHjEBUYIAACAASURBVKIk4Xs8Wr+DROlx40nt2LnLhIeHk5ycbBcgduxch1otQI6lxPHSgmcxmgxM6Pc2g9oNRa10JLswk9cXjWb+2B+JCmxITmEW3/w5l593LOFo8iG+HL0ETyev2zq3VCzl/Sc/QSwWs3zbdwDsTvibkopinFW1p0mfYDHZ6j2MxamACJlzaE0u6Y6w7cRmvlw/kx7NH2bExbQ5O3buFiKxCJ8Ojcneehin8Jvr6ZF/8DQmrZ4jU5fQ6fs3QRAQSas3tSMlJYV+/WreYlwsd8G746cUHvscc84BCo/OQeYaiUgiRx3cg/y9/0MkluHV7kPkbtE1vVw7dq5KWFgYv//+Ox07drxrrlTCjQ+xY+eeotYKkJQLSYydNxxXlRufj/6eMO8IAJv4eHvwB0QFNgTA182fTg27czrjJElZZxj79XAWv7waB/mtRUL2J+5i3f6VnM06TUZ+mu11nUHH1uMb6Rcz2PaaYDagyzuMviD+op+9AYncBZlzOEqfVsicarZoqSJzK0rPZkgcvDCWpCBRuiGR319FnedyzvL2kvHUDYji/SEz7TaFdmqEyGE92D36U0L6d0Th7lSlMYLZQuKi9US/0BeP5nURScT8+eAkWn44CnWQFzK1A2L57d/Ghw0bRkDAvdGETCxzwqPlW1RkbqPw+JdYzDqwmJCovHFt9AIOPq1BZM+tt3Pv4uHhgVwuJzs7G39//xsPqAbi4+PZIzPSokULFAp7Xxw79z61MrndbDHz1pLxSMRS5o1bZhMfpzJO8OL8Z3h78AdEXxQfACt2LeGHvxcyf+wyZjz9BYkZCcz97aNbPn+buu2Z0PctBncYTvOI1pWKkzfErQVAEEyUnltD9ubh5O+bTFnyOrTZe9BdiEOXG0fRyQXkbB1J3p5JGIqTbnktt4vMKZS8PW9i1hdhKEmxRj9EIkzlmVRk/F1j66ouSrTFTFj4HHKpnE+f+wbVLYpOO3ZuF6820fh1bsaR97/DYqhan4DT89ZhMZiIHtsPv85NAIj9+hVco0M4Pf83Ts5djalcR8GRW7+HFBUV8fLLL/PJJ5/c8hx3AlN5NkqvZni2noxnzBTcGo/DwbetXXzYqRVcakp4twgPD6e8vJyffvqJhIQEBMEeE7Fzb1MrIyAbD/9KQvpxPnhqDoEe1gjC6YwTTP/5bT5//jv83C7v5K3YuYTD5/bz1egfkElkdGnUg36xj/Pzzh94stOzBHmG3NIavJx9GBg7hIGxQyjVlrD71Da2Ht/IgTO7yck9hfjMN+gLTqDwaopb0/EovZqhzd4DgCqwC6aKHCoytlF6bg2528fj0nAkTuF3PwVC7lIHi6mC/L1vYSo7j1Tth+bIp+jyDuPTae5dX091YraYefP7F8kqOM/XY3+0dy+3U+O0mDaCHc/MYP8rX9B08lM4eLtd9Tiz1kDC56vJO5BA5x/fqVSA7hjsDUCTSU9i1hkoOpXOybmr6fjdRFJWbiOgRyvkLuoqrykxMZE//viDc+fO8d57793W9VUnutzDqIO61fQy7Ni5JcLDw/nzzz9p167dTUfdD6WY0ZTrq3z82RwzMWFqHuj0ANnZ2ezZs4eTJ0/Srl2760ZgBItAUUIq2QdOIj6cQrrzXjxb1EPl535T67Vj51aolQJk1e6lBHuH0buF9YH9VMYJpq94i5nPzsPXrXITuSPnDjJ92GdIJdYvcKPJQHF5IRbBwtp9K3jxoTduez1ODs70av4IvZo/grYij6xt41GYy3Bv8TpSlR/G0jREEgWqwMtdt/V5x1C4R+MYuhDN4VkUHf8azAacIh+77fXcFCIxSp9WlKf+AYA2axeIJHjFTkeiqN03oTm/zWDXyb95a/B0WtW5NWths8VMYlYCJ9KOkVd8AREivFx9aBTSjEj/KLtDlp0qI1iMGDS7aPpmAMmrRGwfOo2AHq3x7dgYxxBfRBIx2mwNeQcSSP91Ny51g+i2agoOvtf+fyhRyvFoVoeO303EYjRxYfcJAh5oRcrKbagDvPCObXDDdbVu3ZoNGzYQHh5enZd7WwgmLYbC07g3f62ml2LHzi3h5WWtM83NzcXHp+qNfWMjpLirb06wBLqIaRhgjQz6+fnRv39/Tp8+zebNm/H39ycmJgYnp8ppn1lbDnP84+XoC0txqx9KsNqRs99v4tCk+fh0aEyTN4faNjvsXB+xIOCbk463cO88D+QWXCC/phdxA2qdACnTlXIsOY6nu49GLBJfU3ys2LnkoviYfVl8mI28ueQlYqM7oSnNZ/epbTclQDIK0gnwCEJ0Td95gfL4L1GYivFsOxWlV3Pydr+BLv84Kr92iC82zLKYKig8NheZayQ+Hefg0eZdNIc+oChhETLXSJRezW7587kVHHza2AQIgHPdx1F6Nb+ra6hufj+0hiVb5jOg3RAGtx9+0+ONZiOr9yxj6d8LOZ+XCoBcKkfAKmIBQn0iGN5tFP3aPIbkPm7caOf2MOsLKU9dT1nKesy6AlzqP0vLaY9TZ8gDJK/YSsLnqyk/n4tgtuDg445ny3q0/mQ0vh0awU3snIplUmI+GweAVO2AxEHBhd0nyNx8iObvPY1gERCJrz5f7969q+Vaqwu95iQSBy+kqqo/uNmxcy8hEolsaVg3I0DahElpE3Z73ycikYjo6GgiIiI4dOgQK1eupFGjRjRt2hSZTMaJ2StJXr6F+i8NxK9Ls0rW4MaSCpKW/slfj/6Pdl+/glfrqNtay72GxWJBLK5eoSC2mGkcv4+gtLuXcncjipITyL/HW5fVuqem1NxzmC1m6gc3vq74OHxuf+XIh9nIW0teIiaqAwNjh3Au+yxr9izDIliqvItdVKZh1tqpvNbvHQI8riwe1144iDZ7Dy4NRtge4N2avISpIscmPgDEUhWebacjUVp3NkUiCW7NXsVQlERR/Ff4dpl3V/OcFV5NEUkUCGY9Cs/GuEQNvWvnvhOcTD/G1OWTaBreikkDp9z0+EzNeV5fNJqTacdQKx0J9Ylg1oj5BHgEIQC5Rdm8vmg0FfpypiybyG/7V/HxM1/h7eJb/Rdjp1ajzz9KWeoGTGWZmHUFACg8rPVprtHBNH/vaeuBgrWfUXUZJAT1aQOANkcDolYYSyvY1HsiPdbPQDBbULg53pS4uXsIgAhd7mGU3nd3I8aOneomLCyMbdu20bZtzTT3lcvlxMbGUr9+ffbs2cOKFSuILJKTu2oH7b55AwefK1NAZc4qosf0wzUqmD1jPqX72umoA2/PNfRe4sKFCyxbtownn3wSP787Z21u58bcO/GiKlJSYe3am1WYcfW0qx2LrxAfBpOet5a8ROu67RgYOwQAN0d3jCYDu05tIzHrFAWleeiMuusWbjUMaYqXsw8DPnyARX99ieHiTvglypJWIVF5I3epQ+HxrxDMOqSOgSi9W1rfT15LabK1SF3p1QyZUwhmnYbCo3Mwa/NxiR6OsSQNXW5c9X1gVUAsVaHwbIxY4YpHi4m1usgzvySPVxaOwtXRjZkj5iGXym9q/Pn8NJ6ZPYD03BSGdBlBuG8k37z4E3X86uEgV+Egc+DnXUvp2LA7v72zgylDZ3EmI4FnPhtATmHWHboqO7UVhWdT3Ju/jkjqgMwpCJFEidy17pUHirgj7mwOvu74xDZA5uhAh2/fQO6iZv+rX5G+fh/aC4WUpedW+zlvh/LzfwGgyztc66Owduz4+flhMpnIz6/ZZBhXV1f69OlD2+atSJ33O83fe+aq4uOf+HVtTkDPNhz/aPldWuXdwc/Pj+joaHr27Mn7779PVpb9e7umqHURkEvWuSt3/sCCccuviHwcOrefGcPn/ivyMZ7WdWMZ1O7yzn65rhSLIDB+/jNYLBYA5DIFTg5OuDl64O7kiYeTF+5OHng4eeJ+8ecujXuw/uAa5qybwfqDv/DmoGm0rBODxVCMviAep8jH0Gbtoix1A44hvZC5XMyrFgRKklaBIOAU3hcupnEZNAmUpa5H6hiAY9gjiOVfUZG1C6VP6zv+WQpmi9WNRyzCwbctjuGPIHGovTsdBpOB1xY9T1F5IYteXnXTvV70Rh0TFo5EZ9Qyod/b/LL3J2aOmF8psvHj9kWU60p5vf9kRIjo2+YxPJy9GPvVcF5d9DyLX16DTFL9Havt1FYECo/OQbAY8Yr9EM3hmYgkNyeKqwWRCJd6VhOG9vNfBeD8hn3kHzpDs/eeJnX1DkIHdERczf1FbhZt9l4ATKXnUXg0Qpcbh9w1ErHcuUbXZcfOrSASiQgNDSUlJQVPT8+aXg6i+Cy8GkTg1qhq9V4RQx9g68DJ6DWlVbYO/zdms5mMjIx7ypWrfv36xMbGsmzZMlavXs2AAQN4/vnn8fW1ZzHcTWqdAAn2snbpfqTNoEpuVz/v+oHDVxUfL9GmXvtK4iNLk8Gmw78T5BXCO4/PoKA0D01pAZrSfNvPBaX5ZOQfori8CJ1Bi9livryIizuVSVlnGDl3MA+3GcgLsb0QLGYUHo2Ru0ejDu1zWXxcHOPdfualP9hedvCLxafTHGQudRCJZcjd6mEoSqzmT+0igkDBsWQyNuwj7+ApytIvYKrQI5aIkbupcWtYB7/OFQT1boPMufbZ1c5Y9Q5Hkw8xffgcGgQ1vunx32+dT1LWaUb2Gs/afSuYNWI+Pq6XQ7Q/bl9EQvoxpgz51FYHVFxRxHebv2Zo1+dYunUhP+1YzLAuI6vtmuzUbkrPrkSffwzvTnORKNxQh/Sq6SXZ+oaE9GtPSL/2VGRruLD7BGGDOnN6wW/4d2uBc8Td6V3wb+QudS6KNCUXto/DKaI/Su8WNbIWO3aqg/DwcHbv3k2rVq1qeinkHzqNd8yNjSkuoXBzwjnCn4KjZ/HvemsRycmTJ7Nq1apbGns30Ov1NiEycOBAxowZc0+Ixf8CtU6AeDh5Use/HtvjNzOyx4uANfIRd24/Hwybg1Rysau32cib379oq/m4RJbmPGPnPUW2JoPHOgyndWTsdc9ntpip0JdTXFFEQWk+CeeP8cmq9zELVkHi6uiGs8qVkqI01IBILEUsVSF3jQSgNGkV+oJ4PFr9D6nqoroWzBQc+gCZSwTOdZ+0dfTVF8RjKLozPUHyD5/l+IwfyY87g1guwzU6FL8uLZC7qrEYTGhzNGiOnyPzzwMc+2Apkc/0Jvr5R5A41MBu7S2wfOdiVu9extMPjObBlo/e9HitQcvSv7/Fy8WHfad3MuvfkY9t33Ly/HGmDP0U6cWC8+KKIl5ZOJLner5ETN32nMlI4Put8xnc4ambTv2yc/+hzd5DSeJPeHeYhURhTXdQBXSq4VVdicrPnbZzrfdSBJAq5SSv+BuL0USdoQ+AINy1ehG5W10QBASTFoewh3GsAWtyO3aqE39/f7RaLYWFhbi5XT/t6U6jyy/GvUnkTY1Reruhyyu+5XMGBgaiVqvvqQgIgMlkwmC4nEbv6elJVFQULi4uNbiq/xa1ToAA9G3zGLN+mUbcuX0kZSVeFB+fVRIf/yw4v0SWJoM3vhtLuG9dUnKSeKTNwBueSyKW4OTgjJODM84OLkz9aRIAbeq1p3/sE3Rp1AOFTEnF+S0UpIHFUFppvKEoEX3BSQSzHpHYGpkRLEb0+ScQzMZKx5rKMrHoi5EoqtCJXBAuBlKu/2AgWCwkfP4LCV/+gsLNkQbjBxHY6+oRjkue4Od+3MypL9aQsXE/sV9OqLHd0GuRXZiJm6MHyosNIA+c3cPM1VNo36ALLz008Zbm3HdmB8XlhYT6hDNrxLyri48hs64qPtrW6wDA4A5P8dq3z3P43H5iLr5m5/5FMGkxFCVi1hchlqmRu9SxmU0Yis+hOfwJ7i0mInMOuzzoHq+vinr+YQDcm0Rg0RspPJlK3Dvf0n31FAylWuR3ODIqd40EkQhVQGdc6z97R89lx87dQCwWExISQkpKSo0LEJmjA8bSipsaYyytQObocMvnHD16NC+88MI9JUAsFgujRo1i9+7dBAQE8MILL/Doo48ik9nTp+8mtVKADGw3lKXbFvL6ojG0qNOGD6+SdvXPgnO4JD7G8HjHp5n60yQ6NXqARiFVd1nRG3V8uOodOjboxqcj5ttSwS5hq50QV/4H7N7iDTAbEEkvf3GLJEr8Hlh8xbHqkF6Up/+JWafBrM29OOe1BIaF0qRfcIp49JoPNYJFIO5/i0hesYWAHq1p+MpgZE7XvpGIxCLcGobR8sNR5O45ydHp3/P34PfotORtXOvfWsPGO8GhpH34uvrRKjKWzILzvLFoDIGewXz41OdIxLf2gHcsJQ6ZVM78scuvqPmoivgAcFY52+ayC5D7F7NOQ/GpxVRkbEXi4IlE6YHFUIqpLAOFV3Oc6vRHc3gWzvWG4OAbU9PLvSVco6wufxajmZbTRyII8GefiXT49g0kSjlyFzVyV8dqOZextILyzHyrDbG3K+qQXrg1Hgf2Hjt27hPCwsI4dOgQzZvXrLGCW4MwCo6fI+TRqn0/WQwmik+n49Yw7MYHXweRSHRHTDZulQULFpCamsqUKVN49NFHkcvtGQs1Qa0UICqFiveHzGLsV8OsNRpGHY4SGUazkUmLxxET1aFSzUdGQTqTFo9jaNeRzF47HUelE28NmnZT59Sb9EwZMhPZNQpIZS4RiCRy9HlHMFVkUZa8Du/2M5EoPUEqBQTy978LAnjGTEEktQoBQ1Ei+fvfx63JOBSeTTAUnwOLkaw/hyNzDETh1RyldzMUHo0Qy/7xhS+SIJh15B+cjkeLNxBJrjR8TvjyF5JXbCFiSE+iRz9y1TQKQbi69ad3bANiv36NfS/NYefIT+j+yzQcvKsQmbkLHEuJ47yjBw1CmjJh4XOYzEZmj1yIs8Oth06zNZl4u/pWMjVYum0hJ9OPM/UfaVc6g5YJC0cy8l/i42jKIb5cPxMnB2eyCzNv/eLs3NMYihLJ3/cOcvf6CBYTHi3fQu5aF8GsJ3PDAERiMfl730Hp3RynOjeOsN7riGUSXKOtYqTnHx8hc3Tg+CcrULg7EdK3HZpj5/DvfvM1GoLZQtraXZxb9heFJ1OQqh0QScQYS8pR+XsSOmA9kU/1vK2dVzt27hUCAwPZunUrJSUlODvXnKGCZ7emnPhiDdG5RSir8H1+/o99ONcJwDHk/unHk5mZiaenJ5s2bbJHPGqYWilAANrW68DkJz5iyk+TGDLzYd4cNJXVe5ZdteD8tUUv0Ci0GdN/ehOJWMJXY5ZWKi6uCjd6uBXL1Ci9W1CesRXH0D4IZj0IlkrHCGaDNXXqn1jMCBYDgsVIxfktCCYt7s0mYDaUoM87THn6RsqS114sUK97UZA0R+5aF3VwT0o2P0XenjfxbPMeYvnlNWqOnePUF2vw797qCvGRkZnJR3NmEn/yJBKJhAJNAUEBgQx57HEG9x9kEySOwd60+ugF9oyeSdw739rcc2qa4ymHUSsdSbmQxNmsM8wZtZBwnzq3NafJYraJDLgY+Ug7Vqnmw2Ay8NYP43mkzaBK4iM+7Qifrp3GJ898zdBZj2Aym25rLXbuTcw6Dfl738Ex/GGc6w3FVJGDVOWDxViOWKbGt9u3SFXeaLN2oTk8C73mJAr3qhd83uvInKxR3MZvPG41tDiSRM6uePy7NefEZ6uo+0zvKkVFKrIL2PviXHT5xUQMeYDmU5+zOeyYKvQUHE4kecVWkpf/RcxnL+LZst4dva47wamME6ze/SOHzx1EU5qPo4Mj9YOb8EjrgbSv35kbpc7aub+QSqUEBQWRkpJCkyZN7vr5LRYL8fHxHD58GK929Tg6bQmtZ41FLLt2xkBZ2gXOzP+Vdl+/chdXeucJCAhg4MDq3RwyS6RsfuAxXAPqcHv/t4V/jP/nzzfP6W1OcGLFbazlzlNrBQhAv5jB+Lr58+6y13n+iydxc/Ig0j+Krcc3olKo0ZQV8OXvn5BdmMWp9Hiahrdk2rDZBHmGVvtazBYzkuAHsWTvQ7AY8e/xI4jEWIylWPTFSB0D8Yr90Ha8qTzLmjfuHk1ArxWYDSVc+Pt5q4NWcC+rYIgcjGDWodckoMs9jD7vMCVnllFy+gfEMkcUno0Qy53RF5wkd+ereLadilRlFVbHZixD5qym0auDK4kPrVZLz/4PoVKp2L5+MyqVinxNAc3at2brjm1UaLU8O/Qp2/Eu9YKo81Rvziz4ldx9CXjH1K/2z+5mKNeXk5R1GotgQRAEXnjwFdrX73rb83o6e7H31HbMFjM/7fyeE2lHmTp0tq2uyGAyMOn7cbSv35V+bR6zjYtPO8Ina95n1rPzcXJwpri8EE9n79tez73Grr17nxBZLIfbtWt3pqbXUlMUJ3yL1CmI4tM/ogrshlTth1lfSNbGJ/HtOg+ZkzVNUXN0LjLXOhQenYtv13nclw+bIhEezSPxaB6JWWfArDMiVSk5OWc1Hs0i8e3Q+KqXXZGtYevg9/FuU582s8chllX+CpKqFPi0b4RP+0akr9vNjmc/ov2C12r8vlNVjGYjM9dM4bcDq3mgWR/kMjlfjlmCzlDB7oRtjJv3FDFRHfhw+FzcHD1qerl3lLi4OJnWZBhj0uq/7dy5c1lNr6emCQ8PJz4+/q4LkLS0NPbs2YNKpeKhhx7CY4grO579mAOvfkGTt4dftR9I3r4Ejn7wA/XHPYpXm+i7ut7aiHDx1+3f60XX+PlWprr3v3dqtQABiKnXgbVvb2XN3uX8um8l8zbMrmSZq5ApaRXZlkHth9K1ca8qdz2/Gun5qfx1dAMFJXkUlWvQlGkoLtOgKS+gtKKUTo2681qjXpQmrUbmHIY6qDsFh2agzzuKf6/lNi97waQlZ+sLyN0i8W4/C8Gsp+DgVCzGcjybvFjpH45IokTp1dzWlMusL0Kffwxd3mH0uUcw6zQAGEvTyd0xAc+YKZRnyMg7eIroMf2vKDY/m5xEanqa7ecmDRvj6e6Bj7cPmsJCtu3cUUmAAIQP7krKyr9J/O6PGn8QOJl+rNLf7+LNX/Pj398SFdSA53qMI6buzddeaMoK0Bv1lOvKmL7ybXQGbaW0K4NJz6TvX6RDg248GjPYNu546mFm/jKFWc/Ox8vFh7hz+9Eb9dQPanT7F3qPIRIJryMWzwT+kwLEYiylInM7vl2/wWIsQ6r2RbCYkCjc8Ok0F5lTMILFhEgsxbv9x0hUPuT89ezFKEjDml7+HUWilNPkzScB8GxRF1WgF2m/7iZ370lazRiFsVSLzMkBwSKw7+XP8WoVRcNXBt/w+zW4bzvEChl7X5pLzw0fo/S8t3uBWAQLby0ZT2ZBOkte+YUlWxfw2XML8XXzR2uo4Nf9q/h50ia+++trnvv8cb6fsAZH5a31VqgNlJaWKmRK+WcODg6rgf+8AAkODmbbtm2Ul5ejVqvv+PkKCwvZs2cPhYWFxMTEUKfO5SyBjove4PgnP7F96FS82zbErVE4MrUSbW4huXtPUpCSQeQrA6j7dJ87vk47/11qvQABa3PCIZ1GMKTTCArLNWQWnEdn0OKkcibIIxSVonqcW4I9Q+nWpDe/7F3O3tM7KSjJs73nonbjlb5v46Z2wVSWgebwLMwVF1AHdUXmHFqpfkMkUeIU0Q+pUzCmsgwK4j7CUJSEe/PXkLtcP5VIonBFFdAJpU9LNEc+xVSRY3vPrC9Cc2Q22bsaIpZKCOrd5orxUZH1ePrJYYjEIqLrRgGQlp5GappVlLSLaXvlOZVyAh5oRfq6XbaHiTuBWV+IsficVVSJREgcvJC7RCCWXf6SPpZyuUu8SCSiYUhThncbRYfoLogvFqBbBMsNhWaptoRtJzazMW4d+8/sxiJYkIglnD5/giWvrK2UdlUV8QGwdt8KHBQqewH6fYih8AyCxYggmJC7RmIxVZC9aQje7Wcid41EMOvJ2vQknjFTbGlXYpkafcH9L0D+iU97q/hWujvjGh2CLq+IjT1f56FdX5DxxwEqsvJp9fHoKm/uBfZqTc6OY5z6ei3N3hl+B1d++6zdt4KE9GMsePEn5v0xm3EPvo6Pqx8V+nI+Wv0uo3qNJ8A9iClDZvHQlA7MXvcB7wz+8MYT27kvkMlkBAQEkJqaSoMGdy41U6/XExcXx+nTp2nUqBE9e/ZEKq38qCeWy2j69jCyQh0QUkspPZeJqUKH0suVus/2wRDpwYEjcTQxma4Ya8dOdXHf/ctyU7vjpna/Y/OHeIUxtPNzZGky2RT3q+31l/u+aUu98YqZhubIpxSf+h6ZUxCO4X0xaXOxGIoBMTLHQJQ+rajI2Erh0c8QieV4tnoLB/+qPbgaS9MpODgNi7EcB79Y5G51kbvWReZSB4nClYR57+McGYTc7cp8bLlczpez5tj+rNVqGfzMMMQSMR9P+YCRT13d+tKrdTQpP29FE5+MT2x13jwFtDn7LvZLSQDBXOldkVhmLeiNfAyFRyOOpxxGKpHRvWlvhnUdScPgppWO/3n3Uh5pPdBm0ftPtIYKdiX8zR9x69idsA2dQYufewBPdHqaXs0fYfmOxfx5+DfO56cR5h1hS7v6t/iITztyhfg4nXGSDQfXMqDdkzir7D7i9xtmfSFylzrI1FaTArFUhUfrd5BeTLsSSRR4tPqfrf8PgMKrGZaLEcr/GjJnFS4Xo6+9t8xGqlKQuGgDYY91RaK4ucLPOsN6sv+Vz2ky8QnE8tsrGrUYyzBX5IJIhFTlazMDuV0EQeCbTXMZ1Ws88/6YzfiHJ+Hp7E25royPVr/L6D6v4OcWgMliYtbaqbzefzJvLRnP873GV3Lds3N/Ex4ezpkzZ+6IABEEgVOnTnHw4EH8/f0ZNGgQTk7XjrCVl5eTrSum22tDUSgUV7x/JjmJ48eP17hzl537l/tOgNxJTBYTK3ct5av1M9EatLSIjCHu7D5a1m1Lv388oIqkDni0egtVQCeKz/xI4bEvLr4hAsSIRGIEixGRRIEqgWbojwAAIABJREFUsAsuUcMv2/jeCMGCxVCMV+yHSJTuXG0rsSwlG68qdjv9auF8snKy2bx2PU0aXrt7uFOYn23u6hIgFkMxmsMz0ebsR6ryxbneE4gQUXz6B1RB3VAHdkWXd4SK83+Ru/M11CE9aBTcgEkDpxDgEXTFfD9uX8SRcwd47B8mBAaTnn1ndrEx7le2n9hMmbYUD2cvHmkziF4tHqFpWEubde+LD73BjhN/MfG7MXz63DdM/vFVHmo14IrIx4xVk5kz8lub+Cgs0zBx8Vhc1K680Ovlavls7NxbiKVqQMCsL+TC9pfw6fgZSq/mWAzFZP89Bu/2H6P0stp652x5DtdGo8FiQqS8t9OG7gYKN0csBhOladn4tLv5aJBrVBASuYzCk2l4NLs1swld7kFKEldg0JxCJFOBYEEw61F4NsW53pMo3G8vtTQx6xR5xRfYd3onr/d/F09nb3RGHR+s/B8vPTwRH1c/DCYDM1ZPZlDsEKKDGvFz2BJ2JfxN/7ZP3Na57dQeQkJC2LlzJzqdDqXyyk2yWyUrK4vdu3cjEono0aMHfn43NtlJSkoiKCjoquIDIDY2lnXr1lGvXr27kjJm57+HXYBUkaMph5ixajKn0uNpXa8dEwe8z7GUOE6kHuF/j31wlZQfEQ7+7XHwb4exOBl9wQlMFdkIZgNiuQsylzCUnk1tdSFVRiRG4XHtGgOLyYxJq0fuUrUbxrKVK5g1fcZ1xQeA1NEBkViM4RpNjMzaPLS5B1G410fmFHrD85p1GnJ3v465PAfXhqNwDO+LSCxDMOtAJEYV0AnpxUiRS9Qwis8spTRpNf3cG+Dl4nnFfCt2LeGT1e/xwVNzMZlNHE7ez8a4X9l6bCOFZRqcVa50a9KbXi0eoVVkLDLJlTupPq5+TB8+hwnfPEe/aV2YNGgK/ds+bnv/UuTjs5ELbeIj5cI5Xlv0PNmaTL4Y/T3uTleuzU7twWwx89exDZjMJjydvfFy8cbT2Ru1ayTG0jQQSXFt8BxipTXKKpY54dpgBBKHy8YDLvWfQe5ah6L4r3BpMLKmLuWeQldQYu3z4XML0WmRCHWgF+WZeVcIELMuH7HMGdE17NERLBQe/5KKrB04RTyKe7MJSNUBAFZzjwsHyd/7No4R/XGJGnbVKSwWsy2981qk5Sbj4+rPW49Nx0XlitZQwUer3+WF3hPwcfVDb9IzY9Vknuo6ilDvCARBQC5VkJqbfPOfh51ai0KhwNfXl9TUVKKiom57vtLSUvbu3UtOTg6tWrUiKiqqyv02EhMTadWq1TXfd3d3p06dOuzfv5+uXW/f6MWOnX9jFyA3QFNWwNzfZrBu30o8nb348OnP6d28LyKRiL+ObuDZHmMJu64FrAiZSwQyl4i7sl6RWIxIIsasN97wWK1WS/aFHB7qdeNCM4vRjCAISC651ghmDEVn0ebsR3dhP8aSNNyajKuS+BAsJvIPvI9Zm4dX7AcoPBtTmrwOuWtdFO71ca5nbSBpMRRTlvI76tA+uDYYidwlgoK4j9EcmYVHq//Z5lu9Zxkzfn4HQRDYe3oHs9dOJ7coB5VCTYeGXenVoi+xUZ2umpb1bzrU78qsEfN5a8l4Zv0ylSxNBv3bPoGmNJ+Zv0xh5rPz8XL2Ji0vhdV7lrFix/coZArmPL+I1pGxN5zfzt2hKnVAV0MiltChQTe+Wj+TZdsW2QwPHmj+IK/XbUhZyq+4RA3HYqog68/H8W7/MarArtYakD8ewzPmfRz82qHN3oPFWI7S++Z7ZNyXXLIfv1VjFpHIOodgwVCUiDZnH7oLB1GH9MQx7JFrDis6MZ+ylN9waTAC50hrJDN/32TkHg1wjhyMwr0+FmOJ1VlQ6nBF75ZzOYkcPnegkrX7PzGZTWRpznMq4wTerr64qFyp0JczY/Vknu/1MgHuQegMWj5a/S7PdB9NsFcYFsHCZ+s+oExXdk91h7ZzdwgLCyMlJeW2BIjRaOTo0aPEx8cTFRXF4MGDrxnJuBoajYby8nKCgq7MJPgnrVq14qeffiI3Nxdv7/vP3dFOzWIXINfAbDGzZu9yPv/tYyp0ZQzp/Cwv9J5QybXEWe1Sqdv6vYBILMLB2x1tTsENjzWaTDiq1SjkN75xaXM0IAhIlHlojsxEd+GQzYELkQSPFm+gCuxSpTWWnVuDQXMKj5ZvovBsglmXT9Hxr1H6tMKr7eUGkdrsPRSf+h6x3BnHsIdRBXbFVJFDccJiHAJ2ovLvwNp9K5i24k0sF3uubIr7jdjojrz66Dt0aNAVteLmuzV3avgAP0/cyKdrp7N489d89+dXSMQSgr3DmLh4DFmaTC4UZiERS+je7EEm9H0LX1f/G09s544hCALbT/7F2r0riE87QllFCU5qF5qEtaB/2yeIje6EqApPvzqjlu3xf5KYeQrhorFi04hWTB0yG4k2k7ydr6H0bIrCszHuzV5B6hgIWGtA3JpNQOYchqkih8Jjn+PSYAQisb3RFYDCwxmRSIQutwgH35uPglRk5iMSJ5G1aa7tvuPg3x6FZxNrOutVPmdD4RnK0//Eo9VblfqxONd7ArHisvWoY1hf5K5RFB7/Agf/DkhV1ujmmr3LmfvrDFZM3IhFsJBbnENS9hnOZZ0hKTuRpOwzpF44h4BAkFcI+cV5lGiLmfnLFEb3ttZ86AxaZqyezLPdxxLsFYrFYmH2r9Pp2rgXZ7NPE+wVetOfhZ3aTWhoKHv37kWv19+UaADrfS4pKYn9+/fj7u5O//79cXW9+QbBiYmJREREIJFcP7KnVCpp0aIFu3fvpl+/fvdUN3M7tR+7ALkK8WlHmLFqMidSj9IiMoZJA6dQ1/9KL+wBbZ9ELr25G8jdwK1RGDnbjyKYzIik177BODs5cXDbbsTiG+8UF8afA8ArpiOmop8viw+wpTmUpf6OzDkMmXOo9Xen4EoOVgCCWU9J0kqU3i1sgkWi9MQrdrrtYe4SqsBuiGRqHHwuu3k5Rw6m4vwWSs4sZWtmHlOWT8Riudzw0VnlwsD2w2gf3fmG13Q9AjyCmTViPun5qfx9fBMn0o6RV5yDIAg0DWtBoy7P0aVxTwLcr7+DZOfOU1ReyJtLXuJcdiINQprQs/nDDOsykvySPFbu+oHxC0bQvn4Xpg2bfU3b05Ppx/hl3wo2xf2Kr5s//WIG4+Pmx6n0eD4f9R0OcgeQ18Gt6Yvk75uMS/1nUIc9iGAsJ2fH83i1nY6Db1u0OXspPDoHdfADqIMeuMufxL2LRCHDvUkEufsSCOnX/qbGliRlYqrQ4t/tcbQ5HhTFz0Mw6zFX5JC742UEixGpYyAy5zDkl+49zmGUJv+COqQXqoDOAOTt/R/qwC6ogroBUBT/NWKZI85Rw5A5BVORuY3y1N+RhD/G1J8msTHuVzycvXh90WiSLyShM2gJ9Q4nwr8edfzq0bVJT+r41cPPPRCxSEzPyW149dvnmTZ0Nj6uflgECx+unsyoHi8R4BGEgMCn66bRo9lD+LoFcDhpP+8MnlHdH7WdewyzAJ/8qf1HD2IRyfoYUteXoVJZrjeUcZ2VOCmtD/25ubns2bMHnU5Hx44dCQ4OvqX1CILA2bNn6dGjR5WOb9CgAQkJCZw9e5a6deve0jnvd8QWC3XPHsen8N4xHSk/f460ml7EDbhvBEhxRRF/Hd3AwbN7Sc9LQWvQ4qJyIcKvHu2iO9O+fhfk0mvkCV+kqLyQz3//mF/2LMfN0YNpw2bzYKv+10zlUFQhpacm8OvcjLS1O8mPS7xuE6Hf/ljP2vW/8czQ4bSPuX76UNaWOJwjAnEODwXRGyi8mlJ4/EsEkw6poz8Kz8YYS1KoOL8Fi7HcOkgkQergWUmUWAzFWPTFOEX0p+TMj0iU7qhDeqP0bglY+y0UnVyIY2gf5K71UPl3BECbswd9/nFcG4zEMbwvhce+IDLQkU+fW0BGQTqZBefJyE8nsyCdSd+NpWeLh5nQ9+3b9tkP9gzlqa7P39Ycdu4cWoOWF74cQqBnMDOe+pyNh3/llb5vI5XIKNeVIZPK2TRlP+8te41x855mwbjltvuApqyADYd+Ye2+n7lQmE2vFo8wb+wyGgRb66FeXPAMX435oZKrmSqwGxIHLwqPfUFJ4nIUnk2wGKwpPPrCMwgmLS4NnkMd1L1GPo97mbDHupIwdzVBD7a9bgfmf3Pux80EP9wOiVKOY+iDKNzrU3BwOqrArjjVGYCpIhdjSQrGklQMxcmUn9+CqTwLBDNe7Wfa5nEM7YPMOcz2Zwf/9ogklzeQVAEdKUr6hTkH4th8ZD0AJRVFPNR7Ai3qtCHYK+yqtWOXeKb7aFbuWoqTg7Mt0tGvzWCr+BAE5v7+EZ0adqdRSDNe/2403Zs+iL974DXns3N/IAjwxwkjT7SWI5NYxYR30xubKfy4T89z7RVIKrTs37+ftLQ0mjdvTsOGDau0aXgtMjMzkUql+Pj4VOl4sVhMbGws27ZtIywsDJnMHtX9N2LBQmjqaYI0944ASctJJ+3efES1UesFiN6o49vNX7L074WU68rwdPEmwrcuPq6+aMo0bIxbx6pdS/FzD2DcQ2/wYMtHrwgjWixm1h1YydxfZ1BSUcygDsMY2+e1WmunGtC9BQoPF84t24xX66irdsQsKyvjmbGj0Gq17D2wj5P7j1wzvFpwJInCkyk0eXOobS51cE/kbvUoOPghprIMnOs+gVRlbc5m1uZiLEm9+FBg/aXLjbuYKiFFJJGj8GxEwaEPkKr8UIf0tp3LWJxMeeofSBSuyF3r2V4vT9+MNnsfTnUeQ+nT2nqdEi31GvW9Yr0ms4nC8gLKdWX3daMvO/DVhpk4OjgxvOsoNhxay8QB7yERSzmbdYqfdy1l0sApyCQyJg2cQv8PurNg0xwahzZn7b4V7E7YRtPwlox4YCxdGvesVCNUoS/njf7vXtUiVeHRGN8u8yjPO0by2T8ICuqOWOaIQ2BnlF7NEImvv9HxXyXkkVjOfv8Hp+evo/64/lUak7P9KHn7E+i54SPbazLnMHw6f0HJmaXWDRCVD1KVDw6+MbZjLKYKMtcPQKYOIH/fZJQ+rXEMewiAgoPTkLtH4xQxAIDiU99hMZSgDu2DyFDEx0//RO6jOew4uYXt8X/x24HVdGvS67riA2Bwh6fYc3oHL85/hkj/KPq07Efj0OZYBAtzf5tBh/pdaRTajOk/v82ZjJMsffXX685n5/5BBPRqJEMlq3oK008HDBw/Hk/6mcOEh4czePBgHBxu3zb6ViIZQUFBeHp6cvTo0esWrtuxczPUagGSW5zD+AUjOHU+HolYQq8WfZk2bLbti8JgMvDG4jGE+dRh35mdvL1kPHvP7OC9xz9GdnEXNOF8PDNWTeZY8iGahLfgzUFTiQ6s3Z2sJQ5yop5/hGMf/EDm5jgCerS84hi5Qo6Xpxfp59MJCgi8pvgw642cnP0zDt5uRDxe2QlD5hSKT6c5FMZ/TUnictybTkAkliJV+yNV++PgdzmqIpi0GMszKTw2F8FYgUiixKfzV5V2IAEUnk3w6fzFFcXs7s1ewRxdcNF6WEAkkWOqyL7qmqUSKV7OVdvdsVN7KdEWs3LXUiYNmsLGw7/yev93rc0kM0+yZs9yJg54H6lEyoXiHOZt/IzZI79h7NdPEeARxMOtB/Dao5OvuQOtUqhRKa7jJCcSM2/PeowmA5Pajb5DV3h/IZKIaTv3JbY+9h5iqZR6Ix9CJLn2Tm7WlsMc/+hH2s556Yq6EZFEiUv95xAspquOFUsUiMRSLGYdThH9kagv3w8cw/siUVyeTxXQCcFsQDDpbfcjbxdfBsYOYWDsELSGCnIKs/Bwur5VukQsYeaz85jy00Q2HPoFZ5ULAvD7wdVE+NblTGYC7y1/A1e1Gwtf+hkXtdt157NjJy8vj4cffhhPz+pxVzSZTKSkpDBw4MAbH/wv2rZty5o1a4iKirpufxE7dqpKrRUgxRVFjPriCS4UZtMiMgYvZ2+mDf0MqcR6SUaTgTe/f5GODbrRv+0TjHvwNeZvnMOCjXMwmgy8NWg68/6Yzcpd1hSL9578hL5tBt3QbrG2EDm8B+fX7yN+5jIcQ3xwqVe5VkEuk7P1t43sP3iALh07XXUOwWIh/uPllCZnEvv1q0jVV8bzRBIl7k0nUJG1E4uhGLH86lEjkdQBuUsdxDInTLpCEASkaqtXuWDWk3/gfZQ+rXEK74fc1bo7Yyg+S+GxL3BrNAa5Wz1bPUlxwiIEiwnBfGOnLzv3LwcSrfVLpzNO8saj7yIWS0jMOsWq3T8yaeAUpGIp6fmpfLNpLpMGTEGlVBPsFcrEAe8TG331f/NVZevxjfywdQHBXmE3PtiODcdgH7osf5c9Yz4l7+Ap6gzriXeb+kgcrBtCgslM4clUkldspTD+HG0/H49fpybXnE8kvsZXmEiCzDkEfcEJWzpc/oGpqAI6ogqw/t0XnVxorQGpa7XaLjoxD5lL+BVTOchVN3A6vIxSpuSDYXM4cHYPq3Yv5Ze9P1FcUYSDXEX9oEaM7vMKPZs9bOs9ZMfO9ejWrSuejreebvVvUlJScHd3x9n55vsTubq6EhUVxd69e6tcP2LHzvWotQJk6k+TyMhPp0PDriikiivEx6QlL9KufmdbkyeJWErfNo+xbt/PbIr7jb2nd1KuLeXR2Cd48SHrrtT9hFgmJfaL8Wx97F32T/icFtNH4tEsstIxfj6+9Hvo6haWZr2R4x8tI3PTfuq/OICAB65vJ6qqYhd3sdwZs06DIJgRiax/X4JZj0Fz6mJ04x9rqMjFUJiIqeICcrfL6Vj6wjMgWBAr7E3e/suk56VSL6ABr/WbjFgs4VxOIit2LmHigPeQiqWk5SazeOt83h40HaXcgcJyDWaLmfT8FGK5dQGSlpfC5KWvIggC6XkppOenEuwZWn0Xdp/jFOZL97XTSV6+hbOL1nPk/e9w8HZDJBGjyytCqlYS0q8DbT55AYXHrf8fVwV0pjx1A+qgboAIVUAH5P8QGErvFrb+IYLFhDZnPy7RT9/m1VlpHRlrs+U2mY1IxFK7g5CdGicxMfG2CslbtmzJ8uXLycrKwt/f7vxo5/aolQLkwNk9bD6yHm9XX5RSJVOHzraJD8PFyMc/xQdAZkE6ExeP4/PnF/PpumkcOLObz19YTLvbdEu6l1H5e9Bp6f/Y9dwn7Ht5LmGDulBnWM/rNykUBPIOnSFhzirK0rJpMH4QDV58tNrWJHcJtxaq6zW25m1iuTP+PZf9o5mYAIhw8GtHQJ9ViGXqSq87132CvLyjyJ3vTm8VO/cmIkT4uwcilUg5m3WaFbuW2Go+Ui6c44e/FzBp4BQUUgWasgJmr52Ol4s3t96MAltPh8GdnuK7P78izLcOe05tJ7hDaLVd138BiUJG5NO9iHy6F3n7l4G4OYLJgoOfO45B3tdNzaoqjmEPUZa8jgs7XsapzgCbG1bRiQXIXSJsblhlyesoT/8TsUzN3L2bScv9BrXSEUelE2qlIxF+dXm41QBb2u7NIr1B7YgdO3eD8vJysrOz6d791g0y5HI5rVu3Zvfu3QwcONAuqu3cFtUX27uLLNu+CIDmEW2uEB8TF4+9Qnycz09l4uJxTH7iI+oGRPPSw5OwWCwk55ytkfXfTRyDPYiZ3Z6QR2JJWbGVrQPf4dj0H8j6K47S5Gz0mlK0ORoKjiSR9MOf7BzxEftfnotZb6D9gtdp8FL/qxax3yrWAnIR5ef/InfnyxTEWYtLRVIHEEkwlqaTtWkI5el/AtjER3HCIrK3PIfFVEFF5jZrIbtXs2pbl53aR4h3OGezTnE64yQrd//ApAHvI5PIOJdzlqXbvmHSwKkopAoKSvOZvXY6rz76P/KKcwn1vjLNpqqUaIuZPWIBrSNjCfQMZt7YZZzPS62+i/qPUZG1E13OD7g38sa7bX2cQn2rRXyANT3Uo/VkTKXn0WbvRbAYAJC5hCNRW80FLKYKtDl7MesL8Wj5NgPbDSMuaR+bj6xn0+FfiQpqyCOtB92y+LBj514hKSmJoKCgm+498m8udVs/ffp0Na3Mzn+VWhcBMZj07D21nfYNrL7+/675uFrk483vX+Kdx2fYenlEBzUk3C+SbfGbGdZlZI1cxx1HsFCRvYviU9+DIND6k0VEPtWbxMUbydoSx/kNe68YIhKLcKkXQrN3nyFsUCekDtXf40TmFILSqymlSasRyxxtUZB/LBwEi/XXP1+9+JqpNJ2K9L9QBXX/R2TEzn+R1nVjefuH8SzfsZh3Bn+IVCIl5UISP25byJsDpyKTysnSZPDVhlm80f89krLPUFJRRLOI1rd8zkuuWKfOnyAqqBFezt48+8CY6rqk/xSm8mwKj8wGwYzuwgHUIb2q/Rxy10i8O36G5vBMsjc/jYNfO2TOIRiLz1Getglt9h7krpH4dJyDxMGLOmo/XujzCnPWfYiTyoVvN32Bi8qVjg26Vfva7Ni5myQmJtKy5ZWGNDeLSCQiNjaWzZs3ExERgVxuF+d2bo1aJ0BSLpxDZ9DxYMtHbW5XV6v5gMtpV5Of+KhSI8GtxzeRpckgtygHAaFKHZJrE/r8YxQlLMKgOQWAg6+1kZ9bwzDazByNWW+k+HQ6pSnZGIrKEculSBS5eLZsg2PIre8OVxWXhiPJ3f4SUrdo3Ju/AYCxNA2J0h2ZUwj+PZdboy6CBUNRInLXSFwbPIdzvSfJ2/UaIomC/Tp3Zr3TEkelky1Vwva7gxNtozrSvn6X++7v1s5lHJVOPNHxGeKS9iEIFtLzUvh+6wJrJEQqJ0tznvkb5/DmwKlIJFI+WjWZ4V1HoaiG5qGnM04SFWjtrn0jdyQ7VyJYjBQc+sDWM0h7Yf8dESAAMqdgfDrOQZd/BN2FA+guHAREyByD8IyZgsK9fqXjh3cdxZZjfzCmz6tkF2by7o+vER3UkFf6/Y86fvWufpKLFJZrSMw8RYW+HC8XH+oFRCOT2B/Q7NQsGo2G8vJyQkJCqmU+f39//P39iYuLo23bttUyZ61GBHJXM0p3c02vxIYszwz3uE9PrRMgmrJ8ALxcrLaKl8RHbFSnf4mP81cVH1uOb2Tlrh94rMNwfvx7IVqDFpVcdXcv4g5hLEmmOOE7tDkHsNZLWPl3h/FLXYndm1yuoSg8Nhdd9nc4Br8P12i8WF3IXerg2nAUhce/ouDQB7jWf5acrS/g4NsGzzbv2VK+ys9vQXP4E9ybTUDp24aCA1MxFKfg2WYyvXxi+P3YVnad3GqbVyaV061pbwY2H0Kj0GZ28fEf4PneLzPy88G8uOAZfFz9eOux6SikCjIK0vhm0xdMGjgVnaGCNxY+h6ujO093e6Faznv6/An6xgyqlrn+i5Qlr0Md1B2pyhvEMvT58QgWw53roSISofRqjtKr+Q0PlYqlTB3yKQnnjzMwdgi9mj/CN5s+Z9isvjzUegBj+ryKm2Nlw4zTGSeY+9tHHEzcg79HELlFOaiVjuiMWgbEPsnIni/ZexLZuUmEGx9SRRITE4mIiLitBob/JiYmhpUrVxIdHY2rq2u1zVsbEUkEgvqUEhZy72w2eG2sgJ01vYrrU+tqQC4V9BnNxkriY0Dsk7ZjrOJj7FUjHyt3/cCnIxYgk8gQIUJ6LRvHWoapIhtd3lGU3i1xazLWWlNxEaljwPUHCwK6CwfQ5uyn9NzqO7xSK47h/XBtOApt9h5yd09ELHe02WNeQuHZCAf/9hhKUsjZ+jyGoiQ8Wk7EwbctIpGIyY/PwEl12SXH09mb3i362sXHfwiFVMFXo39ArXRkx4ktLNz0OX/Hb2L6z2/TtUlP5m/8jH7TuuDl7M1nI7+tloLgMl0p6fmpRAU2rIYr+C8i4BjeF8fwvhjLMlEHdsGj1dsYChNremE2wn0j6dTwAcAaaZvQ9y1WTtqEpiyfvtM6sWTrAgwma03J2v0/8+ycQTQKbcYf7++lW5PezBn1LZunHmT+2OWsP/gLj3/ch/P5qTV4RXb+qwiCcEvNB2+Ek5MTDRs2ZO/eK9O57dipCrXu6TvA3drPIin7DKt3L7tm2tW7T3xMpH+U7fVLkY9PRyxApVCTnpeCh4sX8vukuFCq8sMpwtpduOjkNwgmHQ6+MWhz9iFzvHqztUsYS5IxVeQCUJzwHXL3BlekJdwJnOoMRO5al8L4rzAWJ1N4bA7laRuROHgBFkzlORiKziKY9Sg8G+HWeBwy58t9F3xc/Xit/7u8u/RV/D2CkEvljJ//LDFRHXj10XcqiU879y+OSidmPTuf/Ym7WLt3BX/EraOkoohZa6bQOKwF/2fvvqOjqNoADv9mdjfZ3fROQkIPnQQIvTfpUqQXUQEFFBsWUFRAsSHFAijtE5HeAyJFUERAQUoSeg0kBBLSe7bNfH9EIkgEAjEF7nMO58DsnTvvLOySd+6975357AJCKjcutOudu3oaL2dv3Bzc795YyIeEJOtQFSvW9Gh0zhVz14KpJWf6AoCT4dYSwP6e5ZkxfB5/nvud6RveZ+2+ZXRv9ARLds1nzpjvqFepEV/9MI2m1VvRMDB3WsofZ/YwZch0Dp7dx/NfD2PVm1vvvMGlIOSRsFltPOhz4piYGLRaLT4+hb85b7169Vi5ciXR0dEEBATc/QRBuEmpS0B83cri4+bLV5un8Va/D25JPo5dDmPKijf5eNiXtyQfS3cvZN/J3XnJh9lq5s+z+2lc/d72rihNTAnhpJ9fj0P5TrjUGEbOjj/ROtx5BCQ77kDe71XFStKhT/BpMwfZ7r+fMmDvGUSZNnPJiT9C9tV9mFPOYkm/DJKMxuCFQ4WunDUbaRwyjPzKp/Zs1I+dYT9yJeEyq8dvZ9XeJczf+gWDpnWlV9OBPN/1NTycCr6LbHp2GunZadjp7HE1uopSmqVA46otaFy1xX9+nVPRx6geIEY/HpQ1IxpJq0ej/+uMlp9zAAAgAElEQVTzKZWOzfkaBjZlxRtbWLd/OR+vfofJQz6jXqWGzN06k7qVGuQlHxv+WIWz0ZXmNdrQrHprDp8/wIIdX/Hy4xOK+Q6EoqaqsPqgGa3m3kfmrTaVzZs306dbG9zd7/9hx38x+nGDTqejcePG7N+/n379+uVN8VIsmSjmlLv+7CE82kpdAiJJEp1DerL05wWEVGmSd/x4VDjT1k1m7pgleZVqIDf5OBV9nK9GLc6rmLXtSCjJGUl0DelZ5PH/lxRLBklHpqM1+uBaZzSy1ojeuyGy/Z03WcyOPZD7RFK1onMMwJadQFLYTDwbvceD7JlwzyQZvXcD9N63V+hIz07jpXcaMNNYPt89WyRJ4t0BHzN0Rg90Gh1PthlJtwa9+WbrTNbtW872I5t5ttOLDGr1NHZ3WXx8/toZ1u5bxm8nfuZa0hVsig1JknA0OBFcMYTuDfvwWN2uIhl5xJ26cpwaYvrVA7OkRaJzKvefrzn7L8iSjJezN/5e5enW4Almb5lOSOXGNK3eCoC1+5YhyzL9mg8F4Ic/11HNvxbr96/g+a6v5RVQER5+sgSDG9ujqndb0yFx87qPAQ3tCHaqRmhoKO3atbuvBeRWq5XIyEj69u1b4HPvVWBgICdOnODEiRPUqVMHc/IpEg99jM2UiludMf9ZcQmh9Ct1CQjA0DYjWP3bEj5Z8y5zxizhZPQxPl37HjNGzMs3+ZgyZHreWo/kjES+3PQp1QNq07Jmu+K6hf9EcsQcbDkJeLWYjqzNXVjvFNgP6Q7rXGymJCypF3APGU/K8XnoXCri1WI66efXkhn1Ew7lOhZV+PmKSYwix5zNqwtG8tmIb2hd6/ZNlHxcfXnx8fEkpMfj5eyNu6MHb/f7kH4tnmTmxqnM3DCVdfuW8UrPibSt0/G2zZOyTJnM3DiV9ftXoKgKejsDIzu9iLerLznmbCLjzvNz+Db2nviFBb7VmDx4GkEV7r6YVXg4nY4+QfvgLsUdRqlnSbt0y5TK0ubwhYM0r9GGuT/OoGFgM5pUyx19W733e+x09vRq3B+AjX+swmQ18c6Aj/jp6A9ciD1L9bK1ijN0oQjJEoxqeb+V92rh6+nMrl27qF+/PkFBQQU6OzIyEnd3d5ydne/e+D7dKMu7beuP+GsjyLq4Fudqg9A6+pMcMYec+MO4Bb8iyuYLtyl9j57IrcX/as+32X/qV95e8nK+ycey3YtuSz4ycjJ4ZcFIUrNSeG/gJ8hy6RjyvxdZMb+QFf0zToEDsHf/+z83e486dzwv5/ph3ILGYizbCp1zRSxpl9DYu+JaayQG3+b/ddh3dSUxCgCTxcTrC0fxc8T2fNs93rAPzkaXW44F+lZn7pjv+XL0t8iyhlcXjOS5OYM4E3Myr01SRgLPfNGX9ftX0LH+45T3qcQXzy3i+a6v0bfZYIa2GUGPRn0J8KrA+0NnkGlKZ8SX/dl2JPS/u2mhxMqx5HDp+oW8ErzC/bOkRZbqBCQh7Tr7T/1Kk2ot85KPpbsXYrA35iUf639fgU2xMaDFMCQkvF19SUyLL86whVImICCAnj17cuLECX799VcURbn7SX85e/bsfzb96maeLlqaev2B6dpuPJtMwujfFjvXQLyafYRqMxO3ewzmpJN370h4pJTKBASgf8thDGz9NFsPbcTZ6HpLMrFs9yJOREXcknycvnKcZz5/gmOXw3h/yHRqlQsurtALnS07nuTw2di5VcW52pACnWvvViNviFTnUhFrxlVUmwmgRDyxiEmMzvu92WrmzW/HsCPsh9vaSZKU7/4OEhKta3VgzfjtvNFnMmeunGDQZ914f+UEriZd4cV5z3Ap7gKTBk/jemosb/X94JZ1BBGXjjBj41RmjphHz8b9WfnmVmqVC2Liklf548ze/+amhRLDYjWz6cAaXlkwkp5T29D7w7ZIwM6wH8nISS/u8EohFcWchjUrFkv65VKdgDjqHWlRs23emo9FP83By8WHxxv2AWDr4VCyTJl5FRp3hW/lUux5HPSOxRazUDq5ubnRu3dvUlNT2bJlCzk5Ofm2s6RlkXruChmX48hISyc2NpbKlSvn27awZMcdIO6XMXj6BeLZZMot6z5krRG34LE4VupB/O8TSTu74rZNhoVHV6mcggW5P1hO6PM+Zdz8mPPDdHq834rujZ7AUe/MmZiTfDB0JteSYjgZHcH2I5vZHbEDFwc3vnzuf7So2ba4wy+w3KRAQdLouWVdhqqQdHQGqmLFPeRNJLlgc4tv3iPEzrkiqmLBmnEFnct/+6V1r64k5I6A6LQ6ArwqUq9SQ2Zs+ACL1UK3Br3vuR+d1o6hbUbQvWFvvt46i3V7l7Hl0HpM5hzeHvAhoX+sZlSXV25JPo5ePMSs0A+ZOWIens65O7a7GF2pVCaQC9fO8e7ScWyYuEvU939InYyOYMLiF7HX6enZpB/+nuWQJJny3hXZdngTi3bMYfLgz2hd+/ZpgcKtFEsm6RfWkRW9C2tmLJLGDlUxkxIxB8dKPXKnepaSReg31AgI4oeDawFYsP1LAsvWoM1fpXs3H1xLek46Q9uMBGDH0R84fOEAkizfdTNDQciPXq+ne/fu7Nmzhw0bNtClS5fc/TdUlcub9nNu8VZSTl3GzsURm8mC1WLBPbgc5nZJ2Ff0LfR4VMVC6omFZEbvwrXOs3fcY8dYtjV2roEkh8/BFH8U95A3/y4+ITyySm0CArlPvZ9pP4bWtTowf/sXbPx9NSZL7pOB9hPr5w1Vujm682S753i6w+hSUzpTVaxkx+4nK+ZXzEmnUMxpoKpIOgfsXKtg8G2Og39bMi5vJef6EdzqvoTO8cHK4N14GmlOiywxCUhMYhSt6zyGl4sPPxxcy9LXQnm731SOR4WRZcoscElLVwd33ur7Ae2DuzBq9iCa1mjF1kOhPNfppVuSj/DIw8zc+AEzR8zPSz4Avvt5HpIk8+Wo//HM531Y/uu3PNfppUK7X6FkOBEVwag5g3iu88s82eZZfo7YhsmSw4jHxgK56462Hd7E20teZvLgaTxWt1sxR1xymVPOkXBgMjrn8jhW6Io59SIeDSagWDLIivmV1DPLyIzagWejScj2pWdDs/bBnZm+fgrvLXuNx+p2o2Wt3DWFG/9YhcVmZnCrZwD48dBGkjISkCWZtnU6igcWwn2TZZk2bdoQHh7Oxo0badOsJdHT1pN+8SpVn+mKd/M6aI25MwEyr8QTtWkfO3u/Q713n6JCn1aFGkvS0ZlY0y/j1fwjNHcpdAOgdfDDs8lk0k59T9yvL+HXcWmpLECRH5tN5aUPNmPUl5ziEnEJGQRUrlvcYdxRqU5AbqhUJpBPnppNenYaEZeOEhUfSbY5CxejK5V9q1IzIKhU7fdhSggnOfxLLOnRaPQe2HvWQevgB5KMLTseU+JxksO+IO30ElSbGUOZxjiWf/AfgLQOfkgaOyxpkYVwF4UjIyedGSPmc/n6RdbuXcruYz/RrUFv6la8vWJWQZyKPoaiKJy/eoapT866LfmYsXEqs0YuuCX5WLzrGyJjzzNp0DQ0soaGVZux8feVjOw4Fvkh+SIVcqf6TfhuLM91foVhbZ9l25FNXEuOyUs+fj2xk9PRx/nwyc/Zc2IXby95ieCKDfB2Kfw6+6WdNfMa8fvfwuDXHINPo7x1ZdbMGMypF3Cs0B2Hch1JOjKd+N8n4t1yFpKmdHxXuxhdGdFxLBt+X8G4XhOB3OpXSNCv+ZMAbDm0ntSsVMp7VeLrLTNZ/uaW4gxZeEgEBwfj7OjEvtEzCSjrT8v/TUCjv/Vz4+DvRY3ne+Hbpi4H35iLzslA2Y4NCy0GO9cqqJaMe0o+bpBkHVrncujM6Q9N8gEwZ+7XmEym4g7jNn5+fsUdwh09FAnIDU4GZ5rXaE3zGq3v3riEyrgYSvKxb9AavfFo9A6GMs1IP78ac9IpPBq999cUK5Wc+CPE738HVBuy3p3c8n0PVjJX0tijdfArMQmIqqo81X4UXs7eeDh5/vXUObRAU6/+ze+n96C3M/DB0Fn/uubjn8nHxdhzeckHQPvgLny8+l2i4iOp4F0yRoyEB/fjoQ0Y7YwMbTOC0AOryTRl8kz7MQAcOLuXiMgjvNj9TSB3E0SjvSPf/7KA13q9U5xhl0jJ4V/gUK4DOufKZMX8mpeAmFPOk3VlN0a/VkiyHU6VnyB+/9ukn1uFc/Uniznqe/dMhzFcjD3LsJm9aRvUkSp+1fPWgOwK30pM4hX0Oj3jF7/A1Cc/p7xX6V3zIpQs6p+XcEdPvSnD0dj/+5N315oVqDfpGQ5NXIh301ronIyFcn2DbwtST32HajMX6KFBTuyfxV5ds7D179+/uEMolR6qBKS0y4z+ieRjczH4NMa9wYS8UrqW1EjMKWdBsYKsAyTs3WqisXcGSUvmpa3IOkdcaz37wDHonCtiSjxOYSQ0D0qSJNoH5ZY7lSWZjvW7s3LPYlIyk3F1uPenLv+kohIZd4G2QZ3yqtdAbvIxfcP7t027yi/5SM5MIvSP1YBKZNx5kYA8RH6O2E6PJv3Z8PtKVNS8qTT7T//K0Yt/Mrb7GwD8ee539p3azeTBn/HR6rdFAvIPlrSL5MQfxVCmKQ7lHsOh3GNYM2IwJUbgUL4LxrKtsWbFknP9EI4VuuPZ9AMS/piEU9WBBV7LVlxkSWbq0M9Zunsh87Z9ToBneU5GRSDLMpsPrEWr0eHl4s03Y5cTVL5ecYcrPCxUlTOLtlB9VM87Jh83eDWqgUvVAC6t20Pg03fel8Nms5GTk0N2dvYdf2VlZVFPb8AtIQK9z73NSFAs6ZhTzuHZeNI9tRcebiIBKSGsmddIDp+NvUdtPBq9mzfSARIeDSagKlYkzd9VniStAd/HloCkISViDunn16L3qo/eO+SB4tA5VyQrZjeKOQ3ZzuXuJxShziE9+f7nBewK35pXWeZ+qIpKVk4GLg5/zze/kXzMyGfNxz+Tj5TMJMYtfI5ezQZyMiqC9GxRDelhEhl3HkeDEw0Dm9K7yUAA9p78hYhLR3m+62tISBw8t58/Tu/h5ccnkJyZxNWkGEwWE/a6+633//DJuX4Ejd4TU9IJHCvlbvpqzbxCduwBHMrnPliwZcaSfW0/jhW6Y+9eE0nWYUk9j51bjeIMvUAkSeLJts/Su+lAfjvxM6euHCfLlMmQNiNpENiEepUaiimaQqHKvJJAdlwyXk1q3vM5vu3qE7XzT7Stq90xsTCbzWg0GvR6PQaDAaPRiMFgwGAw4Onpmfd7g8EAsRLZcQfvOQHJuX4Ee49aJe5nC6F4iASkhEg7/T2oKu7130CSdaSdW0VG5GZ8Wn2BRu+BpNEAKvH7JoBGh1eTqXkJiaFsKzIubSH1xMLcShTS/Y9c6FwqgqpiSbuMvWfBNj36r9UqF0SAVwW2Hg59oAREkiUM9kbSs9IAOHrxT2aFfnTbyMe3O+cSGXfhluQj25zFKwue5YVur6O30wO55TiFh4ei2KjhXzsv+fj1+E7OxJzg+a7jAPj9zB4OnfuDFx8fj4TE+WunUVUFlbvtdPxosWVfxyGgPS41h2PNjMGacRW9T2P0Po1zy/CmXcJQpgleXnVRLOnkxB5E6+CLNet6qUpAbnDUO9ElpCddQnoWdyjCQy4zJh6jnyey9t4rxzmW8yHxfDSZx47dkkS4u7vn/d5oNKLX69HpdLdt2Jsfq10b4n5dm/uA9A4bHt+QE3sQQ9nCXQwvlF4iASkBFHMaWVf34FC+E1pj7maKEhJSfmUpJRnpH0/TJI09sp0z5tQLmJJPYe9+709FbonDmoU1IwZJ1pIcMRtZa0S2c0bnUhG9TyPs3Wo+UHLzoCQkOtd/nEU/zeV6auwtG08WtJ8K3pU5E3OSiEtHmBX6EdOHf53PtKvzTBr8d/JhsuQwYfGL9GsxhIaBTVm7fxmAmH71kPH3LJ+3V8PBc/s5dvkoY7vlTru6GHeOnyO283a/qUhIRMVf4otNn+Lp7J3vPjSPNFmHajMDYEqIIDv2AHqf3EWw5sQTZF7ZhaFMEwAs6dGknV2BJOtKzfQrQSgukkZGtdkKdI5iteHi5kqnHj0KLQ6toz8agzfmxOPYe9254pJizcKUdAr3kAmFdn2hdBMJSAlgSgjPrWbl2xxz0ins3KrhFNgfp8B+gITNlIJiTkXnVB6vZh/lnWdJv4xs54y9e03KtF/Ite2Dybn+Z4ETEFUxk35uDenn16FYMpC0eqyZV7Fzq4Y18yo51w+RdmYFdq5VcKn1LHqv4pvL3DmkJwu2f8WOo1sY2mbEfffTuFoLvtr8KdPWTebzZxfmu+Zj8uDPbkk+3vz2BTrU60q3Bk8AsCt8G2Xc/KjgIxKQh0mLmu3YeiiUMm5lOXzhAGO7vg7A+WtnWbnnW97sMwUJiUvXL/Ldz/Oo4luNqn7V7+mJ4cMoLTuVVb8tQZZk/D3KUdazHGU9AtA6liPzwrq/Hq50waF8F6xZsZiTTmEMaI8xoD227ARyEsJwCOiAT5vZxGwdgM65QnHfkiCUaE4V/ci6moA1y5RXdvdu0s5dwaly2bs3LCCjX0uy4/68awJiij+KnWtVNPrSsRWC8N8TE1NLAEvaZSRZiynxOHF7Xib72r6/Xsn9gSbp0MfE/fI8iiXjr2MSiiWDuN0vkHgoNyHR2LuiMfpgSbtcoGvbshO4/ts4Uk8twd6zDt4tpuNUpS+qzYxz4ADKtF+IX+eVuNV9GWvmVeL3v0XqqcVQTNNNqvhWI7BsdbYdDn2gfro3fAKtRkdFnyp3XfNxc/Jxo8LN8ahw/ji9h55N+ov53Q+ZXk0GcP7aaX44uI6x3V5HkiTOxJxk1W+LGd/3fey0dkTGnWfJz/Pp1bg/O45s5sl2D14AorRyNrgwqNXTnLt6mje/fZ4hn3Wn7Vt12RUdgzXzGpmXfsxra0m9SGbUT3//Of0ymZe3AZAZvQudY9nckuOCIPwrvaczbrUqcvWnQ/d2gqpy5cc/KPvYg5Wvz4/BrwU51w+THXvgjr+yon/B4Nfi7h0KjwwxAlICKJZ0JI09Rr9WKDnJ2LnXuuV1h4rdsHOvkVcVC0DWGnGq0u+Wp4WynTOK5d4XRCvmVK7vexNbTiIejSZi9Gv1Vz9OKKYU7Nyq5fXrWKEbslZPZtRPpJ1ZjqpYCqXq1v3oXL8Hs3/4jOjEywR4lL+vPnxcfRnQ6imW/7KIjvW707JmO777eR7nr51h8qCbRz5MtyUfWeYspix/EzdHd4Y8wCiMUDI5GZyYPHg6E5e8zG8nduHlUoYNv69kfJ/30Wq0nL92hhV7vqVXk/68tmgUo7u+SiWfwOIOu1iYrSb2n97DtsOh/Hp8JxpZg6IqvNFnEr1aDCf1lETO9SNY0qNQTMkYfJth8G2GLTsBS0Y0eu8Q9N4hWDOvkXpyMR4hrxf3LQlCqVBzbG8OvP413s1ro/e886LuS+v3oCoKAV2bFHocOueK6L3rkx3z2x3bSRo9RpGACDcRCUgJIGn0qIoFrYMvbnVfBsCWHY855RwG36a5icFfyYE56RTIMnau1XCp8RSQO4Uq+9p+FHMaOkf/e7yqStKR6diyr+PV9CPs3KqTFbMbg09jdM4VcQvO3d3bmhWHJS0SQ5kmGP3bY/RvR3LYl6SfW4u9e828uv5FqdNfCcj2w5sZ2XHsfffzQrfXOXhmL+O/fYFuDXuTY8m5PflY/PwtyUdGTjqv/280F66dYdazC3Exlp6dm4V717p2ByYPmc7EJa+g09oxadA0UjKTSMlM5t1l46hWtiaj5gzm+a6v8WTbkj/6EZ1wmbDIQySlJ+BkcKZ2+boE+t7ftDGrYuXPc7+z7XAov0Rsx9XRnc71e7D41fU8++VARnR8gSGtcxNz52pDMSefIe6XMdi518S7RTAAOfFHyIzagd6rHqakEyT++RGybys+2bWW6gGnCa5Qnyp+1dFpxHoQQchPmVbBlO/VggOvfEXDT0djLOuZb7uoTfs4u/AH2ix7B1l374vWC8KjwcT/pF/h4SYSkBJA6+iHajNjzbyKzjl3o6rUk9+SGb2TMu2+QedcKbehqhB/YBKSrMOv07K887Ov/kbioU8BMPg0vqdr3hgWda39LPaeQWRG7SDpyHRc64zBqfLfG/2lnphPVsxv+Hb4Fq1jWUDCtc4oMqN2kHLsG/Q+jYp80WiAZ3lql6/LtsOhD5SAGO2MzB79HaPmDGbNb0t5+rHRZJkycTI4k5KZxCsLRtKn2WAeb9gHVVXZf/pXPl07iZjEKN4Z+DGta3coxLsSSpoOwV2oWzGEZbsXMXPjVKLiL6GqCmXc/NDIWla8saXEFyA4f+0Mn62fQtjFQwRVqIdVsZKQep3kzGTKuvvz2hPv0Siw2V37UVWFo5GH2HZ4EzvDfsROa0en+o8zb+wKqvvXQkLi99N7eKr9KJ5qNyrvPEnW4dnkfRLD55IT8wuJhz9F71kHWeeEwbc58fvfxpx8Epeaw3Gs2APbyXF8vDp3PxWDvZHG1VrwTIcx1K1Y+FNHBKG0q/vWEHSOBn4b/jH+XZtSpnVdHMp6YsuxkHL6MlGhe8lJSKHVkrdxrVmhuMMVhFuIBKQEsPcIAkkm+9r+vATEKbAvOtcqaJ1ummIkybgFv3hbdSy9T2MMfs3JvroPe6/ge7pmxoX1aIzeOFbqBYChTFNcag7H6N/mlnZOgQOx86iD1sH37zA0elxqjyQl4muyr+7F6N/2Pu76wXQO6cln6yZz/toZqvhWu+9+fFx9WfLqRj5e+y6Ld85jzW9LaVGrHRdjz1KnYj2sio0ZG6ey9+TPXLx2Dn/Pcsx9fimNqxb9yI9Q9DydvXm5x1u83OMtrDYrKmqpeSq/99Ruxn/7PANaDuOz4V+TmpnMop/mMHfMUmRJZt62WYyePZg3+05hYMunbjtfVVVOx5xg2+FQth/ZjNlqpkPdrswYMY/giiG3rX0K8KpA0+q3l9g8eeUUX/5+kNnDZpMVvYusq/tQLZnIejeibQ5Ua/U1Dk653y9Ptn2WzQfWoqoqLWq145NhX6ItJe+3IBQ5SaLWS30I6NqU80t3cHzmKrKvJSLb6XCpFkD5ns2p2K8NGv2971QuCEVFJCAlgNbBF3uPOmREbkZVrDhVfgKdc6W8kY+c+Nw51E6VemL0a/nXWSrpF0PROQag966PYk5Do/e4p40IFUs6psTjOFXpQ0bk5tw5nF71cK6au++BNfMqmdE/4VS5L3augdi55s5vz4k7iDUrDseKj+NUsSfpZ1eSfW1/sSQgHet1Z8aGD9h2ZFNeidT75Wx04eNhXzK49XBW713C/pO7SUiL52zMKdbtXY69Tk/NckG8O+gTHm/YB3udvpDuQihNtJrS83V56foFJnz7AlOGzKBDcJfcil27vmFC3w/Q6/TEJEaTmJ7A/BdX8sqCkfh7lqNFjdzP8cW4c2w7vIntRzaRlJFEu6BOTBr8GQ2rNL3je+DvUe6WP5utJr7Z9jnf7fwGf8/y6BwDcKnx9C1t1m//grmLX+bL577FaO9AVb8aNK3eCmejC6eijzN+8Vg+GDoTo71Dob9HgvCwcK7iR/3JT+f+IXf/YkEo8UrP/6gPOZcaT3J975uknVmGzrk8xrJt8l5LO7McU+IJHALaI+ucAFAsGaQen4edWw2sWdcwJRzDLXgsknz3Jx2W1EhUxYrWqRxJR2Zi71nnltK6mdG7SDu9DDvXqhjKNM07nnp6CZbUSBzKPYak0WPnXhNz6vnCexMKwNvFh5Aqjdl+ZBMvdHsdqRC+ceuUr0ud8nWxKTaSMhJJyUzCXqfH29kHvZ2hEKIWhKLxeehH9Go6gA7BXTh79RQr9ixmQt8PsNfZE58ax5wfp/PugI8x2BkZ2/0Npq2dzLlmp9l2KJTL8ZG0qt2BV3q+TbMabe5rf5MTURFMWvYa566eBsjbV+WfriRE8efZ33nhm2HMHvUdDnpHhrUfxenoY0zs/yHjF4/lqc+fYNbIBbclOIIg5EMkH0IpIRKQEsLeIwinSr1Iv7Aea0YMNz/GcK/3GracxLzkA0DWOeHV/DPMySdJifgavXd9HCt0u6dr2UxJANi5VMa7xTQ0Bq9bXneq0gc71yoYfBrdctwjZAI2cxqSJncEQKP3whQfBqoCxVCKtnNITz5YMZ4TURHULndvU8/uhUbW4OXsjddN5XkFobRISLvOvlO/snXK75y6cpwNv69kYr8P0Wq0xKVcY86W6bzdbyoGOyMJafGciAon25zF3pO/8Mxjz9O6dgcMdsa7X+hfmCw5ZJoymD7iG9767iWup8biqHfKt+2VxCgAwi4cYvTcIcwd8z1Nq7XEXmePs9GV2aO/44tNHzN0+uN8+swcGlcVVXQEQRAeBiIBKUFcao3EZkoi9dR3mJJOoTF44lZnNFoH37w1GGlnVyLJWhzKdyb72n7SL6zHzjUQj4YTIb+d0/NxYw2JqijYe9QBwJoVS9qpJThVHYjOqVzeyEd27B/kxB3Etc5otI7+ef9g0k5/jyn+SLEkHjd0CO7CJ2veYdvh0EJNQAShNDt+OYxKZaoQl3yVH/5cz/g+k9HIWq4lx/D11pmM7zMFB70j8alxfPnDNN54YhKfrp1EpTKBdK7/4Lsk2+v0NApsxtbDoaRkJjFn9BLmb/8i37YxidGEBDbBxehKv+ZD2RH2A70aD6B+pdyHHxpZw7he71DdvxbjFj7H891eZ3DrZwplxFMQbvI0IGqqC48SU3EHIBKQEkSStXiETEDnVD5vrw1bdgIG36Z5m3OlnV0OioW0M8tRrJk4lOuIW53nkbT3PkVIY8x9sm/NjMHOtQqQW943M3ondh410Tn9PdUhK2Y3WVd24xTYH62xTO5B1UZ65CYUUyo6l0rFloS4OrjRuFpLdhz9gXG93hEbAgoCkJKZTGZOBlsObeD13u+hkVO/KbIAACAASURBVDXEJEUzb9sXTOjzPkZ7B+JSYpm9ZRpvPjEZJ4MzHk6epGQmFVoMOZZsvtj0Ma/0fJvq/rXoVP/x29qYrSaaVGvB0+1HM+izbnz45Oc0s2+db39dG/SmvHdlXl04kjNXTvDOgI+wu4+pYYLwL8zFHYAgPGrET2wljSTjXG0IZdovwLFSDyyp50kO+4L4feOJ3zce1ZqDpNGj92mIT6svcK/3WoGSDwCdUwUkrYGc+CN5x4xl21Cm3Tc4lu96S1v34Jfxbb/g7+QDQNLg3WIGyBrs3Ws80O0+qC4hPYlLvsaRCweLNQ5BKClcHdxQFCUv+YhPjePrH2fyVt/c5CPLlMWs0KlM6PM+TgZnTJYcjl78E1cH90KLYfGubyjj5kenermJx2N1b58emmPJ4fXe71HRpwrlvSqy+9hPt7W5Wa1yQax4YwvRCZcY/kV/rqfGFVq8giAIQtESIyAllNbBD7egsbjVGYM1Kw5bdjyqqqDRu6N18HugvTckjT0G36ZkX/mV65nX0HvVxbnq4L+rbsX9SeqpxXg0eCt32pVjAACpJ/+HJe0Sno0nYUoIB8V2y2L54tC2Tkf0dga2HQ6lQZXC3+VVEEqb2uXrEZ92naSMRGw2K7O3TGd8n/cx2BnJNGXw6brJvNzjLRz0juSYs/lwzURORh/jpccnFMr141KusWTXfOa/uDJvo8P8RiedDX/v3tw5pAdbj4TStUGvO/bt4eTF/BdW8Mm6SQyZ3p3pw78huOLdK/8JgiAIJYsYASnpJA1aBz/sPYPRe9VD51S+UDb+c67SH8VmwhQfhiU96pbXrNnXsaRdwmZOu+W4Jf0y5tQLWLMTSD39PfYetbD3DHrgWB6Eg96RlrXasTPsRyw2S7HGIgglgaezF81qtObz0I+Ys2U6E/pMwcngRGZOBp+uncTzXcbh61aWbHMWn6x9Dx9XX8q6+1O/cqO7d34Pvtz8Ke3rdinQuqxO9Xtw4PReUrNS7tpWp7Xj3QEfM7Lji4yZO5TQA6sfJFxBEAShGIgRkEeUzqUSTlX6kH5uNXYuVf46mlt5y7FCNxwC2udVu7px3LPRe9jM6SQcmIxqzcYt+EVKQs2/ziE9+enoFg6c2UuLmkW/J4kglDSv9pzIoGldmTjgQxz0jqRnpzFt/WRe7P4m3i5lyDJl8sm6SbSq1Y7Jy9/gk6fnoJHvrYjFnRy7fJTdx3awYeIvBTrPz92fGuXqsCt8G080HXhP5wxoOYwqftV4439jOBNzknE937lln5LYlKvsPvYTF66dxWqz4OcRQLPqrahVLoiS8L0lCILwKBMJyCPMpebTWDOukHJ8AaakE5gSj+Ne/3UMPo3zko/U09+RFbUTn9azsZkSSTw8DWv6ZdxDJuRN2SpuLWu2xcnozJr9S4lOuExscgxWmxUPZy+q+9emfuVG6MXmgcIjpIJ3JT4b/jXjF7/A+WtniUu5xrieE/Fy8cFkyWHS8tfx8whg8vI3eKHbG7So0eaBr6mqKtPXv89T7Ufj7eJT4PM71+/B9iOb7jkBAQip3Jilr23i1YXP8vzXTzLtmbnY6+z5fNPHbPx9FfWrNKZmQG10GjvW7l3Kkl3zqOZfi/cGfko5rwoFjlEQBEEoHCIBeYRJkhaPhhNJOT6PjMjNoKpkRe34a51JWSRZh2LJxJoVR+KRTzHFhyHpHPBoPOW2PUKKi6IqbD+6GZ3Wjt3hO9gdvgPInZqVZcpEVVVcHNzo23wIz3QYg5PBuZgjFoSi0aJmW757dQOfrZ9C2MVDxKfG4eXizYVr5zgbc5KqZWsyc+QCGgU2K1C/UfGRbD64jmOXj5KWmYKLoxvBFUIw6h25nhLLsHbP3Ve8j9XtyqyNH5KQdh3PAuzB4+fuz+JX1jNlxRsMndEDJ4MzVxIus/ClVdQpn7vB6qKf5jC+7xSaVGvJN9s+Z+iMx/n6hWXUCijeKaSCIAiPKpGAPOIkWYdb0FiMfq1IPbOUrKv7yIr5DUnWgKRBVXLXVZiTTuFY8XGcqg5CY+9azFHnSs5MYsLisfxx+jeqB9Tmha6vs+fELmoE1ObZji+RYUonPPIwK/csZtGO2Wz5cz3Thn9NcIX6xR26IBSJKr7VmPfCcq4kXibs4iES0xNoGNic2uWDCfSrXqD9NKw2C19s/oS1e5fRNqgjnUN68vvpPXg6eXEx7jy/hG+jTVBHdJr7W6Pm6exNvcoN+SnsRwa1erpA5xrsDHw87CsGz+jO1cQrzBu7gpoBdQCVBdu/IrBsDdrUfgwAd0cPWtRqx7gFz7L2rZ/EQwlBEIRiIBIQAQB7zyC8Padhy76OKfE41sxrqDYzsp0TOpeK2LvXumlNSPFLyUxm5Jf9iYq/xNsDPqRPs8F8tm4y1f1rMabLOABcjblPZhfv/Jq3+n/Akl3zGTV7EHPGLCGkcuNivgNBKDr+HuXx9yh/3+crqsKE717kSkIUq8ZvpZxXRUIPrCakciP6txgGwPztX7Bm71LeW/YaU4fOyquAVRCd6vcg9I9VBU5AAE5EhRN1PZL/vbyGamVrAfDNts+pGRBEq1rtAfjfzrmU86rIU+1HMWbuUJb9uojRnV8t8LUEQRCEByMSEOEWGoM3Rv92xR3GHamqyttLXuLy9Ui+Gr2YJlVb8Mnad3FxcOP5rq/ltUvJTObVhc8yputrNApsRvvgroz4oh+vLxrF6gnb8XIu+Dx1QXgULdu9iPNXz/D9a6E4GZxZsWcxdlq7vOTjp7AfsdPZs2bCdgZPf5z1v6+gT7PBBb5O69od+GTtu0xe8SbxKbFoNVoq+1alXfDdq2ptObSBxxv1oVrZWqiqwhebP6VRYDOa1cjd3HDxrm8I8CxPh+AuANQqF8yWg+tFAiIIglAMRBleodTZdmQT+07u5tVeb989+ejyat4cdy9nb/q2GEJSeiKzNn5UXOELQqlisuSwcMds3h4wFSeDM0t3L8TZ6JKXYOyK2EZUfCRPtxuNq4M7fZoN5puts7Aq1gJd59jlMEZ+1R+9Ts+xS0foULcrzWu2JSrhMkOn9+DlBSNIykj41/NPRR+jXqVGKKrCrE0f06pW+7zkY+nuhfi4+uZtiLh67xJcHd2JTogi05Rxn++MIAiCcL9EAiKUOv/bOZdKZQIZ0OIpPlozESej623Jx0vzhzO6yys0qto87/jRi3/y09Et9Gv5JNsOb+Jq0pXiCF8QSpVD5//AxehKw8BmfPfzPLxcfOjWoDcAO47+QHT8JUY89gIAu4//hNVmRZIkwiMP3/M1Dpzdy+g5g2kX1JnmNduy4MVV9G46kM71e2CwM7Du7Z9w0Dvy1MzexKflvwN6Zk4GRr0DMzZOpV1Qp7x9TW7E3CWkJwAr9nyL0d6BgS2fQqfVkpmT+SBvjyAIgnAfRAIilCqR1y9w9spJnmg2mM82TMHN0YOx3V7Pez01K4VXFz7L2G6v07hqi7zj4ZGHmRX6EbNGLuTJts+iKDZ2hv9YHLcgCKXK+WtnqFkuiG93fk1Zj3J0qvc4AFsPh3I9NZan248GYGf4Vi5cO8uozi9T3b8256+duaf+k9ITePPbFxjddRzXU2MZ32cK7o4epGQmM239ZF7p8TaVy1Rl8qBpKKrCxCWvoKLe1o+Xiw9ztkynS/0e1K3YAMitfnVzzEt3L8TFwY3uDfuQlJGIoii4OboVxtskCIIgFIBIQIRSJSLyCAB7Tu7CJZ+Rj1cWjGBM13G3jHyERR5ixsapzBo5H09nL8p5VsDXwz+vL0EQ/p3FauHAmd/wdSubt35iZ/hW4lPjGNpmJJA7unj6yvG8kRA7rR1Wq+We+v/u5/kE+lXnVPQxXu/9Hq4OblhsFqatn8y4Xu/g4eSJVbHyybpJTHtmLhdiz3LgzL7b+gmp0gQvFx9ql6/7V7/zqOBTOS/mTQfX4Kh3omtILwB+Dt9GUMX66DR2D/YGCYIgCAUmEhChVIlNuQpAvYoN8lnzMZIxXcbdsq9BeORhZm78kFkj5+Ph5AWAioqzwZnY5KtFG7wglEJ+7v54uZTJm8K0K3wrl69fyNvv43hUGDuO/pBXfe5MzEkOnt2Hn4f/PfW/4+hmskyZvNbrHVyMrpitZj5Z+x7DO7yAu6MHVpuFaesm07/5k9QqF0yPRn3ZfmTTbf30aNyPw+f+IOLyUZbuXoSXiw/tgzrnxZyQFk+vJgMA2HNiF7M2fsiAlk898PsjCIIgFJxIQIRSRVEUJEliZMexecduST5uGvkIjzzMjI0f3JJ8KKrCR6snEhl3AUVVijx+QShtGlVtxqW4C1xNupK75iMxihGP5X7+wiMPs/VQKK/3fg+NrOFU9DFmhk4lx5xDSJUmd+07LTuV6ymxzHp2IR5OXpgsJj5e+y5D24ygim9VLDYLn6ybRN/mQ6juXwsVlbTsVM5ePXVbX17O3ox9/E3GzBmKTbHmjXTciHl4h+cB2H1sB+MWPkejqs3ypmYJgiAIRUuU4RVKFS9nb1RVJTblGuU8K5Cckci4Rc/dlnwcvfgns0I/ZOaIBX+PfKgKH615By+XMvi6l8WrALstC8KjytPZm071H2fcoufoGtKLp9uNAnI/Yz9HbOO13u8iSzLHo8LZfHAtsiTTu+lAnA0ud+3bYrUgIeHm4E6OJYdP103i6XajKO9dCbPVzKfrJjG49TNULlMVVVWZveUzHOwdMVlM+fY3uNUzJKUnsGjHbLSyFgeDI2lZqTzdbhQWm4Vth0OZsmI89So35JOnZ9/XXiWCIAjCgxMJiFCq1CwXBMCR8wdwMboybtFzjOr8ym0jHzM3TmXmyAV4OnvlHV+8ax6oKk80Hcg3W2fRtUGvIo9fEEqjcb3fYchn3YlPi8Om2Dh4dh9/nNnLqz0nIksyp64cZ8PvKwC4mniFaU/Pvad+nY0uaLU6LsSeZfmv/+PZTi9RzrMCNsXKx2vf4cm2z1HJpwqKojAzdCqta3cgMu4CZVx9/7XPsd3eoGFgU+ZumUHEpaOU86rA5gNruZp0BWejK+N6v8OAFsPQyJpCeW8EQRCEghMJiFCq1PCvhZ9HAGv2LSX0wJrbp11dujHtauEtyce3u74mMu48kwZN49tdX6OqCm2DOhXHLQhCqePm4M78F1fy8vzhHDy7HzdHdz5+6iskJE5GH2P84hfQaXTotHbMe2E5Tgbne+pXp9FRv3IjJi9/gy+f+x8+rr5YbVY+2zCF/i2GUcmnCjbFxoyNU+lcvwdBFeqxcMdsWtRse8d+G1dtQeOqLbieGkdk3DksNgt+7gFU9K4sRj0EQRBKALEGRChVJElmaNuRHL8URrPqrW5f87HhA2b9Y+Tj251fcynuApMHTSMpI5Hvds6jSfWWVPWrURy3IAilkr9HOZa/sYXHG/UlIS2eDhMb0ODVSjw5oydGeyMDWg5j6WubKOPmV6B+h7YdyfWUa2g1Wiw2C5+ue48+TQdTw782NsXGZ+un0K1BL4Iq1GPvqV84FX2MHo373VPf3i4+NK7aghY12lLJp4pIPgRBEEoIMQIilDr9Wwwl9I9VfP/LAtoFd6FymcC8BeczR/695gNg8a6vuXT9ApMGforZaubNb5/HZM3hjScmFeMdCELpZK+1Z2ibEQxtM4IsUybpOek4G5wx2Bnvu89m1VvTslZ7Xpo/nGplazK49XCq+FbDarMybf1knmg6iOr+tTh2OYyJ373M+L7v42J0LcS7EgRBEIqaGAERSh2dxo7pw+eh1WgZ+WV/VuxZnJd8eP4j+bgYe55JAz/leloco+cOIezin0waNI3KZaoW4x0IQulntHfAx6XMAyUfN7w78BPKe1fi1+M7OXLhIEkZicwMncoTzQahtzMwK/RDnps9kBe6v5G3C7sgCIJQeokREKFUKudVgYUvreHl+cP5ZM27tKzVjuj4S7g5uCFJMp9v+piUjCSGP/Y8c7fOYsWv/8Nms/LhsC/yynMKglAy6DQ6PnryC34+tp0lu+Yxbe0kkCQ2/r4KVVVpUasdS17dSKBf9eIOVRAEQSgEIgERSq1KPlVY+eZW5m/7nNW/fc9vJ37G0eCEu6MH0QmXcXP0YNOBNUiSRKvaHXi159tU8K5c3GELgvAv2tXpRLs6ncjISSfur3Uhvm7+2GnFbuWCIAgPE5GACKWag70Dr/acyIiOY9lzfBdhkYe4lnSFsp7l8HDypHpAHVrX7kA5zwrFHaogCPfIUe+EYxmn4g5DEARB+I+IBER4KDgbXOje8Am6N3yiuEMR/ju/AlWKOwhBKEKJxR2AIAjCf0EkIIIglBZZwIXiDkIQBEEQhAcjqmAJgiAIgiAIglBkRAIiCIIgCIIgCEKREQmIIAiCIAiCIAhFpqSuAfkaCC3uIAShCJ0u7gAEQRAEQRCKQklNQK789UsQBEEQBEEQhIeImIIlCIIgCIIgCEKREQmIIAiCIAiCIAhFRiQggiAIgiAIgiAUGZGACIIgCIIgCIJQZEQCIgiCIAiCIAhCkREJiCAIgiAIgiAIRUYkIIIgCIIgCIIgFBmRgAiCIAiCIAiCUGREAiIIgiAIgiAIQpERCYggCIIgCIIgCEVGJCCCIAiCIAiCIBQZkYAIgiAIgiAIglBkRAIiCIIgCIIgCEKREQmIIAiCIAiCIAhFRiQggiAIgiAIgiAUGZGACIIgCIIgCIJQZLTFHUBRkvqftDNiqwOg03ExZVmd5H+20Q8JC9RYNM72mM8nrQ5JLczrOw0Or6VYZX1WhuG4+mMV04P05dY7zNWs01S++Zgsq6YMRXtJXV0z48EiLRhp1GGdMdkuSAJzxuo6xwAMgyJayqg1slIdvnvQe827TtvdWr232zBQorJX1dtZGH0KQmEw9g8vKyGXQWO9lrmi3tX82khtd2v1ZVyHqqp0NWdl8I6C9H/ju+vmz9i9MvSPqCgjud98zIYl1cSFSHV1P1tB+npQjoOP+6hW1V9Fic1aHRyT95mWpcvZK4J2FdZ1jIOOB4DSW5VtodnL6l0urH6Fks1hYER9FEnKWlPniKqi3vZ6/2MhAFm16hxVJ6EUfYSlg37g8cqypHSTtJY1md+HXCvKaxuHRvhLZslHxhqVvrpefFFeOz+Og48HqVZVl+VmjlDnhViK4ppOAyNqK4pkn4XmmLq6pvlObR2ePOyLyc5PQhOTsbpmrNT1vL3RJfMpBelU9oqg34oi3vv1SI2A6DVmP1VWDqmycshksy3Lr41klb5UZeWQSdI0L+zrKwqrVVk5ZO+c4/+gfZntaX3jXm78sqEeM8iWBIdB4Yvceoe5FkbM98Ih2eChysohRVY2ATgPPuwpqeouVWWewSXrxYL2J/U/aec65JibNCzC4ebjxjJuwyWJRZIkbzcMCqtQSOELwoOTGavKyiFUedS/NdF7uw2TVOlbGbbqBx6v/G/t8j33r+8uRVZ/KGhokqx+9M/vClnWnDPIVaMcBob/a7z/BUW1DVZl5ZCkUV8AMPq4jZAkFkmqut3Y/3C5gvYn9T/p6DrkmJs06rDulhdU2xJU9QvJJuf7PS88nFTUA6qsHOK5w/k+XFVl5U9VVg5x6ZJdUcdWmsjY1qCqX6hm7YIiv7hFHZf7XafpW+TXzodis21TZeWQIVnrXVTXtKFuUGXlkEGj+NytrWLRPqvKyiGbxjocwOCS9aKqMk9S1Z3Ogw97/vfR3r9HKgG5mYTUxTgookT8A39ASRLSTglpJ7AbsKkqw0320g9S/zWa4ggoPYMsVOIAFbhU0PP1GssAs01JMprV2TcflyQpClCARHtZU6ijU4Lw31OiABuSmmxnzkop6qtLcPKm74ojgJ8K3xgHRrxc1LH8Tc79TKsk2ls0aQU9Wy9bV5ltSpJDirbdzccliPzrN5GFE6cgPFLE56f0ugSoqFxPt5ozizuYO3lUE5BsAFT1c2noAedijuWBSEhHMlcGPZa5MuixrJXBbRXFFgzEAs2NcmD34ohJ3RSSpVettRU0gVkrgtYWVr8Zy4O2qZJaWafoquc3fU4QSrLsVfV2qhqlss5mVzVtfaPEor6+Ikkzb/quCJFU9bncV9RJ0jOX9EUdD0DmyjpbVUmtbKeVqydvqFtoSVmWcvZZjUzt7NjkZwqrT0F4VGS7WAdqZGpn1wguxocTwv3IWhG0VkETqFettdXVTbOLO547eaTWgNzkNCqXkOhtsNq/D7xytxOktru1hjLuvVTU9pKKG0hRsqIsz1hdN+y2tqMO6/Spmv6yJLVDRQZpX5aL5Ts9WkXKp299/yNVNJL8jCpJlVRIlSRpZ3Zs0kb1lzbWgt5Yzur6540DI74H9Q2QGwKhDgOPdUFSAjQm0zqr1r4yEkMkSXWVZO2bGctrxwEYB4Q1R6IfyH6gXFUleV1+8wdd+p90t2isw1HVuiClyqq6SdJLR/nHLMUcSddBI9k8nAcfTk1bHpJw47hxaIS/alGfliSqS2BW4Ui2NmeJurRxmjQF2Xg6fKSs0kQFVEmq5jAo/DlFlXKyVwYtcesd5irrpY4WjSUVWHXz9YyDwpqhSH2RKYsqJUiS8mNW9bpbb57nqx8c9phGlSrKOmmjarb5KsgjAHdV4oxk439Zq4NjCvp+C8K9koYecDYq+k4W2ZoGrAQwDoroK6G6Z+ldluhzUpqAPFhWFXtVlSL0qnXh3dahGQaFVZCROub+Sfnh39af5CdzVd0FxoHh7wIBjqbUylL/k+eMGsvTqkJKds3gtfrTYcNk5DYK6oXsFcEfAEhdz9vrnTKHyDLNVRV7VeW0pPJtfp8dx/5hdRVZGgqUlZAuKBrbAknVKNw0Nf/GZ9psVVKA1bfc24BjrSRJ7Q2UBTVOUtWtWTXrblMnoegHhlfVSLQBygEoktrFYVB4eVVRT2StqrvPSPW6iqqEGL089MDhvL+DZy7p9VkpA5Dl5hKKo6TKF22oS3JWBp/Na9N/jcaoqTpCVaT07NVBKw39IwYiqV1UScpBUf/IuZ685H6+m4WSx2FgWEMkqZ5NZbckqWZJlUYBFSRVvWhV1WWm1fVO3tze2D+8BTL9kfBBJVqV1B+yV9Tdnff6oLDBEpJjVpZ+hRpaLf3GcfvBx6prVaXVjX+f0rAIB6NFHaJCfLbt7Ca9XG2IjNoK0CiwJyfNYXl+ayeNgyIao6gDkCR/VPWaKqsbbr6+W+8wV7Ne6o/K1SxFt8Mom0eqktQUpD9ROShJapCiKBf/uYZSartba/R2fQZZkuxt1lUG5JqKhjoOJ47JkLvmTOpx2Gg06p5SUZtLqDZVkk5qJeuim/9/B5B6nnEyGHKeklAbqkgSEKY1m77L76GLYVBES0lV+yGpnqoindSqyjxk+ba1O/mRpiAbToX3UqGjJKnOElyySpolpuV1Tt9oo+8fUU2jUVujqkd1Gs15k6KOkVS1DiqX0Umzs5YGXTEOjfCXrIxWUasAlyWJlZkrgo/edj17q6IfHPaYrMi9QHUBNVyn2C1KXV0z6bb7GnKskmxTh6uSUklVpXRJ4ufs2OR1//zekPqftNPL1oEyahsARWVvzvXkJQYft3zXJzkMDGuoIg8GtYwEZ1WF+Ugo3PSDpdOwCG+NpLbP0WjjgQ0AhgHhQ2UZY+aK4Pl//TzYFxVJlaSwbB2L1CVBt4yUOPY/WcYmm0dIyDVViUxVVdeasvW/G405g0CKzVwRtOle/o7u5lFNQByRNS+j2h4DaazDwIglmSuDjvxr48HHfQw+bhtR1SZ//z2rKLL0unFgxPTsVUHjbyx4c+l/0t0gazcDzdS8j5H6tCFNNxzU2xYwOQyMGCnLmrkq6IDcf0eqOsrg7faz1P9kz/tZUC5JqoeqgiKRBaCgjpVUutrs9OVAfQuQVSQkmCFNId5wKuIrJOn5G/cFEpKqvmwcGP5V1srgl26Ktf7/2Tvz8Kiqs4H/3nsny0yABNytWndFIQGhKmrQZGYCporiMpm47woqxd1aW+TrRu2q/VqL+llrFQLW+lUrJc6dBHFrVSQTFPe61rZaExCyz73v98fciZOQsCgG+/X8nicPzL1nec+5577nvOe851y19BGUnXvrQJhJN7/cUAi9UZVxLoFngX8BhGqaaxH9HxGC2ZyAc4Ppwq8Pi62aCvYrajH/kyrWSQqTBP0XcE970N7Z9rz5wKv4BohULAuEdhr5K5DzkWyiiqrMDL2UenTkdK3JzqxanlyicJLbrWPAmoHf/iVT5BnFsdVjBlImBsPWoKA7b8dM+9a/4hsgqH5LYWyw4+PRILMzK+cCAp0SOF0qlh062GA3ePqqvUWtRkX3APlZx6Lxd7Bw8+WRi1fkhQgUK5AWq2MkHcFODcxHeDn4UvMpIKf6au1F4Nuh2hd2D45wHwHGZnWbCCBcWxhPnZK7sT5U23w1lvwAf5VdUcS1rhS0Lnd00V7g7WKrPR/hZXwDRC5ekRf8OHCXCGfkyqsilwVXNz0k55bUFKpOUmT+J/pYvqYKiPwceBLbPV5V5ojtzsU3QArjL+wTwntYRUaTCYxmZoiuD9U2X9++sPRHAKwfH9ARbfNB3wvWNE8HPRVAVEG4MLTTyHLArKz8f0A5UeEGQe8UlRpgOICKYIvMHn5a6tB1C8peBAjWNt8gFt/14wEgKlcVxVN3ty8qO08VReV7Cl8uLOhKAL0GSMDToxTmg/wCeDLYqSV+X7cqJPvPUDSafS8EzgmOaJ854rQVx2YH9yJIsKb5J6Cz/UECCIjKrGA8dXvnorJLVNGOPGsnS3U+8HjQ6rlSkQoUUN1VbXkBj/ki1ocSW71b7ubm0E4lX1XkdpRXW++fcEewpjmmqrPV9q4EVo2c3lQSDAWeVvTATO4CCq4GbgjWNp2QNYKGx5vHBIM8Auyhn4yKz0znF1xbFFv11bbFY3snA0Lxpp8JkllhUUEEXEtmo2zygBk54y8jQj2Fv1MhKn58BWz1rimKN89oqyu96zJTsAAAIABJREFUE0AsPUKV+QgLu9NeuQi7ZSuZtJ5dFF91Hui9Cr2HdKgya3i8+Svr6kpf6NNUevJutNCZn0ygyOk9Vs/sYfGV1evrxqey4YK1TeeIynyFfFSyj+ui4E4ly+WEV47LGqYjTluxfdAK/BE4rDdF4dzgTiVnAxsYn0W1TTcp8i3QbAsAiysE/jc3nNupe5JpW8/iGyAi/ECVXUPx1AHAlb1FUCXYzXQRKrNj2GDNqslieQ8KMgoUyTS1C0Ohrt+ocjbok8BWMUD+U12wQu0Lx7yLchNgq+htMnfguhBBPNddABwOPC8q0yzLLhNkJrAG9JpgTXPvIL3H7v5v4AjgbZBay7LLRPQyVA8CxvcRoqbpSEV/hejHqJxS0KUjRTgESCJUBq2e721JoWTailAw3nyWKqcD2K63JPe+ot8AeUBEj1M4T+2uD4MvNV8OOlPhBUSPzLetUSoSAV4DLg/Gm07Ppq3o74CdgWWqXlSQCSA/A71kU7IVxZsPQfQ3QAFwswiHqGgFyiPArp7l3ceHH3igMSC792MZaExEzh8s3dDOJTcqnI/ygYpcYFl2GcJJCi8qVHXly10DRLscZb4Ih4jICcBbCLuk7e5ZA4Q1GIYAvVzRGy28cSqcBawBxgd3LokNFDoYa95LXG0E3QP4SXtd6RUDnfozGKFY6kuhtYHbFUYIrO5cMDbX1/tA0OOAr4vqV0FulrlYqFsHjFWRO9T29hTxvoTI14ACCxYOj63cASBY2xxG9WYyfeTNIhxiKVOB53UzBu6hj+25KGcA76twlmXZZSC1KO8hMq2wY81VWDzm64qVAIJ8N6Mr+PVAaUpsdb6F+6Cio4FlHkyx8Maheh3QheoPQ/GmU/tGYjfQKkXOtjwd74f1FM4uOG3VgZtb14YvPoJcACTxOMzv/54FQq7HjQASezooqnOAbhWJdOTLMFXraOB1RUaOOHX1yE+Z9VgVjkS5OtMe5RTgVdCJaS/QOxkXqkldBDobeFlFJufb1ihVKgV5SeCiwnjzef3SLQfGg8wW1a9icXvnwtJG4GVgh6DV08c9WzPlB+SXA+mRrgLOAQ4UeFTy0rtaXt4uwNdV6bTUGg2ZMYKLPgi6uyDftby8XdT29hT4L2BHtbzFUv16AWQG6WSMj05VucHCGyfoiaryd6BmU5UWShf8XIWoIktc3INsz9sR0dOBdYr+cthpL5T2iaBSm6lXmZTRRfoGsLPiPQK8rZaEUT0K+DNQ6KLXbpirzgC9RZAJql6UzJ7bXT2s+7MurKHa5sNE5Q6gDTRW0KUjLbxxCvUgkwtDnTdnU0t7gV8BhwF/RanJjF3kayDjgbG5ORfFm05QlTmAK8q3RTjE182vAGduqr5yuAxktoU3znfBbQeOKaptngKZFTQRbzEZg6wOkcMz7ZKfquqW5LNZ/KeugAhAxwettwR3KjkLlUNDLzVdAuM2mMkPnpo6EqESeLsj0Fmh9x6W3SjZHKppakbkCdBvSGz1bUHb24nMLEq7elLRsbg026k3B2tSq0Vo6CuFXA/YqFzUvqj09/7VlXLCK9ODoY43UTlXpq24Xh+a0D5YQRSNhOKpjOUaCpC1zkX0+/3dw1RZ2rm4tCarYKRiWSC448jrEHoQPb594bi3/KDJotpUjSrPC3IZcF9hUeBklL2AlR3F6aqc4+ieL6pNrVfNKOqNyHktkKfCt7LuHL4MT4R2Lrkx7epif6b3/mBtqtC3ut9qqxt3/2BpylnNRSGVKwDXUp2yfmFZtrzNI056Znk6v+BFhOnDYk3jcutC0T91LBp3Wba+Q/HmEOhCVTlkY2UwGD4/9L876sZ91/+RCsZTewnMRWUCsCA3ZDDWvJdYLAPdA9UftS8ad83m5CCqd4biqTsBsHrn8drU8y5QRUflmDqqOqNj0bjf9OZZszKCWEeALu+sK7s4Z5ByayjevBvoNa5t1wK3olwJiKJzcsqEVL++rHBE2wqBgweV8YRXhgeDMgvocXEjXQsPecm/1VxUm3pFkfLCTr3HX9V8KxhvPkfQ8aL6eNuicfWDpRuUnhPJdOr99VcqVJN6C2GRYM0F+ugbFS7vWFj6W/9nUyjefAToCba6h5AZzBn+f/BqR3E6lm0Xw2JNF3mWrAQmAIwkP78z46XQ6Yn8Te8Z2wYsl9jqgzd1TOpmcGb7orJs/58qql35tKr1IjC9MJ7av2t02etB+DrgerZO67yv7DU/bGNB/PmTbezVonop8D+5iVqW1K5fULq098JCCNXKbajegnIe8HvITEZgcSzQVtDl/YYBULFCoooiazs+Km7x3cPmSWz1T3RRpvyFwUAc2Bfh3raFpbnjgTmheGp/IB4avv5Y4H9RuSqTLhd11PW+X6ni2OrHe6yeFyHrZbEhwdNXflmwzgDe7gyOOFl/vWenf2tBsLY5KKp3eq57EXBZTrSWjrzO6dnxW7A2NVeUewAXL31iR92EdwBCNU1X+2O6sv75iui8toXjbuj9HVu9PGj1PAOUBTvXnAQswNPrEAIIM9oX9o5d1shZzScHu/SvAmdLbPU1hXnujgInAesQ+5j2ujHv+mGbh9WkXvGEpbl5K5n6QuSqtrrSW/3LK30ZUsDeg9VX33T4bkdd6S3+z1Qo3nQwyNdc5RBgaXeBVQu6k0J956Ky03L0/JWhmpSNsFUnaf9TDRAAtPGYdKi2aQbK44p8d1hs9e/Foo+blNoyWVQR5Z4c4wOA9kXjngzFUyuB8SErPUY99kKwgD/lGB8AdCwqawzFUy8DuTNnx/j/Xl4Ub56RvRgKgqrYwLCiYQX7As0bKUYHkD2n21XkNUuY37awbKAlsvtyZzfydyjeH2EXoM1S646ieG42Ahk/hXGZ5V85PLPwqndueBa2dxtYGzVAyMzIYIt9e+5F3+i4aRNxByTYqePVYgTo8v7G1se/P/SjUE1qEcIsz5YK4JP7Kn02xgu86lfKDp9GDoPhM+P1b5Pyqm8i9GuTOlwsGoE9UN7r+GDN17cglw/x3UI0ozeeVOybOxeXvdEvnNup+X2cuSzLOjrjdiW7hWqaHy2K9xF+R99tc1xGdj0coMC2+0zo6JJ9u0Lx1F3AjwcTsLCwezxQhNLQtajX+ADA98vewDd7M5kMIMrt/fVXx+Ky+4M1qVsVHT0stnpnyM8ecKGdbekH+qSi3msZvzPrC328pWFLkT/ktou2TvfVzIRe5v1rWTxhbSjefBvoTNvzXgzFU42AU4D7O+D1T52t8veOxWUP+g6ZmbwXjn+/KN70kCJnCRxZ+OqqNPBloMN2rV/m9tMBAijqAmMldr9dwAHZW/9qW1ha33fqAgo6vXu6CuR7CFOLalfu2rZw/PsqnCtgq8iC1gfLBjwIQtyee7ECM0FPDQ5vO7oonnpElaV0pv8ImR2glujRiiDKIUXx5kS/JDJHbFuMk9hqJ2gxBljT6b66AD4Z669dfFBLKN60iKxr1kCkrXJ/nFUQ6vj44dz6EHSY/59x/er5sb7jN3nL16+r2hdnjA8A8qy3SSsgG6xoqW311WeLD+r2J3R+jlpHAgsQjvbTn1EUb74gGzYEqJAHBEN214GatvfLeMHLw+0Le40PANYvKqsPxVN/xTcqMuMvDgN6Oty2Psci6+KD1gdrmu8R0ZsGra8cPM/r288Ir6mCaLaf0Qn+nQUbrISJLATdqgbIf6oLVi/tC8c9pehdQIlnd/8E6LMZR5QSABUG/CCOZDp1PGGkio7YWFjQD3rjVSwLAEVAWpANfLwFeU4QRzxvox9LEuTJ9rqyffy//TvqSr862AYhy9I+J0fZtmS/FTLghwIFSQryBMcss+ktm/VB/3DtI7wPYcNl234UA9qWfulfmwi32YitxQAoG8gEoJKpb/Hoo0zEkj6G5Kbq2GD4vLEC/Y6gVR2sTY4kMxhpRditcOeRm22AqMjXs7qio65sTEdd2cWddWP6Gx8ItPWf1VVfDwp0bhje+kAQx4O3JOPyPALoWbtg7AaDmcF1YwbL0oxOsthqesIXMqPHkQ3yV0XV36fWQ1fuBxvTG64+S+bDjTkO7oYvFB8DjGwl1P+GTFsRIjOz1snde/bp81S17/u33XYbvH8di0ov891WVgGVwPcty341VJP6ySal0gHPnwHhXwO5PGXbqQgllqub6qcbBVkGu+V+26R1oHRbHxy3RkUWALbn2WfLXCwRzgOw1f3FYOK3L57wDmJPUpiPEFA4F2FRMBh4PVTbdAR8oiN0QB0h7wji4Mk/gvQU+5dbBvoQqrJxHSH+u8zA+awXxFGRPocHDN7n67o+11130LFAR0toQ7k+0Wclvu4rBjxRNtjvK8gKQRxxJS3+eAq8wcr6yZjm+BVBIB9YP9CpVpYMmsYG2Pn9+xmrT3k1MyZFpG+9ALieu8XHpG+K/+gVkCz5Xv51PVbPCRk/QX2DPn2LvgugImP7x5O5WEHlIATUTb8dCATyPE8RZcOwFcsCwZ1Gju5NtfGYdKgm9Q+Enax8Tl93T+mAg+jPFdd9DysAaGtbXVl0Y0FD8dT7AOJpKdDHig59HBijbLJDfg84oCgw+mD6reiIIAP6r4tsNE2RwLuqLgzwbACEzHXPEvMlZMP/GwT5Lp48qJb3Z1G+OSzW9PBAp/Ft5Uzf88+n+H3bwtJvDhquDkJx/g7snl/z/IHQdxVDlDEbz8jXtzq4m9an5F0AsbyxQJ9VDYmtHha02BNwu7tC7w4Q1/DvgvAmyqgOO28ikMy9VRgMHAag8MaW7JfKkokz7g7gjuLY6lHdVs8JIvpDkCuKapuSbQvHPQKZgWfAoqhf9D0HSXYvmbYi1N/QFRjrO1O/K5b1N/VcgHXto0unbOwL7oWxjTlLZLDQ2xQuFNFzC15KrQD2Ap7M3Ug9EP5M/SUizCiqfWGseu5sFc5F5bfAPoj1XmbTsv6qbVHZoB8wlItX5AXXBtLAbsWx1aP6H/wiyEZ1hKi+p5nTL55tqysdcI/c50FoWMcYck7Ug8x4KCOLvqeKhuLyN9Ddyes5a2NfkC+qbd7DPwijtP89ia3OD1qfLGXpQxPaQ/HUGqAkVPvC7v1XTJRN6dQt4h0AUcbhb2DPYom11V3U/+NXQCCz7KeS3XQkfb9QLPb/Aq6onhmKrZyUe6vwpaavZzYq0tS5+JDX29b3LCfj4nBkKN7cx0khtNPIb9LPnUIt/gBY6R6+41vPAAyPrdwhFE81huKpy3Ovb238pceVIPvkLhdCZtN4KJ56oqim+XgAVesRABUuK4w1974ccu5bhajczKbQzEkNruv+QGKre2dqQrUv7B6sSb0SijffnP2asXjS7sf50saSbFs4ZhWZzfIHhOJNfY5SDp7WXEnGx7Ir4Lpb5cQGg2Gbo7zXVld6Y9visStQfgzkeRa/yX2nPg9csf4AqCqXBGub9sy9F4ynvhWqTf02ePqqvQFUeATAwvpxdtMpwPDTUgcDG/3yevuBZSngLYGD++ukYDx1biieag7FUydlr4l67QAuG9cVIt4DAKpyeUH8+dGfXEeCVvoHQJEi9blHpxr+/VDk9wCC/mDk9KbsTDlywivDxWIegCW6xd+mKoy/sE9RPPXrUE3qFsiMGTrqyn6NZk5uU6xsm3obwMWLZOMOi63eWUX6bxLvvR0MBr6f288X1TQfrxABOgrdnsT6BWP+icjTwO7Bl1Iz+0Q+7YXSUDz1ZKg2NX1zy9K2sGyln95+FmRWPVQGXf2AzAmWodrUX4bFmsapousXjGluDxbPBFxgL6l+vcBD/5BJSq4pjq3uXUmUuVjB2tRtoXjzL4ad9sJOOn9CjyKPAvlpu+eHuQcABWubjgFO3pgs7ZrvAOtBT+g/JgvGm04PxpuWFMVWTdjc+thcVLybJfZ0MPu7MNZ8gGZPD5VM2VH9X0DoCXwv95mOOOmZ7ULxVEMo3jRb5mK1txc04m8AD9U09ylv0OqZC/R3AXsEENT9qe89A0CmnHL21iqj0PtuzM6tw2Bt054ifHuQaJ8aswLi01k37jfBmtR5+HsVsrQvHPNuUW3Td1RlDpa1LFTb9IB4vKmWdaQgRwM9eN5M8C3V2uYbUb0F9L5QPHWqIC+pcBRwNBn3rt6ZEQvv24p1sqheGKxJHRSM658EGYFYZwK7gL70aWZqtgRVa7aI16Do7aGapimIrFDYU+AMoMizvO0AOhaNXR6KN/8B9ATL0meDtc11otoahOma6fw3Kmd+wPpBt6u1Ijo1JOmmUE3qIUSGkznicgcRdsz64KryvAhpf4N9I1DcXle2gfWtihaeppdanvwJ5KehmtTxIvK0iu4nynQgD+XqdYvHb/YSpcHwWVC4PBRPndH/eubseD77R6FEemc/O0LFNwU71p4IUhqS7jnANz5z+oPQtWDsy6Ga1C8RLhV4LlSTukdFP7KQyQJVKP+SHu96ALHlu6Q1JsixoRHtK0M1qYfEYgdVYmxipVTn4BXVyCwV/YOit4fiqWmCNCt6oMCJgIjqJ64XwjPAKZbw01A8NR3k9fa60iv6p9u2cPxzwXjTnYJcYGM/WxRP3Y/o34M1VhR0IrBOPa7cqpVmGHI687gl2M1ZKBO6CuS1UDxVj6DBQqpQdgTebG8PDroHaTAsoVuVUxCGBeNN+wnioLoLImcBKuo95gf9AxAGubkonipV1Y+wpFYgNEgH2YFweTDe/JWiGq9RRfZBOJnMMRFzer8DlDnYYTlwa6gmVZlp97oHyJnAMMmUbbNR9DaBScC+wD87NPDARsNn+tRDPUsaQ/HU/yD8LaRSrWAj8rQu2bdLhEQw1vQQItN6pCcViqfuVaE9qFQDh4O+1taWdz2AjXuDhxVW5bzgS82lRbVeQtX6siCnkNlTkjeoLIsPWh+Kp24AbsWyksF46rcCbyKM9+P3YKe/tSX1sVkIhwUl1ByKp36vIiNtS2sURoDc376w7CkAu0C+63ZrTOGcYLz5gGCNLhEYRn7BmcCugvw1s4J1wLpQbfMcVH+I6KJQTdODAq+qSDmZMWg7fOJG6GHPsXCnAScHdypZEYqnlojILmppDAZfEdtS2urGPRuMp24HLsLy/hKKNz0pSKcgk4GNfpPq02BWQHxUUdtiBmzou9deN24umVOcOlCpVZEbUD0aeE3VirQvHv90b9iFpbeiXE3G2DhJ0W+gehgi1wg8mZtu28Lx73uelPud6JGCfAe4FqFERL/f4b12+edaaDKGhaV8FXgHkVOA7wtcjNKmqud0LBx3d2/Y9p7TBL0HKBLVC4FrUYapeieQmQkZlDX3jW3F6ykHGhUdjXAd6ExgBOgt7SN6LuzNZ3Hpm4LMIuPjeQxs6NKWpXPBuIRgHU/mKN3KTH0TQ/RjQS9tX1S2xZ2NwfAZGElm82CfP7XTBRuN9SnQX+/ZiccFgKci14ZiqUO3dh65dOirXxPR76MSQrhCkO8oVCE843lyVPZjhO33lr5nWXZF5ijszLuumVN3nhW4alP5tC0qfdgfhL0PHJ85PpyTUf6OcErbonG9x4t3BEt+jupDmYEAxyE66Obwzn+umYHyA0AUzlGVr/vGx0oLr7xzcekrn7GKDNsYvae0zfLyjlFkCbAdcDrKGQg7COIQkMmfZpWrfeGYd1W96WT2MhwL/BiRq4G0Khe01Y17FqDDy5vv552X2SchVwNve8hgffm7gl6G6ngVuYHMEbTdit7YsWjcj3rzryv9sypTyAy0pwPfB5kBdIrqRW11ZfMHSX9AOguL78ff9yTCHZs6yauzOP194OfAMOAqlJ8oGgH+jNsTh8wYqkM74ipyB8JOwPWi/BdwuH9879HZul9fNz6lolOBt0En+u/iaZl3Wb+/Kfnb68p+LuilQKfARcD3yUxwvKEi0baF45/bkvrYDFQtmUbGKLhWVC9UGCYiv+nw2npXINbdU/qB57nlwFOoThLh25mxDqNAftj+z9beTxZ01JX+GOF6oBORU/znPxH0CuCZ3Mw768a8IWiYzFHCpcD1qnq2ostAturEU+foshmK3gi0gkxWiCjaqJ7nH4+sGx3rbQmi+rlOsH+hkLlYxa+uKrZ6ur3BvjAsZ/xlRLGG7LXDutf3Py1FzmouCnbqeLF0O0Teaa8raxpshWJUbEVxRyBvgu1qnhTIynX3lH4gsdXDivPcvLX7j13b34+zIP786IAE9vPwPs5385s39UE8uXhFXvH6/GFre+yeTX2ssDffAcqUWzehl5omALuq8GFHYcnzOcfb9SF0RvNu4nplHtbHnW09K/ShCe0lp68amVuvvfXY8/LH/TeaFcZf2MfGHe1Z0h5Iu6sGW6GQ2NPBfAr3CvXI+60Pjlsjsfvt4rwDR6yVdrf/iWSZr6ivPASVL6nyr45QyYr+8vfWQ4/dZ5Ntb7rr89PGBWNgnvzzU8+j8qMjJ01asOnQ/3lI7Olgcd6wwsHur+15+WMOPlX765/B3hOJrc4vznOL1trarfeUtm1Md42KrSj28vKttfnDOwZ6Z+Ws5qJiV/LXtgbbB/q6cm84QYpPW1Ui7a5mP945ECOnN5V05st4CxmWVveN/l+Lzk0vVNM8HuVLovLW+sVjV/WWq2d9py6e1LHRd7piWSC486gJot7OKtY/Okb0PD+Y/hp+VvOO3Z3u9t0jvdd0/oSe7PPI5rOB/HlaZtl2sSX6RvZDc7mUnL5qJPgTJ7kyZdPdRF0aYNmyZcPyCvPXBcTe/bDDDntvqPMPxVbsYQXyDgJw1Xu545Nj5nvpfZ793p3B3gWpWBYI7jDyECx2U8/7qJPOZwbaGFx4etN+tisHiGW/s37BmObe/npdd5c+NKHdP/r2PeD99rqyL4046Znt3Lz8wzy0O18Lnh+s/5e5WKEXV43HcndT5V8d2vF8bv7Z92ljY5xPwg4+LpBpK0LFw/ML+tfLiNNWbO+5gQkqWpj29LXB3v3hZzXv2JP2ymwl39XAywMddpGVN2gd8BVRdnAD3sud9417Tc59q7C4e11wc94xiT0dDBKaIJZuh2e/137w2JW5Y6v+erT3esWyQPGu2w3v3+cPpGez+nXNfWNbe/OztVhtK9V+b+mg7bow1nyALezvWd66vK7uVQN9CR58fRS0DlFPA3met3Ld4vEfygmvDC8e1h3oP1bsHaeptXNW90r16wXFIztC2Wc1UNl6+4j+6WXj+u2y9/rFK/K4fUI6eGpq1478znV672EfZ74CL4+C3N++lfbe/EcZIAaD4dNhDBCDwbAlbGsD5ItMfwNkW8tjMAAUnbliF+0O/ALh4HzbOjw7ASOx++1C64CHBK0W9NK2ug2/mfdpMHtADAaDwWAwGAyG/2Da953wz+DLqS+h7N/teiuLapruwxIvaO9/LKoTgNfaO4K/3WRCm4nZA2IwGAwGg8FgMPwHo3PwLLGnqbIU+LKK3KDKjSgTEHlMRau2ppu6WQExGAwGg8FgGCI6Di77e/Grq0ZZPd3mI7iGLxTrF4z5J3Bs8PRVe1uejgFwVV/uXFj66tbOyxggBoPBYDAYDENEZiNw3wMODIYvEh33jf0r8NfPMw/jgmUwGAwGg8FgMBiGDGOAGAwGg8FgMBgMhiHDGCAGg8FgMBgMBoNhyDAGiMFgMBgMBoPBYBgyjAFiMBgMBoPBYDAYhgxjgBgMBoPBYDAYDIYhwxggBoPBYDAYDAaDYcgwBojBYDAYDAaDwWAYMowBYjAYDAaDwWAwGIYMY4AYDAaDwWAwGAyGIcMYIAaDwWAwGAwGg2HIMAaIwWAwGAwGg8FgGDKMAWIwGAwGg8FgMBiGDGOAGAwGg8FgMBgMhiHDGCAGg8FgMBgMBoNhyDAGiMFgMBgMBoPBYBgyjAFiMBgMBoPBYDAYhgxjgBgMBoPBYDAYDIYhwxggBoPBYDAYDAaDYcgwBojBYDAYDAaDwWAYMowBYjAYDAaDwWAwGIYMY4AYDAaDwWAwGAyGIcMYIAaDwWAwGAwGg2HIMAaIwWAwGAwGg8FgGDKMAWIwGAwGg8FgMBiGDGOAGAwGg8FgMBgMhiHDGCAGg8FgMBgMBoNhyDAGiMFgMBgMBoPBYBgyjAFiMBgMBoPBYDAYhgxjgBgMBoPBYDAYDIYhwxggBoPBYDAYDAaDYcgIAFRWVh5s2/YuuTdEZE1PT09TY2NjeqiEqa6uHtHT03OobdsvLl269O9VVVW7uq47raCgYMGSJUs+Hio5/l2pqKjYPhAIjFPVlY7jfLQ1066qqjpaVVsSicSqrZnullBeXj6ysLBwgqquchznn9tKjlxisZjd0tJyBrDScZzmadOmhTo6Oo4AXk0kEu9sa/mGimg0Gsn9raqubdtv1dfXvzmUckyZMuVAVf3So48+mgQIh8OVlmUNTyQSfxhKOTaXKVOmjPE8b/tEIrFsW8uyNamqqgqr6vuJROKlrZFeLBazW1tbK0Tk7UcfffS1TYWPRqOneZ73ejKZfGbixIl5I0eOPNrzvDeTyeQbRx111PCCgoLTPc/7Y2Nj43tbQ76tQUVFxb6WZR0lIj2u6/6hsbFx/baWaVsRiURKgaiI7OZ53noReTkQCPxp6dKlLdtaNsMXi22hQyORSDmw1nGc5k2FDYfDh4qIOo7zLGR0k+u6bzQ0NPzlcxf0M1BdXV3Q3d0dFZFdXddd0dDQsGJr52EBWJZ1taomcv88z3vWtu13otFobGtnOhhdXV0Hqmqip6cnAqCq80Tktq6uriu2NK3y8vKR0Wh071gsZm99Sb+YBAKBw1Q1AXxla6fted7vgG9u7XS3hMLCwjK/fBWfNa2pU6eOikaje4uI5F6vrq7eYcqUKXttbjofffRRNXA3sABg/fr1u/vvz0mfVcb+VFRU7ByNRvfY2ul+VkRE+usPoMF13b+Gw2GnoqJiz6GSJZ1Oz/I8bylARUVFoYjUq+r/hsPhL29pWtFodI+Kioqdt4ZcRx111HCs3p/dAAAgAElEQVRfH+XnXvc87wZVfXBr5PFFwvO8pZ7nbbHeHowPP/ww6L9XF24qbDQaPVxV7xORhwG233774aqasCzrXICCgoJZInJbIBC4ORtnypQpRdFodO+KiorCrSXzlhAOhy+xbftlEfk1cK9lWQdtCzm2NZFIZP9wOOwAKeBHqnq+iNwA3JtOp98Nh8Pf/v/Wp1dUVAzblm3v3x3Xda9X1aGeYFrEZo6HROQXwC2woW76olJRUbF9d3f388DDqjrftu1vfR755LpgtXmeN9HzvImWZX1FRE4B/qGqCysrK6s/j8w3g0XAMtu2H9nSiAUFBZer6hsffvjhyM9BLsO/Oel0+jpVfWPSpEl9lH53d/f3XNd9cXPTEZFngKWqeudWF7Iftm3foarLPu98PgN1WR2iqkeq6jUicrht286UKVOKhlqYxsbGTmC+qi5Yu3bt+1saX1WX2bZ9x9aQJRgMxlX1jdbW1gO2RnqGweno6HhRRB4SkdsGum9Z1hJgmeu6ddlrnud9VVXfCAQCE4dMUJ/q6uoRIvITYKWq7quq+3qe9+pQy7GtCYfDhwJ/EZEyYLZt2zs5jjNi1KhRQWAy0CgiN7a0tMS3raRbF8uyjlfVNyzLOmRby2L4fMnqJsuyfrmtZdkYgUDgauAgETnfdd0dLMu67nPJJ+f/br8llucqKiqSlmW96s9ALPk8BNgYiUTiEWCLjQ+DYajwXcGO3dZyfEH4oJ8OeSoSifwD+K3runHgf4ZaIMdxLhvqPA3blieeeGIdcMJg9xOJxEq2wirq1iKdTu8NBIE7k8nkG9tanm1BNBotFpEHgbWqenQymXw7e2/x4sXdwOMi8kQkEqlKJBL1205Sg+HTsynd9EVBVccA7yUSibv8S//6PPIJbOxmY2Pjmmg0ukxVKwEqKysPEJETCwoK7urq6hoPnC0iw7u6us5+/PHHW2OxWH5LS8uZwFQRGaGqr1uWdcejjz7a1D9tfynqfODLwF8ty7pTRDQ3TCQSOUhVj/c8775cX91oNDrW87wLRWR/oF1Ens7Ly5u/ZMmSjyORyHaqeoGIHAVg2/ascDjcpqpLGxoaUtOmTQu1tbVdJCJhwAZWu677342NjW9trC6qqqrCruvWiMiewPsi8lRra+uvn3vuuZ7B4kQikZmq+qaIPCMiV6jqRFX9CHAaGhruVtU+5a2qqhqnquer6n5Ah4gsz8vL+5+B9r+Ew+FKETlLRHZR1deAX3qep5a14bkC0Wh0vKpeLCJ7AWs9z1uydu3a+zYm++ZQUVFREggELlHVI0TEVtWUbdu31dfXv+uX/3wRsRKJxAazyJWVlWUiMjUQCCzIhg+Hw8dallXred6uIvI3Va1LJpN/2lK5IpHIccB0YHfgXWD5qFGj7l28eLFbUVGxs2VZZwOHiQhFRUVXhcPh3nrwZ98C4XD4OoBAILC4vr7+zXA4fIllWe+q6p9V9WoRGaeqCyZPnnzf8uXLr7Esa0UikXBy0vGmTJmyu+d5s/yXeY2I3O84zoPZ515dXT2iq6trBtCYTCafyS1DOByuBL4yefLkHz722GOjReQ4YF8RKc6R7df19fUfZOvTtu1LgL1V9WMR+VNra+tvP+sz/qzYtv2g67oAowEikcgpQJHruots254FTBaRPycSie8AVFRU7BYIBC5T1VIR6QGeSKfTdzQ2Nq7JTTcWi+W3trae7XneVBHJB55xXffntm331yE1IlKcSCRuz4lrt7S0nCIiJ3iet6OIvOd53uLGxsY/qapGo9FjPM87TESKVXXfbH1Pnjz5h3PmzPGmTJlyoOu6M1T1YOAjEVk+atSoO/yBUh+qq6sLurq6ZgOH+t5+54XD4X+o6hMNDQ1PAoiIVlRUBGzbvhg4VkTE87ynAoHAz+rr69ty0wuHw18WkUuBMSLSo6rL8/Pz79jYHrmqqqqw53ljHMe5JRKJnCgiJ6nqjqq6GvhF7qC3srLyMBE5Zu3atT8pLi4+xQ/rJZPJmpzynEZGx48C3haRewfyv/bLVWLb9uXAoUCPiCwdOXLk3f3rKhwOHyIi5wH7iEirqj7X2dl5h99h56IVFRXb27Y9S0QmqGo78EfXde/L3a8YDodnq+pfGxoaHuovV1YHiMifHMdp9p9vqf98Tg+Hw0cCz9q2La7rTvQ8b37/9heJRHZS1XNyn+MA+QRs2z5FRKb57exvIrLYcZwlWR0QDodnW5a1D4CqVobD4RLLsv6eSCTuGfBh/j9FVa8EdhWRcsdx3h4kjAKDGh/ZPlFVdxORf/j69qFsXWefu+d5D+Tl5Q1T1VnAl/w++u5EIvHn3PQ2Zzyzqfelqqpqoud555DR3S3As74+Wy8iUllZea1lWWW+iGeEw+Fyy7L+kn2fysvLRxYUFFwCTPL72CbXdW/rv3cpHA4fKyKnq+rOlmW947ru7xoaGjY6aTxlypTdXde9BChV1W5gdSAQuD3bH0NvH/4vYBlwhaoeDrQCy8rLy++YM2eOt7E8qqqqdvU871K/v3RF5Cnbtm/P3csTDofPsSxrnWVZS13XnSUiR/l92C8SicQT0Wi02PO82SJyOBkDtX6A8ZP69Rn3V5RKVDXled4vBtrn5e/hOAfYQ0Q+9DzvwYaGht8NMCbb1fO8S4HxwFrgkfLy8gWA9k9z6tSpo9Lp9CXAEUCniCRbW1vvLCkp6RM2VzdVVFQUWpb1Ndu2k67rtlqWNVtVDwTeVtUH+o9/KioqAoFA4DRVnSYiw1U1ZVnWz1zX3RMob2houLl/GfrF39e27QtFZIyqesAzwO3ZPbX+KmSFiOwHFPYfBw2UZjQaHe2Pxw9U1XUikgLmb84+5I2eguX7dh8IfABg2/YYEZnX3d19jYjUi8jRwNGFhYXDjzrqqOEtLS0J4E4R2d3vGE70PO/ZSCRyfj+Br1HVp4ATRaRDRCZ6nvcXVa3tl/84EZln23avT34kErlUVZ8XkVoR6QJ2VtUfdHd3P1dRUbGbbds7iMg8IOpH+aafxqGxWCy/vb39MRH5EVAEdAHn2rb9cjQanTJYHUQikV95nueIyGTgY+AQVZ1fUlJS39+nux/Xi8gM4FlVnQ50i0hERO4Kh8N9/Aej0egVnuc9B5wO9ADbq+qPuru7m6LR6OjcsJFI5AcikgSqgE4yy9Mpy7I2KEM4HL5EVZ8FjlPVDjIv3K9LSkoePeKII4IbkX2j+A15parOBYJ+o7/M87xUZWXlkQAisp+qzg+Hw/v0j29Z1hzgKtu2P8jWsYgs8TzvKyLysaoeKiJLIpHILzZXplgsZkcikTrgYTL7YD4GJgF3t7S0PDB37lwrLy9vNxGZ57ddVPXb/u95frv5CpCX/e153v5+Wa5V1XPJuAhcDhwsIhMffvhhW0TmqerxubKIyGjXdVer6gki0g0cqqoPRCKR++bOnWsB9PT0lIjIPMuyju5fFhH5qojM89M/xJfnQGBUVraenp5dAKLR6IWWZa1Q1Wn+M95NVe8sKSlJTps2LbS59fd54LruQX55socGnC0il1uW9Ufg28DBQDlAOByebNv2C6o6M6N6NKSq37Fte0XuvpxoNFrc0tLSqKq3i8h+IuIBM2zbXqWq2/cT4XzgmuyPioqKYS0tLUuBOlUdIyLrgaMty3qksrIy29am+m1hlIgcmK3vZcuWWZWVlUe6rvs8cJqIrBORHYFbW1panj3qqKOG9y9/T09PoR//JABVne0/83A2jKq22bb9EDCXTMe2v4h8x3XdP+X6u1dVVYVFZBVwEeCp6jDg+93d3c9NmTJl98Geged5xwM3RSKR/wZ+p6q7kemwZ4hIKhwO967gWZZ1tIjMKykp+amI3EfGcJoKmf1R3d3dj4vIXSJyANAGVKlqYzgc/qVI3/1Uqlpi23YzMMN/Rvuq6vyWlpbG6urqEdlw4XD4OhF5DjheVdtVdS/gx4WFhX+eOnXqqH7F2dmyrNVkBg9pMu3nbtu2l+TqMxGZY9v2gK46WR2gqof6YeeJyGm+zJf4v6tVtUtE5gUCgXMHSOZiv40MuFH8qKOOGm7bdj2wUFXL/HZ2pKr+MRwOL5w4cWJeVk5Vvcz/f8yX6z9x1a5WRP6SSCSe+DSRo9HoD/0+8QgR+RgYp5m9X3dn26WIfMl/nld5nve0qu6pqhZwrqour6qq6nW/q66uHtHS0uIAd+SMZ6Z7nvdsNBo9LxtORI7x35fv5LwvUwDC4fA3PM97hkzf20ZmcugnlmU9XVFRUXLTTTeJ/7xr/bRm+L+Phcx+mIKCgpXATWRWyAAut207FYlEJmVliEQiXxeRJcBEy7JaVPVQy7IeiUQivxqsviKRyFTXdV8FZoqIJyIjRORav/8uywl6pape6nnen305u8mMN371xBNP/GBjzyQSiUzyPG8VMEtVBShU1ZvS6fTKqqqq/XLq8HJVvdR13SeAM/2B8VRVbYxGozFVfVoykxM9/vO9KxKJzO6XXVskErlPRO72+4A8EbkqEAg0R6PRo3IDRqPRm4DlwDHAOlU9SEQWh8Phxdm+2Q93uOd5zcA1qpovmQmXO5cvX34/mbFjL1OmTDkwnU6vJNOnFftlvbmkpKSBfsZKrm5Kp9NBf5xxmYisVNWJZPRajYg8EolETszGq66uLggEAo+o6m+A0X5ff4rneSkRmSUi80499dRBx/TRaDRm2/Yq4DJVtVQ1SGYfywu+QQaZCcF5wP7A9v3HQQOkeZ6qviAitUCXiOzs18HKioqK3QaTJcugwlZUVAyrrKy8BRjrFziXmZ7nfdVxnN1c190unU6/X1hYOA+YpKrViUTicMdxptu2vT+wFPhFtsFFIpFyVZ0H/NG27T0TicQJiURiooicJCIbVbz+wPZWEVmSjes4zhEiUgnsbtv2vPr6+pcdxxFgDoDrujs4jiOJROKO1tbWKDBRRGY6jlPpOM70QCCwH+AAwwbK89RTT7VEZA1wXTKZHO04zimO45QCs4GK1tbWMzZRx8cDv08mk2Mcx5nmuu4+wPPAddnBYTgcrlTVH4vI79Pp9B6O4xzvOE65qn4FKPQ87/fV1dUFftjpwLXAPaNGjcrWQamqXghcnptxVVXVOBH5OXD/qFGj9nYc58REInE4EAeODoVCn2pTuYiIbdv3AQW2bZc5jhN1HOc427YPVtV/WJa1oLq6usDzvDsAzW7+zOJv7D3Osqy7lixZ0hUOh88CLgZuaGhoOMhxnJMaGhoO8tvJTH/WfJOsX78+oKotInKx4ziljuOckkwmDxaRucAJjz/++LRHH330Ob993AzQ3t4echxHsn/AnUBH9ne/5f7pwHOdnZ27OI6zh+u6g/pFquolwA/Ly8sPTCQSJ5SXl++nqvNUtfaJJ5742mZXNpBMJn/ry/ZH4M2sbA0NDaloNDpWVX8hIr/Pz8/f23GcEx3HmSQipwJHtre337QleW1NIpHIQWQ26Hd6nrc4e11Vx4tIUSAQ2NNxnL3S6fQJvgtGHfCm67p7JxKJrzqOE1XVSUBJOp2+Iyf+j4HDReRsx3FKE4nECfn5+XsBj4nIRg/NsG37x0BlNq7jOCeOGjVqX+CXIjIjGo1GEonE9X59vwn8MVvfjY2NacuyriDTER7oOM50x3Eq/AH+mwUFBf0HyyQSibWO44iIXAQgIqV+ev+VE2w3YLuurq79/Hd/fxH5GVDe2to6FTKza57nLQBeCQQCezuOc5zjOGHgSGBH13Xnb+JxlAAnA4f4uu9YXz+/Y1nWfQNstj8ZOMxxnC+7rrsTQE9Pz51k+oNpjuOM8+tuL80cFjIjEon00d/+s1ien5+/V46eOheY1N3d/eOccB3Ara7r7pNMJk/22++JwIHpdPqqfnKdBfzObyMnOI5zEPA1IFpUVPRffAr851Pjy1LuP5+r/cHws8DM3IGJbxReACxraGhIDZRmYWHhT4Cj/XZ2sOM4JyaTyf2Aq4Ga4uLib/l5j7QsK3toyDl+3od+mnL8u+Ibjvt5nvdU/3uxWMwuLy8fOdBf1jiPRqMnqerVwPfKy8sPdBznpGQyOVZVbwTOqqysPDs3TVU937Ks4/z3ICqZmXXP87wbs2G6urrmAYcDueOZ/YB6Vf1lRUXFvv1EPdOyrEP992VnAMuy2lX1Z67r7ptMJk9OJBKHi8gpInKQbdtXzJkzx3McR1Q1a/we6T//6/z2dh+QB4x1HCeaSCS+KiJjgA+BBbFYLN83rq5X1WR5eflBiUQilkwmxwLfE5GOioqKAb1cJONt8ojrutl3MyqZVQq1LOtH/YJHRWR5eXn5aMdxprW3t+8LPKGqsyKRyHYDpV9RUTGMzB7ev7uuu5/jONWO41SJyFdUNei67q/7R1HVp8vLy8c4jnO8ZrwG2lS1DngvPz9/fz/vA4AmVb2+X/xdVfUw13UPdhynynGcKDBGVdepal12wiMajU5R1TnALeXl5fs5jnOS4zjjVPUK4JTly5fPhMyhFKq6iIyHTVkymYwkEokptm2P9p9B74TY3LlzLdd1FwIjRORIx3HK/fHQPkABmdXfTXG2qs5yHGeSr5sPAj4iMykFQHd397dUtQq4NKtTysvL99XM/tOajSXunw75G+CZ/Pz8PR3HOTaZTEb8Sc0PVfWB6urqHZLJ5I/8vm8p8Nog46BePM/rEZHfuK67l98fHk3GsNvRtu2bNlXoXANkeDQabcn+2ba9RjIzvfdst912fRqkiPwsu7zX2NiYDgQCRcB5qnp37pJRfX19m23bM4ECfxkSYBawLj8//4xc9wL/mMxbNyasiFwpIms7Ojr6x12mqlPy8/Nnbiy+53nZ5cIds7MiS5cubXEc57hEIvHAQHEWL17s+gOSm1VVs5tpHce5Bfinqk7eWJ7AB2vWrPm6v0KAf7TiXUBo/fr1+/nlugb4ezqdPiv36MVkMvm8iFwuIgd2d3dnLeGvkXkhL8p1Y0gmk3cDfZbtNbPE3CUil+SGdRxnEZk9PRfGYjG7urq6IBqNXjTA34AnOUUikSPJzPTMqa+vfzl7vb6+/l0R+SawR3d397HJZPINEXFU9ezcmVzfBcr2DRSAK4GmZDI5L1tPqqqe530TeI+McbJJlixZ0pVMJmdm3W18JaLpdPo7QAf+TPunRUTWhkKh87JuIf4m58FY7jjOf2WXqOfMmeM1NDTcICIrVPVr/WeLPy2e510OpDs7Oy9esmRJ76yM354fBs4frBP6HLg4qz8ikcjHwItk3OBqc326AbEs66ylS5f+HTL16HleLbCLql7a2NjY62+aTCafB24RkXBVVdV+/oz4mcA9uW4qS5Ys6XJd9wLgb4MJV11dvQOZmfO7cuMuXrzYdV33KsuyIo7jJDdRxjQQ8Dyvd6WloaFhiT/AHNB1ZHPwPG/m448/3gq9riY/9a9PBEin02cAO1qWNSPXfcFxnGdF5L+BqZtx2tjs3GMj/ff1PFUd6b+TvYjIN7LHRjY2NnZGo9HRqjoN+F4ymew9wWXx4sVuQ0PDDWQGJNfmDtSBv7mue0Fuu0wmk3eLyL3AWdnBi+M4tzqOM7uxsTGd1a9+f/BnNnxnX/E8b1auu5XjOLeSOa3l4q294iciP1PVfR977LGp2Wutra3HkWnXtwwUJxKJ7ERmpe+23Hamquo4zo+BB0Vk1rZenfyiMHz48OEA/ipRH9asWTO+oKCgZaC/1tbWrwB4nncl8NqoUaO+ldW3qqoNDQ3fA16xLOuSfskuyB7TDb17gh4HyiDjWiwi5wK/dhxnaTZcfX19m4jMBAoCgcA5uQmq6jceffTR5+CTfiGRSPw0mUxe2a9dPwA8xyb6oieffLIcmAh8y3Gc3gMJEonEO6r6TWDPjz76aIqvKzzLsoYlEomibNkdx/lGIpG4YrDPKCQSiXrHcU5pbGxcc8QRRwTnzp1rJRKJl0TkHqC8X/+0LhQKXZGt26eeeqpDRO4A8oEBT2uzLOsUYHdVndXY2PiPnHxXiciPROTIaDQ6NidKWyAQuCabRzKZ/BvQCIht21dmdYif9wPAjv57lpvnRY2Nja9nf/v1dinwpe7u7lP9urkCeG/UqFHX5rqPJZPJnwHPi8glAK7rTgf28DzvstyjxH03pAty833iiScmA+OAb+S68dXX13/guu5pwEbd1HyW+2O4bNx3NXM64kETJ07M81dMLwYedhyndwP7nDlzvMmTJ39DVV/YWOLpdHqW/9/YkiVLPsxeTyQSf/U870wR2aGnp+f8QaIPSjKZ/G0ikTivsbGxM2ds/DiZ8eUmx1u5A5MeVb0fMv7rZPY5JPr7RQK4rtt/pmI8UCgiR0YikcQA+bhAtrFNAJYN5LPsed6DlmX1n/HqRUQOA5wB/IJJJpPLB4uXk34iEAg8pKrfrqysPD0SiTwELC0vL39sY76MkUhkf2AeMBUIRiKRNjIKS8m4cg2KiDT198MXkX+oKoFAoMS/NAn43UAD2nQ6/QfbttOqeiSwSP6PvTMPbKM42/jzzsiW7VzGCfddbkKAQgrE2CSrnbWMgQApGGg5CpSbthQK5ShfMOEshZYCBVJuWigpZwATWysrxCZc4QhHAgESbigkdhISO5a1835/7MisZUl2jgIF/f5JvJqdnZ2de973GaI9Afwr2KkH4n2EmXsHEuzba3rM/KBSKjP4dgBGffnllxuHQqGvmDnbav48AA9nuT7OxH+8UipzxTmdH2MAPArgVgAPLV68OAqgkYjItu0TADTH4/H3TCe8K4CPbdtuDqZTSgkAZfi67AyI8YW4gpltAGGl1PJQKDSTmbsxwLcaCGZ+dfr06Z2DDN4v35iZHcd5FMAU27Y3WJu0BBgHIBUOh6dl+cbbAqgQQmwKYI0Hx4OFfV+kdNvQBWCB53kPBzsgw5eZZzkQ0Tj49elSpVSmDesGJv4xWusl8Du+frKLiURilVLqKfiTjH4kk8ndARQzc79vY+reQJMPENHlzDxBCPGm4zhNWusYgEfWZvIBoCtTY33lypVflpWVgYjSg9Rx8NdQrsz8zsy8kdmV3AXA+zmeoYuLix/NvBiLxZ5TSn2GjFU6IURmGz8OfgJyleuHmfm6tra2rQAsNNcbs7VpWutHiOgYZt4NQItlWRtJKa+Ev8M4QinVDeAFZq4gosz6Nj3boIqZHyGig1auXLkj/B3mdUJHR8e/y8vLrxZCnAkjxMLMpwBYVFFRkUtK8ycAirKVM8NDAA7t6uraFf4k6wfNiBEjlrS3t6cAbJL5WygU+qCnp6fPApTZFf2F1jrZ0NAgiGgvAIvb29tnBOuGbdtg5gr4O4xBXkB/Poc/4IeU8scASgAMZjyTTlO/3RvjP3AFgEPwdbl+Hv5uZF7MOwLAiUqpPqaERDTE/DsG/iLTWcx8e0lJycdKqUcAuMXFxdPz+YVZllUipbwYwHFlZWWbtra2ekqpeez7XoT33HPPEPydXgB4I7Pf01p/TkRg5qzvYtpzENFFSqkLMn5OL96MAZA+V2xBpr8bfMfnVHNzcx9VSq31YjM/GgEgbdq7NBaLtWSmo7q6urG1tXUV/PbtdvhWOt3t7e1PZukvNwGwvvHdGgt/TNzPj8Z13ValVO8imdZ6rMmLbH3Su0qpAc9OI19NM/Pa5wBCpaWlQ0tKStZj5pHwdyb6MHnyZO04jmt2jXLFPw7AbDfL+WktLS0vKaUWmTHmamF8lKbA34EZpZTqAfAK/J27AcdbwQnIKtd1B7XSLKXs40Doed5wIQSI6E2YzieDlwC8BwDMPJSI+k0gAKCoqGiZcVjNxVBmznrvYEgkEinLsn4aCoUmATgawOkAzmttbZ0XiUTqW1pa+smv1tXVrc/MbUTkEdHvtdZvCiHWY98xf0AlFfadu3JiVgyH5MqTRCKRUkp1EtFwE7YsVx4wc2aDM4yIvoCf/5m8BAClpaXdjY2NywD089PIBTMPM//OEUJkG5DPMv4sSKVS06WUnwohTgDQ6DjOfsae8PcA0NnZmbabf5+IsqZzoDxMY1nWVlLKWczcAX+naAH79qBR5DCxW036TfpykeVb9Lnued6wUCiU872YeVCbJEQ0DMCXufLO/Jtvp2adQUQtrusOxrysXz6adqFTCDEnxz1Paa3fJ6JNTPhcnWvOTtc8A0KINW5DYrHY65Zl7S6lPImZDyGiawFcp5S6w/O8U9fw4NbBlKuhAFZk+86mnDzped5H/e4KPCPbooVhuSlHvWit+5TNdJ2XUubKu2WAX64D6cr6LYQQXzEzhBDD6uvri6WULfAHiVcS0Yue55VJKSsBnArTbwwizuXMDCllPz+ctWHOnDk9tm3fSERXWJa1bSgU0vDbk3OnTZuWtbNi5mH5ypkZ5PXm6Q+dadOmeUqpZwFELcsKBeuQWa2dGgyvlNoWQEprvfCll14qgT/Y+ShH3XjJ/EuW5XfX2foT8n3O0v8fzswgonkYYDyTxixw9WJMkBPwfVSvEEK8BGAIM6fLdR9Rg0wCZeMFIsrWfs9i5pcBwHXdu2tqauZ6nncyEdUBOC6ZTK60bfuseDyeVR4+FArdwcxHArhBa90kpfTgLwSekSUtg+p/M+4ZSkTdRPRijiBNWuve3Qry/SQz42AiSvV+mPysyBZu8uTJWim1koiGBcZaC/P1l6WlpTKZTA4B0JWnPQ+2Q0MAIBwO52sbi/IlPkce976PEKLM8zyw7/eR7f6BFkaHIf8i5HIAw/P83g8iokgk8jj8ifsfzTi5mJn3BHBGtm+ayToxzQiFQh9qrcHMM4PbQzkS/TFybNt5npdzBmf4gIhGr2k6AX9AD2AagGlG0WUSEd0shLgH/u5MH3p6eg4jovWZuc513V7zMiJ62LbttT5szlSQD5EjT8zBc8MBLDRhPyWirGHJVzYIXvqImYe6rptpL7lWENFHpoG+NxaL5WpgAPj5bdv2HUR0npnMnQjgY8/zngCA6igF/MsAACAASURBVOrqL1tbW7sAvBeLxdYqnaFQ6GhmHs7MTlBVauzYsY+Wl5cfle/edY2xE83GGACpYcOGfZpMJkekUimw7wzWByHERoNrd/ERgJFrm3ffNkT0EYDioqKiK/Kt3CmlUib8LgD6rXhlqQO9MPOHRASt9S4A1sjZFQDMjs4UAFNqamo2YeaLmflUIcSrAG5c03jzwcwfEVGxEGJKlpXCwVBq2/Y2mTKvxjZ6S/g7ujnRWi8SQkBrPRrAh1mCjIG/2xrs5LK21VrrXcyk6cMlS5ZMIKKdAJzpum5QcGK6cYzsc04P+8pj2RgDAESULW1rRVFR0d9TqdTFQojTtdbdRNTped4ducILIRYxM5h5Z/jmNn1Itw2e52VVlfmBMhXAvVLK3wC4NlcgU16PAZBIK5MppZYw88cD9XORSGSwaUmXoUQsFst6lsxAmMOUtwdwajweD/pnPaaUmjDQ/aa+g5nvcV13wB09o8x1OtCrenkTEd3iOM4zQRMioHfV+kjy1euCC0ZNSqlN4S/erRVGMbKYma8ZjBrSOmAjpdTIzGfV1NRsAqBCa/2BGT99AuA/A/WXtm1/TETDo9Ho5kFVMMA30ZNSbgaz02rGtEgmk6MBPJsRNiSE2ImI3sVakEwmP5ZSenkOKM1rJcLM7+caNxorlG0APLg6aXIcZzet9b4ApriuGzyo8Aml1G4YhO9LXhWswRKLxebCXyk4I9Ou1XGcLWzbfigaje4CIL1NtUfm4YbGyfpc5OchZt47U7GqoaFBKKXudxyn9/707CsUCgXVVmocx3ky7ZBkfAbuh+/cu0u2JWdmLgMAIUQfMxKl1OEA1skpyQAeYGY7U63BcAn8mXC6cDwGQDmOs08wUFVV1TBm7tNwkK+rPkYpVYsMbNu+zrbtTNvYQZFKpR6HL6nZ73vV1NTYSqk7jb09AEAI8XcAMplM/oqZD2Pm29IrC8b0bTqASY7j/CgYl2VZJY7j3Jct/dnI9a3WW2+9X8I35eqFfIlXlJWV9Zn1M3MPgHDa6X9NIaLjM5WJjBrYUUTUPH369M5x48b9B75cX5+Jb21t7cbMfECWaHvg72r1llMiehjATkZ6uA9KqT8ppfqtaH0XEUI8AqAomUxmqptAKXWKUurqurq6cDwefx1+W/PrTNUppdQ4Y3qXlUQi8TKAhUR0tnGSDN67vVJqlpEhTNODwKoQEZFS6nLbtqekrzU3N3+aSqV+C8AzJhFZSa9weZ63WqtMgWc/Al+Q4leZv9m2faZS6oq0slKeOPqdZptMJn8H33y2n3lWkFWrVsWIqIOILsx8jmVZWzHzLwDEMuRqnaBaD9B76N6v4YspvJo2MTPmBr0opX4C3wk4k0MzbMfTu9SnAHg1l1TkQOT7Psbn5m7y/QJOBHB3pixvkKqqqhcAvK+1PjfzAE7TLp5BRC8F7dV/6Bhp0xYAVwVVpoJEo9EhyWTyPvgmPJMDPz1CRHVG9KKXsWPHFjmOc09QSWgwuK77KoBFzHxGplKkbdtb2rb9UCQSybsQqrXOVa7HwTfR60UIkTRhg2XvcQCpbH2sbds1SqnbLcsaVVtbu7HjOPcFlexisdgrRHQ5AJltwl5aWloKgJi5T9osyxoFX6BmrfE87xEABKCfSb1t28crpa5dGxXOLIQAXJglHX+A/66PAr39pW3bdp9DH42C5m1KqSMAQEr5WOD+Pkgpz4NvBpx+xpPwfQMvzBxDSilPIaL1sZYkEomlRBRn5hPMonQvps/KexaZEOIBANs5jvOzzN86OzvPgW9dNK3/nbnxPC89pupTjozg1KDORlsnOyDMzLZtn0lET3R2ds6MRCKXFRUVLfQ8bzcAlxPRMK31cgAIh8N/TiaTxwgh/m3b9mQiambmTYnoDwA2zPccKeW1qVTqKAAPp+8VQqyvtT4LwIFa614bbiNNBma+3rbth4UQ7zDzSmbeP5lMxh3H+YPZAtyDiPZn5tZsW3hE1Mi+GtPNkUjknKKioiVa6/3hy/t2DHKVOi/FxcVX9fT0HM7MM5RSl2qtm4hoJPyOahKAP7quO88EnwLgMGZ+Sin1ByKaBeBHJSUlDchYLVy5cuWNZWVlxwF4MBDvCCI6nYiOIKLz1iS9iUTic8dxLmXmKUqpB5n5b0S0mJkjRDQF/vZ07yp2LBb7UCk1A34DwfCVpnqRUl7geV6UmZ+2bfviUCg0p6enZwsp5R/Yl8rMVMzIxePwFUHutG37AiHEV1rrSUT0W2TIZTJzWsv9Rtu2G5l5XktLy/NCiFeZWSSTyalKqVkAXgw67g4WZl7ped4sx3EuAvC61np3IroCfmN4LuBPvmzbfhjAUUqpBiHEI1rrzeD7G61E/y3RVwEcGolErncc5w0AbiqVullK+QsADziOM8XzvKeEEMPJl38+Clka5e8izc3NTxsJ5Uscx1kfwD89z+shXzXmXACPpE2IbNs+i4geKykpmRWJRCZLKRdprccT0WXwG8Os7Qgzs+M4pzPzk1LK1sC9exLRFGYOFxUVBVe7XgFwmFLqAmbumjBhwi3MvD4RnaSUGg7gdq21FwqFfsnMEr60Y1aEEHPNOT1X2LZ9pznrYdAHqrmuGzNl5XLHcTbUWt8vpdRa63oiOoeZ/zXAmS89AOqUUvdrrW8MhULdWutj4CvnPem6bt4zA2bPnt2llPoVgHvLy8tn2rZ9lRDifWbeS0p5KfzFrMyV088APKWUuoiIZnmet7UQogHAVgAmMjNXV1c/HQ6Hl8MfeK4gooWe5+0L4I/km49m8qXWOm7b9oVCiOeZeQf2pbRHMXNeJZh8MPPrROQJIS4x/llLgs728B3OTyUi0lrfkC8us9J6GhE94XneM47jTIFvDrorfFWbUVrrtd49/z4xefJkXVtbe3gqlXqUmW+3bfsEIrqPmd8FMEIIMZp9BbVNiOj0WCzWu9IshJjsed7BRNTiOM7FQohnU6nUJuXl5Rcw837sO/MOGmbmSCRyphDi8bKyspmRSOTy9HjGDOyHhUKhnLu0AKC1Tkgpv2Lmq5VSnfCtN6oAXA1zrEEaInqNfenZBtu2NyKixfF4/Anbti8jokuUUiEAf4N/5pBt+ti3119//eUrVqygVCq1JxEdaNv2ecycEEJUwB/4r/I8r5+PUXNz86eO47zEzKcqpeZ6nve8lHJHKeVVGJzD9IC0tLQ8r5S6C8D5juOsx8z3sC9rfQgRXQBgxrPPPrsuTYOXAfilbdvDmPl2KSUx80nky/fe09LS8jwApFKpy6WUhxNRs1JqspTyaWbeUGt9LvxjDWYC/o6SUuo2ACcrpUqY+Vb4k4xfENGxCJjQmfHQ5cw82bbtx5RSfyaipcx8OHyBnX5+F2sC+w70LzDz80qpa5j5HfKPqvgdfEuIrXPdm0ql7jRCI3fbtj2GmR8UQhTD95c8ifwzc1brzLVwOPxSMpn8BMBFjuN8kkql3gyFQnsw85/gnxUzIOtkBwQAjPrVwQBGCiEe8zzvdQD/APCZ1nq/WCz2IQA0NjYuZ+bxAJ4homsAzCVfw/or+AOmnDQ1Na3UWlfDd2a+CsBcrbULX5Hp5KC9o0nP9fAP9rpDa72LOTDqGGbekplnENG7RDSNiOZorbPK6Zrty2OYeQchRJvnefOZ+VxmPjbD3GCNMXlSBV8O+EohxKtEFCdfY/y8eDzeu13ouu5/hBDV8B3Eb2Tm18zs/l32pXh7mT17dldxcbEFoDkQ79MAbABnxWKxa9Y0zbFY7DIiOhvABPL11+eSbw/fBMDOYm9+C/wVmelG4aKXpqamRVrrKvIP6rrT87zXhRBPwlcrO9h13WyOgP1wXfdZAKfBLw/PM/M8IvolER0GoD0YNh6PPwR/238SEd0hhNgOADo6Ou4E8C/42/y3mQnBakNEF8GXbLyHmV8jX11kGTNbgckkpJTnEtHLAP5Pa/0KfAfVJ4monymj53nXAXiKfN30W4iowjj5RgA8xcyXGTOgWcwcJaKzXde9ck3S/21QUVFxHDP/hf3DOJ8VQswhorOY+W8VFRU/T4eLx+OPm286SgjxmMnfq5n5GiK6K98zzKB/fwClgXvvZOYPhBDj08pcAOB53vkA5sKXtLymuLhYDhky5CwiuhO+rfRcIcQbzHwygIvi8fh9uZ7b3Nz8KvvqNfuY+8flCpuLcDj8M2a+if2zKp7XWr8I4Ewi+uuyZct+McDtwuyq/VgI0WbuPQ3AHZ2dnYdnW3zJxHXdfxLRkQA2JaLpzPwa/MWED6SU+wbVegCAiO5m5mvg2we/JoR4DL62/GGu6z4BAEb56xBmLmXmGVrrBeRLEF8IoN8Bf0T0VyK6jYhuNM//NxGFiejAwQiR5MKYpp0Nfyf8TiFEH3sd827vA5jR0tLy9kDxua47g4gOgH8ewIMmrf+A78czIfPg0QL+TtPSpUttAGcR0WYAbiL/vLFp7EvqLiCi6syDbZubmz+VUu4Lf5I31fO814moCf6A7Ih4PL5aExDAV7Zj5kNg2pjAeOZTANWZZjmZJBKJxUKIQ+Hb3jcDeJuZr2Xm85AhPNDc3PwOM58DYIxpGyyThkvhyzZH4O8OzWXmPxFRYygUcqZNm5ZsbGzsFkIcCOBN8k3J34IvBLItgEOzHcJnqAfwDoB/SCnfAfAI/L477+R6dfA87yQA1zDzMQBmk+93cR6A2z3PG1Sbsxp8IYSYKISwhBDPMfOzAH5ORH9ZunRpr2pVIpFYzMz7mgXIGzzPe92MIXcBcKzruv8IpP808s+MO5yIniGi54koKoQ4GBlqi67rNpiJ1XgALez75xxv3r3fQdxrguu684QQVcz8HoBriWg6fFXZ/zPjCx49enTWPE0kEintH9p7LxGdbXwtZ5s8urajo+Pn2e7LR2NjY7fW+mAi6mTmR6WU7zDzneQf/TCoOkfrtgz45lBtbW07MPMoZv4wnzpMNBrdvKenZwtm/iBPRclKXV3d+qtWrdpeSrm8qKhoQS4HS2NKMzwoPWZOkxxDRMN7enreG8yzLcsqIaLdQqFQd3l5+bxspx6vC6LR6AY9PT3bwXeqejuf1Gs0Gt26p6dnE2Z+L4vSUL94jfP3slQq9c4AErKDxrKskqKioh1TqdSQkpKSBcF8XsP4tpJSbi6l/LKysnLBQCetZiMajQ5JpVK7hUKh5UuWLHk738pwZWVlaTgcHhKUfk3H0dnZWZyWRl1TamtrK3p6enYMhULtzc3Nb+dqdM0J15t4njc/n3kH4B/EV1RUxJm+EulvzMzLOzs735k9e3ZWh7U14ZnnZr8Mpj/tO25czkH2usKcEL8jETERLYjFYsuyhTNqJTszc4nWel5QwnogiIgsy9peSrm+1vqjfO2UZVmjuru7VwbzUyk1kvzDJnuGDh36+mDV0caOHVtUXl5ekU2NZLCYk4F3kFLqUCi0IJ/PjEnrXwD8pqKiIjx69OjU7Nmzd9ZalzPz/DWxzzYnye8AYORAbTzgnwkghNiZiFYtXbp0frb6WF9fLxcvXry7EEKUlZW9OVB+1tXVDe/p6dlZCLFixIgR83M5hK8upkyNjMfjXwTrqjlN3gWwf1CadSDS5UwIsQEzf5rpg/N9ZubMmUOLSoq/CpHcfO+9916t/h3oNVndlPzDinO2A0Ecx9mCmbcEsKSiouLttS0XqzOeyYZlWSEi2m0w5TpX21BZWVk6dOjQHTzPKwuHw+/k6mOj0ejWWusthRCLKysr5w2m74xGoztqrUetWrXqzbXt63JRVVU1rKSkZEdmJq31goH6t7XB5Pc2RDTS9Ak5n2Xb9qZE9COtdfvy5csX5BonOI4zAsBO+HrslFNoxEj/75RKpXpGjRo1/781TrQsqzwUCg3v6Oj4bM6cOT1KqTsB1Lmum9eKCPB9gIqKirYHkEomkwuyqcquDg0NDaK1tXUXIirr6up6c3XiW+cTkAIFCnz/+CYnIAXWLcEJyH+rQ/y+YySXtzAHpBY6zUGwthOQAgUKfI1t278gol91d3er4GTRLIa9w74I1P+Uaec3dUBZgQIFChQo8D+HURWqEUKcXph8FChQ4FtiIYDdi4uLZzmOM9nzvPeklDsAuISZS7XWl3/bCVxd1pkPSIECBQoUKPB9w9jnLzVS7QUKFCjwjWN83I4gopHM/JARzHmA/TPD9s88zPZ/gYIJVoECBQakYIJVoECB1aFgglWgwLrH+MvtYiYinwxGFOO7SsEEq0CBAgUKFChQoECB7zhGWGHut52OdUHBBKtAgQIFChQoUKBAgQLfGIUJSIECBQoUKFCgQIECBb4xChOQAgUKFChQoECBAgUKfGMUJiAFChQoUKBAgQIFChT4xihMQAoUKFCgQIECBQoUKPCNUZiAFChQoECBAgUKFChQ4Btjncrw1tbWVqRSqRcAfOC6rr0u4/6ho5Q6EsBUIjorFovd8U09t7KysnTIkCGtWuu74vH4jdnCOI7zJIAFsVjst99UulYX27YfIKIqIhoXi8U+/LbTU6A/tm1fR0Tbua570LedliCO49QDOA9AfSwWW/htp6dAbpRScWaeHY/HL/6201Lgu4tSansius/zvEtbWlqmr4s4I5HIRCHExUS0DTO3ua47cV3EW2D1sCyrPBQKuVrrW+Lx+G3Zwph24oV4PH7BN52+IDU1Nbsz821a64vj8fhT32Zavg3W6QSku7t7uJRySwBDiYi4cMrhumQ7AMMAbP9NPnTYsGHC87w9AcRyhWHmMQD0N5eq1YeIRgPYWGs9CkBhAvIdRAixjSlL3ym01hsQ0Z5a69J1Ga9lWduGQqErmPkG13Vb12XcP2B2I6LPvu1EFPhuI4Qo01rvKaUctS7ii0QiewohHgKwCMDtzCzXRbyZOI4zCcCRqVTq5EQisfS/8YzvASFm3pOINskTZjcAi7+pBOWCmYeatFZ822n5NlinE5BEIvG+Ump0KBT6qjD5WLdUVFRc0dHREevo6Hjp207L/yKe500oLi7eqKmp6Y1vOy0FCgCAEKKCmQ8HMB1AYQJSoMD/KEKIn8If+O7vuu57/63naK13IqLDAfz6v/WMAgW+KdZoAuI4zk7MvJnW+tNRo0a9ZU5mBAC4rrsg1322bW8qhNhJCPHxiBEj3gnel8YcM797KBQqBzC/ubn508Gmq7a2duNUKrWLEKKDiOY3NTWtXO2Xy8BxnC2IaDut9Weu687LFc6yrFFFRUWjmdkrKip6rbGxcXmedFZ4nrdrKpV6P5FIvD+YdJi8ei7zum3bmxLRzlrr9lWrVs2bPXt2V2aYhoYG0drauguADaWUHzU1Nb2V6zm2bW8KYAci+ry6ujpnuMFQVVU1LBwOjxFCFDPzfNd1/5MtnMmPnQGkVq1a9XZra2tHrjjTeae1XhSPxz8I/mZZ1kZFRUWjU6nU58z8diKRSKV/SyQSi5FjxaOqqmpYaWnpnkS0oqSkZN706dM7s4WzLGtbKeXWWutPW1pa3hxMHvzQsCxrMynldlrrTmaem0gkVmULN3HixLKVK1fuTkTsed7riURiRa44KysrS8vKynYnorCU8rUZM2a05wpbX19fvHjx4jGhUKiciBY2NTUtyhbOcZwtmHkHZl6itZ6XK52ZEBFNmDBhm1AotBWAhflMskzd3J6IulauXDk3W938bzFx4sSyFStWjBFClDHzgng8/kmusJFIZAchxBZa6y/Gjx//+uTJk3PuZtbU1GwHYAtmXuS67qLBLjRZlhWSUv6EiKSUcl6ubxiNRod4nrcrgBLP895JJBIf54ozGo1uoLXemYiW9vT0vBGs75nU1dUN7+np2VVrLcz37tcWEBHZtj0GwIZa6w/Hjx//Tr68KPDfwZjQ7K61Xt7V1TU/V72xLGujUCi0o+d5PVLKN2Kx2LJccdq2vY0QYmvTb+SdIAy2DctgOAAvV9yWZW1VVFS0jdZ6eUVFxdxp06YlM8Ok25aioqItmfnTjo6Od+fMmdMziGevEZZlhYQQuwIoaWlpeTZdlysrK0tLS0vHENGQfHXQtu1tiOhHWusvli9fPm8waa2qqhpWXFy8sxCiuLi4+K3GxsYvc4WdOHFi2apVq37ieV6XqbNZ+4hoNLp5KpXaLhQKfVpZWZlz/DkYLMsqkVKOEUIMZ+ZF+dr3ysrK0pKSkl2JaEgoFFqUra8hInIcZ1tm3tLzvI+WL1++8L/5Tf/XoMMPP7y4vb29G8C1ruv+LvijUuo8AFdLKbdoamr6yHTaDwDYJxBsAYBTXddNmHs+APC667oHmk6nB8BkABsAOBVAemtyPhHVBu3xHceZwMx3ANjaXPLgb2e+Q0TXdHd3V2QbnNbV1YWTyeStAI4FQABARB3MfLHrujeZuJ9k5h1c19024x23B/A2Ef06FovdEIlE6oQQTxJRLTOfBuDgQPAXABwTnGSNHTu2qLy8/BoApwEoNpdXAvhjPB6fkq7USqkXAXwO4CkA1wIoAfBPIvqYmc/zPG/7RCLxbjBttm0fSkQPM/MkIcRXzBwDcLjrug9OnDixrKur6zZmPjL9zvAH2Oe7rnt7II69iOhOADunrxHRSwCOicVi89PXzCD8VmY+Al+LE7wmhDhRa/0iM1+Vy15SKfUhgLlB232l1Nnwv/twcynFzHd2dXX9Jt2hmIHlXwAcD6DIhPOY+Y6RI0eemW6klVIvEdHHWusYEV0DoISI7o3FYscqpb4CcCv8svLbQDwLAeyf/la2bU8louNd1y0yf7tEFDb33hRI5zIi+nksFnsy/S41NTWbeJ53DxH1+jUx8zNE1ACgGcBRruv+K1vefF945rnZL4PpT/uOG3dftt+rqqqGlZSUTAVQj6/LT7/yCABKqdOJ6DJmXs9cWsbM55v8/YnrululwzqOcywzXwsgbSrRBWAKM29NRHWu626WDmtssG8C0HsNQFMoFDp+xowZn6XTGQ6H7yKiSYEw/wHwO9d1/5Ht3WzbPpOIbiAih5knA6gK/DwDwC+Ck2vLsoYKIW4loiMDebGEiC6MxWJTTR4sArAV+vKQ1voiIcRbAH7vuu4fA2nYw9TbmOu6Nenr9fX1sr29/XMATa7rHm3iPhHAVYE80wCmFRcXnxJcGDET6nvRtz1/h4hOiMVibSaunQG8CeCXAA4AcGgg7KxVq1Yd2NbW9lW2fFNKXQ7gXK11tRDiX4H3TTHzH+Lx+NXpsGaR5CIAv8PXdVEDmC6lPKWpqemLwDsXt7e3XwfgJHzd5i4UQpygtb4fQEs6L4iIlFIXMfPvAQw1YZPMfOvIkSN/l25jotHojp7nPQBg18ArzAVwkuu6L2Z7vwL5mTlz5tCikuKvQiQ333vvvXNOJAFAKfUsgKWmv7sOX3+rdiFEfXNzczwd1rKscinlzQAOx9fjiU4iuiGVSv0hOBE1C2p3B9tuAAlmvoqImojoxLRPZV1d3fBkMjnVxJu3DUvjOM4kZn4o8zoRnR6LxW6uq6tbP5lM3gm/7qT5jIh+FYvFeu9TSv2EiG5m5j0D4f4Df2z1aE1NzXZa636Da2Y+LR6P36KUehnAStd1q4O/19TUjNdaz4QZN5i24TYAEWb+KxHtAgDFxcUjGhsbl5v29hr44zXAr4OPdHd3n5Qee5l3ug+ACjzqQ2Y+Mx6PP54tn0z9ngLgNwCGpJMP4FEiOj49ebRt+yEi2o6ZJxPRbQDSpkkrTbs0LR1ndXX1euFw+O8AJuHrcd9LAM5k5mcBTHZd99Js6VFKLWbmeDwePyJ9zbbtUwFcSkTrB4LOZOZfZk4slVK/BvB/AEYGLjcBOC7dF9i2XUlEN6Nvm/Kpie8pAHAcp4qZWwEc7bruP7Ol9fvMaqlgMfNtAHYmop8z87YAapg5CeDf0Wh0SJ5bTwNQKYSISinHwB+Y7sjMf0kHcBznx8zcxMzdzDxJa70jER0BYAIR/T5furq7u38H4DgAl2itdxRC/ERrnQBwg+M4++S7d4B33YSZ66SUOxHRyQC2BTArGo2mKyfKy8tvAfArMzDeDcB+zHwfgAbbtjMdIXcBcCUzX8XMPyOiR7TWfwfAUsrjM9MghPglgE+01v0qdVdX14XMfCQzXwBgB2beG8DzAKZGIpHdAH9lk4ha4A/OD9Ja7wLgaGbekJln1tXVrQ/4jUNJScl0Zq4HcDWA3bTW+wCYZ569WquASqlz4E+yHgawF4C9TIN/fFlZ2Z3pcGVlZb+FP2m8hIh2JqI9ANxCRCe1t7dfFoyTmXclostN/v0MwKOBn38O4CAAEwGMJqLzAGwJ4OYBkrojgD8z85lEtLPJmyQz315VVTUM8FdktdYJItobwFlEtDMzjyeipQCyDsZ/aDQ0NIhwOPw4/Pw/G/43cOCXx9uMgAIAwHGc0wDcpLV+GUAEwGj4jf61APYNxquUOoKZ7wYwj4gcKeVOAH4F4GwiCg6E4TiOEkI8DOAdE+9uRHQ6gH16enqa6+rqwgBQWlp6KREdTERnCyG2B1AJ4DUAd5nFiJww810AVgohJph0/x7ABABxy7JKAH/AK6V8jIgOJaLfpfOCiJ5h5luVUkebuC4EcKWJ+g4iOgXArS0tLW/DH/D3ccQXQkyC32FPqK2t7bUX7ujoGAdgFBE9bPLhWPgDjKcBVDLznuZZh3R3dz9MRAT4O7ZSylYAm5h2djQzTwLQw8zNjuPslPH6lwEoI6JxZpA0FcB+4XD4wnx5BkAKIR5h5ttMHdsf/ve8PN1OAUBra+t1AC4F8Dgz72vy9zwAyvO8tnR9BID29vY7AJxBRHdorceadmOGaauGBh+ulJrMzFOY+V5m3tO0a9cT0Rnt7e03pcN5nncfgI0AHGqciCfC97l7wLKsdWqqXCAnezDzFAAnmXb2eCIirfUd6fpVX19fHAqFXPgT4UuJaFcAexHRPcx8nhDi7nRkZiFgJhHtQ0Rnwy/jNgAQ0V3BBzc0NIhkA506WQAAIABJREFUMvk4/HqXtw0LIoR4xdTdmQA0EZ1CRKcw89P19fXFyWTSBbCvGT+MBrA/My9g5n87jqMAfxETwL3MXKK1PpiZt9VaHwB/wXJaNBrdhZm/MM95DACY+VwiOkUI8fQa5vXfiegjIvo5EZ3d1dWVVEodycx3EdFzWusqU6/OA1AXDocfa2hoEADQ3d39FwBVzHyS53nbmf7wMyL6V3BsFGTWrFlHADjfDMh3I6JdmbkBfp/x92BYItqMiP7OzL835aAewFJmvtWyrFGAv3sTDocbAUwkokullGOYeV+t9QfM/PDqZoZS6mwiupmI5jCzHRjz7QzgWTORBQDYtn0RgOuZ+WmtdZXWehdTvvYDMKO+vl5allVCRP+EXyYO9DxvO631wTCTbLOT/INndRvWXYmoLRaLpQde79XU1ER7enq067r5zJ1Ki4uLo4HttjeUUrsCOMiyrJBZsbiKmZeFQqHxgdWut2tra2d7nvdmvp1+IcRuzLwiuONQX19/1LJly7ZfC5v/lWVlZRMC5jhvRSKR14QQz6RSqT8A+LXjOD8GcAIRNcRisUsC97YqpYYDuMBxnOsDW8NbATgoHo8/EXyQ4zguMx9XX1//f2mzNLMNHCWiyxKJRMpxnD6JY+bdAXwZXEW0LOuwUCi0dUtLy3yTL1cB6PY8LxIwOXgzEom8KoSY29PTcw6A81tbWyfBH0id5bru9YHHHGXb9j+J6GeDzTSzOjWFmafF4/HgpOpF27ZTRHSJ4zhXx2KxV+Lx+JU1NTXTM77RmUqpTQGcCL/xS7OV1vqAlpaWxiyPLSeivQO7afOUUjsAOKGqqmpYrhVaAKO01gcHVFDmO45TxsxTS0tL9wbgep53CnzH//1d152RDtfQ0NDW2trqArAGmzffV9ra2g4jovFm5yjdNsxraGhoaWtre4GZrySiBw466KBSM/l+ZuTIkdGACeY8pdQCAI8D+ADwBwQA/gTg1YqKCidgsvBWTU3NXK318wB6nY3NYsaCioqK2kDY15RSHxHR48lk8mj4u6m7A/g4Fov92YR5x7KsSVLKTfKZjxoWVFdX1wXMcuaZHd9/CSFOBfAXpdShzBwhouNisdg96XBEFFdKPcvMVzQ0NNwXj8fvt237PSK6AEAiFosFd18eBnChUmqk67pLzPtNAnA3gGNTqdRB5v9g5oMArCwtLZ1RX19fbHaLEvF4/HD+utF82XGc5QD+Ztu2AhATQlwIoEJrvauZ9ADAvGg0+oznee+YnZ7goGt5RUXFxHTeEtGptm3vTUQHA8inJCMA3BmPxy83f89XSn0KYC4RHQhgbjQa3QW+TftfXdf9TeDeebZtv0hEM0tKSi4AcGEkEtlbCPFzANfGYrHgjv0ZjuN8ZXY6APgmuWbydXs8Hj89EPZ5pZQAcLZlWVfPnDnzPdu2dwXwD9d10wsbCy3Lej0cDi93XTenaVeBdcoGzGzH4/EW8/d827bLiejPoVBodwDPtbe3nwpgTwCHBr4V4PcvnxPRJTU1NXc0NzfHpZS/ZuZtmbnOdd20wtA8y7JmSSlnAdg4fXNbW9thAPYbqA3jjIGIMbuZasYz1ekdTgBQSp0BYFdmjrqu25yOs66uLpFMJt9k5isBuHPmzOmxLGtCaWmpFxgjvVdbW/tKKpX6QGt9dCwWOx/AVNu21yeig7XW/4jH45+vRV7Pj8fjE9PvYybZ1zHz7Orq6kMDbdwrjuN0MPPts2bNOgDA40S0O4DXAwpT7yqlDiCiIcGdyiDxePx+x3HeiMVirwcuv+44Tjkz/8qyrPK0Uz0zr8fMx8bj8XvTaXUch5j5gVAoNB7AQ6FQ6GfMvA8RnRyLxXonMET0rG3bD6HvTm1ezELsFQAeicfjPw1847ei0Wir53kvw99RPqa2tnZjIpoM4MGWlpb6QNg3lVJfMvP+K1asqEgkEl9allWdSqVWBqx23rVtey4RLdJaHwV/weUHzepOQBLMfLhS6i8A7ojH468z84A+GszclGnrx8yvENFPAWxUWVm5pKyszBZC3JBZgGfMmPGZUmoagFNyxa+1biGiw23bfqympub68vLyVtNRro3D8Z8zfQFaWlqeV0olzOrrr7XWk4gIWuu306sZARbAN7OaALNqAeAj13WfQH9uBfBQR0dHLYAnAcDsiHAqlcoqIwegBcABtm0/JKX8a3t7++w5c+asAjAf6DVvqgOQCIVCuwcnMFJKMPNHzFwL4Hz4ZmZLKyoq+u0YMPMVqzMBCYVCdcxcKoR4JUuepO3QowBeYWZWSn1pGurtAawiojeZ+Q0iOiQajQ4J+PF8kGPyAQBPZ5HWfQUAFRcXbwF/RbkfRNQxfvz4JzKuvcLMYOYtzaUDAcwPTD4AAJMnT9a2bd9ARD/4CYjW+qdE1AVgSZZv/hqA45VSO65YsWJrIcRwIcQ1mf5frus+oZR6DcAIAJg1a9buRLQZMzdk2ks3NzfPsW37WSLaCvDNZ+CvLt7R0dGxX7CsM3OSiJIAagHcDr/eXKqUup+IbkqlUs8Z2+IBbYeZ+apMn4B4PD7Ntu3LTZvwF5MX3cz8RTAvlFJg5tcB7D179uydkadtEkI8rLW+mJnrANxrdiN2gr/Luy38zjW90nsQgBnTp0/vVEo5AEYx8/NKKTsjH9Ltby2AmGl750kpN3ccZ/OMJMwz4YI8EPwOpu7OBfDTfHkGAFrre4J/V1dXv9Ha2toDf5cSWuvDzE9XZN4bj8dnKaVmwjftu1AIcTAAXVxcfHVmWCnlH1Op1Dnpv1Op1ET4JpmvZSmXHwIgKWWUmW+ybXsmER2plPqUfPPO+YP1zyuwzvg4MPkAAEgpX9Fap9vj5+CbR72eMfkAAIRCoT95nnee53lHAIgT0URmnpspb5pIJFJKqT8CeCR9bTBtWE1NzQ4AVscv8qcAvhBC6BxxHlpXV7d+Y2Pjl4lE4nPHcfaxbft08k2A/kNErQAWaq0z6+e64NbgZEoIUQlgYyK6v62tLZLRdrQDABHVwp+AtDDzGUqpvwOYWlFR8bJZKFmS74FFRUUfKKVOJKJdmFkz83wA7xGRJKJNAaRVvZIjR458IHhvZr+stT6YiL5Yb731+hxJYNqlK7EaE5BkMjkRQFhrfXmWCeZbZvxZb1nW8VLKiQCKmPnKzLDGhLd3ISmRSHzsOE6V4ziKmUcx82dENAvAxwD+G9/0f47VmoB4nnd8KBRaysynAPhNJBL5Uik1zfO8yxKJRM7ZOBFlczTqBgAhhAyHwxsCkFrrXM5hebX3jQ1kGMCFWmu3vb29UynVqLW+qqWlZY1Uo0zlyMabACL19fVSCLElM4OI7su1Q8PMG6X/T0RZVwdSqdR0KeVnWusTADxpVn9PAPBELgcw13WvU0qFiOhcrfWk8vLylUqpxwFc6brua0OGDNmYmYsBRJk5muNd0t9sUwCLsjnGjRo16q329vZBm2ClGwizutMHY/0BABsCgOM4UQAPwrfffBm++ccxRLQxAKxcuTJoIpjVgd3Qr3wxczcRQQiRUw6RmRdnDiiZudukNX3fJgDezbzXhF0YeKcfLKYelDLzjFxhmHkjIcTmAOB5Xq5OfB6AcSbOjU3dytomCCEWMvNWAKC1Tk8WT2DmE4LhMstcPB6/zLZtENFvmflIKeVXSqnpRHR50CcqG1rrfiIUpsObD9+8EvAH1WEAT+VqE7TWGyHPBKS5uflVpdRCIcRB8E0zDgXwYTwenxOJRB4ioissyxoKYCMp5U7sm62AmbckIhDR+cx8fra4mXkj45u3GXxn8pzy2sYhE0DONnwVvrbBz0lxcXGfeydPnqyVUj1EJEyatgLQnkukwixKVDc0NAgzKW3P5rw6Y8aMdqVUsB9Kl4vrB2qfw+HwpGQy+TcA5zDzBUqpT4jo/lWrVl2RTxSjwDolazsO9GmPt2Lm2dlubmpqWqmU+oCItjb3bgagLcez+tRls5iRtw0z9XZ1JiBbwt/VyVnHPM/bkIgW27Z9G3xrig/MM/Yyu3clRPTKajxzUAgh+ozXiGgL89+zmfnsHLdtCACpVOq3oVBolRkH/rKjo6NDKfWwGQe+n+1GY2I/A8B6zPwifJ+OSTB+YVLKYF+/NHMsklkOhBCbaq2zChmVlZW92dmZVUcmK8y8FRFh6NChudr/eQBKAWxERJszM7q6uvL2FfX19bKjo+NeZj4KvjTzAvLNuC+G3z8UDgFHYALCzNkyJBz8w6hBnOY4zvla64jZfj9JSnmIUmq3tLlAJkSUVyklHA4vT6VSIKL1sv3OzOsNNNBzXff6+vr6mzs6OioBHKC1Pk4IcbDjOJG0QyWyfHQiCufonEbkeNR6ADqnTZvm2ba9goi6q6urywajlpI5Y06TSCRStm3fTkS/j0ajG3ietxuArYjo1Hzxua77x7q6uutTqVSV53kHENFxAA6tqampKioq+iCZTALA/7muO2WApK3A1511Hz777LPh4XB40JWFiFYwM4QQ2zc3N7+TK1xDQ4Ng5tsBfBEKharSTsKm4t7IzH3ePV8ZGqh8rSXLmLk82w9CiB+kdncmWusVRDTPdd3R+cI5jrOxqQJZ8zN4nZmXA8jbJgSe/5VpH450XfeBbOED9zGAKZZlXSOE2I+IDgBwLDNPikaje+Uz2SSiEfBXr7KlO53eFQDedl13x3zpGAjynXFPrq+vLyaiScz8CDOz4zgPM/N1Qoj9hRCbMXN3OBx+MvBsMPO+8Xg86yAtjVKqC8ADruueOEC4tXmNQcHMy4loaH19vcw2oDD1b4WZuHwFIGtYIiLbtoPt9goA8Dxv43wLZABgnPOPtizrLCmlDeBQZv5tOBw+aOLEiXvkUsYr8I2z3NTDXJTj68nFCuTox5m5PGNMsQLAgG3YarKCmZ+Ox+MT8gVyHOcAZj6Bmf+83377/S49ljBjgbmr8bx+/bTneeFBLpKlFaYiaUGhXBiT+XOj0eglnudZ7JuBHh0KhSY5jrN7toN+mfkmACEp5a5BFU6l1AXIsvM5EFrrFTATokxWrlw54HgxiBBiOTMjmUyOAJCtnpcDQE9PzzIhxFdEhOHDh5fDF0TJypIlSyYR0VEArqiurr44/U0ty9pISpl38vJDQpiZ5vL0qnMGlcE/otHojrZtbxqLxZbF4/FHXNf9BREdA2BTUwjXCCPJ+DaAQ9OOTmmMbeIh+e63LGtUJBLZbdq0aclYLDYzFoudq7XeAwCY+UTz72IAG2XGz8yVWaIEER2WeS0ajQ5h5v3hq2HB/BueNWtWv166trZ2Y8dxtsi8nofbAAjP845h5l8CeK+qqirnyolSasNoNLpLY2Njd3Nzczwej5/NzHsBKNJaH//UU08thj/z3j/H/T8J5MUcAD+qqanZPTNccXHxgCYWQTzPewEAtNZ1mb9NnDixzNjKYvbs2aPg77w8lJ58AL7csFm5+q7wHBHtFY1Gs22Z9isjP0SI6AUAOzqO86PM32pqarZTSo0EAGaeA/Q6VPfBOBeOT/9dXFw8F0CX1rpf+TPxTUj/HQ6H3wDQZSYT/YhEInun/19bW7uxUmrnRCKxKh6PN7uu+xspZTWAUq31cQO8Z7Y2YXMAe6Nvm7B9NidDy7K2TTtRZsTbb+DAviPl8CVLlhzLzHuYv2E69zlmUnIQgFha2cqsLHK2fLAsKxSJRIIKOy8CcIwDbB8cx/lx2mn/m4CIngEQXrx4cb90G+fz/QE8A/SWoZIlS5b0a1+UUjXwHcfT8b4AAFLKfvE6jjMi6GhfU1Oze21tbUUikVjsuu4DruseCV+xZ4eurq4fvJnldwVmfgbAftmcnR3HqYJvRvSMuTQHwH456lyfdoWZB9WGrSYvENHe2Z6vlNo5Laygtd7dpOmW4EJmMpksxtdKdn3IsrO/GP5ufWa4rOObTKSUL8EXm+lXV+rr66VS6ifpv6PR6NaO4/yoqalppeu6T7iuewozH2QWhY7IvN+wGxHFM48AoPyHBeZECDGHiEYb89s+ZH7bgdBaPwMAPT09/e6zLCtkdqBfa2tr+0pK+RwAeJ6XrS/YwJRBGD8ZeJ53c8bi9FB8rfL3gyfd8b0K4MCgCoxS6jgE7IAtyxrled4cIrqvrq6uNwO11hL4evVtTWHmPwH48axZs24x5gWoqqoaJqW8jYjyriaGQqEnhBCNwcIYCoWEnyw/XUT0KoDS1tbW0wLvuD2AS3JEe4zjOCel/6isrCz1PO8uIlqfjXqX1vpBAB8R0S3BvItGoxukUqnHmPmFAdTBejFnWswAcLrZWZqaa1eF/Ol9s+d5T9q2vU36emAbcwUzMxH9GcA4x3EuC068bNs+HsBzbW1t55j77gSwXGv9j+BA23GcfYion611PlpaWp43ncSlSqleScCJEyeWdXZ23gNgjuM4P1qyZEkHfJvP8cHBjlLqdPh+F98VbgKQ8jzv4XTn1NDQIJRSp8CXle7FcZydlFJxx3FOyBbR9xXP86bCXzm6TynVuyqllNreSCc3EREZJ++nAPxWKdW7qGCECx6Av80NoHdFeioR/UwpdV56oFxbW7sxfLO9ocGwzHwbMx8dzHvznS4VQjynlDqkvr5eplKpp4noseDigOd5aVOgXGIFAAAiOj8SifQOfGtraytSqdT9AGDkf0G+dOQKrfU/LcvqNb80krdNUkqXvl6e+495bjXgtzHp8NXV1c8D+ISIrgLwxciRI9ODKrAv+3kAfCWaXilPIxX5GIBzHMfpHUjU19cXSylvFkK8mJ6EsC91unl5efnUjPpXy8zPJJPJ6/LlxbqkuLj4UWZ+SwhxS3Cg4zjOiNLS0n/BN9v4IwB0d3c/CD9f/mYkggH4i2PMfGsw3qqqqhb4fdvVwXirqqqGMfP9zPxSbW3txtFodGut9Yue590Z/AbpiSERrSAiUkrd6DjOtGCYAt8sRPQXAOR53oPBSYg5m+xuAJ9JKe82YW8EEBZC/MtxnN6dENu2D4Uv2d6L1vpWmDYsGG9mG7Y6adVa3whASCnvsyyrd3fXiNckSkpK/m0uLQQAZu6V166rqxtuJLIzzeT/AwBCiH2BPm3GqwC2MielAwBqamrG5jGn6kNTU9NHAP4N4NfBtnns2LFF7e3tfwXwgm3blRMnTizzPO85Zn4wraJp0iPMO+dqQxcx817BfIhEIhMzLR0Gi1EP7fI87z7TJwAAbNveD8BAFh99iMfjs5n5aSK6wrbt3m9gWVZJKBSaSkQ7MvNVABCLxWYR0fPMfFlNTU3vgllVVdUwz/PuYWbXtu0tYb5pKBTqja+2trZCSnkPCuZXvYQAgIgmGzvFV23bnk1EI+E7O04FcDLgH+Rm5FVvTiaTixzHaWPmoUS0HzM/s2zZssdyPmUQtLS03K6UGg3gLCnlUUqpj0pKSraAP7P/K/Kc/MnMvwIQ8zxvruM4M4294H7wbUr/DABCiKme5/0GwI2O4xyvtV5KRFUAbgRwTpZo/8a+c+LZRPR+WVnZWPhylw2u6z5u8mSFUuqn8NV73lRKPQffLnqcnyz+6eochsjMtxLRdADdxcXFd+YJx47jnMHMTxHRm0qpBIAU/BXkTzzPuwEAXNe90bbtXZn5otbW1uOUUq/DN+3aCcDjqVTqBsBvfGzbPpqI/uV53ju2bbcJIYaaHZU/AzhjsO8AAEKInzHzUwCedhznZfYdYMcCWI98ffSFAOA4zoXMfFMymVxg2/YcItoOvnPW7fBVsL51XNdd4DjO0ezLeC4wak3rAxjKzP9HRL1ywWZFOsLMwwDckSvO7xuJROL9SCTycyHEfQAWKaWegW++uQ+AL415AQOA53knSCmbADyilJpLRP+RUu4DfyI/nZl7pVk7OzsvKCsr+xGAq9dbb73zjX3/tvAFDKYRUa9s75AhQ87v7OzcnplvV0r9HsB78CUUtwTwt3g8/pipN6cz83QAbzmOkzY1GA+/w+gzgM3CdUKIJ5RSrxLRl8w8johKieiU5ubmVwF/h0Ip9TMA90spF9m2/QwRFUkpxwFYIoToVVlpaWn50LbtGIBfKqUOGTJkyGyYc4eMudGj8Ove1KC5kZTyYa31VQB6iKiPRLfneSdJKRuZOe3U/wmAH8M3V7go7RMXj8cfV0r9H4CGZDJ5oPLPKdoQwB5E9LyUMlNC/L9GY2Njt2lHG+ErVM0h/xyncQBKiehM13VnAUBbW9tXSqnD4be5rymlZsOXJ64E8BB8yXEAfh5alnW4lHIGgOeUUnMAdJSWlu7FzEMAnJDefTVyvZeXlZUtNO14BfzzXp6IxWKzlFLDmfk0ZhalpaVXwfdZK/AN47ruPMdxjmPmuzzPe9+27dnw/ST2gX/WzsHpQy5jsdhzSqnfENH1zPyB4zjPM/NGRDSGiP7IAcW0YBvmed77mW0YgONzmVDnoqWlZa5S6mQAU6WUH5qyOhz+juki8mXCMXLkyIfa29ufJaIblFJHwHfmriZfDvet4LwnHA4/lkwmL2fm+5VSN5SVlV0N4DrP866TUp7IzA/+P3tnHh43cf7x7zuSd22HJLYT+mug3DeJA4FwBTtkVyPbGBqgBQOlHIVSjtIWSikBCiEtlFKgB23KUegJhBpKCxRjW5I3xCHhSKA0XClXOBOaxM5BfKxX8/7+kOQqm3WOkpv5PI8f745GM69mpdG8M+/7jgz2VMkhGIf8CkFY4XXS29t7UTKZ3BXA3yzLepmI3i8rKzsYwarSFNd1ZwH9+yI9kM1m35JSPoUg0MMEAP/q7e0daC+LSQD+ZhjG/LAdPi+EGENEdzLzJRvSrkAw2RJOkv85l8u9Ff5eQ4loLIKIVWuLzFeIMwA0EVFL2G9+ZBjGYcxcwcw3eJ43DQjGXpZlnUpETUqpTNRXFRcXH45gUuxrnue9O3HixGldXV0XM/M9lmWdJYRYzszjAXgAFmzo9W6vCABwHGc6ggH73aENf7NhGAcCmMrMk1atWrUMAFzXvUsIcTgRPQDAZOZFAC5cvnx5KtrdMZyx+wMATJgwQTHzpPCFn88MZp6UTCY7geCHdRznMgQxvX8JIAPgykQiMZqIOgHwiBEjCg7mXdd93vf9UQj2sOhB8PD9FEBlNNhtaWlZ5fv+kUQ0hZkXENFbRFTX1dV1LTNP8n0/32a6WSl1mBDCRfCif1gpVZUXbheu6z6fSCQqmfkHRLQQwHIAt/m+f4Dnea2xrL8O/wZk2LBhTUT0fSI6N9/JMpfLvcnMk5RS8wDAcZyZzDwKwG1ElOVgP5YbfN8fHTmuMzO7rnu+EEIiiK7lA5jNzF+qrq4+Mb7Dq+d5jxNRJYJB1ipmfomIjnVd93IA14Qv8YH4Cf4blQeO47xnGMbhzHwxM78ett8flFIHxcMUOo5zBwALgCuEMAH8QwgxUghxJzNP2mmnnXrD6/iVUmoqCkBE1zFzY4H055h5UiKRWBh+f4SIro4dv4eIbs0/r6ioaFF4Pzwbk/MRZt4XwNVE9BSA25l5FAV7rAD/tTW/m4iuVUpdlF/u9k5bW9tj4TN4K4L2+JCIrjFNs9J13X9F+TKZzKLS0tKjwpfva0qpZQCu6+rqOsz3/bviK26zZs3q9jzvBASx8+9hZpeZz1u2bFlVODvdv+r62GOPdVVUVBxHRCeHJhg5InoSgU3zN6PBg+M4LjNXEtHtzOwzcxeAyYlEYsxATtAAZjHzpK6urhsQ7FUyG0HEtnuJ6JBoI7MI13X/ET6bPyWiFQA+AvADAKMiRQUIns9EIvFFDjY8/ZNS6g/xcohoavjM3x5Pb21tfYOZv8XMF+T73WUymSWJRKIawDlE9BKCvrBRCHG467qrBYYIfcOOQLCfjQ/gZSI6u7y8fHw0iPN9/z8D9I9AEEXoBwO0GTjY12lSb29vIbvqa+OrN67rvhr29d9h5jcQ9ON3ENHosJ+Iyz3bNM1KBGEsl4Z9+Rme551ORDdzsFlu1B5vJhKJQ4joUgoCGvQw828BVMY3/nJd98cc7GfwdyJKENG7RHRmdXX1CeG7aTkzn0ZEF3uep5WPjQwz/4aZb89PJ6L3wjFE/3PjOE6jYRgjEfQ1y4noAwBX+74/0nGcZ+Lnu647VQgxFsH7qYeIZgghDi8qKvpxWO6cKO/a+rC88LGFeBTAGnviuK77R6XUaCL6DRH1AnibiC7t6ekZE41NGhsbs77vp5n5Mmb+DzOvIqIrysvLJwohbgk31wQANDU1LRZCjAFwLRH9SSnlAUG/ahhGNPj+GMDLQohxvu/fFB83AHiemSf19fWt4cvW3t7eWVFRMQHAmUT0AoINQx9WSh0ZH/d4nveQYRhjONgrDcy8HMB3u7q6jhwo5H3YJx4RPvMGBxsFjg774UlFRUWRj9Z98Um9iKKiomXhdTwVK/PhcCf3XxFRF4AXmFl6nndNmNfLLyfGD8MJs+iaPuzq6jqSgyAm/0TQb94H4AjP81abjPE8793S0tLDKNiXZT6Cvuq3AEZGm9k+9thjXaWlpdXM/H0i6mDmFcz87erq6gYEW048DAC5XG4BBwFDNnqggW0B2kClfpNRX18/pLe397hI04wjpWwGsJ/runsUOHWjEu2EzswTB9rVU/PZIdxp+ouJROLpfKXQtu3LmPlnhmEckG/bur2xrp3QNwV1dXUjstnsYbF9WgAEdrmGYbwB4BXXdbcmcz2NRhOyITuhazSazx5bjS1aX1/fBUT0gJTy6ri/gm3b3wBQS0R/XMvpGs0mobOzczSAh/v6+v4Q3405nU5HO0w/s70rH1uKXC73EyHEXy3LOiVKC5WPmxGE49R9gkaj0Wg02yAbuhHhJqO8vPyXHR0dNQBubG9v/3poa783gL2IqLWoqGiNfSU0mk2N4zgvSimvY+YbiouLF0gpXwBQES79fiSEOGtLy7i9YhjGFb7vH0ZEjaH/0sehaehOAKZ6nvfQOorQaDQajUazFbKpVCkxAAAgAElEQVTVrIA0NjZmq6ura4noDABPhxEn2pm5oaqq6timpqbezSGHYRivh/aDOlazBkBgHw7g6NC2PBfakn+PiEavba8TzaejpaXlP11dXYcCuASBrW0uDNKQdl13gx0XNRqNRqPRbB1sNT4gGo1m62VL+IBoNJptF+0DotFo1sZWswKi0Wg0Go1Go9Fotn+0AqLRaDQajUaj0Wg2G1oB0Wg0Go1Go9FoNJsNrYBoNBqNRqPRaDSazYZWQDQajUaj0Wg0Gs1mQysgGo1Go9FoNBqNZrOx1WxEqNFoNBqNRvNpSaVSxUKIS5i5va2t7dkNObe+vn5Ib29vrVLqxUwm8+ba8kopU8xc5Hle66eTeP2RUtpCCNXa2uptrjrXl6jtALzged5bW1oezdaNXgHRaDQajUaz3WAYxglEdIsQ4q4NPTebzX6BiBpN0zx2PbJPAXDbhkv4qbiZmW/czHWuF319fTsTUaMQomZLy6LZ+tErIBqNRqPRaLYbEolEWzabfRDAE1taFo1GUxitgGg0Go1Go9luaGpqWgzg9C0th0ajGRitgGg0Go1Go9liSCkvJqIeZn4UwNUAqgCsYOaZ3d3dP501a1Z3Xv4UEV0EYE+l1DIiaurq6rojytfQ0JDo7Oz8oe/7M9ra2pqi82zbrgRwGTOPArAKwEMVFRV3dXR0XEdEzzmO079iwswqnU4fTUTfJaI9AHzEzFM9z3syLgsRqYkTJ5Z2dXV9B4AEUArgWcMwbmtpaXk//1rT6fREIcRZRLQ7My8nIgfAHY7jLI/yWJZ1OhHt5Pv+XaZpTlJKTSCif7que0kkW319/Y59fX1XM/PRzJwF8OSwYcNuaWxszMbrq6mpOVgp9U0iqlRK5YhoDhHd7jjO2/my2bY9hpkvXp+8NTU1llLqQgB7MvMCAH9g5n8LoS37NeuHvlM0Go1Go9FsSc5k5vMBzAYwAcCrAEYQ0fUlJSX3xDPatj0JgMfMo5j5ZQQTqT8dNGhQe1VV1WAAWLx4cYKZrxRCTIjOsyzrJGZ+nplPIqI3AXwM4KaOjo6HAXyPmVPxepg5LYTIEFECwFsAjiKiJyzLOilPdu7u7p7BzJcB+AjAIgAX+r7/YjqdPjTKREQkpbxXCPEoM49k5nkA+pj5BgBzLMvaK5Z3IoDzDcN4jJm/Q0QlzLx/rE4zm80+p5SSAF4jogoiuqGjo+P+uGBSyvOUUs8DOFEp9RaADwGcy8wvpdPp+nhey7K+zszPATiRmd9cR95rlFIOgCMBzBNCFBPRI0KI70OjWU+0AqLRaDQajWZLcyQzu57njXVd92vV1dWjmfkRIjq9trZ2DwCQUh7FzDcy8z3V1dWjXNc9x/O8CUR0HDMfXFxc/KNCBdfX1+9IRL8H8Irv+/s4jvMV13VP831/PwD7IFi1WA0iOoGIvui67hdd1z1FCFHJzEuI6Ad5WQ9i5lXJZHJv13XPdF33JCIaAyBnGMaDY8eOLQKAdDp9AYBzAfywra3tQNd1v+a6bp0Q4kgAw4joQSKiWLn7ASgyTXM313UPHT9+fNyx+zAiyowfP/4g13XP9jxvJIAHAZxs2/YBYVuNBnAHEbX29PTs6XneGZ7nnWoYxt4AXhdCPJBKpb4QynYQEf0GQEtPT8+erut+1fO8UwHsQ0TzhRAPWJa1c5j3aCL6IYC/+76/j+u65ziOc5xSqgrAKev9a2s+82gFRKPRaDQazZamu7e390pmZgCYPHmyYuY/AiDf90eGeb4JYKVpmpdNnjxZRSc6jtMSmm+dnUql1jAt7+vrOw3AUAAXZjKZJVF6JpNZJIT4zgDyPOw4Tkv0pbW19SMiehxA5ZQpU+JjJ9/3/bObmppWxOR5jZmvYua9y8rKagGAiL4LYLbrupOjawzLnaOUuhrAWCnlMXEBDMM4v7m5uSNqj9ihrqKiokujNA74bfj5oPD/JQD6enp6vjpz5syV0YktLS3/IaKzAAwxTfPrACCEuARAtre398x4Xtd1P2bmswAMARDlvQhAl+/752YymZ4ob1tb27Nba3QuzdaJVkA0Go1Go9FsUZj5rfjgFwAMw/gPABDRkDBpLABTKfWUbdtz4n9ENA5AmWmaO+WXrZSqBLDSdd3n8491dHTMAKDy0wE8VyCtE0DRK6+8Eldy5mUymQX5GZPJ5D/Cj2NSqVQZgtWEpvx84XU+Fsp5RCz5k5aWltcL5QcwP67wAIAQohMAiKg0/H8YM89ub2/vzD/ZcZzXALzJzIeHSYcBKJjXdd1XiegtIcThQL+CMzuTySzLz8vM/8hP02gGQjuhazQajUaj2aIQUd96ZCtl5veZ+aGBMpim+Ukul8svOwEgWyj/F7/4Rb+9vd1fH3mIKL54EdGdnwAAvu93AQAzlxYVFZUqpcDMBfP29fV1GYbRrzwMVH/sWMFryaOUiD5Yy/FVAAZFeTHAdQAAM69i5kFh3SVEVDBvUVFRl++v0ZQaTUG0AqLRaDQajWZbYAERfc513ZvXlimVSu0Q/87M7xBRRW1t7S75kana29tHASj6XwUiov1TqVRx3BwJAHzfPwQAhBDvlJWVfdzR0fEJER1UqAwhxCGhnGvdeX0D5XqTmSupgNZUVVU1uLi4eF8EfiMA8CaAteXdh4imhUnvMvPoQnlzudzBq7uxaDQDo02wNBqNRqPRbPUwcyOA/SzLWs3ZecqUKUJK+UfLsq4sdB4RPQSAfd+/Ke6/EfqLfCq/BWYuNwzjinhaWO71ALK5XO6xxsZGn5mnMfMplmUdEs/b0NCQAPBDAJ8IIR77NLLkyfUAgN0sy7oo/1hxcfG1CFaToqhZDwDYXUp5YX7ekpKS6xCskER5/wpg93Q6fV4837hx40qI6Np4GhFRTU2NVVdXN+LTX5Fme0OvgGg0Go1Go9nqWb58+V3l5eVfBXCfbduHKqVaAQwVQlwIoIaI1hhAA4Efg5TyZgBXtbe37yqlvB+AYZrmWcw8HEDvpxDrAwCTpJS7EtFfAAw1DOMSABOY+YpMJrMIAJRSVxuGUUdE023b/ikzPw1gJyK6jIgOIaJz4nuBfFo8z3tASvkVZv61ZVkHCyH+ysxFoQP6KQB+53leW5j3fsuyvsLMUy3LGhPlBXA2gJMB3Ou6bgYAli1b9tuysrKziehOKeUBQogmpdSOgwYN+h4zD4nLYFnWqUqpaUqplwAcvLGuTbN9oFdANBqNRqPRbElWMvOK/EQiyhFRZ7jRHubMmdPX3d1tA/hTuD+GR0SPMPO+AM50XfcuAEgkEkxEnYj5NbiuezURXQxgZwB3AvilUupD3/cnIHBCVwCglPLDc1czqQIAZu4Oj0XyrQQwVwgxEcB4ZnaY+WEA+zPzRZ7n3RrlzWQyS8KQuy3MPBlAG4D7mLmUiE5yHOdPsapWMfMaTt6hDCuYeWWBdD9sq97wO5eXl58E4FYiamDmZgCPK6UmALi6urr6/Ni5nEgkorynRnmZ+Rgiuqq6uvobUd45c+b0JRIJG8B9AC5RSrkAHlBKvWsYxolh+/QCgBDiTQDLAMwtdC2azzZUwKFKo9FoVuPpZ2a9AKZbjz7qqAe2tCwajWbrZ/r06TsUFSdWmmTscsQRR6zNGfp/Yty4cSWlpaV7CCGWOY6zsJB3eD6R30Jtbe2gbDbbm8lkcnV1dSNyudxHAC53Xfdnn0ammpqanXzfL1m+fPl7c+bMGdCJPJVK7WAYxq6maXY2Nzcv/DR1rg8NDQ2JxYsX75pIJPylS5d+sDbZ6uvrk93d3bsAyK1cufLDdV2HaZq7dHd3f5AfwUyjWRdaAdFoNOtEKyAajWZD2NQKyIZg2/ZQZn6ciOY6jnNZ/JiU8ocAriWiQxzHeXELiajRfObQPiAajUaj0Wi2WxzHWW5Z1vsALrVte5hSahozFwkhjgNwPoAHtfKh0WxetAKi0Wg0Go1mu0YpdZ5pmh3MfC4RnRmGi10O4JZEInHdFhZPo/nMoRUQjUaj0Wg02zXhPh3fmjhx4pWffPLJHkVFRf7QoUPfbmxsXJ9N/TQazUZGKyAajUaj0Wg+Ezz22GNdAF7Z0nJoNJ91dBhejUaj0Wg0Go1Gs9nQCohGo9FoNBqNRqPZbGgFRKPRaDQajUaj0Ww2tAKi0Wg0Go1Go9FoNhtaAdFoNBqNRqPRaDSbDa2AfEZpaGhIbGkZNBrNtsnYsWOLKNxIQaPRaDSaDaU/DK+Ucl8AT64tsxDi1NbW1jmbXKpNjGVZpxPRDQC4tLR0dBiWb6Ni2/ZQZn4hnkZEipnnCyFuaW1tfWpj1zkQNTU1xyilfkdEZzuOM9OyrNuJ6ELLsuo8z2vbXHJsjViWtRcRtQK42nXdv2xpebZlpJRXAfj6WrK867puenPJsymRUj4MYAwzP+F53rc3UR2XA7g4L3kVgOZEInFDU1PTik1R7wCy/JmIKhzHOa6urm5EWVnZPMuy3gJwxOaSIcK27SuY+YJN1XdrNBqNZtMTXwEZA2DPtfzt0dfX9+Zml3DTcBSCaxKb8AVWGdYxH4ALwFVKvQCgSinVZllWzSaqdw2UUmMB7KmUegcAiKgCQBERDdnQsizLul9K+cONLeOWgojGIGibD7a0LNsB47H2PmTZlhNt4xHO/FsA9iSixZuwqqOJqBxh/wGgDcAnAK7IZrPu2LFjizZh3flIBLtGo7e3NwlgBwDDNrQQy7LGSykd27Yr11mhlCdLKZ1UKvX5vENVAIzPkvJBRCSl/Idt25dtaVk0Go1mY9C/AsLMo8IV9R5mvj4/IxH1ZDKZ7WUAMTL8+PImrKYSAAzD+G5LS8vrUWJtbe0evu//k4gmA2jdhPXHGQ2gw/O8DwGgoqLi3M7Ozuscx3l7Qwqpra0dRESnAbh1Uwi5hRgNgA3D2JT3wmeFUQDAzE8DeDz/IBE9t9kl2gRIKXdh5jIAIKJNtqEZEVUy8yzXdS/Iq//HAK4aOnTo8QD+tqnqj6ivr98RwOeZ+WUAyGQyC2pqavY0TfOTDS2LiI4BIIuKihatKy8z1xDRuB133HFxXnolEf1rQ+velpkwYcJuAI5j5s/0irVGo9l+6FdAiCiakXrN87ybC2UeN25ciWEYxQAwYsSIVY2NjVkAaGhoMBYuXDgEAAYPHuxHpgENDQ2JpUuXHglgZyHEip6enlnt7e2dhcpk5tzMmTNXWpZ1CID9hBDvO44zEwCmTJki2tvbj2bmLzDze5lMZhYzc1RGKpXaIZfLFZWXl/ceeuihPVFeIlrguu7sApcyOvw/L/9AODN3ADNnDcOY29LS8n5+nnQ6fQQRLfE8763CzQoopSqJqGfo0KFvxNNbWlrekVI+zcxjozZauHDhoBEjRqz46KOPSktKSsb7vm+sWLHiyTlz5vQBQFVV1eBkMlkFoMwwjDfWZgZXW1s7yvf9kUT0SUlJSSacJRwNoP+FvXDhwkHl5eVrDABqamr2YebRAAwAc+IKSiqVKisqKjoUwarZ29XV1eXl5eW9a5uFTKVSewshxhBRkohechxnjfaOU19fn1y5cmWplHL59OnTS03TPAbAIKXUs57nvTvA9e7v+/4oIlJKqbkD5YvkMU3zIGb2TdOc0dzc3BG2zbuO4yzPK3cXpdQhzFxERC+sr7IWnncYgB2UUq97nrfGoLu2tnaQ7/vVRDTE9/35xxxzzLzJkyer9Sl/a6Wurq4CwBcAgIgaXde9vVA+27aH9vT0CNM0OT6hEe9bBg8e3NXU1NQLAFLK/2PmwxHMuL87bNiwZxsbG/1CZY4YMWLVBx98YBQXF6cMw9ghft/U1dVV9PX1HS2EKMm/n4iIqqqqygBgxIgRKxYuXDikuLh4fPjbP+c4znt5l9E/e5/L5VYbCKdSqeKioqKjfd//HBEtSSQSM6Jriaivrx+SzWYP9X3/+UwmU3AQn0qldjAMY08ADxU4fB+AqwDsCwT9Q1FREWcymU/S6fRIIcQBRDQ//rxJKQ8kolG+7/cUFxfPbmpqKrhyM3HixNLu7u5qpVQFM7/a1tb2Ui6Xi/rLf4XyJ7u7u7ullKvJXl9fn8xms+OY+fMAliqlZmQymZ7o2MqVK0uTyeQhzLx45cqVuerq6nIp5fL8ez+6F5LJ5MEA5i9cuHBIdXU1Zs6cuezYY48dDGB3AA+E74VDAezFzG8XetYAoLa29nO5XO4IIUQJgHmO47xWKF9E9D7r7u7+ZPDgwYYQIiWEGBy/bxoaGhIdHR3HMPNwInrFdd2CClFtbe3+SqmRADiXy72QyWQW5OchIkqlUocIIfYhokXd3d1zZ86cuTI6VlVVVZZIJI4Kvy+orq4u932/Z9asWd1ruw6NRqPZmjFjn6OX6oAzSyUlJYcR0VMA0NHR8RsA3wSAzs7OXySTyUsAcG9v7xkApkkpvw1gcmjuA2ZGMpnM2rY92XGcn0RllpaW/gLAN5j5ESmlIKITo/xSyr8qpa4RQjwC4EAiAhFBStnU0NAwMRqIGIbRZhjGYV1dXVPb29urABwU+UdKKdt93/9SJpNZAgB1dXUjAAwPq4+/oA8DcCeAQ6I03/d9KeVU3/cvz2QyOQCwbXuSEOImAH2WZVUN9NIjokoieiV/sBRSRkSLAGDp0qXfSCaTty9duvQbxcXFtzHzECFEds899xwcvmB/UFxcfCWAUgBQSkFK2W4YxsktLS3/iQqsqak5WCl1F4DDo/br6upalE6nTxRC7MvM9wJAKpUankwmF69atep6AFPCtL0Nw7gbQComI1uWdeewYcO+1dnZuYNhGB1Kqcjp9M5kMnlnd3f3FADX519cKpX6gmEY9xiGUdtfWPB7/j2RSJyWPyCL6O3t/V4ymbxu5syZZ5qmeSczl4dt6Uspv+m67l1R3lDR+i2AI6Pyici3bfuOzs7O70bKWyjP7qZp3mUYRk2kt+ZyuRWWZX2ViPZB7D6oq6uryOVyUwE0IDRRZGZlWdZdw4YN+9YAvyeqq6vLk8nkHQBOic4L79UZvu+fEA22bdv+MoDfAihnZggh0N7e/kJtbe2JhZTdbQWlVNykZm2K5s+TyeTXAKCmpuaw1tbWOalUanhpaekcALsBWJDNZo+sra1lpdSvAXyZiPpNRTs6Ot6uqanp90VraGgwmHlRMpks7uzsvKq0tPQSADuH90OfZVmnCiF2ZOZfEFFJmJ6TUp7juu79oRwjfd+fBwBLly69LJlM3sDMgwCAmfssy5ried6NkQyhkg4AqyZMmPA20G8ic6lhGNcppcqi/iebzf5HSnm267rNkbzZbHY2gAMNw3iJiMbEJ1MiwlVawcxrtCUzl4V94SIAKC4ufgnALCnlICHEiWG2WwFckU6n9xNC3AOgKrrfstlsj23b33cc51ex+khKeSUzXw1gcNTX2rY9DcDssN55ANDb23t9Mpm8cvr06UMmT578CQDYtn0RM98AoCK6dsMwFlqWdZrneTOy2ezVyWTyurAuJJPJDgD87LPPlgFYzZdl0KBBU5n5a9H3MO8yZi63LGsUEREzd7a3t89BYDocPWt/r66u/nKk0EycOLG0q6vrZwDOIyIzamYp5YNdXV3nDjSAX7JkSW0ymXwikUhcRETXAtgpup+klF8homXM/EcAO0XXalnWzz3P+25URn7/FLaHL6W8c9myZZdF/VNNTc1OlmX9DbF+u7i4eIVt2+c7jtM4YcKEvQzD6J/EYua/JpNJIHj3/qaQ/BqNRrMtIIBgBg3AHmHaTpZlXRn/s227AQA8z5tBRE1hvq9JKf/Ptu2zmPmSMO0az/OmWZY1DsELsBzAbGZ+AMACAAlmvsm27f6BKcLVCCI6HsAJAJbGjn1ZCPEMgAPj6cxcv2TJkuOAYHUEQGRSdSGAg5h5MYBoVq3aNM17o3P7+vr6B0pKqXkAEMo7HYHysQjAgwCeQbAS8G3DMK6K1R29UIqEEIcWatTQRnxUocGDbdvHIXgpPRzmrQSQI6KbmflnRGQDOLuxsTE7c+bMqQgG+HcC2M/3/RHMfBGAw5RSv4/KTKfThyql2gEMBnCS7/sjlFJjmfkVIcQ/AJQKIeYBgGEYqymalmXtZRjGbAD7MfNZQoidwzz3E9FFHR0dF4TXfSozewCyzNzAzA1CiD8Wun7DMC4DMJaZz/J9v9w0zZ0A/BLAidls9puFzom1hc/MNzHzuYlE4nMAxgF4D8Bt48aNKwnb8ADf92cimHH/qhBiZyHEvgB+z8yXlJWVTYnKtCxrN8MwZjPzSGY+i5m/gOB+aSKiBxD4JswDgpnXXC7nAqhn5m/6vr8LEe3FzFOJ6KKlS5cOaH9dXFx8DoAvAvh2KPdwANcAGC+EuC6UZWdmvj80ZRkZ5jkHwMhcLrdNm7X5vt//XDFzTYE+5IDw8PUAesNzrmpoaDCEEA8C2I2IOomo3nXdj33fn8zMpyAYnD4e/nUj8Nd5pKGhwQCAzs7OfQEUh/VOQeCXEM3MFxHR75n5TgTP8sow3WTmn4d9B3K5XL/sRHQrAJOIOmNl3GDb9pdi1zcq/PhKNNi1LOvnzPwzBJMLzxLRQwA+APA5AI+k0+n9AGDFihVDAURtUVlTU1NaqD2jFWlmXm1CqL6+PimEuAZAt+/7T9TX1w9BsCLwRQTPw2lEdLxSalptbe0eRNSOYAD9JQCfRzA508bMv7Qsqz8gQDqdvouZfwzgz0Q02jTNnYjoPGY+jpmvA7C8ra3tvZhs70SrN5Zl/YiZf0NEnhDisLCekwAoInoolUrtwMxPILjXAeCvzNxARCcN4Ej/JwDfCz/fHfY3p+a1y5VE9Ijv+7v4vr8PggAqJ7a3t9cAgaLX1dX1OIBzmPkq3/f3MAxjVyKaAuDU0tLSHxVq93gdRPQDAFebprmTUuo4BAEAfsXMDwP4tRBiZyIaDeAlIrrUtu1dgf5V2ah/OjPePwH45tChQ/t96Jj5twjebyeZpjmMmQ8FsICZfyelHGYYxsqwreYy8+Ko7zVNc5Ob3mk0Gs2mxASA4uLiUQCi2W07HAT3o5S6A0AjABDRNcxcB6AEwJ3MXAsEHanneTcBgOd5s9LpdLVhGJ93HOdRIBjclZaWvg5gV2Y+FkBLOACIXuZdAOpd151t2/YJzPz3ML1YKXVcW1tbUzqdrhdCPBHKcQCAx55++um9EK4OAJirlDqrra1tfl1d3YhcLvcwgHHMPDGVSu2dyWTejJma9a5YseLfY8eOLSorK/tTWEaLYRhfbmlpWQUAUsrvA7gZwHemTJly4+TJk5VS6iYi2ksI8VFPT8+DhRo1ZiO+t23bPwnbMEFEBwKwAczu6uq6JcxeCcBg5lPiEals25bMfCEzX+953pRY8XdalrUzgGts2961vLz8QyHEnwF87Pv+uJhZy6JUKnWiYRjzw++RMlQZyhN9vxfBTOu4mFnKR1OmTDm7vb3dB/BRaJ70kJTyWwhM9AqZhfTjuu7lqVTqlkwmE5l5LSOiyyzLOomIjlzLqZUAyDCM42J+M4ullFMB3Dp48ODdALzOzHcD8A3DGJe3anC+ZVl7ENF3qqqqbpo5c+ZKIcTdzJwgojGu60amNB82NDR8taOjYw6Ag6O2GTRo0DXMfLAQIpUXpezbUsoxRHQhBvB/cRzn56lUalrsmgHgx1LK42LXPBpAEsD9ruu+Gqb9UUr5olLqVWzDxJ4rENGk/ONKqQwAOI7znm3bdzDzpUR04tKlSx8gIgtAFsCXIvOY6urqb82YMWMegMcj3yUp5fEIFJFdlixZMgrAS4iZQwHIENGp5eXln3R0dPwFwJcBDAXwnO/7J+y4446LOzo67kMwSN9x1qxZwwH8h4hGxcq42TCMH7e0tKyybbuWmf8GoISZLwPwSN61RhMYaSL6DgAw8/mu694DBOZYhmHMAjCGiC4A8N3m5uYOy7KmENFZRPTbqK8ZqD2FEOfatt0XtmEFEdUC2JmIzs1kMkssyxoXTni8nEgk0vHVRSnlk0IIM5fLHZPJZKIgCx9PnDjxlK6urncBXACgLZ1OTxRCnI8gEtxNMTF+Z9u2ycx3MfPTsZWaSgAvhHUcRkTXALjbcZy4r8rfbdv+hJlPSiaTg5ubm5+zLAtEBGZ+dG19iOM4023bHhau2DwQfxaJaFS4GjHFcZypUXptbe33fd8/lpn3B9Dc2dl5MYA0EZ3qum5jrPjrbds+kJnPT6VSk6KV7UJtT0TfjN5fABaGkc++HtYdtdNHtm1PZea7fd8fAeA93/fvJiIF4Og8873zpZS7E9G3q6qqfjxz5syVobnmS57nRe+7jlQqdVxJSUlvU1NTNOn2kJTyR0KIFx3HWWvfq9FoNNsKJtDv7Ijwc2d+JiLq9zlobW39p23bf2Hm0wFEy/3NSqmLY/nJsqwRzCxt2z6DmbtKS0ufRzjzSUR9ADBjxow9iGgHAGDmWz3Pmx0efz6Sh5nvaGtrawKA4uLimdlsNqpmaXg8Ggz0mKZ5YnNz80IAaG5uXphOp78thJgDAIZhHAzgTfxX4Xl9zpw5feGgZq8w7WOl1LW23a9/RVFehkWDlba2tmex+qBnDWImGqMRrs4QUR8RvQPge+Xl5VNd182G7TQKwGP54XCZ+WIEKyNvWZZ1SvwYESUAkFJq787OzgMQzKh+NT9IQCaT+URKOQNAQy6XezksdxQRfTJhwoS3a2trR1HgFHpNvu9EOLN7TqxOsiyrkojWcC7OJ5VKlRmG8aXQrK0EwLtSyieZWTBzwdCh4WBtXwD3x532w7qLmBm+73fW1tbujyAKznUDmCzdD8AqKSk5KJ1OLxZC1DDzD2LKBwCgsbHRtyyrmYgOVkrNC015LmTm+b7vf65Amy8HMK6hocEoZIY1bty4kkGDBtVLKY8ioqFKqQ+EEB6CZ6wTAIqKiv6Zy+WWA7hRSpn0fb8pk8m8OZD9+DZG9ExkiWi1QTUzq0GDBvU7+RcVFf04m82eh8TCbp8AACAASURBVMDUpwEAAzjPcZzpUZ7p06dXGIZRCuDHtm2XMPNCIvog6heEENGgvDI0g1GGYZzf0tKyHACklC8gUEAA4PxIMZRSzgZwGgIFNvL7iWR/1HXdayIZHMdpsW373nCF92Cgf/+c/cLrehkAiCjq+5YIIfonHUzTBIIVQxDR/lG54YRCfFKhEKMB9BFRvykSEa0goqeVUr9wXfe5WD4AmBRXPsLw0nUAmoQQR1mWtVrhRLSYAvNDCCEuAfBhRUXFT/OFUEo9Ebbvv4D+Z3sXANHq58UAenzfvzL/XMdxouhdUZ0FV3UKEfWh0cptTJ5KIlpYUVFxZzw9l8slKDAbWxLmu5iIFimluMCznAUwpKio6HMAPipQ/WgEviKP5qXvAKC3u7v7tjxZh4T/PwxN3qqZeXJ+nxPmuZ+IZHFx8cEA2ikwaT5ZSnmPUur3K1aseG7OnDmrReSbOHFiKYC9mfkfAzSXRqPRbHNEPiDRi2Gx67qfW9dJSqmHiOj08OuSnp6ehpkzZ+YAIJVKmVLKR5m5PiwzOu3s6AMzvwmsPmtqmuZfY1XsE30QQvTPXuVyuX2jzxRGn4kNQJ6JlI+IRCLxUS6Xi/JHdvmRshW92A6LnXIWr2mODQD+ypUrVxY6MABRBKx94n4a+Ugp92DmwcxcaE+QoxGYMNxOhff76mTmZcx8AgAIITKFMhFRHzP3m0uEbf7y5MmTlZQyiuFf8Nw46XR6VwBlSqm1Dh5s2640DMMBMATB4GMpgMOZ+VIEJngFo02ZpnkAM5vMPLPA4QMBdLiu+7GUMhVe74xC5QghVjEzmLmEiMat7fpCRbiXmed3dHTsB2AYEZUQ0V2F8jPzR4WUj9ra2l1KS0szzLwrglCpHwkhRjLzhQgUsNuAQCm2bTullLqFiH5hGMYvpZSzmfkSz/NeyC93WyGmSAPALx3H+f7a8jc1NS2WUk5HYDYEIrrLcZz7ouNSytGGYbQhnACITUZEWfzS0tIFACCEGB2mP5enkEZ9xWt5Ct5+4f83YwP2qB9aw6yFmaMBqgCAzs7O/QAkAMAwjKgPOTz8P5yZ1xiIh+WsMbGzDiqJ6EHHcc5aW6bwee71fT8/2Ma4sN4qIcRRA8g0K8rLzI8WureJKBf+fxkATNMcxczEoXkpMx9JRC+uZ4TE0QD6hg8fvlYn8Fje98NAEXF5KpnZzZdVCDEqfO5fraurqwgVvm4hxEDPckdXV9cafXqkYBLR3QXOGUVE/Q7iMUYBWJbJZD6QUkamYgX7JwRmXGDmEgDo6uo6a9CgQYs4MH89r6ys7GPbtm+M++d0dXWNBGDQZyzyl0aj2b4xgdUG8evs4EIH46mxpOHJZLISwCwAME2zIVI+iOjbhmE8nMvlBhPRWcx8DfBfZ0b8d/ZuWWtra2QqFF9ByHZ2ds6N0n3fj+TkyGk0psSssZTe19d3ZDR4J6J/h7PcB4Z1RCsCidAs4HVm/mqhaxZC9G1IxJHwJfnx2pSP+HXGBjJxBgOY5jjOOWsrw7btM0NThZ5Cx5VShxFR1FZkWdYoZp4WHh4U/i9oBpLHapFwBkIpdQcRke/7B8YjvoSbPz6wlvMHDILAzJWxAU8RESEXaZZrciAA+L7/hmEYB4ZpBdsGwcDxtUwmk7Msa4fwXrnYcZyCvi0D4fv+LQBGCCEOb21t/WeULqWsBjAjPnBwHOdFADKVSn3eNM2JSqkbADRXV1fvlx8hbluhpqZmd9/3B4df19mHSCnPQ6h8AAAzH5O3snQrAuVjATOfQURvKaV2F0LcjuA3ezOKvhZbAX02r5rofs0fmI8CgOg3CX0odguPFbqnosF71D/FTc2i5zYRfr/V9/2CZpkUOoyvDzU1NTsBGLYuZT8mz2v5pkRCiB2YGYZhHN3S0jJgiOlUKmUahlFKRAM9I4eF8keyrLaKQUSDAKxv8ITRAF6PoieuR97Vrt+yrJ2JqIKIXsrPzIFfTk4p9WpfX9//hX36FNd1C0Z0HIiOjo79Efyeq9Xd0NCQIKL9mPme/HPC/j66F6K9WdbaP5mm+QYAhO+Vb9XX138vm82OBzCJmW+3bXup4zgPhOdEfpJaAdFoNNsNAghmEcP/aw2TOnHixFLDMB4FMAKBs/bH4aG4U10URWqZ4zi/am5uXui67r+VUtGmd9zb27uaPwKAeTH74rgC8lo8mlHMVvvtWPjKqIzq+OZWqVTq80QURdv699FHH/2vJUuW7I1gRjquBL0alr2HEKK7ra1tbltb29xMJvOCEOJUwzCO9X3/bSCINGVZ1l8sy4ocJAsSRgRa38EDTNMsNOh+K262EceyrJMih2yl1IcAkMvl1nCID30u9ke42iOl3APADtHvrJR6M6xrXP65tm0fKaXsnzkVQowMZR1w9rKhocEgoiOY2c0PNymEOAkACg0eQioB+HFTHSAYIAE4IJKZiF4Pyzsmv4DQ2fVsAHPC+j8MDx2Sn7empmYsABkNIk3TXIBgxemA/Lzjxo0rkVKeSAMsRSGYbZ4TVz5CWb8EAL7vvxQqf2kp5b4AkMlkFjmOczeA64hox5KSkjVk3FZQSo2OfV5rH1JTU3MM/hu9J7oXDujo6Dgtli1qi0c9z5vluu7H2Wz2VQC7hOnzgH7lYfd4GtB/z0TKZ/6zNSqe3tvbG/d/+3o4Aw4g2AgPwPHh14eB1RzQ4xMMr4bHDh4+fPi8qA8pKip6XwhxPYD/i/mxfFNK+VA6nR5wB/GoPdfVH4f340B9zdsAkMvl1rifbdve1bbtKgAIFZePARySf3+HPnrfRxBCtt+EE0D38OHDo01p3wAwJpVKFefLZlnWOalUqiyWPIqZ17n6UVtbOwhBUJR8U8xI+SnULpUA3shkMj3JZHIRgK5C/We4Qn9yFMRggHLg+/5qbRoqJkVYUzGJJrXmAYAQ4nUAUEoV7J+I6BwimtvS0vKOZVm7pdPpegBoamrqdV3X8X3/BACKQ9/K8HrX2fdqNBrNtoZIpVJf4DDcKTNfKqXkAn91RERdXV1/QjA46COiU4joFwAQvGus8WGZkRlUmZTyasuy0pZl3U5E3wrT34ktYQ+0H0fBGZ+YAjIP6LeNjfw3ksw8W0r5oG3bDxiG8ToCMwwGcPnkyZOVEKJfQSkqKooG4X8LZU4S0Uzbtu+QUt5sWdYzAK5g5msNwxgT5v0tETUQ0S3xwXmcaKaswDWtQfhCXVQoJj8F0XuOsCzryihaTyqVKpZS3khEj5SWlp4QZv0bgD4At6VSqS9E54fKxx+A/76wI8Uuatfhw4e7AN4loilx5a2mpuYYZn4UwA3RoEQpNRQAcrncHmPHji0K2341whnsJUR0aBhZLXrhT+EgotGHrusuzT8vZDSAt/L3FQn9QpLRipXneXMAvAhgUrjCACAIryyE+DuAXZj5cgDo6elpAbCciK6XUkYDUkgpq5VSjyGIjDQPAMLBZBMzX1JTU2PFyy0tLX0IwF9qamoi8518Pgawf7RjMxFRGJb02wCyw4cPf62mpmY/InIA3GHb9lAgGLwAqAbARLRBm0JuZfTfO0KIfxboPxYCgV+CUuphBDPMLxiGcTQzR4PM60LFAQgmNwDgJNu2T5BSnlhSUpJBMPEBFFAe4oPS8PlLxvOG9e8MoCL8mr+CCgATOjo6XpZS3iOlzCAIvEEA3urp6YlMYlZzQA/LiFaEZUdHxzOWZd1kWdZvfN9/FcDxQogfpVKp4tra2lEAfg3g5IEiyMVlMgxjrZMYUspdAJQVGpB3dna2IXi2b44icAGBiaRS6glmfjD2DDcCONiyrKui572urq5ixowZf0Zwf74fM7GqBNAfXpyI7gUw3DCMqZHyFirstxPR7wzDqArzEQKzzJ0bGhoS0TMwwPUPQdDuu02ZMkVESkzstyrULv2rEKFp3QMAzoj7f1RXV5cbhvEHAH/p7Ows2H+HdShmzt9gsmDd0aRWZKLmOM5cAC8Q0ZWxdyJSqdTnDcP4G4BdiOhyABBC/EUI0Rj6ygEAwr2PBAJ/xUimoQC4r69vz3HjxpXU19dH97ZGo9Fss4j4oHwgDMN4JZ1O/xChUycRXe44zsxcLncn/hva8ocAYJrm74ko6jxvJCIvVFaitH8BqysPUecN9M+6jQKAfBOE2OzjvwCgp6fnQASDSCBwPh4E4FQOHOSHIpgF+7rruv8I64nOXxbZi2cymU+UUqcAWMrM5aHd/vcRmHp0M/M5ruu2h+dFA6OcEGJJobaKZsoGmKVbjXClpGA+3/dvB/A3IvpJe3v7u1LK5w3D+BjAJCKa4rrugwDged5b4QttpGEYb0gp50op3w2Vj/vCdlvNfCKaWQ1NIU6nwMn7Rdu250gp3wojFr0L4CvRypQQogXBCsFTZWVlq1atWlVeSG4iuhLAPslk8i0pZcYwjA+Y+WgEoSoHWv2ITGkKDSxGh8cjJYqVUqcD6AAww7KseVLK53O53LtENJaZT/c8bwYAzJw5cyURnYsg3O1LUsoXbdt+A4ADIDJD668z/O0/UEq5UsqXpZT/yuVyCwBUM/Np+c7xsWv+AYJNIl+XUmYsy3qXmb+BILTz642Njdnw3CkA0gDekVI+1dHR8Q4RfQXArS0tLe8M1DZbO3l7gKwBM79i2/ZQInoMwW+x1Pf9L7e0tKyiIOwtAOxrGMYZAEBE1wHwEUTM+zuAvzFz3AQy34RTmabZP2CkIDRqRP/vK4QYFfu82jNBQejcNxH4n50HYAKCQfArzFwbmzRZYxbecZy/ElFk6jOGiCYR0UUAhjHzy+E+MD25XK4T/zUHXM1fLY9KAEvyfdry4QEctQFgzpw5fUR0GoChQohXpJTP27b9BjO/SESDlVLHRcp+2N4vAbjRsqz3bduek8vl3qNgD6d/YvU+qjJmeobQTOgeAOd2dHS8L6V8vrS0dBEzX0REl0d9LzMzETUT0dEdHR2fALhjoOtqaWlZhGCS4eT29vaeKAx62P8vi8IBR4SbYO4cbwff968A8E8iarRt+w0p5QvJZPJDBO+wb0Sb3BagkojiK+wI26gSq6/eR+lR/xStEDGA0wEsJaKnov7JMIz3ABzGzKdHUb3CwC29AGaFfe/zzPw4ghW1+B4fTwIgZn61tLR0NZ8YjUaj2VYxiehdCkJEDgRns9lO0zTfA3CBUmql67rTACCTySyzLOtkIcTuRITa2tpBLS0tHXV1dUf09fVdREGUlbd83/+tYRhVRFQRvbhXrlyZME3zYgAQQjwZVTZ37txiIroMAEJnZgCBYkJBDHf4vv9U+L/fJySRSJzX19c3TSlVS0SDEZh1/cV13cgMBwCmE9FHWH2vEbS1tT1dX19/QDabPZOCMI8cmgpMi0wnwvrOFEJ8lYjmtra2rrbDeX9jMS8XQlxgmmbLOtoeRHQrMxcsJ5PJ5KZMmXLyU089dbwQwiKicgBPCCEezrfpdhznV5ZlPUtEpyIY4M33ff9PhmFUENFLkbkEB87uX4s7drquOzuVSh1oGMbXOAhh2cnMTw8bNuzRuK224zjTpZRVRGQx8+J4u+TJ8ifbtv9NRKdyEPXqN+PHj//rjBkzjmPmgueEplvX+76/hoIihHidmS8wDKP/WFtb2/z6+vrRvb29Z1GwkZtPRL9PJBIP5a8mOY7ziG3bY5j5TAQb1L2rlLq/qKgoq5San8vl+n0HPM/7MJVKjTVN8ytKqbFCCJOZ7zUMY9ra/Hkcx2mpra091Pf9sxCEc/5DRUXFtM7OzvG+7/cPnF3X/aFt263hatDnAMwhosfj0Z+2RYhoGhF5Ax0XQsxn5j2J6JcA4Pv+3MhEL5FI3NfX12eE6YuB4DcLo4mdpZRKEtEM3/dd0zQnAgAzRxMCLxDRBUqp7ng4W8MwXmPmC5RSfZ7nxZ/198K+jltbWxeEaZFC8WZPT4+dTCbPI6KDmHklgPbly5f/PTIDJSKSUt6IIFT0rFi5cBxnkmVZjxDRyQhWajoAzFq+fPkj0fme531YU1NTzcxH5XK5aRgApdRDKOAQXyDfO4ZhXIBgz6I1cBznmVQqNdI0zTPDwXsXMz87aNCgxvhKo+M4yydOnDiuu7v7qwCODJW9n1RXVz/S3t7+FaXUO0D/6u6VSqk58Xpc1z3fsqyHiOg4BBM/jyul/tLW1jY/nk8I8ZVcLnc6EY1QSj0x0HUxM9fV1clcLncaM5dTsKcKmPlhIcRjcXNdAOjt7RWmaV5ARP3BJjKZzLL6+vrqvr6+UxH48ZQAeJACx/41olPF2vQPQoiu/PRw9fLVfAd0IppPRBfkcrkXY+3x7/r6+oPW1T95nvdCbW3tAblc7msITAZ7iOjuVatW3Rf3OXRd9+GamhqplBrHzAsG2shVo9FotiWIC0d92iawbfvnHERXett13b3WeYJGo/mfePqZWS+A6dajjzrqgXXn3naQUi5FsHv3tY7j3LCl5dFothemT5++Q1FxYqVJxi5HHHHEB+s+Q6PRfJYQW1qAT0PM9GOd5k4ajUYTJ/SZWs0nRKPRaDQazaZnm1ZAaO1RUTQajWZA8vzfdB+i0Wg0Gs1mwlx3lq2T0G/gWgAwDGOgTZ80Go2mIHH/t6qqqgVbWByNRqPRaD4zbLMKSBgGco3dajUajWZ9cF33VYR7eGg0Go1Go9l8bNMmWBqNRqPRaDQajWbbQisgGo1Go9FoNBqNZrOhFRCNRqPRaDQajUaz2dAKiEaj0Wg0Go1Go9lsaAVEo9FoNBqNRqPRbDa0AqLRaDSajYJlWcemUqndt7QcG4qU8vi6urqKdefUaDQazcZgmw3Dq9Foti6klKcCWO66bnMqldrBMIyriajNcRx37NixReXl5V/zfX9uW1vb3I1Q19lE9K7jONM/veRbF6lUqtg0zbOUUnM8z3thS8uzvliWdSYR/U4IcRmAX2+scjf1bx3eq9Nyudy9AC7d2OXX1dWNyOVypxDRTkqppzzPe3Jj17G9YNv2uVj7uGS24zjb/KahqVRquGmaXwIAwzAeb25uXrgp6gmfneQAhz90HOcJy7K+TkRDXNf92caqN+rDADzvOM6L/0sZ6XT6aCHECaZp/qS5ubnj08pk2/YYZj7DNM3bmpubF9bW1u6vlBrf09PzUHt7e+enLX9rwbKscwzDeKe1tfWpLS3LutArIBqN5lPT0NBgAPgdgOMBwDCM4wFcxcy3AUBZWdkBzHwXEVWupZj1IpVKlQH4vVLqmE9b1taIYRiVzHyXEGLUlpZlfbFtewIR3QvgFs/zNpryUV9fPwTA7wFM2Fhl5pPJZD4BcAeAi1Kp1PCNWXZNTY2Vy+XeAPAzZr5ICLFd3rMbg/r6+h2Z+V5mvmugPwB7bmk5NwamaY6Prsn3/aJNUYdt20MB/H4t7XniuHHjSojobgC32ba90dqWiEaGdRz4v5YhhLgBwBW5XO60jSETM18L4PK+vr5zASCXy53BzHeYptm3McrfGqiuri4nom3m3ahXQDQazaemo6NjLwClzPwyABDRk8w8VSnVGmapBABm/tSzl6ZpjmJmEkJs8zOhhSCiSmYGgG3i+qqrq8uTyeSDAKZXV1f/YGOW3dPTUymEoOi+2lQope4VQlwRzkpvlA1uU6mUaRjGfQDmG4ZxbEtLy39SqdQOG6Ps7ZFcLjc69nUFEfn5eYQQ/9qMIm0ylFKVRAQEK8bvb4o6fN8fJYQgAOf09vY+VuB4z6xZs7oty/quEKLMdd13NlbdRBT9lv9zHyaEuIGZXxdCPLyRxLqFmZcIIf4MBP0sgHfCCYjtgkQisdHes5sDrYBoNJpPTTRojjo+x3GWA7gkOs7Mo4jI7+np2Rg7j1eGdW4TneyGopQaRUS5XC732paWZX1IJBI3Ahji+/43Jk+erDZm2YZhjA6VsU068Gxra5svpXwVwJexkRQQ0zSPZObPM/N3W1pa/gP0r7ZoCsDM0aCVfd/fLZPJLNuiAm1CopVgZn6Zwxt8YxM9O0qpZ9ZmYuR53i82dt1CiNHM3FdRUfH6/1pGa2urB8DbWDK5rjsbwOxY0mgAL22s8rcGhBAHMTOEENvEdfUrIJZlnU5E5wPYC8CHRNQmhLippaVlFRAsJSulGnzfv9I0zUuZ+UQA5QDmMfOPPc+bFS84nU6PFEJ8F8DRAMqY+U0AUz3PmxblsW27ipnPFEJMYebLlFLHE1ERgH8kEokrAST7+vquVkodR0SDieg5IcTlLS0ta50xkFKeCOAcAKOIiAE8z8w3uO7/s3fm8XVU5f9/PzM3N2na0ialIPsiO00BKQglKb2ZmaQELAISXFAEZVHcEBcQ+ZbgAgiiIC7IqqhgEcGiscmdm1vaULYqSAHZVwGhJS0U2mxznt8fcyZMbm8K/L6KX/B+Xq++mjtz5sw5Z855zrOfcIT5yeVyk13XPc8Y80sR2VVEPgVsAnw6DMNiur5cLlfjuu5FqvpbVZ0gIl8Wke1U9RngvEKhcHO6vOd573Uc50uqeiAwGXgS+HkYhr8qaecXVbXKGHOT4zgdIrI/sA74XRRF3yoWi8Mb6mcCq4E8TVXbRKQW+LuIXJbP5/+QatM84u96J/ANYIaqDjmOs3B4eLgjTexFRJqbm48WkU8AuwJDqrrUGDOvWCw+mqrz68Brruveboz5H2AP4EUR+XY+n/+D53n7ishpwF7Aa6p6zaxZs87/VzMpFfyfQAOgruveB7EfqojsFYbhF2Fkw3106dKl65IHWltbt4qi6DRVHXAcpyOfz7/s+35OVT8nInsAkYjcaozpKBQKTyXPqeo0YO0BBxzwWBAEx6vqHk1NTV8onVdBEOynqseKyFX5fP729L25c+fWrl279geO4/wmiqKNROQLwE7AS6r6y1mzZl2c1Nfe3u729fX9WETy+Xz+hnQ9vu8HwIeiKPp6sVhcHQTB4ap6oOM45xljOlS1UUSeC8PQs+PyXhH5MnAgsBHwBPDTMAyvS+q0Y/VQVVXVVr7vnwHMBAaBG0rpQhAEjcaYz4vIDKAGuF9EvpfP58OkjKUPp6vqQQl9MMb8vKenZ5RWtLW1dVoURacC77d13WOM+c6GYnZ8399URI4DLikWi0+m782YMaNq8uTJxwMfBrYB+oDr6+vrL5g/f/5gumxqv9jfvvvWKIpOd113GrC2vr7+sVQ7txseHv6qiBwIVAN3u67bYYzZQlUPr6+v/3xSf2tr6yZRFH0FaCHerx4DLg7D8KYy3blbVQ8aq68Q00bf9w8DjrHzMALudF33211dXQ8CBEGwtaqeQTyfEJEjfd+fLSJd+Xz+9+Xq9DyvHTgW2B0wqnq74zhn5fP5d4QQ+q+AHU+AZ8YSPlpbW7eLoug0W/62QqFwNYDneduIyDdssYfCMLywra2temBg4HgROQzYEVgL3OW67nldXV0jFjUba/AJYKXrutdEUfRtYB/i+XpBGIa/9n1/joicoqq7ENOIi5N3A/i+/2EgB9yrqn+3+97OwEpVvaynp+fSEkFjGkCpFTcIgtnGmM+IyJ5AFlhujDmvp6fn1tS7NgUuA6pE5MR8Pv/0BsZz3cYbb/xoufsALS0texpjPmOMubCnp+ehlpaWA40xH42i6AzXdT+vqoeLyATgdtd1v1bKe/m+32Tp9V72nQurq6u/qaq7Aw+WrvNUP7e3a6QREFW9G/h+oVC4E0ZoxyXJmvF9fwrwXWPMlY7j7Al8mphf+4uqntLT0/O053knEPN9m6vqE67rzkvHQQRB8CNjzJ2FQuEaG/e1LfCrkjadSvwd64GnReTyMAwvS75dc3PzHo7jfDaKovMzmczRqnoYUBNF0YHFYvGfJWOzE3Cq4zgXRFHUKiLHAhuLyH0ickZ3d/c9JeX3B05W1b1FZBxwn4hcUC72LQiCBlX9EpZeikivMeZ0x3F2A15tbGx8Minred42juN8RVVzwETgAcdxLrACHjBirT1ZRD6iqpsBj4nIzXV1dRfPnz9/PUtk6rnJrut+XkQOVtXNief7DcaY7xeLxf5U3w6x/GmHqs4TkdnAasfe/JqI/AZwVfU3wHOq+o3h4eGujo4OB8AKHO2ZTGahqn5ERJYAPUCTiCyyG3HS4cMcx7kb8IGFwBUiMlVEfuN53qdT7T8E+IQx5nequp+IdAFrgC8ODg5eNDg4eJsVPrpF5G+qemQURaMYgDTa29tdz/N+DdwIbAf8UVUX2U3ljiAI9kvKOo4zHTjBcZxzROTHQL999wul9WYymV2BE0TkNMdxLgMeAG4WkV1E5MaWlpY9UwP9QRG5T1WPBv4CXE/80a/xff+Ckqq/ICJHuq57mw0U+x0wAPyP67pnjtXPNNra2qqz2exi4HNWuLgB2F5Vb0rGuqOjwxGRrwFHA7eJyERVXQA8q6pfcl138dy5c2thZDO8QUR+aV9xJVAQkcMcx1na2tq6Ser1p4rIR1W1m5hYh/bdN9g5tRgwtk01InLukiVLjn8z/argnQW72T1tLR8QbxCNqSINpMzxQRDsFUXR7cChwK/y+fzLnuedA/SIyHtF5GYRuVVVjxSRO3O53JYldd0/b948o6oTgZOXLFkyKl6ivb3dtT7js6uqqtYLgly7du3uwAnGmNNF5FridX+TqlaJyA96e3svT8quXLlyB+BEVV0vPsBuQEfPnj37Ffv7aGCuMeZ2oEVEnlHVlQC+739IRO4D2oHbgZuBzYBrgyA4paR/rxljlgITRCRRJIyiC0EQnKKqi0VkmohcD1wH7K6qXZ7nNcMo+nCyiNyBpQ+O4/zBBvxi23ZMFEV3E8daFIE/AzMdx+m1G+NYOA7IOo5zafpiLpebPHny5MXAj4DVIjIf+Cfwnb6+vhvE+p/YfhzvOM49QCAitwDdgO+6bi+wJ/BAsgkGQdAaRdF9wIeA21S1E9gjiqJeY8xXgYMTpsf3/X2iKHqAmHb/VVVvIGZabvR9/7OlHRGR54H6BXQmcAAAIABJREFUsdykcrlcxvO8a1X1BlXdmvj73QIcEkXRnb7v72Pr2VFE9rZC9JCIbC0iextj3HL1ep53DXCdqtYQx7sstBv60pJ5/25HEh82pmWzu7v7SeI5cYKIXJTL5Sbncrkax3FuAE4APigiN7W2tm4yODh4l4j8CGgGtiIWCI6Oouguz/Pel9QpIofYZz8aRVEvsRVsa/uea4Ig+CnQqaot9vpeInKV7/tzUk07xtZxsoh0A4Et+z4R+anneeclBWfOnDkO2AFiC4htgwRB8ANVLYpIO7Hwui3wAcdxFvm+/6HkeVX9HPABYI4x5isbGM/pIvLAhhhIVQ2AE6qqqtYCGGPmAh9zHOcmYI7lyf4CHBVF0c3pdWvp9WKJrd8LRaRXRD45NDTUDewwlhuQFdDvAA5R1S4R+ZOIzBKRJZ7nzQSYMmXKznY8p9jx2QvLrwFfB261tPSDIvJ7z/OuVtVvAX8FukVkX2NMZy6Xe49t6xZ23DaH2I3XDvt9tk2zbXuPAHpU9TIgq6qXep53etJ2x3E84ATXda9S1S+q6gpVHZw9e/aLZcZ2FnCCql4iIp+3/FCPqnrGmLyNb0vG8gzgVuI9s2j5s+mqGvq+f3S6Xs/zTlLVvwJ+Qi9VtUVEelV1TxG5P1GeeZ7XIiL3G2OOsvXfBOxkjOkOguCIpE7Xda8AfqCqz1kZAFW9sK+vb0xrcBAEu7quuxw40xjzrIj8VkRWiMi3XdftnjFjRjq26SgR+aD9bnOBf4jIisQCcipwW6FQmJ1Ier7vf0FENl66dOk4Yu31dGCyMebOKVOmnJLaEL5lB+OHxNobjDG3ZTKZCx3H+VZiQZk5c+a3a2tr/+Y4zklAsrk3ADWqekuhUDgdRiTfh4ATgZtefvnl9mXLlg3ZwfytiLTncrnJ5TQkfX193xCRj4rI6WEYnpf0JQiCc1T1TmPMZbxO5KbbQZ7qOM6u+Xz+8bEGWl83DU8FdisUCi8BNDc3X+M4zu1RFB0K3NPc3Lyz4zi/Bu4GPhCG4UsQM0OrVq26VlW/7Pv+b8MwvKuxsXFiTU3NdsDmjuPMSSR12/8HgcOAeWO1KcHAwECLiExT1eMLhcLlAHPnzj1r3bp1PwNWAixZsmQHoFZE3uc4TltaK+D7/teA89auXXsScaCkep53GXBzoVC4KikXBMH1wEJjzBHAT+fMmbOZiEwl1lYeGIbh3alv1AWcKyIfSjR+uVzuXNd1nyUWSkcxKxW8KzAiYFghdhqxIiDRkmylqlcCBEFwsKpeBzyqqocUCoVnbQal04DvFgqFb6bo0A+JNcxnEtOE5F2JJjnR9OdIuen09fWdSLwBB52dnQNl2pus/20ymcyuiWbPrr/rVfXYlpaWn3R3dy9zHKcBwBizXhyCxNaKEYJvN+NtgXPq6+vPTNHJvYi1bUvsungZYmaktra2R1W/PGPGjEumTJlSB2wKTBKRgxLtVy6XO9N13YeBD2LpQhRFna7rThoeHv52YhXJ5XLnu677oIicAPQMDQ3NEZFpxJbdKyCmD2vXrr00iqKVANZSeTnwh2w2+7FkvFpbWzusUHIBsSV7Pahqq4g83N3d/Uj6uuu6VwINIuKlNXhBEJymquc0NzfPAf7c0tLiqerPgGuz2eynkndby89dxNafZN5sr6rXA8scx5mbHsPx48cXAY9Y4YVNqXuTqj5vjAkS7eSMGTOqJk2a9GcROa+1tfUXyf5k+/KKbft4YD1XKTsHjxKRr+Xz+fOT67lc7rxMJnOnZVr2tJrFGZ7n9YpIdT6f36fc2CVwHOcqY8ySQqEwQhd93/8VsNhxnA/b8X9Xwyax2N3+PNj3/VFuSar6YKFQ2NXuT6eLSAHYyHXdzwI7qerewFpjzNyenp7H7Tr+s1UofFVV73YcZ09VvZpYC/1lYoXciDsUMcO/VFWvEpH9gE8Ra+dPUtX7ROQSYsvIp2z5w7Dzjdf5il1FZIEx5mYR2VxEvqSqdcCpvu9fHobhwzU1NbsBru3XcgDP8z5vNdrDwDkicrWI1BtjzgA+qKo/aW9vXzB//vzBhGkGcBynbGxUQoNV9ZEgCE4ovT9u3LhfLViwYK1VHK1OWTYagPHAkkKh8I2EDnued7GIfH727NnvBR4NguCEcvS6paXlHGPMMmAjx3HKMq+qegywseM4+3R3dy8DaG1tvTCKou+pagQwPDycxMgst88k9NrJZDINybr1ff8qYqtHnTFmerLOgyC4S1UvzWQyM4Hfy+sub4lQNErYHR4evieTyfzIdd3vJRm3crlch+u6twInAd8teW6NiGwbhmGicCv3DZKyL9fX1zekFCN3AT8eGhpqAv7ked5hIvJt4BerV68+PuF3gyA4U1UXAz8JguBm6yEQWIX5b+rr6z+V1JnL5b7puu5dwH6Wz02sgtcDdzuOc0hCL1tbW78RRVGvqv6go6PjxjvuuGMK8HHgqjAMk7mN7/vfE5HnRUSS75vAegX9AXBFZO8wDJennjsVuGDy5MmfBS5KjdtmwEWrV6/+atLHJAvWELCNnVwAhGF4cRiG/5N8aDuYd6WFD4B8Pv+4iFwF7OZ53hYAxWLxn2EYnm6M2bS5uXnvXC63pXW9uFNVR7RLdlI9NGXKlJHARduwJwETRdHnk4baNjwJKLG1YhSs1uqrwJ/y+fy56QGzwsXFIjLN9/3dUv0xmUzmyA0JH6nBQ0SOS4QKgAkTJiwHcBynCsB13a8DThRF7elydry+Elch7QA1NTXTAAEuTAsEtr8Pq2oVQGNj48QgCM4t/ef7/sm2TUP2/wPa2tqqARYsWLA2n89/InEzSBaCiHy9NDVbfX3990VkFbGLAgCFQuHPmUxmfmtr6zTf96c3NjZOHB4eTp6bABBF0XRb53fTafYcx0nMvTek3Q2KxeJK4FURGXHBqeDdAWs9ey+WmDc3N28NTEp+J9omx3GW+75/sqr+AShGUdRUKBSetRa6bwHL0psZQBiG9xJr5GdD7LYFTE424UKhsJxYsz47ecaa688Grku7IqVhmXIjIkel3QqWLVs25DjOWQDGmJZU2RH3slQdQuzmeZ9t23hV3V5EusMw/EaaTqpqh4isBT6cshKxdOnSdZlM5vBsNrvDsmXLhlQ12ShHmd6tOfshVR3JXNjT0/NQXV3dt0Tkvc3NzXu3tbVNLRaL/xSRB4mZCKIoKksfwjD8eOKCJSJnqeqqbDZ7XFpY6+rqelFiy/j+ybNlxnFfiS2vI2hubt4bOExVzyl1Hxg3btzFgCbZoIwx5wBPpIUPgDAMX1LVC+077rNjcjqQFZFjS8cQSASChKE4mVjbeUzaNcJ+358CE4aHh/cu6Us9gOu66wkfVlt5KvCHtPABUCwWH1XVS4A9rNsFEmOavIk4pe7u7kJtbe01zc3Nuzc3N+/R1ta2UaFQ6AWGHcf5rwhaT5JYjHVfREZ82guFQg+Qtz/nEVsfjKoe3dPTcwfE3zkMw69XVVU1q+pzIjJleHi4APzRPrdZqvpEwfhQNpttLhQKlxcKheOBRMn5nDGmKQzDS8Mw/DTwD3vdhRFhdwsAVT03n88fWigULg/D8Owoij5gyzqq2mb7MpIJcHBwcHkul6ux/UBVfzQwMPCD/v7+VevWrXvMCiVGRKauXLlyL4AwDH8rIk1Acz6fL8vk+76/FbH79z66fvarC/fee++Eh2pgdHxVg6reN2vWrDPSdDgRdDKZTFVbW1u1tTYUS+m1VURcbX+WnfsJzxJF0ezEw6arq+uZMAw/kny/hD4PDw8nNLcBiBzHOa5EafCo/X9eep0bY5607+hPj7mq3mv/nwb02wx1FIvF1fl8/rShoaGJlmfdtlgsDovIUold0BJMB17o7+8/Kk2DxsB0y1sdX+KKtty2MWvb1gE8Ul9ff0Ka383n8y+r6lnARH3dNfRc4NFsNvvpdJ2Wv/qhrS8Zs68D1ap6dLqtXV1dr6nqFcBWt95663uNMRGxp8p0u3cCEIbh1/L5/A9KhQ8Ax3GOBnYUkc+UpsVuamr6AfC0qh4JsdIH2AXoLRQKp6T7mAFQ1c+IyHzXdf/u+/5tqrrAGHN5YmVobW3dSlXrVPXn5cx5ySRwHKceeNbzvE96nne2qm7lOPF+6fv+I8Sb4r22kXXV1dVbquq5ZercCbilWCz+o8z1J9O+ZSMdyWQO0Ngd4+rSexbJItua2IVqOnBP2hd0A5gOPF6az3pgYGB7eH2yq+ocEekp027y+fzTvu+/rKpbpepEY7eAUmxnhS3Gjx+/RRRFe5cpsxYgiqLQdd1fA58cHBw8xPO8HuDqnp6ehamJ0wCsHTdu3C9KK5k/f37k+/5TxD6Pibb6B8R+2zUANTU1wxqbTAEet31uEBGGh4dHZahQ1WQDvj59PZfLbey6br0x5qEyfangHYz+/v7diDUhiTtBIvAmay4h/p8gdrmKHMc5KQzDVwFuueWWvRzH2QaY7Hneo77vl75iEywzMDQ01GBpSqIZU9/3C8BBHR0djrVEnA1UOY5z6lhttsqPW62AMwpr1659rKamBuyasHP96dINx/f9rVR1slo3iqGhoWmO4zhqTdgJrLaoRVV/nVZMJEifARBFUYOI4DjO/DLN3iFhxKyG83Tgy47jTAEYHBzE9/2/AjtaTS+q2m3bc+zg4OAH7FhdXSgUulRVrS+0LyLDg4ODd5eOvYjUAbJu3bpxxO6hI7BM+ThK3FatqR0R+azv+8dRBsaYWs/zthCRfYCzy1mpHMd5QlXT8+gQESmUUxiJyFO2bELPP0jMuF1fZj6Ns8+MYnit5eqFNIOTwGorx/MG+4uIbA08bOfGJH2D7F3WEn4BcLTjOLUAg4ODUXNz8+3E+/MbKcfeFZDXM78BnCEiK9P3jTGj/OUdx/mGMSYgjpNAVU8tFAo3Jvetr/5Pgf2tJh3XdfuxyksReQFG+JCt7LWrk3m49957Z3hdIPpVwgvZdTfZXn/Mti0RKAYGBgYSTTkAPT09t/q+v5pYabKJrSNxF/3HkiVLVgVB4Ktqvb13SnV1ddodcwSu645L/s7n873lyiRILAYi8jFid8/02A3NmzfPWL//XYErII6XAt4jIpeVxtMZY7YTEeM4ztODg4MziWnyl8sxp6r6RNp6UYr+/v5Lq6urDxWR85csWXKy7/uh4ziXJtYQi9IMVdOBv5aufRHZEViXyWRGxVY5jrOzquK67oO2TdOAvhRv1gD8PbEcB0FwOHCeiOyQzBff958mjvF6AkasdLup6jW9vb1ryvWtBA1AWEZQSdIdP9XS0rK5LXf2GPEyCV3ZKgiCrYH3AfPK0UtjzBOO44zQS0uHHRHpKUPXJwAMDw/X9vT0POJ53ldE5ELgGd/3byGOQ/51OV7bPt8KvNTY2Hhz6b158+aZIAjus/1iypQpO0dRVA1cWzpfMgCFQuFm3/e3JTZJHiwi57mue/KcOXNmLly48Plk01fVsQLiNgVwHOeFIAjarUXkJmPMYar6VCaT2cwYc7iInCUi18Dr6cKIffZGkNIm/JL10QCUJeiq+h4AY8xYAeqbA4jIaktEpgG/HaNsuffeWnoxMRM6jnOfrXNTjQPT10Nra+t44qDTRKvSAAxNmTJlVH+sS8YOInIzgA1sDBgDdgEd7fv+j4EPS+xf1+553k+Ak2GEQD69YMGCtWNUsymwDMB13euItclfi6JoQXV19cDw8PAuInIWkJ7cDcDLixYteixdUbKRGGNGBa66rtsAY5uMK3jnImGaeX3DGWXu1teDSw8mdr/8tA0M/jKA4zjb2ftXiEhZAdUYswJGabJGNjdVDUXkY729vXs0Nzcbx3FOBE7t7u5+bqw2W0tDuUBkqqqqNrVl/pl653qbqTFmj3S/U5rNUYqKbDa7WRRF1ar6hsykZU7W1dXVjcoWZpn9bYBrADzPOw34joj8LIqiS2pqal7s7+/f2nGc44k3qXthhD58zPf9S4iVCnOBozzP+zHwuWw2u6U9h+D3ItIzRrOici6v69atq3VdF6CUYd8eWGc1e2P18y5VTTbiscZlC1t2eXt7exZ4j6o+Uq5gFEXblXyL7VT1LxK7lpWFqt6Vao94nrcfJcxaquyb3l/s7zeMZwCorq6+GpirqqdnMpkbRKTfGLOTdb0hiqJ3RDabfwFGmPjVq1efn9aSlkMURbPl9XAEVdXEIpIIdV3E+9rzwO9V1RGR9xOvjZHYixQfAtCV/FFXV7eLqiYa6pGDI1taWraNomiCvX6vbUtC//5Zypja/TwRZFba50al2TbGbGmfHxaRGxkDjuO8lQyCyX4bJhnYSpHJZHbUOO4oodMJbV0vZk5EGkTk8a6urteCINjOtr/sunUcZwtVXT1WeuElS5asam9vb+zr6ztIRA5X1cOMMZ8OguDElEWnAbgHXmf8gfUUqLbc8lKlgd1z0sHYDTo6JqUB6ATwPK9ZRK7XOP7mE1EUPeK67iYi0qqq54vIIoCXX355R+JY1lHCcDlY5cqUcmNp3x0ZYx5Q1d0tb112rERkcwBjzCrHcTZIL13X3UJtJkqr9NoMCEuVwWn09/c/DHEmtFwu98dMJnM0cWzO5a7rHjdz5kw/nTgmgapuJiLPjZVQSOOA9NUwyp1uvbHI2E6Kqr4AfB/4vu/7hwA3R1H0ReC0ZGN1HGdiaQXWhHY4cG9XV9eLvu+fCLwQRdGRqWwtKz3Pm2r/TrRZ0wEymcwoAp3SJowivFZTtz1jCA2q2meFga2AO8oUyQFrqqqq7m5ubt6e2JXoDc3jiVaAMmkg7bjo8PDwfVYTuwooGzQ4NDR0oM3JvcReaqBMlojEP/SNNGcl7RBVvQ24LZfLnWqFiM+2tLT8sLu7+xGbEq+m3LM2A8hmQNja2rod0Ap8LwzDi1PFnrenpb56wAEHJALHdOIMaKW+utOANbNmzSrNKf6mNuQK3pFoAAbr6uoegpEN9oUwDF9I3V8DtIdhuND3/d1E5KQ5c+acb7X/iZarmM/n/1hauZ3fav+eBryYqhtjTOi6LqqacxznUGB5fX39mIfhJfFLMtq0PoJMJvMhqz0LLeP7XlVdWFrOaoHIZDJpy896qSejKHrF3t+ktA6IN9iUFbgBuL/UKjwwMDBNYiR04SQRuSOfz38mVWyF7/sz7LtG6GcpfXAc57cicnIul/sh8Kodu7vDMFzPnSM99mXwKoAxpr7kmVcBqauru6K0H+n6WlpaZhhjxhwXVT2K2CLxohUQBsS6SZVp57HASOpiVX1NRJ4v56JSrk9BEMwyxrwHG7dUpi3p/aVcVrAc8HJfX9/f7JhMExGiKBqT3tkMYocBPykUCuk4j+c9z/uYiAxtvPHG/xVZsFLnYvz9jYQP3/cPEZHzUpfEuk0eCVBTU9OKVYqKyMGJ54I9CTtReKYVaQDDVVVVIwy+vh73SX9//8haSllgR84kSVk03uP7/pS0lbO2tvZYrJXGcZzFtu7SvTAp77que1L61O9cLrdlNput6urqGtlPm5ubdxeR1YVC4dmxxsjS4H+OJXxYJO1I3JKmp3+XllXVZN4nVqT11q2NvTmcMrxBqm2icazHH4E/WoHxVlW9oKOj4/I77rhjArGy5ZcAq1at2onYG2NUu1KCya9YHw1ig7ETS4/jOJdDTP+x2ahse44HhjKZzKEpQWZla2urE0WRa4y5F8AYk1iV3kxK8DHLikiDMeaRYrHYn8vlXrLXtiotZ5EDUNUlURRNtnOvLL00xhwlccbFlzo6OpwlS5YMicijb4YG2t+PAmcBZyWxwePHjz+SMsYAx3FeUtWdS/YuYCRL267YWO+ETy5HC50gCK70PK9HUuoE13XvtJ2uSVUAcWabUVi8eHEHcfBYYnqcDLy2aNGikUblcrkJInI+vK7RsXWumzRp0iiNVhRFpe4bwOt+5GMx5nZxrwO+kMvlRp1vYqP9j1DVH3d2dg6U06KOhRSxKLsoSZkJVbULCHzfTx+oRGtr6yaO4/wQeCqbzf4u1f+xhJo31bbm5ubdfd+/p7m5eSRwyAp9f7F11CR+6cCWJVk7yOVyG4vIz4Dn+/v7rzLGJKblUSZDe8rxUcDyZEETT7CxxuS+MpJxA/BqctiR7/u5IAga13u6gncc7Jx9KBGmrRKhVNt0fRiGCwFU9dvAuOHh4dMAbDasdcCn0nQIIAiCEzzP+0vqhOpSTRbWrP53VT0NaBSRz2wo88vQ0FCyxtqsCXwEnue9T1XPEJHO7u7ue5577rlqYjP21HS5lpaWGcTBqCtTLlTTKaNUsAzJ3cCH29raRtXj+/7H+vr6ngiCYHurzNldysQNpJRAyZqbTMk6tX05E4hqa2vvtzFc9/i+f2xqrIZF5C8A2Wy2ZtGiRc8CjwBH2+w86foO9H3/4VJ6lqrrVeAFx3HeU3oLqOnr6xuVvSWXy2V8378pCIIfAdTU1DwArFHVY0oypuB53ueIsxclzJECS1T1UBsHlC77JeAg4JGUy0CPqnq5XG7bkjZs2dzc/EA6AwxAFEVfUNUVa9euLef6RlVV1S3ELmhfLN1ffN8/CjhUVX+UMM8SJwZZUZqaMw1Lb4X16e1+InIM8Pex0pi+25DiMWrLxTy2tLR4EKcfBX5DHL/ao6rn2ueOaG5u3gNAR8eZ1kDsWmyM+VHqfaMUocDDJW4tyfVn0mdoOI6TCBulgdsQp4S+KQiCRt/3dwuC4DTgB7Ydt3R3dy+zdGwze+0+gOrq6luIrYgyPDx8Q2tr67QgCCb5vp9zXbcriqKHkkDyIAg+Yz0uHk2yrpVDGRo8Vhnt7+9Px1m8Wqo8zOVyk4mziCUWmzvi5uuojJYiIpMnT76A2AJa9t2e5x3ked4Tln4C0Nvbu8Z611Tff//90t/f30C8LsoGjCdIWSRG8SF2D5mW7BOu644SYFL0P6lvMtA/ODg48v3b29uzURRdZOtLlAoNxDHIb8iblaHXI1DVhiT9sk1d/hBwbFNTU126nM2u+lUR6ezp6XlIVe8nVvp8spRe+r7/RYnTkt8LsRsUcIuqfjC1dyZld/N9/+GWlhZv7ty5tUEQdHmeVyqk3GXbOo4y0Djz6carVq06Jn3d7iGXA47ruokScDpjHPiYUdUVwLGe593s+/7VIuIQu0ZExpjrUxU8AHzA9/2biH25xonIh60W8KIwDH9rG7ZARM7yPO9Kz/PmA9u4rvsFYi1Av6omLhZlNX1WmzASHJTCBl14bBrP/xGR813X7QmC4HJjzDrbvk8CS4wxiUvAdIDq6uo3I4BsSOodMRPatp2pqgcDBc/zLnAc5xFi7emXiC0uczo7OweSmBrGFkAiY8wbmltV9SmgTkR+FATBllEU3Skiu4nI6ap6X1NT0/2LFy+eYb/pPcD1vu+fbyX/nVzXPRnYyBjT1tvbu2bGjBn3TZ48+THgq77vvywiT2qcSu4I4kQFicZnZ+LgplHjZzUNu0nsgleuX/erqgZBsD1QUNXI9/0t09rsCt6RaFDVAsSE286Pblg/aBziJAe+7y8DTvA873vFYvFZ3/e/DXzH87w/e573C8dxxmmc9rId+GWxWFxptWu7WN/uUdDYDevzqnpZGIZl3WgSpJidJ4wxS3zfv1BEVqjqviJykjWHHwfx5mjb+iHf9x+R2Pw+3W6+q4G0taPBKiHK4evAnwcHB3vt+9aoqgd8UlWvC8Pw8Vwut4PruuMTjVtJm6epan9KYbNAVT+WSoW5K/AFYp/lRxcsWLC2sbHxqZqamjpVvcTzvK1U9S5LH04Dls+cOfOBrq4uDYLgq8CNtbW1t/q+/2MRWaeqBwDHq+qd1dXVG4rb6lXVA9IatSiKfue67heByzzP28NxnF5V3dJ13U+p6u5iE3EsWLBgrU1N3jF58uTeIAguVdVB4tSae9q+jNAYETlTVRdHUbTM9/0riK1qgcR+4Kt1tFve2SLyQdd1lwZBcD7wLLCb67qfB151HCexRON53iwROUxETiznbgCwcOHCviAIzlLVcxzHCT3Pu8JxnAFgDvAJ4Jbq6upvpx5pKCdIprHxxhs/2tfX9wDwBd/3XzTGPOK67kxV/RixsPOuOPX7jWDdk5MEODup6tdLy6jqfa2trZtonJ50IvB0Npv98ODgoAN8ERhnrSCHWYvoOuJYn9D3/ftd192ZWEAASLsHjWWZL2sNsOtw5Lq1zCV1vAQ0quoS2+bksWcymcyx8Lorsv17OUBnZ+crvu9/HbgEmF1GU/z48PBwEuOQs//XSJyp666SsmkaPBYtSvrSADyRuI1ZZevyUuWhbfOI8rdQKDzm+/6vgaODIPiTql4LVHuedzSvB+OPFYD+GLCJMeZPvu9/xxjzqOM4B4rIh1T1uvnz50fJeBpjEoGnQUTW06AnFgljzChvGd/3t1PViZZhh5JvXKrkFZEFqjrHcZzfBkFwJbCpqp4sIhuVfOsGRqea3xAaRGRV6bkpNhZ2s7QiXVVPEZGbs9nsUs/zLnIc50VgX43TBr9gLTQUi8VXgyD4vqrOmzx58mIrNAwT08s9iAPJ02P0DaDXdd3bfd+/EHhJ4nTGn1PVZ1X1HkuDB4FP+b4vInKzMaZeRL4JvKqq63kkAERRdIXruser6qW+7+8uccr8qePGjfscsVXqs8m5SHb8/1quHqe+vv4bEvvq7g9cr6q/NcZMUtUje3p6brUuCDsDf1LVDxEvzOssk7kZcHQYhl9KKjTGfEdEfkgcj9ApIl8hto78HvhDyi2rVqxvXQkmA2GZQ/g2BR5J3DzKoVAoXCBx+sntVPUXEueeD4CObDbbkmjHNM7nf1dnZ+eKsepKoKpTgfsTzX0CSzSHRWQkNiSfzz9ujDkAuFdEztU4X/xZtsw+9iROhoeHtyP241uPeBAHvt4yVvBPGsVi8VURyYlIj6p+03GcP4nId4CCMeYgS0gSQnqMql4FnGYkL7S4AAAgAElEQVTb9T/Ermr7JIccLVu2bMgYczAxU3WJqt4oItOsae9+YJGtaxvg8USTmiCTyWxNvNGnTxtNxnGcqt4CcawQsa91b319/ZgntFbwfx9WO/aaiCwFWL169TbAMxKfOUEURdsCj6vqspJHO4DnHMf5OEAYht8lZiSmi8hvNM7SsS9wSlNT03EAkyZN2pr47Jr1XCxtJrqVInJ66b0yaCB2E/2AxBmjfqiqvwWOEZHLjDH7l7h4HU28CZ2pqr8D5mh8/sczwGIAa9l4OU0P0gjDMG9z7EfAz1T118Ra+zOnTJnyCYCqqqptgMcdxykdK6zCopAobPr7+z+rqteKyFdE5E/Ax1X1BGCp2Pix3t7eNSKSAxaJyJlp+hBFUVvCaNgDSw+32q7LVfXXqnqkqn5//PjxLWOkMU5wHbB5c3PziDbWWlnmiMivROSzGifauBB42XEcL50dr1AofIs46HhH+82vBjI2b/1TqjoiTObz+dsdxwmAR4kzUn2deFNtAzaS0ZmSHjPGzFLVh1X1Qo0Pkf0K8Ocoig5IXFOamprq7F7WHYbhmPEi9v3napywZQcR+aWts1lV52Wz2TnJOFlLVlbis7LGxPz58yMR+QDx5vxDx3FuskLw4cCDqjpWTM67DdsRZ758fKx/xpjlxpgLiBMLPOo4zhGdnZ0r7Dq9xJZryOVyOxSLxX9I7Nr2NHEg+R7Ebjo/Jt638omwbO8/Dow6SFnjhDaPJ8JE6vpG9vpiiGNCiAUiiBWdNxAzhQCvAFdEUbRvyoVqC9uGRzOZzIh7XRiGP9Y4SUc6pnIN8XqYabMcYcfgYaC3qqrqOsqgr69vW+CZcnSyBBOJz3JLrAbjeH2PT/d5S+Bx13VHlK3ZbPbTwMUaH253DfYbWKb5ccZgOMMwfFhVfeAp4CLHcf4EfEZEfp7JZE6wbdlEVR9MDlB0HGdj4LbSODSND8x7fHBwsFQw2ZbRdPQ9xEJc4nI1Ffhb4sIWhuHPRKRDRFpU9Y+q2kGc+vsKiTMaJi5yG/HmT2afxOsp4kdg4zge11T8WaFQ+DPxmXjDIvJTy599mvjb75uOZWxsbDxbRM4EdhGRK+0/x6brfjLZd22/7iI+9mAV8dy/TlVPIl4LM5N+RVH0EeKDLT+iqjeJyBUSn+cRjOXmVywW+zOZTDOxNfJzlsb/WET6HMdpCcPwUhjJTjsElE2aIMk6bG9vd1955ZVNHMd5rbOz85WkgD1x8V5VPTY5+TOXy22czWbXlcsWkqC9vT373HPPVb/JbAH/crS1tU1ds2bNcNp8+nYil8vVVFdX1w0MDKx4syea/28wd+7c2v7+/smZTOalNMPg+/5FwIlRFE0oFovD7e3t7ooVK6auWbPmpQ352ra1tW304osvrnsjf9wK/jtw6+1L/4rKBQfsv/9v3rj0/w5vde3aE89vFZHj8/n8lW9U3maKejkMw5x9X/Xg4ODkQqHwYooxWQ9z5sypV9WBDdG9N4Ompqa6TCajY532/FaQy+VqJk6c6GwgwQQwNn0oRRAEk1S15o3GIoENsn0aKIRh+OEy7ctUVVVt8uqrr64ay7oAMdO+aNGiTcaNG7dqrPa1tbVVl7tnT6G+1hizX5LGs7SNEyZMqBsaGnoxTYvtAXZ/FJFdoyh6f7nshWPhX72/NDY2Ttx8880H3k0uV4sWLZpQVZNdkxF3q/e///1vemz/VbB+8FNXr17d9+/ax5qbm+c6jvMHABHZJp/PP93e3p5dsWJF/dSpU1dsyBV0LNhMlNX19fUr/3+efzsxY8aMqokTJ055I36iHNra2jYyxox/u3ikN0Iul8uMGzeuNs3/vt0IgmCS4zjV3d3dKzZEf98MvUyjtbV1vIhstNFGG7041pxqb2/Prl69emMRefmt7HG5XC5TXV09dWBgYNWbUZynIW+0x9hTGK9JHxpTwTsHvu8XgbowDPd8w8IVVDAG3k4B5K3AHiz3V4mDMme9EdNs3QTXiMjl+Xz+829XO9/NsGe7zBsYGHjvv0vhZA+qvNhxnCC9D9mAx9uBlYVCYd83IzSl6pwnIie6rtvyJtOxV/AW8J8WQN4OeJ53hsSHyL1cKBTq3sr8q6CC/3Zk3rgIDcRmz/+KbBzvQjTw+mmtFVTwrsKkSZO+JiI7AXu9mc0/ST2pbyHLXAUbRhRFl1ZVVd3477R2u6673BgzzhjT5fv+90XkQVXdBThZRMap6qFvlfmrrq4+d2Bg4PKurq4xswlVUMGGkIonGzPrUwUVVFAeb0YAyQCd/1u3gwrefjQ1NdXV1NQ8aYxZ9J9uSwUV/KuRy+U2dhznIKAjDMM3lSPfGLOZ4zh/McaUi7+q4P8D1n1izDNX/hXo7u6+JwiCOap6EfAdy+u9BtwMnBGG4Vs+sM+6LlSEjwr+vyEihjjr5AYDviuooIL18YYuWBVUUEEF/1ddsCr474Pv+1Mcx6kujemo4P8W/htcsCqooIL/f7wZC0gFFVRQQQUV/J9A+qC3CiqooIIK3plw/tMNqKCCCiqooIIKKqigggr+e1ARQCqooIIKKqigggoqqKCCtw0VAaSCCiqooIIKKqigggoqeNtQEUAqqKCCCiqooIIKKqiggrcNFQGkggoqqKCCCiqooIIKKnjbUBFAKqigggre5ejo6HDa2tqm/qfbUUEFFVRQQQVQEUAqqKCCCt518H0/CIKgr6Wl5UCA3t7eywcHB/8ZBMHBb/Rsa2vrtCAI+nzf//C/v6XvLPi+/13f9/+Zy+UqKewreMvwPG+bIAj6giA4/j/dlgoq+E+jQkQrqKCC/xp4nvcVETkyiqID3s2H2Knq+4C64eHhhwCMMcMiAvCGfY6iaE+gzhjzyFt9r+/7ncB9YRh+7a0++06Aqu4rIv/WAxA9z7tYRLYKw/Cwf9c7KviPYbqq1onIW15bFVTwbkPFAlJBBRX810BEWlR1o3ez8GExHXixWCz+E2DWrFknDQwMbJzP57ve5LNRf3//A2/lhUEQTALmiMjgW2/uOwMi0qCqy//N75grIpW9+V0IEWkAqKqquv8/3ZYKKvhPo2IBqaCCCv7XyOVyGwMTisXik+nrIiK+72/nuu7qhQsX9gG0tbVV9/f3e47jbCsiz1ZVVS3t7OxcUVpnEASTVDUANhWR51977bU/L126dF1p3cPDw31AfyaTaQU2dRwn39XV9US6rtbW1k2MMROA6SJydxAE2xtjokKh8FRSpqWlZXNVna2qdcCzURQtLBaL/W+m/42NjRNramo8Vd1SRFZls9nu0j6JiDQ3N+8DvE9EsiJyb2Nj4+J58+aZpMycOXPqBwYGNioWi096nvdeEZmtquI4Tk8+n3+89L3t7e3uSy+9dICITFPVNSLSaU8Kn55mlHt7e7fNZDKvlLYnl8vt67ruHsaYIaC7UCg8a599JD3WTU1NdTU1NYHt38rh4eHuRLix47sd8D5AVHVFEATbDw8Pv1IsFlcmZZqbm/cWkb0cxxFVvSMMw3s3NKa5XG5CJpPZZNWqVc9MnDhxvOu6c4ApjuM8eMABBxTT4zZjxoyqurq6rRzHecF1XXdoaGiOMWaj6urqPyTfIZfLTc5kMs2quhnwkv2+qzfw7uR7/mPVqlWdEydOnOK67iaO4yxPvvm4ceOm1tXV/WP+/PmDJc9vWV1dHS1cuPD59HXf93dT1X2BCSJyf1NT0y1JPzzP28JxnHpga2NMZxAE24tIf3d393P22Z1EpNEY47iue093d/eyDY3fOxG+708BAmBzVV1RXV29ML2OOjo6nN7e3m2rqqpWTpgwoX/lypVzHMfZyhjzhKp2l1MstLa2bmeMaVLVicBjURSFpeXmzJmzGcDChQufD4KgUVWnAy9ms9mbOzs7BwCCINgP2MsY86ox5s/puQ2Qy+UyVVVVB6rqbqoaicht+Xz+7nQZu06f7+zsXBEEwdYiMpx83zSSuTU8PPxisVh8tdxYtbe3Z1etWjVLVXcGXhWRJaU0oq2trXpoaGiL11577fnq6mrXdd2DgY1VdXlPT88SVdXSeoMgaAD2UdUqY8yynp6ev5SW8X1/U8BX1Yki8tDq1at7ly1bNlSunRVUMBakzPyroIIKKhiFW29f+ldULjhg//1/U+6+7/sLgF3CMNwpfd0y0Y8CnwvD8MdBEDSo6gJgW8AQW2HXquqnC4XCtan6vgx8C6hNVfc8cGgYhnfZurcRkSdV9VwR+QiwDYCIHJLP5/9U0r4eIFfS7HvCMNxr5syZ42pra38IfApwU/efU9UjC4XC0g2NTRAEp6hqBzAxdfk1ETkun8/Pt+/fCfgVsA8wZPvtAotra2sPWrBgwVpb7hpgb+A6YB6vW6mHVfWjhULh+lSfmoDLgfSYv+I4zuHGmJtF5NJ8Pn+KZRb+qarfLBQK37HPTgeuIhYaEvQDnwS+IyJ/zefz7bZ/X7X9G2fL1AADwCfDMLzOMo2jmDEAETkzn89/O5fLbeu67i+BppIiV9bX158wf/78aAPjegFwHHAxsFHq9m3ZbPbQhDn1PK9ZRAqq+k0R+TJQD+C67q7d3d0PeZ73TeA0Rs+nNSLy6eQbpcb1ZOA7wKTU5fuJ5+N1xpiDe3p6On3fPxM4O5PJTEmE61QdK4E/hWF4DMTCrTHmSqC1pJu3A4eEYfiS7/vPApunb6rq/FmzZn2kt7f3IlU9GRBeXzdhFEWHjcWg/qexaNGiCVU12TUZcbd6//vf/48NlRURsd/oDKCa1+fZWuBjYRjeBBAEwa6q+gDwDeBYYMdUNUvr6+tziTDY1NRUV11d/XPgCOJxS/CwiHwwn8//Pbng+/79wN/tO9NxUndnMpmDh4eHf0EsGCV4SUSakjrsWrwa2D7VdoBfFAqFYxNG3/O85Y7jPJfP51t93/8z8L5CofCeUkHA9/0/Avtns9ldyilnPM87TEQuYfR8McAFhULhtNT7DhOR34vIZ1X1LGCTVPlfhWH48eSHnaNXl/QTVZ0/ZcqUjyfjGgTBCar6Q2J6kMzFh1W1rVAoPFba1goqGAsVM28FFVTwr0BZ15TE5SC5p6pXE2/O+4Zh6AI7A4+KyIVz586tBQiC4JvA90Xkl6q6bX19fUZVDyRm3G/I5XLJ5j7dvuMLwEIR2R9oUdXbS9thjDlTVb9py58uIoEx5pMdHR1ObW3tDcCnVPUcx3G2iKKoSlU9wIjIAsvAl0UQBN9W1QuBG4Gd6+vrq40x+wFPqupViWZVVT8ObG6MObipqalm7dq1Ey1DMGvt2rVfSlU5HdhGVVuMMe8rFAqOMWY/VV0lIucmhTzPmwXkgZeBWfX19dWu624PFIwxNxIzB8vT3wC4F6C5uXl3YDEwHjgoiqJxmUxmc2IB6Spg6+R75XK5LVX1c8C1mUxm8zAMxxljpgGPA5e1traO7+/vHxSRAOgC1olIICKB67pXtLW1TXVddzGwnTHm0CiKxgEbAxcCx/X19X1urLEFGoAIOEdEPhNF0URi7e1ZwP6Dg4M/SQomfRSRr4nIT4F9VXXupEmTHvF9/0LgbBH5reM4O9XX12dE5H3Ao6r6K8/zRoQwO/cuAW4Cdl69enUWmKmqLnAZQFVVVTKXpwHPlQofLS0tmwNTiIUWcrncxsaYW4mFz09ms9lJAwMD9VZQ2ge4QGIcIyI/s9V83I7hNxcvXnyE/QYXuK47IZvN1orI6YDnuu4xGxi/dwyam5u3B04CrnBdd9MwDMc5jrMX8LyIXDljxowqAGuZAPgKcFEURROz2ewk4OfAzL6+vsMBcrlcTU1NTR74gIicGkXR1Pr6+mpVPVxEpqrqzQm9sfRkJ+Ag4IUoijaLomgqcCmw1/Dw8F8Aoija0c6dE4EpqvrFVBdOAV5R1feHYThuYGCgHvglcIzv+3MhtliIyM4pOhkCm+Ryud3SY2ETRRwsIqePYRk+XER+p6pPiMj+q1evzkZRtJWqXgd8zfO8zyZlU/T3dODkbDZbA7yHeK0e3dLSsqetc5Ixpqiqe6pqe21t7fgoiupEpENE2vv6+k6DWKGkqj8BeoD3NDU1VQGHAVuJyJlv7mtXUEGMigtWBRVU8L+C9f3fRkR+WeZ24vN8X/JbRIr5fP4ugDAMH87lcq3A6gULFvRbS8FZwBX5fP4zqXoWB0HwBVW9yXGcALg5xVhfEIbhvA21saen51bf9/cDGB4evi5xFfN9/2hixuOLhULh4vQjnucdIyIFYsvId0vrbGlp2VNVvwH8PAzDE1O37mhubv6Y67ofSS4UCoUzGxsbv9fb27sGYN68eeuADt/3PwHsBbEb0eTJk3cBnq+urm7r7OxMXKbu8H3/euAzuVwus2bNGpk8efIvgEdra2tnJ9YT4IlcLvdR13UfBCamGJ0GAGPMcgDXda9Q1deiKGpMuZE839HRceKSJUv2BGZghZdisfiPXC63+6JFi15LtKo9PT33+75/NnCtqu7Y29t7DxDaa/fn8/kw6XcQBFcCm7iuu2cYhg/ay/3AqVZrfBxwUblvpqoNgBpjDurp6fmbvfyqHbcG4LC2traplklLhNHP5/P5kXnoed77ROSLwKX5fP6kVPV3B0HwQVV9VEROAT7e3Ny8u+M4HWW+522+7x8F/A1Y3dXV9Yx9V0MyTmXaPSJ0ZzKZ76nq5iKyX4lLzg88z8s6jvOMHdvQ9/0jgbX19fXXJpYhz/M+Yd93bVdX12v22XNzudx1pS6P71QUCoXHGhsbd0nWB0B3d/c9vu+fo6qX19fXbws8YmNwUNUzCoVCIqzhed7ZInKCiOwAkMlkvqSqe4vIEfl8/vepV93o+/5GwNWvvfbaEcA1mUxmV1XNiEhPGIafTlkPzrfCRuS67mFhGCZj/3Pf97+jqiNprQuFwhGzZ88en1ijlixZsqq1tfWzURQdTby+/9DX17cLUKWq9wEYY0LHcRCRHFZYbW9vz6rqD4DbGxsbLy8dp7lz59aq6k+Bv1VXV3uJexjwj1wud4zrunsCXxORn9h+JALbMWEYFu3fL7S0tJxvjGmNomhH4B5imvteVX1/yuVqLXCW7/sHEK/Ts4HdiC23N4dh+IItd1Mul9vt3TIXK3j7UBFAKqiggv8VoiiaZv367yu9ZxmGZ1Na4k5VPdTzvF+r6q+rqqpuSccSYN2gROQfQRCckK7LGDNVRBCR7eylBuCVbDa7nnAwBqYDLy9atOip1LVjgGebmpouKS3c09NT9DxvJTFDXq7fJ4jIUBRFZ5R59m/EDCsQa8Vramo+5fv+PsTWiadEpBMYp6qrAOrq6nZR1ayI/DglfCQYLyKri8XisOd5HwC2VdX2lPABQLFY7Pd9vxfYsra29n4Y0dS/vGjRoqcsQ/5+VT2l1Id93rx5xrqqzRCRkfgM13Vne573Qd/3tyZ2vbpLVUVEiKJoFYy40EwDRlzEcrncBNd1Pwr83RgzKwiCWSV9GgJ2KTe27e3tLrCbqv4iJXykkQeOGBwc3AFYQTwXHkgLH7ZdxwDD2Wx2Pe1sPp9/2vf924mtEDiOcwIQZbPZb5aWDcPwXt/3XwQetH2rcV13RxH5U2lZY0yDiGCMWZ4ag9+UxgMAFAqF80ouNQD3p93SRKQb+Lqq/t7zvO+7rtvV3d39yLuJ4bPzx/N9/1BgCxFZZ4y5U0TGAwwNDa2yRRuAlVOmTLms5PnxAMaYlQCq+kngrhLhA4Da2trr165dexXxd78mZVU5P+0KpaqTRQRVvTol+CWKgo0cx3k6ueb7/i6qeqzv+7sBGYmzXIWAk6xv2/YRwbRYLN7red4LIjKb2OpGX1/fl4DtReSodIxTgrVr17YBm4jICSnhA1vfcBAEN6rqGZ7nbQK8YN95e0r4ACCKov/X3pnHSVGcffz3VPXsBSgLYsT7SGIi4rkeIIvMdPUOjgnGGFdjzKWJSUzyvq8m0VwGN4mJvvGNRk28NYfnmqhBs7o7PTvIIhEPPEBBFIMXaoQFdNllZ7rref+Y6nV2dhZ3ERY19f18+LDTXV31dHV1dT1Vz/NUjelLV5v7OQ3A81LKQz3PO7S0XAC7NzY2SiJ6mJnfJKL/dV13JyJqyWQyjzLzyjLXWCybxJpgWSyW90S0EuE4zoDZYK11v1niMAxPBvC/RKSEEP8Iw/AV13W/H51n5iMA5Jj5i8x8bvE/IjoNwAtEFNmTH8DMj5R+iDfBAQAWl9hbTwbwcLmPvUkXAKgY5L7rACwrHciXkkgkjtBaLyWis4hoNQqmUGOZ+U8AJkZOzUYWhGFYzudkktY6ikp1OABorTs2UeyKIuXkAGZeYu7ncHNvm7q2y/f9fxEReZ73ZwD3MPMniOhpU/fHE9H5AN5qb29/ydzj3ij4wPQpLo7jHISCPf/E0mfJzOeiYApSNqLU6tWrPwqgRgjhlztPRHkAEELkjbIyCcD8MkknM/OKcqYshjzM8yWiIwAs20RaRPI6jvNJAM4gSvf+RLQ2m82+IqWcDKCSiDZV3wAKDtYotIF+zvm+73cIIY4B8BIRXaa1Xq6UyiaTyd3eLc8PAk1NTcJ13TsA3MXMe5m2+hqAkwH8CMBr0TtmlIWFpX5DzDwJAKSUS1OpVCWAj5czxQSAIAhCFMwro/d6MoCwurq633tXZNbXUnx8u+22+ziAiujZu677eWZ+EsBJRPQyES1l5n2Y+Q4jW7HJXl90OS6QAXA0EZEx1/wpgN+XU1aLZIUQ4p/lTppgEgjDMGZMzPYBMKA/IaL9TbqltbW1nwCwHTOPG+Q93RvAkubm5tD3/TeYuR5AGxH9GMDDrus+nUgkjhpEXotlUOwKiMVieU+Yj9nG7bffvl9se+ME+jEiujc6ZqJKndvU1PSj+fPnHw6gCcBvlFIrfd//KxHVAHjK9/3DNlVmNMgQQrQNRUYzy/dJANcXH2dmAaBsJI5kMrkjCoPkZeXOo+DL8q5RsoQQVwB4m5kPLTJbgOu6JxJRs9b6SaBv5pyrqqr6Dcqj1QAhRDS7PwoApJQDos6YmeQjUTCr6LsWwJ9MktFGpsEi1kwBsISZ2XXdBi74rvw8k8n0M3FTSj0OoKtImYtmkfsGz1rraJb1G+l0+u+bqKIBCCGi2eLSlSCY44cDyOXz+WWdnZ37AKjhMj5I5vmWxdTVJACLjLyjIsWmFBNMYQLeUZj6zWaXcHiRYlJl0r2rkjxv3ry9iGg0ShQQAGhra8sAyCSTyR2NEn+R8fUpuzr3QWLevHnHEdEJRHSO7/u/KT6nlFqOgr9Rn6kngFvLZHMAAAghFvf09JCUEkRU9r3u7e2dRESSmZcBfau0K0pXE02/psMwXFxy/AAA0FovMQEsriCiRUEQzCiOmue67mwiOl9r/VRUDoDni6PLobBKcorrupODIPgegK5yq3XFxW/iXFQP6wG83t3dfTAK5lID2pNRht7MZrOvJxKJPYQQEEJ8v3QFsRzt7e3PAjgxHo+PFkIcQ0SXCCHuVUp91ETgs1iGhF0BsVgs75VdAYSls5KVlZXfBCCLZgoP9zzvBKBg7pNOpx8KgiByoo0iJD0L4GORg2gxnuc1uq67BwDk8/n9ADjRx/3dGD9+/D4ozMY/W3yciJYAqC9XXpFp1e3l8mTmpQAmmyhQxXmS67r/47ruHkYBOBhAtlj5AAAhxFdQ8HF4yvw+AMDKUvOraDVAax3tHRCZfkwplSmRSJyKgi33YgCIBudFqywvDnZtQ0ODC2AazIDFrPCAiG4rKeNQFAY6fXUvhJhk/u+rX6318kI18YGlZaVSqQlKqS+bWf9yRP49AxRRz/P2BvAFIvpbNpvtohIn+2KIaAkRfTwej+9Zes513S+gEOL5NiP7SwD2nTlz5rjStEKI2ShEjYxms3cBAMdx+oXfdV03AeCTMIoKMy9HIVJQaQQ2JBKJSa7rfqXIwXqSkbmvDuPx+Fil1LdTqdR2ANDa2vpv3/cvA3AbMx86derU6tJ8P2hE7Qwl75nxEfoYjCljGIb7o+gZlOTRFxAgm81uJKIVANxy7YuIfgogJ4S4E+hbVSnXdiYT0QulUcZMWczMT1dVVe0NYBwz/71Y+UilUpVEdAqAV4tWSAf4DDmOE63w/QjAFwGcXcb8spinTV00lJ7wPG8yEc0C8LdsNhsUreCU6yMPIKKoja5AIcregPfU87ztlVKnx+Nxp7GxscLzvMZkMrk/AGSz2a5MJnMHEV0AYKwQYv9NyG2xDMAqIBaL5T3BzKsAjDK+CZFJxVdQCCPbN0tMRNcy8w3Fy/WO45xo/ozMEq4FsF13d/ef4vH4WKDgmOm67rnMfKtxCgXMAFVKOaRN4fL5/Kjoung8PjaKTkVEvyeiCRs2bLgxGuTF4/EqpdSPAXwHwB+jsL9luBoFpeam+vr6WnPtaNd1rzSzggcZpWwVgGlmRQXJZHKU53mXM/NMAC9Fe1FwwXl5wGAhWg2I7tVxnL8B2EhElySTyU+Ych3Xdb9GRFeby4pnXQEz8NFa3wdgDYAm13UPN2nI87wTtNbN6D/Ae8nI1bcjdyKROEII8VcAgoj6fDO01qNM2sPi8fgOqVRqu2w2u5KI0gDOSSQSqaI8JuVyuRYAl86bN2/iIHU7GQXztx+YQT0AwHXdqcycZuaeIAjOKUqLXC5Xri1cg4IT8a0mOhUaGxul67rfBHAtMz8YBMEt5h5uAlATBMENRW1hrFLqCmb+AgCurKyMVjZWAUAQBCcRFbaYV0rVE9FNpk6XAIDZV+UeAF9RSn2hpB7/TkQ/qKiocABASjna1OEh8Xh8bCqVmuA4TgrAFfl8/lKz6oeZM2dOJKJpAFYuWLCgx/O8Y5VSr5jwwR9EBrQzo3xEiu9TACClPMCkKzugRtHgXmv9ewCT5s2bd2mkpE2bNm2MUuo6FKI2XZROp19KpVITMIgpIDNPHmSC4wAA/8pms11CiNdRMBlNRpMYqVRqQhKLQ4AAACAASURBVC6X+yuAj0aym75st1KTvdbW1pfNSszJADK+7/dT9kupqam5x9TXxYlE4ojoeENDw9HMfB+AdWEYRquVkwEEsVis34aiUTQuvBNoYjUz3wng22ZVlkyeH2Pme1AIErHn6tWr92LmW8IwvNrUW+QUfywK7+pzDQ0NH/M87zmlVD8fHYulHFYBsVgs7wnHcS4DsI4KIWtf6OjoeIOIZqNg9hP29PQsBQBm/ioz9wohOpRSzyulXmTmywC0h2H4JwDIZDLziOhcACdIKVcppZ7v7Ox8m4h+zcxXjxs3LjJPmAwg7OrqGtKOwjvssMNiFFY/vialXBsEwfEAkE6n/0ZEFxDRiblc7nWl1HIp5ZsAfsnM148bN+4bg+WZyWTamfk8AMnKyspVSqkVUspOFEL6fjcyO+JCuM6dwzB8QSn1VBiGr3NhM7zFMAMUo8DshvIzsfsDBfMSoDBoIaJvMPMeYRg+o5RaKaVcY2YirwTeiXhl6onxzmCjy5hVjSaihUqpl1zXXc3MN1AhfG2folNRUXEHgLkALlBKvaCUel4IkcY7jubFKyB/B5Bn5rullG/mcrlqI8dpAFYKIf5hBsmvCyGWANiJiI4xA/QBGMXpbgAZIsoopV5SSr1GRA8C2MjM8Ww2+4qp3wMAvNzR0bG2NB+z2eHXARyitX7R87znOjs7/23udW4sFpsVbUrX3t5+G4AbAByXy+XeNM/zDRQ2ZbsbRatTNTU1dwBYSkQ/dV33NaXUiwD+gUIYYxQPNMMw/CYKM9c3KaVeM3XwEIBeIvp0ZJITBEE7gE4Av5JSrs3n8wf6vn8rgJuY+au5XO7frusuDoLgBWaeyMxnmrKmAtgFZVZZPgjU1NT8BYX9UC41/cILAObAKCBSyj4TRQC9zNxvFTPydYhm9AFg+vTplxHRjUT03ZqamjeUUsurqqreAPAlAL+qr68/HwCCIBhgOgj0baw6sfS4YXKkYPq+v4aZf0ZE03t6el5xXXdxLpd7GYUJlR7jGwLHcfYHQEUrkX0Q0UIAvVrrTYWkBgDMmTOnWwhxAgAWQjyklPqXUupVrfVcFCLEuSXvxbOlPnKl0bgAoLKy8jsAFhNRs+u6q0yeywHsy8yzstns8+3t7c8y8/cBHJHL5V52XXdxd3f3KhSiCP6kra1tFRc2YfwogAErNBZLKdYHxGKxDAH+pUDZpXy0trYu8zzvQACnABivtX6mp6fntjFjxuwRBMHN0QArk8ksqq+v37eiouIkIcS+ADZorRdOnz79H8VO4Ol0+jee593LzMcT0QSt9evMfG97e3uxsvFnZr6nxJ56UJqbm3P19fVTKioqPgWgUmt9d1F5P1VKNRPRLK31OCJ6FcB9mUzmmcFzLJDJZC7wPG8OgOO4sIP6i1LKu6JQrSbNXZ7nHcTMn0NhEPJgW1tbxnXd6Y7j/BsAHMfJU2FvkqWlZTBzM4AHivebSKfTf04kEguJ6HMo7DmxPBaLNefz+TFCiPtmzJjxQnQtEc1Np9Pri+S5z/O8/QCcpLWeSEQvOo7T3Nvby47jzO3q6noYAFpaWnrj8bjnOE4jMx9IRCtjsdhfe3t7K4QQbbFY7PEieR5SSh3EzNOJaHVkbpbJZF5NpVKH9vb2fpaIDjIyPdHT03P3YM/ORI7am5lva29v/5nZR2EKM+eY+ZH169ffX7zrMhc2ohx0R13f9//kuu5cIUSj1noigDVCiEw6ne7npGz8WU5vaGi4RWudICIJ4JG1a9fOqa2t3d/8BlAYCKZSqSNzudwXUXDSfRHA7Y7jiDAMsxs2bOjbpTybzb7e2Nh42Jo1a04QQhzCzN3M/MT69evvLb6PbDb7uuu6BwghUlrrjTU1NQuMTF9saGi4TmvtCiHGMPO1zPy3SHmrqam5oKen5+nBHPa3IRvB9M31NesHKIbFmLqckc/nT0Ih8tm/HMe5IwzD0QDuy+VyywBASnlNEAQ3l+5ivnr1ah41atRMAMujY6Y/Oc113etQ2FhveyHEv4IguKc4epgQ4hlm9mKx2MPFeebz+V7HcTwhxIA+QAhxuukjABQimbmu28HMx6DgF5UeP378krVr16bDMFwGAEEQPO84zoByTCCBzzLzb4xvxbvS1tb2aCqV2jefz5+ktf4kEXUN8l6cJ4QY8I6FYbjKcRwPQN/729LS8mZdXd3UsWPHHsfMhwohHK31U1rru4pN0DKZzKVKqTZmPl4IsQMz/5uI7k2n04sBwPf9OYlE4rOO4wzoxyyWUuxO6BaLxWJ53+B53pHM/E8AJ/u+X9b/xmL5MKCUmgNgUnd39/5DnUyxWD4s2BUQi8VisbyfiDZOHBDi1mL5sGD8LT7NzCmrfFj+E7E+IBaLxWJ532Bs/XM77LDDc++a2GL5AFJfX19LRJcD+Gsmk7lvW8tjsWwL7AqIxWKxWN43ENF1RNTc3Nyce/fUFssHj+rq6mpmPjUIgie2tSwWy7bC+oBYLBaLxWKxWCyWEcOaYFksFovFYrFYLJYRwyogFovFYrFYLBaLZcSwCojFYrFYLBaLxWIZMawCYrFYLBaLxWKxWEYMq4BYLBaLxWKxWCyWEcMqIBaLxWKxWCwWi2XEsAqIxWKxWCwWi8ViGTGsAmKxWCwWi8VisVhGDKuAWCwWi8VisVgslhHDKiAWi8VisVgsFotlxLAKiMVisVgsFovFYhkxrAJisVgsFovFYrFYRgyrgFgsFovFYrFYLJYRwyogFovFYrFYLBaLZcSwCojFYrFYLBaLxWIZMawCYrFYLBaLxWKxWEYMq4BYLBaLxWKxWCyWEcMqIBaLxWKxWCwWi2XEsAqIxWKxWCwWi8ViGTGsAmKxWCwWi8VisVhGDKuAWCwWi8VisVgslhHDKiAWi8VisVgsFotlxLAKiMVisVgsFovFYhkxrAJisVgsFovFYrFYRgyrgFgsFovFYrFYLJYRwyogFovFYrFYLBaLZcSwCojFYrFYLBaLxWIZMawCYrFYLBaLxWKxWEYMq4BYLBaLxWKxWCyWEcMqIBaLxWKxWCwWi2XEcDbnIs/z9q6trX2lubk5N5zrGhsbK9asWXOcEGIHIURHa2vrkqFet3bt2l2j377v/4uZebhyjxRKqQuYeUImkzljW8uyNVFKzQTwvTAMv5rNZl/Z1vK8G01NTWL+/Pl7BkHwUjabDba1PIORSqW2C4JgdFtb26qhpJ86dWr1qFGjJgKAEIJbW1v/tXUlfG9MmzZtTGVl5XaZTObV4V7red7uzPwpIloXi8XubWlpeWso1ymlxhPR9gAghOhqbW3993DLHimSyeSOWuu/APhDOp3++7aWZ2viuu5lRJT3ff97WzrvVCo1IZ/PjwGAWCz2dktLy5vl0nmedwIzfz0Mw1Oz2ezq4ZSRTCZ3q6ysXDNnzpzuLSHzlsJ13V16e3vfmj9//ttDSW/a3GgACILgreHWg2Ug0bglnU6/MNxr4/H4WCHEZ4hISinbWltbXx7idaMdx9kRAIQQ+aFety1oamoSHR0dtwBY5Pv+/25reSwjz7BXQBobGyUzr+js7PzocK5ramoSnZ2daSKazcxHh2F4c2NjY8VQrl27du0+zJxm5rnMvOKYY44Z0nXbkAYiSjU1NW2zFSal1GEzZ86cuDXLYOZDAbhSyt22ZjlbCt/3t2fmFQB22taybIpcLneq1vq2oaYfNWrUgeb9mBeG4dNbU7YtQVVVVSMR3Tnc6xoaGg5i5iUAZjLz6blc7ofDuPxMU0ePaa0vHW7ZI0k+n5/IzC4zH7GtZEgmk6M8z1NbuxwhxDEAjtkaeedyubPMM1+Uy+V+NVg6Zj4cgKqoqBh2vxCG4dyurq4Z70XOrQER3VVdXX3iUNOHYXiOqavHpZRNW1O2/xTWrVu3HzMvH+51M2fOHCelfBzA1wAcG4bhlUO9Vgjhmee4IAxDf7hljyStra2VAJJE5G5rWSzbhs1aAdkcOjo6jgBwsJRyYmtr6wYioqGuYqTT6aUA9mloaPiY1nrYL/RIU1NTc3RnZ6ecPXu23lYyENHlQRBcCeBPW6uM6dOn/3rBggXXvp9nk/8TSKfTDwHYx3Xdw4lo7raWZ2uhtf4WgDt83z8dAIiIhnqt7/u/APALpdQFAPbaSiJuEdrb259MpVITR48e3bmtZAjDcA8AbdjKZrpBEBxcUVGxVVazfd//MYAfK6UuBTBqsHTjxo37cVdX18WDrZD8J+D7/vcBfN913T8M47WybAXCMPwcgPWZTGYaMLx+LpPJ3AXgLrOqN6jS/X5gwYIFPfF4fK/q6uqebS0LUFg1FEJ8MZ1OX7itZSnGdd3ZWuvffxhXJYekgCQSiSOklB6ANwA0l56Px+N7SilPAABmvltKOYaIgtbW1iUzZ84cl8/nXSI6DEBXEAQp13WhlFoCYOmWvBnP86YBmKG17hJC3JlOp18qPq+U2g+AIqJXgiCYE4vFjs7n889G5kOu6yYAPCuE2BFAEsCGIAhuzGazXbNmzarp7u5O1dTUtHR3d3+eiCYQ0f1tbW1PRPm7rnsIgH0AoLKycg2A9uLyE4nEvgDGMvMax3GOZ+YNvb29t3Z0dKyN0iSTyVFBEDQKIXZh5gcBPMPMB2Yymbah1EEymfxEEASTiWgcgMNc1+0GgPb29r9GCp9Js53jOCu11idorceZ+up7Hp7nTWbmODO/rLW+R0oZF0I83dbWtioej+8khKiP0qZSqTktLS290e94PD5aCOE5jtMWBMHnhRA7MHOL7/tPDeUeTPmfZObRWuu3hBDHEdHbAG5Jp9ProzSNjY0VnZ2ds5h5khDitY0bN95RXJcAoJSqBzCDiF6trq6+X+v+OuGsWbNqenp6Pqe13ksI8VwsFvtb8b1sCqXUYQB6iegorfVGIppDRKcBeNX3/Vuj+o7H4ztJKU9g5h2EEI9OmzbtvmLlNB6PVzmO0whgdwAPldPNk8nkXmEYHk9ENQB8o3R8IEilUpW5XO5EItpTa/2wEGLA/bmu20BERxDRi7FY7Pbe3t6U1jqdzWa7oveKiA5m5udc1z0RAKZMmXIvgC328Xq3ttDY2CjXrFmTEkLsCWB+LBZb0dvb65qPPlKp1Ha5XC4xbty4e9auXXsCM38CBfOCe4FC/6O1ro7FYhuCIDgOwPrKyspbis3IPM/7rNZaAkA+n38MQD/zDc/zksz8BBHVAThYa/1kJpO5pziNUurjzHw8EW2UUt4ZBMFEIlrn+/6QJm+UUjOJ6JPMjKiuAbyYyWQeLk4jpVyUz+f3EkK4ANZVVFRcH9VXPB53HMc5FsDuYRjOY+YXHcc5OjIpSyQSRxDR7kIIaK1fAzC/WIZkMrm/1joWhmGOiD5FROvCMLw5m812DeUehkI8Ht9VCDEl+t3Y2Hhnc3NzGP2ur6+vraiomCaEmKe1/jwRbSelnNPa2rpssDyN3PuuXbt2zqOPPpp/Nxlc1z3RcZylYRjOYub7iaiSiNwwDO9pb29/MkqXSCSOIqIZRLQRwD2lzzKZTO4VBMEJQohQa31n6ZiViCiRSHgAphDRainlHXbiaMtCRBSPx48RQhzKzM8z83Ih+uvvDQ0NB4VheCwRrQ3DsNlxnP2FECtaW1tfNs+wjogSAHqid8/zvAcBDMkcdyg0NTWJBx544FNEdDARvd7b29tc+t30PG8aMx8CYKnv+2nP8z7b09OTjkz6PM/7bBAEbbFY7AhmnsLMr/m+fz0AzJw5c2IYhpOYeTERNTJzBRHdUTweU0rFzfcQvb29KwAsKi7fdd3DHcd5KwiC7YjIZebXxo8ff0ux2X8ymdzRjJVGh2F4nxCih5nHFvdTQyGVSlXm8/mziOhcZr6s+NysWbNqNmzY0EhEuxPR86XfBCIi13UVgCkA1kkp7yo1ezMT559m5moimuf7fkfRfe4CYN9YLLY0CIJGIhJBENxRbNJORBOklMuUUrPHjRt3VXEf9UHnXWe3PM87SwiRZuY9AHjMnAXQZz+vlKqXUi4B4ALYm4jSWuurgyA4CQByudwEIjoDQAOA7YjoDCI6wzTuLYZS6lJmvpWZJxDRDGZ+ynXdqdF513W/BuAxANOZ+TQp5b1a699IKY+M0hDRBUT0K2a+h5mPZOYTKyoqdgWADRs2TABwe09PTzuAeq31wVrrha7rTi+6fpq5v/OJ6BelMkopZwkhrpdS3ovCYPOrlZWVC+PxeBVQGMCEYbiQiH6gtd6RiH4N4A4i+s1Q6yEMwwNNfe+IwsD7DCI64/zzz+/7ImmtP0NETWEYLgTwGSKaorXuWwZVSp3JzAsB1BPR16WUdzHzb4MgqAOAWCy2qynj20TUHIbh9sUyxGKxiUR0axiGWSHEVGOq9YjneUdi6JzAzDcQ0d1EtBszf42Z/xmZ7cXj8dGdnZ0dzPwLAOOZ+eTKyspnjJIJoDBzAGAOgF2Y+dNa63729PF4fKfu7u5FzPwVIhrHzD/M5XLZVCpVOUQZzwRwHzNPIqLfAXgYwO7M/PtEInEqUOjIpZTLABxDRBOY+Q/z58+/p7GxUQIFhVNK+SAzfw/AR5j5twBOLy7Edd3jwzBcBGAyM+/DzPd6nveDYdTlNiMej1fl8/kHAPwQwEeI6CIU6q0P4wdwOzPvBuBT+Xz+ASK6WQjxEeCd94qZ9xRCHB616crKykFntTdDznJtYW7UFurq6mLGhPRyZq5j5r/k8/nziOjPUR75fH4XAH/u7Oy8jZnPZeYDAXwhOs/MJwohbgzD8E4hxK5E9I3e3t4H6+rqYkWinG7u7xoU+tR+MPNviegfzPzfWuudhRB/Ukr9PDrved6xAJ4koilE9IkwDOcR0VVEdOxQ68IMGk4s/FmoayKaUSLHJWEYXiSEuANAHYBTgyAYDxQ+6FLKdma+hJnrhBC3OI7zE2a+PrpeCOGafC8A8KNSGcIw/Dwz/1EI0SyE2BXAmVLKefF4fIut2gsh9jb92H8TUfObb75Z/BwQi8X2JqK/MPNcIjqUiI4Kw/Bxz/Mml8vPdd3pWut5ACqHonxQgeYgCK5g5kOFED6A32mtJwsh5sbj8dEm399KKe8xg6BDATyulDq1qNxEGIZPEdERzLwfEc0notrofFNTk0gkErcR0RXm+KfDMHw6mUx+YrMqzlIWpdT1QojrAOxERKdJKa8uPu+67le11g8R0f4ADpFSPsTMV2mtpwBAEASfMO2xDsBe0buntd5jS8lYV1cX6+joaDFjnVoAJ1dWVi6Ox+N9PrZmLHUPgMOYebb5fUtFRcXOURpm/ovjOJcx81XMfBARfSs6FwTBocx8AzPPBbAfgCQzP2kG2xHHmfu7WAjx9VI5hRDfCcPwZiK6koh2JqLz1q5de1eRjB8PgmAJgJNNP3i7GS98dzj1kUgkUrlcbrHWWkkp633fnx2di8fjTnd394NE9GUiGsPM/93b25uJvt1G+bgVBSuTjwCIh2H4jOd5ySgPz/NO0Vo/AeBwItodwN1Kqd8V3ecRRHR9EARZZt6XmT8lpXwyHo/3mYP6vv8dZk4R0Rc7OzsfKx5zftDZZGduZm4vBDDD9/1/AoBS6tsArojSENElWuvLM5nMj6JrHMd5hpnbAKC9vf1ZAJ5S6jPM/OtMJuNt6ZtoaGg4GsCpACb5vv8GAHiedz6AiwDUe563PRH9jog+l06n/2Hu40wAvy+T3ZFhGO6fzWbXlTknAFzv+/61powbAHwZwDwA8H3/MgCXeZ53GjOfXuZ6oDAjuF82m30lHo9XSSlfEUJMB9CWy+X+B0C+oqLiyJaWll7jNH0PM28/SF4D8H3/dgC3e573EDNf6fv+YCZYDUR0dDqd7jfzqJQaD+D/AMzyfT9t7vMsAL+NZq3b2toeBeDNnDlzXBAEawbJv5KIrkin0382+d6ktf4SgCHP3AshdpFSTrr//vtfSyaTo8IwfHXt2rVTAcx1HOf7zExa64Oz2exGADDmA5cCaHBddx8i+jGAw6KVF6XUj1Do3AEAUsoLiejhdDr9JaDQOY8dO3ZZPp//AoAbhiIjEd2ZTqe/63neOK31Ot/3v6uUqgZwMBHd5Lru9QAu8n3/16Yuf8LMT65Zs+ZLAG4Mw/C/AITjxo2b0tzcnDOOiwui/GfNmlVDRDdorb/Q3t7eAhRmQ4UQmfr6+utKZ67eb0gpv83MVWEY1mWz2Y2mjh8AIAFAKXUAEX1TSnlIFJTC87wzAPR9vKP3Sil1t9b6n5lM5qKtIGe5trA0agu1tbVfZeY9KioqDmhpaXnLrL5ly2Q1hohWpNPpwezvdwYwKZ1OvxGPx0dLKVfV1tYeAbMCkE6njwUApdS8wWRl5pWZTOZEZmbXdbOmzf/MfBAvJ6KfpdPp3wCFgCFc8JsZMul0+jSjyC/xfX9T/fUBpi/rtyrR29v7dSLaMQzDA7LZbJdZAesoTuP7/q8A/Eop9T0AiUHy3ykMw0nZbHa153nbM/NrQohDUFD03zOZTGYeAM+s4A8WuGF7AE2+798NAEqpOSgolf38j1zXTZjJmK+0t7fPGaYoZ/f09CytqanpYubvrF+//vGxY8e+5TjOXq7rjiGiM4IgOCibzT5vyjqeiP48bdq0v8+fP/9tIrqSmc/LZDKXAkBDQ0Od1nohmVWQ+fPnn0xEh4ZheFD0rJRS14ZheD6Ak4cpq6UMDQ0NRzPzZ5l5UhRcQyl1FYCDgEKgkJqamkuJ6LR0On2LOX8YgIVRHplM5j4A9ymlfmbyOWlLy1lbW/tNZt6xsrKyLprJV0rdKaX8EYBvNzQ01AE4A8BB0SqbuY8Bk3LMvNO6dev2G0TZ3o2Zj0yn0wtNHo8Q0ecA/A4AfN//HwDwPO8SAFWDiFtdU1NTN2fOnG7XdS8F8Hw8Ht8pm82+DuACIspkMplTmJnN+OBxAEMOwKKUOlsI8RNm/lq0il3CngAmV1RUjGppaek134TL33zzzQkAXldKncjMR0spD4xWE13X/Q6A65qamvZIp9OjqqqqriaiL6bT6TvN/V7EzE8lEonm9vb2B005ezBzXSaTWWT68McdxzkeQJ/vTyaTeZiIpiilvgigxXXdb2Uymb8M9V7fr2xyBURKeTSAFZHyYY79EUBkWlLFzIdKKfsGa9ls9nVmbtlK8pZFa50CsJqIzvI870LP8y5k5kkADjXnpwJYFykfAFBRUXE9gAGmNsx8wyDKBwAgCIK7itIuQ2GlYcgw88PR8poZOP8LBe0ZRHQUgL9EHYMx09kqPhxE9Eip8mGOTwPweqR8AMCGDRuuQtGq11Dp6enpqysiWkZEw62rh+6///7XAKC1tXUDgJeY+SPmXNI8q41Resdx/gAgUVdXFxNCJFAwf+kz+wrD8MaS/FPMXB21mdra2l8AyJsVmyGhte6K/ieiKBLOBiKSDQ0NewL4eLETYTqdXk9ENxFRNEviAbg5Wlo2/98cpd+4ceMUANVSyumRnFLKzwCoqKys3H+ocm5DPAC3RM/JfKxuik4y81QAC4sj4gVB8GcAI+o/NUhbCIragiKiWyNzqebm5hwzX1suryAINuXkviCaJDGDwZdh3v9hcBeb2QAhRF8f5HneRAB7SSn7VhpMBJ65w8x/qFxTziSKiBQKz7wLAFpaWnqJ6LrNyL8jsns2pperMPy6eq/kwjC8t+j3gD5fSukR0b0ALtsM5QPMvGHBggU9ALTjON3mHckBcEw/0RYpH0Cfnf/6qqqqo1zX3QPAx4UQfX2bmSB6uij/FBGtcxznp1H7BrCrWU2xbAGY2QNwf3FkP6113zOpqanZH4CcNm1aX3AR3/cfATCsyYEtIGcKwIZ8Pt9UNFYaF7WFMAxdAA8Um/gRUbmJWmitr9jESt+q9vb2hUW/N2esdH8UXS6TyawAkCOi6P0/CsANUT9o/IoHuAe8S/6PAniLiL4cj8cHBFWqrq5+GcCL+Xz+Qdd1zx07dmxdfX39mUYBAjMnieivxaaMlZWV1wKYMH/+/ElVVVVHAeiKlA+grz++TwjRUFTUi5lMZpHJk5n5Wa31gLpSSu1vJnFfEkKMaLvZWmxSASGiUQD6hfFra2vrxjuDUcek21hyaenvrc1oAJ1a67XRP2Z+lJl/ZuQbxcz9wnWaQf6GMnm9samCxowZ0xdukYiG7TxZNEiN6MuDmR0MVIqG5I+wGQx2n6MA9Ksr83Ecthw777xz371qrTfH0XTQugIwioj6KYrM3AlA7rjjjtUotIl+bXfMmDH97ouIRjPzayXt5kYiGvYgYhBGAdAzZszoVy4zrzXyAcDo0rZZ/FtrPQbA28Uyaq1XM/OPiOjFLSTn1mTAu4f+7cth5n5ta8aMGTmMsAIyhLYw4D6EEGVXn6IP1CBsqk0PVdY+vxchRGn/gYqKitL+d8T7kNK60loPe6WutK/cnP52C5AvDtlNRAPaJTOfxsxXAfivrRB5cBSA9aUHmXktEY0y32g9bdq00nC7xdeMZubVJW17rtb6/7awrP+xMPOA742Usl8/ByBfJjDNthgrvVncFlAwI74SKD9WCoJgsHd30LFSmXHOsPtzIcS7jZXe07gzk8nM6+7u3g/AIinlI0qpi1Kp1HbR+ZaWll4p5RSt9V0oTKS1d3R0PBCZzKPMu2kmW7pNexhdet7IXvz9B0q+CUSkhRB9JvNKqfGu6/6BmecKIf6xbt26A9Pp9OPDudf3K5tUQJj5OQD7Tp06tTo6ppTaH0AM6JvBeyEMw1R03thMb1ZYtXg8PjYej48d7nVEtAxAmMlkLor+SSn/AuAOANBaP0dEe9XX1/fZxRqH8HGbI+fWgoieIqJU8TFmLhuisr6+vtbzvEFNs5hZYxMmdtHMQZnjzwH4aPGLqJQ6AJuIIrONeJYL4TP7YOYpAFaZWerlAPYvtq/v6uo6uCSPpUKIJ4rbDTPfX11d3YEtQC6XWwkgN2/evLqSU1MAhsRYaAAADGdJREFUPGv+Xg6gVK6+31rrpQDG5HK5ayIZ169f/1spZcb3/fdtjPcilhPRoPcnpVxMRIfH4/EdomPz58/3sJkR+pLJ5G6Rje4wWUpET26iLSwvszI2pTSTbUkQBKsArNmwYUOfv4d5j6eVS+953u6DhQqXUmqgYAe9GaIsN6ZSxbyv6moLc2YmkzmbmR8IguB6oi0XRoqZlzFzXXGe8Xh8ByL6mBDi2TAMXwKQnzdv3kHRefMN3q8oj6UA1hW3bcdxmrXWd28pOS1YDmNuVURfP+c4zrMAakr8UncBcODmFBaPx3cqHpcNFdMWXisZK90ZhmFkHbKciA4u7heKAzW8j3iKivzaiIgGGyvF4/GdipSGfixYsKDH9/2fM/NBAPbJ5XJ9AT2SyeReWuspmUzmgkwmoxzH2QXArlLKmabMZaVjEBM8Z0wYhs+Zb/dexd824/d1JBE9i6HTQUTVYRhOSqfTlwzFv+yDwiY/LuPGjVvQ2dm5sqam5gbXdX8qpRzDzJejSJsloiZmvlIptQ+AF1GwKR3WBoURUsrFAJiI9igeIBunv48w8y4A0Nvbm/A8Ly+EeLa1tfVlKeXNQRD8UCl1ARFdDWB7Zr5KCJEGcH57e/uTSqlHKisrb00kEj8BUCWE+DWATc1UDgsiIqWUCwDMvB+AsVEc/Vgs1jGUyEpBEPzOcZxFSqk7APgADmfmqUTUrz6bmppEZWXlM8y8AcBg+7EsBXCy53kLADjpdHrxUO4jk8k8qpRanMvlbnZdd7YQogbAr5m5L0RlIpE4UEo5QWs9hogQhuF0z/PWBUGwbKQ2JNRaXyqESLuu+4yUsoWZ92PmS409PGKxmJ/L5brGjh17VTwe/4WUcoIQ4pLiPJj5YgCXJBKJ1ysqKh4Pw3CqEOL6t99+uw7A8+XKHQ7ZbLZLKXUNEd2YSCTOIKKVQohTUNj34GAAIKKrALQppR4WQrSHYegS0akAngIKPlSe57VWVlbe4nneOUKIdWPHjj1Xa30wMx8FAJ7nHQxgPBHtC0BE7U5K+XRkwratYOariegB13X/KaVsC8PwaCHEV42ii6OOOqpj3rx5D0sp25VS11PBMfJT2IxZe6WUB6Cts7PzFwB+Fh03vlQJACCivZh5p6iOgiCYm81mA2a+mIgu8TzvdSnloqgtbNy48TAAz4VheJ0Q4lGl1PeklP8IgmAKEX16C1RRHw0NDTubvgMAxmqtP2nkXDOUGa9sNhu4rvtLIrrO87wDAbzBzF8EMGAG0/O8Y5n53vnz558H4Jel5ysrK1/q7u7eIKU8Mx6PtziOkyuNKjgYWutrhBALlVKPSSlbtdb1RDQzOl9XVxerra092vz8GDPvED2P2tra7JaI8lJSxu4AqqIyfN/PMDMnEolDpZS1UsrIBDbheV5uuO9NtLqjtf6GlHKxUuqbKLLffi8IIe5g5tmu616RTCYv1lqPllL+lpnnR2aLSqm/CCGu8Tzv6wDWM3MTgJoi+a6WUj7hed5ZWutmItoZwJ8cx7kEwLUmClC9qYNdAcDzPEVE3NbWltkS9/FhJwzDZinlz5VSF4RheKXjOB9l5vOj8/fff3+n53mXAfib53m/Z+ZuIvoSNmMM0tDQsLOUcsWoUaPaAfQLLqGUmmJWMQ4AUBO1+Y0bNz7W0dGxVghxOTM/6Hnek1LKe8Iw3IeZb3Ic52wAd27cuPHvVVVVF3Z0dPwhkUhcDmAPIcRZ2AwT7MEwmyUeCQBa692JqMK0t3xbW9sDQ8mDiC5g5vuMb9gTrut+GsB4DIwauLuUcjmAFgCfHSy/TCbzIoDPeZ73yehYGIZjANyklPqalDKbz+c/JqXcXmvdCQBCiOvDMPwfz/POD4LgOinlRCK6EsDNxnR0ted5c6WUtyulzgrD8C2l1PeYeXwQBEM2FyOiE4ojlH6Y2OQKSHNzcyil/DQKjpVPaq1vJ6JLACyQUvYAQDqd/jMRRSFEj9Ra/5qZM0KIfh8RIlpHRO+2SVoGQKZ0dl5rfQqAC4nou0T0mBDiFwAuDMPwKKDwcgOIE9FBzPw0M/vMvDAIgr4PaxiGxzPzGiHEPUKI3zLzL4koXWJGsQxAWafqWCyWI6LH3n777T7lS2v9OhE9DwAmytSFRs4ZxkziQiPn9kXpV5TUyzJmXgMA2Wx2JRUimbzJzA0o2PGeA6BfXc6ePVsTUZaZ+4X5LUYIcR4zx5h5ETPfWjyTqbV+HcCKctcxMwshZgHoMnbNFzPzbCLKRCZPQogvAbhQCPETInqMiH5ofk8x97SRiB6bNGlS33MUQryGks5hUzBzufTPAOgEAOPA1UhEZ2qtVzDzNUR0me/7FwOFpVAzI7K7idL2JyJqIqLHKioqcgCQyWRuJaJzhBC/CYJghdb6fACnFttbbwpjAvWqub+XmPkVI/vLzPwyAKxbt+77AO4WQtxGRM8z82eIKBnZ2Bo/nNMAnKe1XkZEJxHR2XhnhQRGaVnBzOkwDJ9BwWmtsUiU01Bod18loiV4p91t0Uhzm4NxnjsVwLla62eJ6Eta67NReNcwe/ZsPWrUqFnGfjcOYHut9XEAtNa6dCC6gogG/WALIVYCeBQlIR0XLlwYg6kTAB8nou2i3xUVFZVGzluJ6Bxm/t/ittDW1vYcALS3tz9NRJ8mohPDMMwQUZKITgfweFH5G4nosU3It4qZ+zlJEtEz0QDWRJmL+pCcCU5xIfcPaPGMWcKHuaan+H4zmcylXIgYty8zH0JE5wBYxMyldfmCqauyio2xvf4qgLOllE8x85dL5H661AQywoSP/QyAU8IwzDDz0VrrrwN4EgCMiWR0n3VEJKPfr7zySrTJ7CpmXlmcLzMvGazMUsaMGTOqqIzdqeB/diGAC0888UQBACb6zoVEdBYRPSal/DmAC3O53MEAIKXsLlM/r2qt+0wfzfu2HgCy2exqrfXpzHxy8Wr7pjD9Z2Q6ssg8TwB4IgzD7nQ6vV4IkSCiPcMwfIoLUShfEkKcEOXR3d39X1ywZ88wcwcKs9h/DMNwtZFrpRBCMfMsInoOwF1EdIvv+5FfTt/7QIWIQzsDuFBr/euh3IOl8OyFEDMBJEzUw4tR+Hb3vZu+75/DzOdxIQLoJ5n5NAAvoeT7zsyrsIlvJRGtZ+YHmXmADyeAs1F4jscS0Rswz7W6unofAEin04uZ+VPM/JUgCFYw800ALon8FObPn/+2lFKhoHikiehsKeUZAB4paqfgwoaVpSZSEet5YOCLlTChhGOx2MRILiHEHkQ0EYX21jdpxMwrTT0Us8hxnG4A8H0/y8wJZq5h5gSA24joepTUZU9Pz1oA/2TmBRgCxQN93/efYuZTAPx3GIbPSSn/qLX+PxO8Aq2trS9rrV0uOKIvB/B3AB1hGH4zyiMWi52IwrPMSCkXM/NHtdaJyM/Y9OXPlIjxLxSFXf6wKh8AQINY4gwZpdQUAIFxqIqOZQHcFMWFtgwNz/N2Z+ZDoogr5thZAGak0+njtqFoFstWwUSC+5yJn77BHNtTSvlsd3f3WON/ZBkiSqk4Ea0uXvFUSj3JzOcPEunFYrGMAEqpTzHzUuNQjXg87kgpX2HmWcPdu+I/HbNP2Q6+7/dFIlRK3cjMr2QymfO2pWyWofOeY6oT0dHM/M1EIvEFZl4hpTwJhfCMf3/Xiy2lbA+gWSn1LQD3EtFkZj6Hmc98tws/SCSTyd3y+XzZiBjM/Oq7OPGOGIlE4kAM8o5UVVUtGeqGhZbBmTBhQr6zs/PHWusG13XPY+Yqx3EuA3C7VT6GDxGlmPnYhoaGLwFYpbU+HcB4x3GGtJHpB4WGhoaDgiAo6+vz1ltvPfV+sJP2PG/7MAzLmsgKId4e6saQlg8HRHQKgD0SicQZUsq3Hcf5gdb6zenTpz+6rWX7oGFMVa9SSn0ewONE5AL4LBG9H/1VLIPwnhWQ2tra33R2dm4nhPgrgLEAHhFCJH3f/9BtG7+1SafTi13XPYmIfgngD8z8IhHN9n3/QzVzqbU+2Siq5c79HsCN5c6NNEKIi6loQ69igiCYhS24O+1/Ks3NzWE8Hp8lpbycCsEk3tZa/1Vrfc62lu2DSCwW+2kul6vQWt+HQuCIB7XWSd/3y0X8+8Citb5MSllT7tyOO+54DIA3y50bSZj5ACnlJeXOaa0XobDfguU/h28BuFQI8SAzSwC+EOLYMpGxLO+C7/u3e563Mxeid+3MzE8TUWM6nS41Z7K8j/l/Y65MVkWVxfoAAAAASUVORK5CYII=)" + ] + }, + { + "cell_type": "markdown", + "id": "A5BuRrYALiK_", + "metadata": {}, + "source": [ + "This tutorial focuses on the **node prediction task**. Detailed tutorials for\n", + "all three tasks are available in the **left menu**." + ] + }, + { + "cell_type": "markdown", + "id": "9C6k7qhjHG4W", + "metadata": {}, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "id": "d1hs5OKmGh7l", + "metadata": {}, + "source": [ + "## Installing GF\n", + "\n", + "Make sure your machine has a GPU or TPU, otherwise training is going to take forever.\n", + "If you are using Google Colab, you can get one for free. Just go to Edit > Notebook settings and select your hardware accelerator as TPU or GPU." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "6V95B8oxGmBU", + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-23T18:12:59.401199Z", + "iopub.status.busy": "2026-09-23T18:12:59.401017Z", + "iopub.status.idle": "2026-09-23T18:12:59.652694Z", + "shell.execute_reply": "2026-09-23T18:12:59.652143Z" }, - "kernelspec": { - "name": "python3", - "display_name": "Python 3" + "executionInfo": { + "elapsed": 253, + "status": "ok", + "timestamp": 1790187179653.6162, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 + } + }, + "outputs": [], + "source": [ + "# Install DGF (Distributed Graph Flow) and OGB (for the toy dataset).\n", + "!pip install dgf ogb -U" + ] + }, + { + "cell_type": "markdown", + "id": "RKXovz93qQAB", + "metadata": {}, + "source": [ + "## Importing libraries" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "z3Tx-P_MlPuu", + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-23T18:12:59.654917Z", + "iopub.status.busy": "2026-09-23T18:12:59.654647Z", + "iopub.status.idle": "2026-09-23T18:13:03.920235Z", + "shell.execute_reply": "2026-09-23T18:13:03.919646Z" }, - "language_info": { - "name": "python" + "executionInfo": { + "elapsed": 4267, + "status": "ok", + "timestamp": 1790187183921.6465, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 } + }, + "outputs": [], + "source": [ + "import dgf # Import Graph Flow" + ] }, - "cells": [ - { - "cell_type": "markdown", - "source": [ - "## 🧭 Getting Started\n", - "\n", - "[![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/google/distributed_graph_flow/blob/main/doc/docs/tutorial/getting_started_simple_api.ipynb)\n", - "\n", - "**Distributed Graph Flow** (DGF or GF) is a python toolbox for training,\n", - "evaluating, and deploying Graph Neural Networks (GNNs) models and other\n", - "relational data ML models. GF contains two APIs:\n", - "\n", - "- **Advanced API:** A library of low-level, modular, framework agnostic functions designed for GNN experts and ML engineers. It is great\n", - " for those who like freedom, need custom pipelines, or simply want to augment\n", - " existing custom pipelines.\n", - "\n", - "- **Simple API (this tutorial):** Built on top of the Advanced API, it\n", - " allows you to train and deploy a GNN on any data in 10 lines of code.\n", - "\n", - "**This document shows the Simple API**. You'll learn how\n", - "to train, analyse, evaluate, and productionze a node prediction GNN model.\n", - "\n", - "The Advanced API guide is available\n", - "[here](/tutorial/getting_started_advanced_api.ipynb). We recommend starting with the\n", - "Simple API even if you are an expert; you'll learn about GF general basic\n", - "concepts, and see how the Simple API can be customized using Advanced\n", - "components.\n", - "\n", - "**Remember:** See the [API page](https://dgf.readthedocs.io/en/latest/api.html) for all the available methods. And in Colab, use the `?` operator for method details, e.g., `?dgf.io.fetch_ogb_graph`.\n" - ], - "metadata": { - "id": "woDLT7o2leXI" - } + { + "cell_type": "markdown", + "id": "DXhSqZWSq6aV", + "metadata": {}, + "source": [ + "## Download a graph / dataset\n", + "\n", + "The core of GF is the `dgf.data.InMemoryGraph` python dataclass. It is a\n", + "logic-free representation of a graph in memory. It is generally paired with a\n", + "`dgf.data.GraphSchema`, another dataclass that defines the graph’s structure,\n", + "including nodesets, edgesets, and their connections.\n", + "\n", + "GF provides various importers and exports to popular graph formats like TF-GNN,\n", + "Spanner Graph, BigQuery Graph, and Sparse Deferred. You can find the full list\n", + "in the `dgf.io.*` and `dgf.beam.io.*` modules.\n", + "\n", + "Additionally, GF can ingest data directly from repositories like OGB. This\n", + "tutorial uses the OGB Arxiv graph:" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "fkSx2Pq0tXH1", + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-23T18:13:03.922918Z", + "iopub.status.busy": "2026-09-23T18:13:03.922429Z", + "iopub.status.idle": "2026-09-23T18:13:03.996884Z", + "shell.execute_reply": "2026-09-23T18:13:03.996383Z" }, + "executionInfo": { + "elapsed": 76, + "status": "ok", + "timestamp": 1790187183998.6182, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 + }, + "outputId": "6052a60a-7110-472e-a973-69d4b3bfd08f" + }, + "outputs": [ { - "cell_type": "markdown", - "source": [ - "## A word about GNN task selection\n", - "\n", - "Graph Neural Networks (GNNs) are **versatile**, but their application **must\n", - "match the underlying problem**. To guide this selection, the GF high-level API\n", - "defines **three distinct tasks**. Given a training graph, you can train a GNN\n", - "for:" - ], - "metadata": { - "id": "g5zVw3GOUNX8" - } + "name": "stdout", + "output_type": "stream", + "text": [ + "Caching arxiv graph at /tmp/gf_fetch/arxiv.cache\n", + "OGB dependency not available. Downloading graph from CNS.\n" + ] + } + ], + "source": [ + "# Download the Arxiv graph from the OGB repo.\n", + "graph, schema = dgf.io.fetch_ogb_graph(\"arxiv\")" + ] + }, + { + "cell_type": "markdown", + "id": "iy9W1Cu2tmoc", + "metadata": {}, + "source": [ + "**The More You Know:** The ogbn-arxiv dataset contains ~160k Computer Science\n", + "papers and their citation links. The goal is to predict each paper's subject\n", + "area across 41 categories using its text embeddings, publication year, and\n", + "citation structure.\n", + "\n", + "While you can inspect the `graph` and `schema` objects directly, using the\n", + "built-in printer and plotter give a clearer overview." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "Dz27FflNt5gS", + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-23T18:13:03.999659Z", + "iopub.status.busy": "2026-09-23T18:13:03.999303Z", + "iopub.status.idle": "2026-09-23T18:13:04.003233Z", + "shell.execute_reply": "2026-09-23T18:13:04.002873Z" + }, + "executionInfo": { + "elapsed": 5, + "status": "ok", + "timestamp": 1790187184004.1726, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 }, + "outputId": "14868cea-ff18-4781-dab6-7213b88f7c4c" + }, + "outputs": [ { - "cell_type": "markdown", - "source": [ - "![tasks.png](data:image/png;base64,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)" - ], - "metadata": { - "id": "UzBPkuAKUPJL" - } + "name": "stdout", + "output_type": "stream", + "text": [ + "Graph Schema:\n", + "\n", + "Node Sets:\n", + " nodes:\n", + " | Feature | Format | Semantic | Shape | Detail |\n", + " |-----------|------------|-------------|---------|-------------|\n", + " | #id | BYTES | PRIMARY_ID | None | |\n", + " | #split | BYTES | CATEGORICAL | None | |\n", + " | feat | FLOAT_32 | EMBEDDING | (128,) | |\n", + " | labels | INTEGER_64 | CATEGORICAL | None | #num.cat:40 |\n", + " | year | INTEGER_64 | NUMERICAL | None | |\n", + "\n", + "\n", + "Edge Sets:\n", + " edges: (Source: nodes, Target: nodes)\n", + " (No features)\n", + "\n" + ] + } + ], + "source": [ + "# Show the schema\n", + "dgf.analyse.print_schema(schema)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "Az1KUBTNt-LF", + "metadata": { + "colab": { + "height": 155 + }, + "execution": { + "iopub.execute_input": "2026-09-23T18:13:04.004861Z", + "iopub.status.busy": "2026-09-23T18:13:04.004694Z", + "iopub.status.idle": "2026-09-23T18:13:04.045302Z", + "shell.execute_reply": "2026-09-23T18:13:04.044891Z" }, + "executionInfo": { + "elapsed": 42, + "status": "ok", + "timestamp": 1790187184046.248, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 + }, + "outputId": "73590d44-60f5-4f0b-eb85-67a93d7bfef8" + }, + "outputs": [ { - "cell_type": "markdown", - "source": [ - "This tutorial focuses on the **node prediction task**. Detailed tutorials for\n", - "all three tasks are available in the **left menu**." + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "nodes\n", + "\n", + "nodes\n", + "#id\n", + "#split\n", + "feat\n", + "labels\n", + "year\n", + "\n", + "\n", + "\n", + "nodes->nodes\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n" ], - "metadata": { - "id": "jzFK0v-bUTUN" - } + "text/plain": [ + "" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Plot the schema\n", + "dgf.plot.plot_schema(schema)" + ] + }, + { + "cell_type": "markdown", + "id": "5zm6ImfnuCtC", + "metadata": {}, + "source": [ + "**Remark:**\n", + "\n", + "- You can plot graphs with `dgf.plot.plot`, but the Arxiv graph is too large\n", + " for this. You'll probably crash your notebook.\n", + "- You can freely modify the graph and schema objects. You can then use the\n", + " `dgf.validate.validate_graph` to ensure the object remains consistent." + ] + }, + { + "cell_type": "markdown", + "id": "Itoxiu6Lu3-F", + "metadata": {}, + "source": [ + "## Train a model\n", + "\n", + "Let's train a GNN to predict the `labels` column by using all available node\n", + "features and the relations between papers." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "LUytl4VxB0Aj", + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-23T18:13:04.047120Z", + "iopub.status.busy": "2026-09-23T18:13:04.046847Z", + "iopub.status.idle": "2026-09-23T18:15:05.835498Z", + "shell.execute_reply": "2026-09-23T18:15:05.834875Z" }, + "executionInfo": { + "elapsed": 121790, + "status": "ok", + "timestamp": 1790187305836.4763, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 + }, + "outputId": "0ed44b1f-ff19-419c-c5c2-3a58b9826b98" + }, + "outputs": [ { - "cell_type": "markdown", - "source": [ - "## Installing GF\n", - "\n", - "Make sure your machine has a GPU or TPU, otherwise training is going to take forever.\n", - "If you are using Google Colab, you can get one for free. Just go to Edit > Notebook settings and select your hardware accelerator as TPU or GPU." - ], - "metadata": { - "id": "K0cgvBnso_Ij" - } + "name": "stdout", + "output_type": "stream", + "text": [ + "Preparing dataset\n", + "Num. training seed nodes: 152409, Num. validation seed nodes: 16934\n" + ] }, { - "cell_type": "code", - "source": [ - "# Install DGF (Distributed Graph Flow) and OGB (for the toy dataset).\n", - "!pip install dgf ogb -U" - ], - "metadata": { - "id": "l0gQK-RtUXkU" - }, - "execution_count": null, - "outputs": [] + "name": "stderr", + "output_type": "stream", + "text": [ + "[Warning] No normalizer created for node set 'nodes', feature '#id'.\n" + ] }, { - "cell_type": "markdown", - "source": [ - "## Importing libraries" - ], - "metadata": { - "id": "RKXovz93qQAB" - } + "name": "stdout", + "output_type": "stream", + "text": [ + "Preparing dataset finished in 3.05 seconds\n", + "Caching validation dataset\n", + "Caching validation dataset finished in 3.84 seconds\n", + "Number of cache validation batches: 529\n", + "Training model\n", + "Generate first batch to initialize model\n", + "Create model variables\n", + "...Tracing model\n", + "Create model variables finished in 6.40 seconds\n", + "Will validate model every 1000 step(s)\n", + "Will checkpoint model every 1000 step(s)\n", + "Start training. The first two steps are generally slow.\n" + ] }, { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "z3Tx-P_MlPuu" - }, - "outputs": [], - "source": [ - "import os\n", - "\n", - "os.environ[\"TF_USE_LEGACY_KERAS\"] = \"1\"\n", - "\n", - "import dgf # Import Graph Flow" - ] + "name": "stderr", + "output_type": "stream", + "text": [ + "Training: 0%| | 0/10000 [00:00\n\n\n\n\n\n\n\n\nnodes\n\nnodes\n#id\n#split\nfeat\nlabels\nyear\n\n\n\nnodes->nodes\n\n\nedges\n\n\n\n", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "execution_count": 4 - } - ] + "name": "stdout", + "output_type": "stream", + "text": [ + "Validation loop took 1.18s (only printed once)\n" + ] }, { - "cell_type": "markdown", - "source": [ - "**Remark:**\n", - "\n", - "- You can plot graphs with `dgf.plot.plot`, but the Arxiv graph is too large\n", - " for this. You'll probably crash your notebook.\n", - "- You can freely modify the graph and schema objects. You can then use the\n", - " `dgf.validate.validate_graph` to ensure the object remains consistent." - ], - "metadata": { - "id": "5zm6ImfnuCtC" - } + "name": "stderr", + "output_type": "stream", + "text": [ + "Training: 100%|██████████| 10000/10000 [01:42<00:00, 97.89it/s, step=10000, train-accuracy=0.7087, train-loss=0.8977, valid-accuracy=0.7022, valid-loss=0.9556]" + ] }, { - "cell_type": "markdown", - "source": [ - "## Train a model\n", - "\n", - "Let's train a GNN to predict the `labels` column by using all available node\n", - "features and the relations between papers." - ], - "metadata": { - "id": "Itoxiu6Lu3-F" - } + "name": "stdout", + "output_type": "stream", + "text": [ + "Restoring best model parameters with validation loss 0.951214 from step 10000\n", + "Final metrics: {'step': '10000', 'train-accuracy': '0.7087', 'train-loss': '0.8977', 'valid-accuracy': '0.7022', 'valid-loss': '0.9556'}\n", + "Training model finished in 108.81 seconds\n", + "Final model evaluation\n", + "Evaluating model on generator\n" + ] }, { - "cell_type": "code", - "source": [ - "model = dgf.learning.train_node_model(\n", - " graph=graph, schema=schema, target_column=\"labels\", verbose=1\n", - ")\n", - "\n", - "# Note: If you don't have a GPU + JAX GPU, this command will be slow. Reduce\n", - "# the number of training steps (e.g., num_train_steps=1000) or the maximum\n", - "# training time (e.g., max_training_time_seconds=60)" - ], - "metadata": { - "id": "LUytl4VxB0Aj", - "executionInfo": { - "status": "ok", - "timestamp": 1779800419584, - "user_tz": -120, - "elapsed": 159172, - "user": { - "displayName": "", - "userId": "" - } - }, - "outputId": "f62dc4e3-9d52-4770-ad2d-49d361fb9c18" - }, - "execution_count": null, - "outputs": [ - { - "output_type": "stream", - "name": "stdout", - "text": [ - "Preparing dataset\n", - "Num. training seed nodes: 152409, Num. validation seed nodes: 16934\n" - ] - }, - { - "output_type": "stream", - "name": "stderr", - "text": [ - "[Warning] No normalizer created for node set 'nodes', feature '#id'.\n" - ] - }, - { - "output_type": "stream", - "name": "stdout", - "text": [ - "Preparing dataset finished in 3.50 seconds\n", - "Caching validation dataset\n", - "Caching validation dataset finished in 6.90 seconds\n", - "Number of cache validation batches: 529\n", - "Training model\n", - "Generate first batch to initialize model\n", - "Create model variables\n", - "...Tracing model\n", - "Create model variables finished in 12.05 seconds\n", - "Will validate model every 1000 step(s)\n", - "Will checkpoint model every 1000 step(s)\n", - "Start training. The first two steps are generally slow.\n" - ] - }, - { - "output_type": "stream", - "name": "stderr", - "text": [ - "\rTraining: 0%| | 0/10000 [00:00\n", + "\n", + "\n", + "\n", + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
\n", + "
\n", + "

Node prediction model: Predict the value of a node feature.

\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
TypeNode prediction model - Predict the value of a node feature.
Target nodesetnodes
Target columnlabels
Number of label classes40
\n", + "
\n", + "
\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "
WARNINGNo normalizer created for node set 'nodes', feature '#id'.
\n", + "
\n", + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
Accuracy0.7077
Num Examples10000
AUC0.9504952526630209
Per Class Metrics [40]:
    \n", + "
  • Class 0: AUC=0.9779, PR-AUC=0.3094
  • \n", + "
  • Class 1: AUC=0.9728, PR-AUC=0.4191
  • \n", + "
  • Class 2: AUC=0.9872, PR-AUC=0.6924
  • \n", + "
  • ... (35 omitted) ...
  • \n", + "
  • Class 38: AUC=0.9960, PR-AUC=0.8769
  • \n", + "
  • Class 39: AUC=0.9612, PR-AUC=0.3136
  • \n", + "
\n", + "

*Showing plots for the first 20 classes out of 40 total classes.

\n", + "\n", + " \n", + " \n", + " \n", + " \n", + "
\n", + "
\n", + "
\n", + "\n", + " \n", + "\n", + " \n", + " \n", + " \n", + "\n", + " \n", + " \n", + "
Number of training seed nodes152409
Number of validation seed nodes16934
Training duration1m 55s
Number of training steps (final model)10000
\n", + "
\n", + " \n", + " \n", + " \n", + " \n", + "
\n", + "
\n", + "

Note: The logs for the first training step are not shown.

\n", + "
\n", + "
\n", + "\n", + " \n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + " \n", + "
num_sampling_hops2
sampling_width15
num_layers2
batch_size32
max_training_time_secondsNone
num_train_steps10000
random_seed42
node_embedding_dim128
learning_rate0.001
opt_weight_decay0.0001
dropout0.1
message_pooling'sum'
architecture<Architecture.HETEROGENEOUS_MESSAGE_PASSING: 'HETEROGENEOUS_MESSAGE_PASSING'>
early_stopping{'patience': 5, 'min_improvement': 1e-06}
\n", + "
\n", + "
Raw schema plot\n", + "\n", + "Raw schema textual\n", + "
Node Sets:\n",
+       "  nodes:\n",
+       "    | Feature   | Format     | Semantic    | Shape   | Detail      |\n",
+       "    |-----------|------------|-------------|---------|-------------|\n",
+       "    | #id       | BYTES      | PRIMARY_ID  | None    |             |\n",
+       "    | #split    | BYTES      | CATEGORICAL | None    |             |\n",
+       "    | feat      | FLOAT_32   | EMBEDDING   | (128,)  |             |\n",
+       "    | labels    | INTEGER_64 | CATEGORICAL | None    | #num.cat:40 |\n",
+       "    | year      | INTEGER_64 | NUMERICAL   | None    |             |\n",
+       "\n",
+       "\n",
+       "Edge Sets:\n",
+       "  edges: (Source: nodes, Target: nodes)\n",
+       "    (No features)\n",
+       "
\n", + "Normalized schema plot\n", + "\n", + "Normalized schema textual\n", + "
Node Sets:\n",
+       "  nodes:\n",
+       "    | Feature            | Format     | Semantic    | Shape   | Detail      |\n",
+       "    |--------------------|------------|-------------|---------|-------------|\n",
+       "    | #split_INDEX       | INTEGER_64 | CATEGORICAL | ()      | #num.cat:4  |\n",
+       "    | feat               | FLOAT_32   | EMBEDDING   | (128,)  |             |\n",
+       "    | labels             | INTEGER_64 | CATEGORICAL | None    | #num.cat:40 |\n",
+       "    | year_SOFT_QUANTILE | FLOAT_32   | EMBEDDING   | ()      |             |\n",
+       "\n",
+       "\n",
+       "Edge Sets:\n",
+       "  edges: (Source: nodes, Target: nodes)\n",
+       "    (No features)\n",
+       "
\n", + "
\n", + "
Default feature statistics\n", + "
GraphFeatureStatistics:\n",
+       "  Node Sets (1):\n",
+       "    'nodes':\n",
+       "      '#id': count=1213158, min=nan, max=nan\n",
+       "      '#split': count=1213158, min=nan, max=nan, dictionary=(3)['train': 664508, 'test': 348030, 'valid': 200620]\n",
+       "      'feat': count=1213158, min=nan, max=nan\n",
+       "      'labels': count=1213158, min=0.0000, max=39.0000\n",
+       "      'year': count=1213158, min=1971.0000, max=2020.0000, quantiles=(100)[1971.0000, 2005.0000, 2008.0000, ..., 2020.0000, 2020.0000, 2020.0000]\n",
+       "
\n", + "
\n", + "
Default sampling plan\n", + "
Root: nodes\n",
+       "├── edges [width=15] ➔ nodes\n",
+       "│   ├── edges [width=15] ➔ nodes\n",
+       "│   └── edges (reversed) [width=15] ➔ nodes\n",
+       "└── edges (reversed) [width=15] ➔ nodes\n",
+       "    ├── edges [width=15] ➔ nodes\n",
+       "    └── edges (reversed) [width=15] ➔ nodes
\n", + "
\n", + "
Model Structure\n", + "
EmbedGraph(cat-embedding=64)\n",
+       "Dense(128)\n",
+       "Activation(silu)\n",
+       "Norm(layer_norm)\n",
+       "Graph Convolution Block x2:\n",
+       "    X = ...\n",
+       "    MPNN:\n",
+       "      Message:\n",
+       "        Dense(128)\n",
+       "        Activation(silu)\n",
+       "        Dense(128)\n",
+       "      Update:\n",
+       "        Dense(128)\n",
+       "        Activation(silu)\n",
+       "        Dropout(0.1)\n",
+       "        Dense(128)\n",
+       "    Residual(X)\n",
+       "    # Post MPNN\n",
+       "    X = ...\n",
+       "    Norm(rms_norm)\n",
+       "    Dense(512)\n",
+       "    Activation(silu)\n",
+       "    Dense(128)\n",
+       "    Dropout(0.1)\n",
+       "    Residual(X)\n",
+       "Identity\n",
+       "Dense(40) # Classification head
\n", + "Model Weights\n", + "
{'float32': 590632}
\n", + "
Default padding\n", + "
Node Sets:\n",
+       "  nodes: 6387 nodes\n",
+       "\n",
+       "Edge Sets:\n",
+       "  edges: 8507 edges
\n", + "
\n", + "\n", + "\n", + "\n", + "" ], - "metadata": { - "colab": { - "height": 219 - }, - "id": "4_ZBLHwPFKyA", - "executionInfo": { - "status": "ok", - "timestamp": 1779800420139, - "user_tz": -120, - "elapsed": 511, - "user": { - "displayName": "", - "userId": "" - } - }, - "outputId": "c52a051d-8d59-496d-d722-9e3a9aa1a773" - }, - "execution_count": null, - "outputs": [ - { - "output_type": "execute_result", - "data": { - "text/plain": [ - "" - ], - "text/html": [ - "
\n", - "\n", - "\n", - "\n", - "
\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "
\n", - "
\n", - "

Node prediction model: Predict the value of a node feature.

\n", - "
    \n", - "
  • Target nodeset: nodes
  • \n", - "
  • Target column: labels
  • \n", - "
  • Number of label classes: 40
  • \n", - "
\n", - "
\n", - "
\n", - "\n", - "
    \n", - "
  • Number of training seed nodes: 152409
  • \n", - "
  • Number of validation seed nodes: 16934
  • \n", - "
  • Training duration: 2m 39s
  • \n", - "
\n", - "\n", - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - "\n", - "\n", - "
\n", - " \n", - "\n", - "
\n", - "
\n", - "
num_sampling_hops=2\n",
-              "sampling_width=15\n",
-              "num_layers=2\n",
-              "batch_size=32\n",
-              "max_training_time_seconds=None\n",
-              "num_train_steps=10000\n",
-              "random_seed=42\n",
-              "node_embedding_dim=128\n",
-              "learning_rate=0.001\n",
-              "opt_weight_decay=0.0001\n",
-              "dropout=0.1\n",
-              "message_pooling='sum'\n",
-              "architecture=
\n", - "
Raw schema
\n",
-              "Node Sets:\n",
-              "  nodes:\n",
-              "    | Feature   | Format     | Semantic    | Shape   | Num cat. vals   |\n",
-              "    |-----------|------------|-------------|---------|-----------------|\n",
-              "    | #id       | BYTES      | PRIMARY_ID  | None    | None            |\n",
-              "    | #split    | BYTES      | CATEGORICAL | None    | None            |\n",
-              "    | feat      | FLOAT_32   | EMBEDDING   | (128,)  | None            |\n",
-              "    | labels    | INTEGER_64 | CATEGORICAL | None    | 40              |\n",
-              "    | year      | INTEGER_64 | NUMERICAL   | None    | None            |\n",
-              "\n",
-              "\n",
-              "Edge Sets:\n",
-              "  edges: (Source: nodes, Target: nodes)\n",
-              "    (No features)\n",
-              "

\n", - "Normalized schema
\n",
-              "Node Sets:\n",
-              "  nodes:\n",
-              "    | Feature            | Format     | Semantic    | Shape   | Num cat. vals   |\n",
-              "    |--------------------|------------|-------------|---------|-----------------|\n",
-              "    | #split_INDEX       | INTEGER_64 | CATEGORICAL | None    | 4               |\n",
-              "    | feat               | FLOAT_32   | EMBEDDING   | (128,)  | None            |\n",
-              "    | labels             | INTEGER_64 | CATEGORICAL | None    | 40              |\n",
-              "    | year_SOFT_QUANTILE | FLOAT_32   | EMBEDDING   | None    | None            |\n",
-              "\n",
-              "\n",
-              "Edge Sets:\n",
-              "  edges: (Source: nodes, Target: nodes)\n",
-              "    (No features)\n",
-              "

\n", - "
\n", - "
Default feature statistics\n", - "
GraphFeatureStatistics:\n",
-              "  Node Sets (1):\n",
-              "    'nodes':\n",
-              "      '#id': count=1190844, min=nan, max=nan\n",
-              "      '#split': count=1190844, min=nan, max=nan, dictionary=(3)['train': 653408, 'test': 339436, 'valid': 198000]\n",
-              "      'feat': count=1190844, min=nan, max=nan\n",
-              "      'labels': count=1190844, min=0.0000, max=39.0000\n",
-              "      'year': count=1190844, min=1971.0000, max=2020.0000, quantiles=(100)[1971.0000, 2006.0000, 2009.0000, ..., 2020.0000, 2020.0000, 2020.0000]\n",
-              "

\n", - "
\n", - "
Default sampling plan
\n",
-              "Root: nodes\n",
-              "├── edges [width=15] ➔ nodes\n",
-              "│   ├── edges [width=15] ➔ nodes\n",
-              "│   └── edges (reversed) [width=15] ➔ nodes\n",
-              "└── edges (reversed) [width=15] ➔ nodes\n",
-              "    ├── edges [width=15] ➔ nodes\n",
-              "    └── edges (reversed) [width=15] ➔ nodes

\n", - "
\n", - "
Model Structure
EmbedGraph(cat-embedding=64)\n",
-              "Dense(128)\n",
-              "Activation(silu)\n",
-              "Norm(layer_norm)\n",
-              "Graph Convolution Block x2:\n",
-              "    X = ...\n",
-              "    MPNN:\n",
-              "      Message:\n",
-              "        Dense(128)\n",
-              "        Activation(silu)\n",
-              "        Dense(128)\n",
-              "      Update:\n",
-              "        Dense(128)\n",
-              "        Activation(silu)\n",
-              "        Dropout(0.1)\n",
-              "        Dense(128)\n",
-              "    Residual(X)\n",
-              "    # Post MPNN\n",
-              "    X = ...\n",
-              "    Norm(rms_norm)\n",
-              "    Dense(512)\n",
-              "    Activation(silu)\n",
-              "    Dense(128)\n",
-              "    Dropout(0.1)\n",
-              "    Residual(X)\n",
-              "Identity\n",
-              "Dense(40) # Classification head
Model Weights
{'float32': 590632}
\n", - "
Default padding
\n",
-              "Node Sets:\n",
-              "  nodes: 6762 nodes\n",
-              "\n",
-              "Edge Sets:\n",
-              "  edges: 9041 edges

\n", - "
\n", - "\n", - "\n", - "\n", - "
" - ] - }, - "metadata": {}, - "execution_count": 6 - } + "text/plain": [ + "" ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "model.describe()" + ] + }, + { + "cell_type": "markdown", + "id": "0uHBi26kFmbp", + "metadata": {}, + "source": [ + "## Making predictions\n", + "\n", + "To generate predictions, simply call `model.predict`. This method returns\n", + "prediction probabilities as a Numpy array." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "0jn7KRFOFycL", + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-23T18:15:06.407627Z", + "iopub.status.busy": "2026-09-23T18:15:06.407438Z", + "iopub.status.idle": "2026-09-23T18:15:07.213322Z", + "shell.execute_reply": "2026-09-23T18:15:07.212831Z" }, + "executionInfo": { + "elapsed": 807, + "status": "ok", + "timestamp": 1790187307214.2886, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 + }, + "outputId": "96da5091-1ec5-4f91-afcb-1a28507d72d7" + }, + "outputs": [ { - "cell_type": "markdown", - "source": [ - "## Making predictions\n", - "\n", - "To generate predictions, simply call `model.predict`. This method returns\n", - "prediction probabilities as a Numpy array." - ], - "metadata": { - "id": "0uHBi26kFmbp" - } + "name": "stderr", + "output_type": "stream", + "text": [ + "Inference: 0%| | 0/1 [00:00Note: We are evaluating on the original graph here for demonstration,\n", - "but in a real-world scenario, you should always use a separate, unseen graph.*" - ], - "metadata": { - "id": "gr9R09kqGsL5" - } + "executionInfo": { + "elapsed": 4, + "status": "ok", + "timestamp": 1790187307223.4302, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 }, + "outputId": "6d9d1d71-be4b-4bd6-dd5e-5473a55ba345" + }, + "outputs": [ { - "cell_type": "code", - "source": [ - "model.evaluate(graph)" - ], - "metadata": { - "colab": { - "height": 133 - }, - "id": "OazCzM0CGxWF", - "executionInfo": { - "status": "ok", - "timestamp": 1779800428308, - "user_tz": -120, - "elapsed": 6224, - "user": { - "displayName": "", - "userId": "" - } - }, - "outputId": "8379f735-c52d-45b6-f9e6-538ac66317ae" - }, - "execution_count": null, - "outputs": [ - { - "output_type": "stream", - "name": "stdout", - "text": [ - "Evaluating model on 10000 samples\n" - ] - }, - { - "output_type": "stream", - "name": "stderr", - "text": [ - "\rInference: 0%| | 0/625 [00:00Evaluation\n", - "
    \n", - "
  • Accuracy: 0.7317
  • \n", - "
  • Num Examples: 10000
  • \n", - "
" - ] - }, - "metadata": {}, - "execution_count": 10 - } + "data": { + "text/plain": [ + "np.int64(4)" ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "graph.node_sets[\"nodes\"].features[\"labels\"][0]" + ] + }, + { + "cell_type": "markdown", + "id": "gr9R09kqGsL5", + "metadata": {}, + "source": [ + "## Evaluating model\n", + "\n", + "The validation metrics in `model.describe()` already give a good estimate of\n", + "model quality.\n", + "\n", + "We can also evaluate performance on a different graph with `model.evaluate()`:\n", + "\n", + "*Note: We are evaluating on the original graph here for demonstration,\n", + "but in a real-world scenario, you should always use a separate, unseen graph.*" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "OazCzM0CGxWF", + "metadata": { + "colab": { + "height": 667 }, - { - "cell_type": "markdown", - "source": [ - "## Saving model\n", - "\n", - "The model can be saved to disk for future reuse." - ], - "metadata": { - "id": "LqYKiGy1HhZU" - } + "execution": { + "iopub.execute_input": "2026-09-23T18:15:07.224191Z", + "iopub.status.busy": "2026-09-23T18:15:07.224022Z", + "iopub.status.idle": "2026-09-23T18:15:11.642591Z", + "shell.execute_reply": "2026-09-23T18:15:11.642202Z" }, + "executionInfo": { + "elapsed": 4420, + "status": "ok", + "timestamp": 1790187311643.6592, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 + }, + "outputId": "69b402ef-91ea-432a-ddd6-a1078121502f" + }, + "outputs": [ { - "cell_type": "code", - "source": [ - "# Save the model\n", - "model.save(\"/tmp/my_model\")" - ], - "metadata": { - "id": "9a7_oH2RH09a" - }, - "execution_count": null, - "outputs": [] + "name": "stdout", + "output_type": "stream", + "text": [ + "Evaluating model on 10000 samples\n" + ] }, { - "cell_type": "markdown", - "source": [ - "Loading it back is just as easy:" - ], - "metadata": { - "id": "xSqdP3RbH3c2" - } + "name": "stderr", + "output_type": "stream", + "text": [ + "Inference: 100%|██████████| 625/625 [00:04<00:00, 150.57it/s]\n" + ] }, { - "cell_type": "code", - "source": [ - "loaded_model = dgf.learning.load_model(\"/tmp/my_model\")" + "data": { + "text/html": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
Accuracy0.7217
Num Examples10000
AUC0.955949642010081
Per Class Metrics [40]:
    \n", + "
  • Class 0: AUC=0.9915, PR-AUC=0.3693
  • \n", + "
  • Class 1: AUC=0.9814, PR-AUC=0.3654
  • \n", + "
  • Class 2: AUC=0.9872, PR-AUC=0.7006
  • \n", + "
  • ... (35 omitted) ...
  • \n", + "
  • Class 38: AUC=0.9978, PR-AUC=0.8438
  • \n", + "
  • Class 39: AUC=0.9706, PR-AUC=0.4024
  • \n", + "
\n", + "

*Showing plots for the first 20 classes out of 40 total classes.

\n", + "\n", + " \n", + " \n", + " \n", + " \n", + "
\n", + " " ], - "metadata": { - "id": "uBiwqb0IH5iA", - "executionInfo": { - "status": "ok", - "timestamp": 1779800431133, - "user_tz": -120, - "elapsed": 132, - "user": { - "displayName": "", - "userId": "" - } - }, - "outputId": "15eeffcc-acd2-4fd5-eeb6-38a334a0e06a" - }, - "execution_count": null, - "outputs": [ - { - "output_type": "stream", - "name": "stderr", - "text": [ - "/usr/local/google/_blaze_gbm/d2b7567989dadc71cf8c44476b538c3c_buildrabbit/execroot/google3/blaze-out/haswell-opt/bin/third_party/py/dgf/notebook_gpu.runfiles/google3/third_party/py/dataclasses_json/core.py:201: RuntimeWarning: 'NoneType' object value of non-optional type output_shape detected when decoding DictionaryIndexNormalizer.\n", - " warnings.warn(\n", - "/usr/local/google/_blaze_gbm/d2b7567989dadc71cf8c44476b538c3c_buildrabbit/execroot/google3/blaze-out/haswell-opt/bin/third_party/py/dgf/notebook_gpu.runfiles/google3/third_party/py/dataclasses_json/core.py:201: RuntimeWarning: 'NoneType' object value of non-optional type output_shape detected when decoding SoftQuantileNormalizer.\n", - " warnings.warn(\n" - ] - } + "text/plain": [ + "Evaluation(loss=None, accuracy=0.7217, rmse=None, r2=None, num_examples=10000, num_examples_weighted=None, mrr=None, auc=np.float64(0.955949642010081), hit_at={}, user_metrics={}, per_classes=[PerClass(auc_value=0.9914602760787459, pr_auc_value=0.3692870390317155, tp=array([ 0, 0, 0, ..., 27, 27, 27], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 2819, 3713, 9973],\n", + " shape=(10001,), dtype=uint64), tn=array([9973, 9973, 9973, ..., 7154, 6260, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([27, 27, 27, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9814466023520956, pr_auc_value=0.36537767300289975, tp=array([ 0, 0, 0, ..., 38, 38, 38], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 4410, 5330, 9962],\n", + " shape=(10001,), dtype=uint64), tn=array([9962, 9962, 9962, ..., 5552, 4632, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([38, 38, 38, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9871858573913233, pr_auc_value=0.700579129846338, tp=array([ 0, 0, 0, ..., 261, 261, 261], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 3770, 4468, 9739],\n", + " shape=(10001,), dtype=uint64), tn=array([9739, 9739, 9739, ..., 5969, 5271, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([261, 261, 261, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9649966562818498, pr_auc_value=0.39449784342813365, tp=array([ 0, 0, 0, ..., 126, 126, 126], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 5576, 6482, 9874],\n", + " shape=(10001,), dtype=uint64), tn=array([9874, 9874, 9874, ..., 4298, 3392, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([126, 126, 126, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9858751498818806, pr_auc_value=0.7791233762499975, tp=array([ 0, 0, 0, ..., 339, 339, 339], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 6243, 7125, 9661],\n", + " shape=(10001,), dtype=uint64), tn=array([9661, 9661, 9661, ..., 3418, 2536, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([339, 339, 339, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.975043628951711, pr_auc_value=0.6354049652033217, tp=array([ 0, 0, 0, ..., 304, 304, 304], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 6566, 7297, 9696],\n", + " shape=(10001,), dtype=uint64), tn=array([9696, 9696, 9696, ..., 3130, 2399, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([304, 304, 304, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9683397539785861, pr_auc_value=0.5047750327707347, tp=array([ 0, 0, 0, ..., 95, 95, 95], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 5662, 6449, 9905],\n", + " shape=(10001,), dtype=uint64), tn=array([9905, 9905, 9905, ..., 4243, 3456, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([95, 95, 95, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9718026562290913, pr_auc_value=0.29724911378956315, tp=array([ 0, 0, 0, ..., 36, 36, 36], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 4864, 5925, 9964],\n", + " shape=(10001,), dtype=uint64), tn=array([9964, 9964, 9964, ..., 5100, 4039, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([36, 36, 36, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9772571388427693, pr_auc_value=0.7215077896890872, tp=array([ 0, 0, 0, ..., 387, 388, 388], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 5544, 6299, 9612],\n", + " shape=(10001,), dtype=uint64), tn=array([9612, 9612, 9612, ..., 4068, 3313, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([388, 388, 388, ..., 1, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9829518296088064, pr_auc_value=0.5868706192367863, tp=array([ 0, 0, 0, ..., 172, 172, 172], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 3702, 4390, 9828],\n", + " shape=(10001,), dtype=uint64), tn=array([9828, 9828, 9828, ..., 6126, 5438, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([172, 172, 172, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.954258270226801, pr_auc_value=0.608773116234642, tp=array([ 0, 0, 0, ..., 438, 438, 438], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 7709, 8108, 9562],\n", + " shape=(10001,), dtype=uint64), tn=array([9562, 9562, 9562, ..., 1853, 1454, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([438, 438, 438, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.975390176172992, pr_auc_value=0.30330834074334084, tp=array([ 0, 0, 0, ..., 51, 51, 51], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 4268, 5138, 9949],\n", + " shape=(10001,), dtype=uint64), tn=array([9949, 9949, 9949, ..., 5681, 4811, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([51, 51, 51, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.0, pr_auc_value=0.0, tp=array([0, 0, 0, ..., 0, 0, 0], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 1055, 1603, 10000],\n", + " shape=(10001,), dtype=uint64), tn=array([10000, 10000, 10000, ..., 8945, 8397, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([0, 0, 0, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9664980893222919, pr_auc_value=0.47324497573711305, tp=array([ 0, 0, 0, ..., 134, 134, 134], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 6694, 7501, 9866],\n", + " shape=(10001,), dtype=uint64), tn=array([9866, 9866, 9866, ..., 3172, 2365, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([134, 134, 134, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9777980427571389, pr_auc_value=0.5894774352232163, tp=array([ 0, 0, 0, ..., 33, 33, 34], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 1877, 2418, 9966],\n", + " shape=(10001,), dtype=uint64), tn=array([9966, 9966, 9966, ..., 8089, 7548, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([34, 34, 34, ..., 1, 1, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9952194394934436, pr_auc_value=0.49975555441051667, tp=array([ 0, 0, 0, ..., 23, 23, 23], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 2553, 3409, 9977],\n", + " shape=(10001,), dtype=uint64), tn=array([9977, 9977, 9977, ..., 7424, 6568, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([23, 23, 23, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9875324332130135, pr_auc_value=0.9387985502871069, tp=array([ 0, 0, 0, ..., 1636, 1636, 1636],\n", + " shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 5380, 6000, 8364],\n", + " shape=(10001,), dtype=uint64), tn=array([8364, 8364, 8364, ..., 2984, 2364, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([1636, 1636, 1636, ..., 0, 0, 0],\n", + " shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9650495133500949, pr_auc_value=0.23664346558504384, tp=array([ 0, 0, 0, ..., 26, 26, 26], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 3997, 4689, 9974],\n", + " shape=(10001,), dtype=uint64), tn=array([9974, 9974, 9974, ..., 5977, 5285, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([26, 26, 26, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9910941162717726, pr_auc_value=0.7298974444404654, tp=array([ 0, 0, 0, ..., 49, 49, 49], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 3185, 4095, 9951],\n", + " shape=(10001,), dtype=uint64), tn=array([9951, 9951, 9951, ..., 6766, 5856, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([49, 49, 49, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9838133851486208, pr_auc_value=0.7277085870513842, tp=array([ 0, 0, 0, ..., 189, 189, 189], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 5386, 6186, 9811],\n", + " shape=(10001,), dtype=uint64), tn=array([9811, 9811, 9811, ..., 4425, 3625, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([189, 189, 189, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9895975498417119, pr_auc_value=0.6385623349449374, tp=array([ 0, 0, 0, ..., 97, 97, 97], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 3701, 4759, 9903],\n", + " shape=(10001,), dtype=uint64), tn=array([9903, 9903, 9903, ..., 6202, 5144, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([97, 97, 97, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9493395190707229, pr_auc_value=0.1032856798639163, tp=array([ 0, 0, 0, ..., 34, 34, 34], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 5748, 6812, 9966],\n", + " shape=(10001,), dtype=uint64), tn=array([9966, 9966, 9966, ..., 4218, 3154, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([34, 34, 34, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9883844871276661, pr_auc_value=0.5549029199962274, tp=array([ 0, 0, 0, ..., 104, 104, 104], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 3490, 4241, 9896],\n", + " shape=(10001,), dtype=uint64), tn=array([9896, 9896, 9896, ..., 6406, 5655, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([104, 104, 104, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9920829146997906, pr_auc_value=0.7727236118529364, tp=array([ 0, 0, 0, ..., 163, 163, 163], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 4065, 4847, 9837],\n", + " shape=(10001,), dtype=uint64), tn=array([9837, 9837, 9837, ..., 5772, 4990, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([163, 163, 163, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9558966207446448, pr_auc_value=0.7975607000039399, tp=array([ 0, 0, 0, ..., 1318, 1318, 1318],\n", + " shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 7354, 7760, 8682],\n", + " shape=(10001,), dtype=uint64), tn=array([8682, 8682, 8682, ..., 1328, 922, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([1318, 1318, 1318, ..., 0, 0, 0],\n", + " shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9952091617203558, pr_auc_value=0.8447234875627669, tp=array([ 0, 0, 0, ..., 84, 84, 84], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 2831, 3722, 9916],\n", + " shape=(10001,), dtype=uint64), tn=array([9916, 9916, 9916, ..., 7085, 6194, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([84, 84, 84, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.98678096172503, pr_auc_value=0.7214965012060317, tp=array([ 0, 0, 0, ..., 291, 291, 291], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 5504, 6357, 9709],\n", + " shape=(10001,), dtype=uint64), tn=array([9709, 9709, 9709, ..., 4205, 3352, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([291, 291, 291, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9889363484932939, pr_auc_value=0.8112220208166616, tp=array([ 0, 0, 0, ..., 317, 317, 317], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 5414, 6223, 9683],\n", + " shape=(10001,), dtype=uint64), tn=array([9683, 9683, 9683, ..., 4269, 3460, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([317, 317, 317, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9931568545046684, pr_auc_value=0.9588233166078026, tp=array([ 0, 2, 6, ..., 1246, 1246, 1246],\n", + " shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 5370, 6139, 8754],\n", + " shape=(10001,), dtype=uint64), tn=array([8754, 8754, 8754, ..., 3384, 2615, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([1246, 1244, 1240, ..., 0, 0, 0],\n", + " shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9676181168136861, pr_auc_value=0.13242239684086785, tp=array([ 0, 0, 0, ..., 24, 24, 24], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 3861, 4693, 9976],\n", + " shape=(10001,), dtype=uint64), tn=array([9976, 9976, 9976, ..., 6115, 5283, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([24, 24, 24, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.992743110980231, pr_auc_value=0.9182490237859845, tp=array([ 0, 0, 0, ..., 698, 698, 698], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 4655, 5441, 9302],\n", + " shape=(10001,), dtype=uint64), tn=array([9302, 9302, 9302, ..., 4647, 3861, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([698, 698, 698, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9743857184657653, pr_auc_value=0.5603828149720295, tp=array([ 0, 0, 0, ..., 164, 164, 164], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 5300, 6071, 9836],\n", + " shape=(10001,), dtype=uint64), tn=array([9836, 9836, 9836, ..., 4536, 3765, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([164, 164, 164, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9901654964894684, pr_auc_value=0.44146556615223287, tp=array([ 0, 0, 0, ..., 30, 30, 30], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 2447, 3275, 9970],\n", + " shape=(10001,), dtype=uint64), tn=array([9970, 9970, 9970, ..., 7523, 6695, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([30, 30, 30, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9902287440656021, pr_auc_value=0.645236020541875, tp=array([ 0, 0, 0, ..., 70, 70, 70], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 2708, 3463, 9930],\n", + " shape=(10001,), dtype=uint64), tn=array([9930, 9930, 9930, ..., 7222, 6467, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([70, 70, 70, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9779226506235249, pr_auc_value=0.7210452387696104, tp=array([ 0, 0, 0, ..., 443, 443, 443], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 6074, 6897, 9557],\n", + " shape=(10001,), dtype=uint64), tn=array([9557, 9557, 9557, ..., 3483, 2660, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([443, 443, 443, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9845245245245245, pr_auc_value=0.09679061004629953, tp=array([ 0, 0, 0, ..., 10, 10, 10], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 1520, 1913, 9990],\n", + " shape=(10001,), dtype=uint64), tn=array([9990, 9990, 9990, ..., 8470, 8077, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([10, 10, 10, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9889562969172915, pr_auc_value=0.7583316530334493, tp=array([ 0, 0, 0, ..., 197, 197, 197], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 5052, 5761, 9803],\n", + " shape=(10001,), dtype=uint64), tn=array([9803, 9803, 9803, ..., 4751, 4042, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([197, 197, 197, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9806852186714887, pr_auc_value=0.5981033790522027, tp=array([ 0, 0, 0, ..., 133, 133, 133], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 5341, 6171, 9867],\n", + " shape=(10001,), dtype=uint64), tn=array([9867, 9867, 9867, ..., 4526, 3696, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([133, 133, 133, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9977597704508621, pr_auc_value=0.843837191356611, tp=array([ 0, 4, 7, ..., 95, 95, 95], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 2609, 3222, 9905],\n", + " shape=(10001,), dtype=uint64), tn=array([9905, 9905, 9905, ..., 7296, 6683, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([95, 91, 88, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9705985989198843, pr_auc_value=0.40235990469824245, tp=array([ 0, 0, 0, ..., 116, 116, 116], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 4525, 5350, 9884],\n", + " shape=(10001,), dtype=uint64), tn=array([9884, 9884, 9884, ..., 5359, 4534, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([116, 116, 116, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,)))])" ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "model.evaluate(graph)" + ] + }, + { + "cell_type": "markdown", + "id": "LqYKiGy1HhZU", + "metadata": {}, + "source": [ + "## Saving model\n", + "\n", + "The model can be saved to disk for future reuse." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "9a7_oH2RH09a", + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-23T18:15:11.644537Z", + "iopub.status.busy": "2026-09-23T18:15:11.644324Z", + "iopub.status.idle": "2026-09-23T18:15:17.265828Z", + "shell.execute_reply": "2026-09-23T18:15:17.265415Z" + }, + "executionInfo": { + "elapsed": 5623, + "status": "ok", + "timestamp": 1790187317266.9897, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 + } + }, + "outputs": [], + "source": [ + "# Save the model\n", + "model.save(\"/tmp/my_model\")" + ] + }, + { + "cell_type": "markdown", + "id": "xSqdP3RbH3c2", + "metadata": {}, + "source": [ + "Loading it back is just as easy:" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "uBiwqb0IH5iA", + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-23T18:15:17.267863Z", + "iopub.status.busy": "2026-09-23T18:15:17.267681Z", + "iopub.status.idle": "2026-09-23T18:15:18.268423Z", + "shell.execute_reply": "2026-09-23T18:15:18.267989Z" }, + "executionInfo": { + "elapsed": 1002, + "status": "ok", + "timestamp": 1790187318269.6746, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 + } + }, + "outputs": [], + "source": [ + "loaded_model = dgf.learning.load_model(\"/tmp/my_model\")" + ] + }, + { + "cell_type": "markdown", + "id": "BDO4J6wFICmG", + "metadata": {}, + "source": [ + "## Conclusion\n", + "\n", + "Congratulations 🥳. You now know the basics of the GF Simple API.\n", + "\n", + "For more details, explore the tutorials and examples at go/graph-flow.\n", + "\n", + "Also, make sure to join the 💬 [chat](http://go/gnn-user-chat) for questions,\n", + "issues or feature requests." + ] + } + ], + "metadata": { + "colab": { + "last_runtime": { + "build_target": "", + "kind": "local" + }, + "provenance": [ { - "cell_type": "markdown", - "source": [ - "## Conclusion\n", - "\n", - "Congratulations 🥳. You now know the basics of the GF Simple API.\n", - "\n", - "For more details, explore the tutorials and examples at go/graph-flow.\n", - "\n", - "Also, make sure to join the 💬 [chat](http://go/gnn-user-chat) for questions,\n", - "issues or feature requests." - ], - "metadata": { - "id": "BDO4J6wFICmG" + "file_id": "1hMr9gK0hhKcCtIs1Pro3_ksOnYcdAL2P", + "timestamp": 1771583153488 + } + ], + "views": { + "output_only": { + "cells": [ + { + "id": "woDLT7o2leXI" + }, + { + "id": "e_tVcMnEC7iO" + }, + { + "id": "GrVvb1oCLi5O" + }, + { + "id": "A5BuRrYALiK_" + }, + { + "id": "9C6k7qhjHG4W" + }, + { + "id": "d1hs5OKmGh7l" + }, + { + "id": "6V95B8oxGmBU" + }, + { + "id": "RKXovz93qQAB" + }, + { + "id": "z3Tx-P_MlPuu" + }, + { + "id": "DXhSqZWSq6aV" + }, + { + "id": "fkSx2Pq0tXH1" + }, + { + "id": "iy9W1Cu2tmoc" + }, + { + "id": "Dz27FflNt5gS" + }, + { + "id": "Az1KUBTNt-LF" + }, + { + "id": "5zm6ImfnuCtC" + }, + { + "id": "Itoxiu6Lu3-F" + }, + { + "id": "LUytl4VxB0Aj" + }, + { + "id": "VWEfGfMBB8O7" + }, + { + "id": "jB8RssSaFGCD" + }, + { + "id": "4_ZBLHwPFKyA" + }, + { + "id": "0uHBi26kFmbp" + }, + { + "id": "0jn7KRFOFycL" + }, + { + "id": "QoG0Seg1GOVp" + }, + { + "id": "VnGlj3V0GdKt" + }, + { + "id": "hRnWRP1kGADM" + }, + { + "id": "OuWE3bx9GG1n" + }, + { + "id": "gr9R09kqGsL5" + }, + { + "id": "OazCzM0CGxWF" + }, + { + "id": "LqYKiGy1HhZU" + }, + { + "id": "9a7_oH2RH09a" + }, + { + "id": "xSqdP3RbH3c2" + }, + { + "id": "uBiwqb0IH5iA" + }, + { + "id": "BDO4J6wFICmG" } + ], + "hide_code": true } - ] + } + }, + "kernelspec": { + "display_name": "Python 3", + "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.14.5" + } + }, + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/doc/docs/tutorial/in_memory_graph.ipynb b/doc/docs/tutorial/in_memory_graph.ipynb index 9f77f7b..5be19ee 100644 --- a/doc/docs/tutorial/in_memory_graph.ipynb +++ b/doc/docs/tutorial/in_memory_graph.ipynb @@ -1,929 +1,1658 @@ { - "nbformat": 4, - "nbformat_minor": 0, - "metadata": { - "colab": { - "provenance": [ - { - "file_id": "1hMr9gK0hhKcCtIs1Pro3_ksOnYcdAL2P", - "timestamp": 1771583153488 - } - ], - "last_runtime": { - "build_target": "", - "kind": "local" - }, - "toc_visible": true + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "woDLT7o2leXI" + }, + "source": [ + "## In-memory graph\n", + "\n", + "[![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/google/distributed_graph_flow/blob/main/doc/docs/tutorial/in_memory_graph.ipynb)\n", + "\n", + "This tutorial explains how GF stores graphs in memory and shows you how to\n", + "inspect, create, modify, and transform them." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "vsWpEbNGXFdO" + }, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "QdunX1vTlM3G" + }, + "source": [ + "## Installing GF\n", + "\n", + "Make sure your machine has a GPU or TPU, otherwise training is going to take forever.\n", + "If you are using Google Colab, you can get one for free. Just go to Edit > Notebook settings and select your hardware accelerator as TPU or GPU." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-23T18:16:11.015240Z", + "iopub.status.busy": "2026-09-23T18:16:11.015056Z", + "iopub.status.idle": "2026-09-23T18:16:11.262213Z", + "shell.execute_reply": "2026-09-23T18:16:11.261841Z" }, - "kernelspec": { - "name": "python3", - "display_name": "Python 3" + "executionInfo": { + "elapsed": 291, + "status": "ok", + "timestamp": 1790187371305.724, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 }, - "language_info": { - "name": "python" - } + "id": "Jzjqg7FWV6dq" + }, + "outputs": [], + "source": [ + "# Install DGF (Distributed Graph Flow) and OGB (for the toy dataset).\n", + "!pip install dgf ogb -U" + ] }, - "cells": [ - { - "cell_type": "markdown", - "source": [ - "## In-memory graph\n", - "\n", - "[![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/google/distributed_graph_flow/blob/main/doc/docs/tutorial/in_memory_graph.ipynb)\n", - "\n", - "This tutorial explains how GF stores graphs in memory and shows you how to\n", - "inspect, create, modify, and transform them." - ], - "metadata": { - "id": "woDLT7o2leXI" - } + { + "cell_type": "markdown", + "metadata": { + "id": "RKXovz93qQAB" + }, + "source": [ + "## Importing libraries" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-23T18:16:11.307126Z", + "iopub.status.busy": "2026-09-23T18:16:11.306914Z", + "iopub.status.idle": "2026-09-23T18:16:15.697334Z", + "shell.execute_reply": "2026-09-23T18:16:15.696897Z" }, - { - "cell_type": "markdown", - "source": [ - "## Installing GF\n", - "\n", - "Make sure your machine has a GPU or TPU, otherwise training is going to take forever.\n", - "If you are using Google Colab, you can get one for free. Just go to Edit > Notebook settings and select your hardware accelerator as TPU or GPU." - ], - "metadata": { - "id": "bPyOiwA6W2XQ" - } + "executionInfo": { + "elapsed": 4392, + "status": "ok", + "timestamp": 1790187375698.7834, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 }, - { - "cell_type": "code", - "source": [ - "# Install DGF (Distributed Graph Flow) and OGB (for the toy dataset).\n", - "!pip install dgf ogb -U" - ], - "metadata": { - "id": "cmstNDHcW6vg" - }, - "execution_count": null, - "outputs": [] + "id": "z3Tx-P_MlPuu" + }, + "outputs": [], + "source": [ + "import copy\n", + "import dgf # Import Graph Flow\n", + "import numpy as np" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "r4YYlN8uLDib" + }, + "source": [ + "## Creating a graph in-memory\n", + "\n", + "A graph is always attached to a schema. Let's first create our schema:\n", + "\n", + "Our graph will have two nodesets, \"nodeset_1\" and \"nodeset_2\", and two\n", + "edgesets. The edgeset \"edgeset_1\" connects nodes within \"nodeset_1\" (a self-loop\n", + "edgeset), and \"edgeset_2\" connects nodes from \"nodeset_1\" to \"nodeset_2\".\n", + "Additionally, the \"nodeset_1\" nodeset will have two features, \"f1\" and \"f2\".\n", + "\n", + "**Note:**\n", + "\n", + "- The schema defines the relation between the nodesets/edgesets as well as the\n", + " features (i.e., extra information attached to nodes and edges).\n", + "- Plotting the schema (done below) will make this clearer :)." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "colab": { + "height": 234 }, - { - "cell_type": "markdown", - "source": [ - "## Importing libraries" - ], - "metadata": { - "id": "RKXovz93qQAB" - } + "execution": { + "iopub.execute_input": "2026-09-23T18:16:15.700197Z", + "iopub.status.busy": "2026-09-23T18:16:15.699702Z", + "iopub.status.idle": "2026-09-23T18:16:15.750466Z", + "shell.execute_reply": "2026-09-23T18:16:15.749894Z" }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "z3Tx-P_MlPuu" - }, - "outputs": [], - "source": [ - "import os\n", - "\n", - "os.environ[\"TF_USE_LEGACY_KERAS\"] = \"1\"\n", - "\n", - "import copy\n", - "import dgf # Import Graph Flow\n", - "import numpy as np" - ] + "executionInfo": { + "elapsed": 52, + "status": "ok", + "timestamp": 1790187375751.7278, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 }, + "id": "lh4TDnbCLSZf", + "outputId": "b02b671b-405c-4319-cee9-0f62c587731f" + }, + "outputs": [ { - "cell_type": "markdown", - "source": [ - "## Creating a graph in-memory\n", - "\n", - "A graph is always attached to a schema. Let's first create our schema:\n", - "\n", - "Our graph will have two nodesets, \"nodeset_1\" and \"nodeset_2\", and two\n", - "edgesets. The edgeset \"edgeset_1\" connects nodes within \"nodeset_1\" (a self-loop\n", - "edgeset), and \"edgeset_2\" connects nodes from \"nodeset_1\" to \"nodeset_2\".\n", - "Additionally, the \"nodeset_1\" nodeset will have two features, \"f1\" and \"f2\".\n", - "\n", - "**Note:**\n", - "\n", - "- The schema defines the relation between the nodesets/edgesets as well as the\n", - " features (i.e., extra information attached to nodes and edges).\n", - "- Plotting the schema (done below) will make this clearer :)." + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "nodeset_1\n", + "\n", + "nodeset_1\n", + "#id\n", + "f1\n", + "f2\n", + "\n", + "\n", + "\n", + "nodeset_1->nodeset_1\n", + "\n", + "\n", + "edgeset_1\n", + "\n", + "\n", + "\n", + "nodeset_2\n", + "\n", + "nodeset_2\n", + "#id\n", + "\n", + "\n", + "\n", + "nodeset_1->nodeset_2\n", + "\n", + "\n", + "edgeset_2\n", + "\n", + "\n", + "\n" ], - "metadata": { - "id": "r4YYlN8uLDib" - } - }, - { - "cell_type": "code", - "source": [ - "schema = dgf.data.GraphSchema(\n", - " node_sets={\n", - " \"nodeset_1\": dgf.data.NodeSchema(\n", - " features={\n", - " \"#id\": dgf.data.FeatureSchema(\n", - " format=dgf.data.FeatureFormat.BYTES,\n", - " semantic=dgf.data.FeatureSemantic.PRIMARY_ID,\n", - " ),\n", - " \"f1\": dgf.data.FeatureSchema(\n", - " format=dgf.data.FeatureFormat.BYTES,\n", - " semantic=dgf.data.FeatureSemantic.CATEGORICAL,\n", - " ),\n", - " \"f2\": dgf.data.FeatureSchema(\n", - " format=dgf.data.FeatureFormat.FLOAT_32,\n", - " semantic=dgf.data.FeatureSemantic.NUMERICAL,\n", - " ),\n", - " }\n", - " ),\n", - " \"nodeset_2\": dgf.data.NodeSchema(\n", - " features={\n", - " \"#id\": dgf.data.FeatureSchema(\n", - " format=dgf.data.FeatureFormat.BYTES,\n", - " semantic=dgf.data.FeatureSemantic.PRIMARY_ID,\n", - " )\n", - " }\n", - " ),\n", - " },\n", - " edge_sets={\n", - " \"edgeset_1\": dgf.data.EdgeSchema(\n", - " source=\"nodeset_1\", target=\"nodeset_1\"\n", - " ),\n", - " \"edgeset_2\": dgf.data.EdgeSchema(\n", - " source=\"nodeset_1\", target=\"nodeset_2\"\n", - " ),\n", - " },\n", - ")\n", - "\n", - "# Plot the schema\n", - "dgf.plot.plot_schema(schema)" - ], - "metadata": { - "colab": { - "height": 234 - }, - "id": "lh4TDnbCLSZf", - "executionInfo": { - "status": "ok", - "timestamp": 1777976575492, - "user_tz": -120, - "elapsed": 58, - "user": { - "displayName": "", - "userId": "" - } - }, - "outputId": "b02b671b-405c-4319-cee9-0f62c587731f" - }, - "execution_count": null, - "outputs": [ - { - "output_type": "execute_result", - "data": { - "image/svg+xml": "\n\n\n\n\n\n\n\n\nnodeset_1\n\nnodeset_1\n#id\nf1\nf2\n\n\n\nnodeset_1->nodeset_1\n\n\nedgeset_1\n\n\n\nnodeset_2\n\nnodeset_2\n#id\n\n\n\nnodeset_1->nodeset_2\n\n\nedgeset_2\n\n\n\n", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "execution_count": 2 - } + "text/plain": [ + "" ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "schema = dgf.data.GraphSchema(\n", + " node_sets={\n", + " \"nodeset_1\": dgf.data.NodeSchema(\n", + " features={\n", + " \"#id\": dgf.data.FeatureSchema(\n", + " format=dgf.data.FeatureFormat.BYTES,\n", + " semantic=dgf.data.FeatureSemantic.PRIMARY_ID,\n", + " ),\n", + " \"f1\": dgf.data.FeatureSchema(\n", + " format=dgf.data.FeatureFormat.BYTES,\n", + " semantic=dgf.data.FeatureSemantic.CATEGORICAL,\n", + " ),\n", + " \"f2\": dgf.data.FeatureSchema(\n", + " format=dgf.data.FeatureFormat.FLOAT_32,\n", + " semantic=dgf.data.FeatureSemantic.NUMERICAL,\n", + " ),\n", + " }\n", + " ),\n", + " \"nodeset_2\": dgf.data.NodeSchema(\n", + " features={\n", + " \"#id\": dgf.data.FeatureSchema(\n", + " format=dgf.data.FeatureFormat.BYTES,\n", + " semantic=dgf.data.FeatureSemantic.PRIMARY_ID,\n", + " )\n", + " }\n", + " ),\n", + " },\n", + " edge_sets={\n", + " \"edgeset_1\": dgf.data.EdgeSchema(\n", + " source=\"nodeset_1\", target=\"nodeset_1\"\n", + " ),\n", + " \"edgeset_2\": dgf.data.EdgeSchema(\n", + " source=\"nodeset_1\", target=\"nodeset_2\"\n", + " ),\n", + " },\n", + ")\n", + "\n", + "# Plot the schema\n", + "dgf.plot.plot_schema(schema)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "N0KZpip-LVIn" + }, + "source": [ + "**Remark:**\n", + "\n", + "- The schema is a pure dataclass object without any methods.\n", + "- The `format` of a feature specifies its underlying data type (e.g., bytes,\n", + " float32).\n", + "- The `semantic` of a feature specifies how the feature should be interpreted\n", + " (e.g., as numerical, categorical, or an embedding). Specifying the semantic\n", + " is optional when creating a schema, but it is required by certain downstream\n", + " tools. When you can, define the semantics.\n", + "- Primary keys are optional for in-memory processing, but required for saving\n", + " / restoring the graph from / to disk.\n", + "- There are no reserved feature names. We used `#id` but we could also have\n", + " called our primary key `id` or `my_primary_key`.\n", + "\n", + "For complex schemas, printing it in text is a good alternative to plotting:" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-23T18:16:15.752903Z", + "iopub.status.busy": "2026-09-23T18:16:15.752537Z", + "iopub.status.idle": "2026-09-23T18:16:15.760059Z", + "shell.execute_reply": "2026-09-23T18:16:15.759558Z" }, - { - "cell_type": "markdown", - "source": [ - "**Remark:**\n", - "\n", - "- The schema is a pure dataclass object without any methods.\n", - "- The `format` of a feature specifies its underlying data type (e.g., bytes,\n", - " float32).\n", - "- The `semantic` of a feature specifies how the feature should be interpreted\n", - " (e.g., as numerical, categorical, or an embedding). Specifying the semantic\n", - " is optional when creating a schema, but it is required by certain downstream\n", - " tools. When you can, define the semantics.\n", - "- Primary keys are optional for in-memory processing, but required for saving\n", - " / restoring the graph from / to disk.\n", - "- There are no reserved feature names. We used `#id` but we could also have\n", - " called our primary key `id` or `my_primary_key`.\n", - "\n", - "For complex schemas, printing it in text is a good alternative to plotting:" - ], - "metadata": { - "id": "N0KZpip-LVIn" - } + "executionInfo": { + "elapsed": 9, + "status": "ok", + "timestamp": 1790187375761.2917, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 }, + "id": "6b0ih-HtnTIy", + "outputId": "f6eb4447-9eb2-4eb9-c0ad-91f521de7901" + }, + "outputs": [ { - "cell_type": "code", - "source": [ - "dgf.print.schema(schema)" - ], - "metadata": { - "id": "6b0ih-HtnTIy", - "executionInfo": { - "status": "ok", - "timestamp": 1777976575633, - "user_tz": -120, - "elapsed": 27, - "user": { - "displayName": "", - "userId": "" - } - }, - "outputId": "f6eb4447-9eb2-4eb9-c0ad-91f521de7901" - }, - "execution_count": null, - "outputs": [ - { - "output_type": "stream", - "name": "stdout", - "text": [ - "Graph Schema:\n", - "\n", - "Node Sets:\n", - " nodeset_1:\n", - " | Feature | Format | Semantic | Shape | Num cat. vals |\n", - " |-----------|----------|-------------|---------|-----------------|\n", - " | #id | BYTES | PRIMARY_ID | None | None |\n", - " | f1 | BYTES | CATEGORICAL | None | None |\n", - " | f2 | FLOAT_32 | NUMERICAL | None | None |\n", - "\n", - " nodeset_2:\n", - " | Feature | Format | Semantic | Shape | Num cat. vals |\n", - " |-----------|----------|------------|---------|-----------------|\n", - " | #id | BYTES | PRIMARY_ID | None | None |\n", - "\n", - "\n", - "Edge Sets:\n", - " edgeset_1: (Source: nodeset_1, Target: nodeset_1)\n", - " (No features)\n", - "\n", - " edgeset_2: (Source: nodeset_1, Target: nodeset_2)\n", - " (No features)\n", - "\n" - ] - } - ] + "name": "stdout", + "output_type": "stream", + "text": [ + "Graph Schema:\n", + "\n", + "Node Sets:\n", + " nodeset_1:\n", + " | Feature | Format | Semantic | Shape | Detail |\n", + " |-----------|----------|-------------|---------|----------|\n", + " | #id | BYTES | PRIMARY_ID | None | |\n", + " | f1 | BYTES | CATEGORICAL | None | |\n", + " | f2 | FLOAT_32 | NUMERICAL | None | |\n", + "\n", + " nodeset_2:\n", + " | Feature | Format | Semantic | Shape | Detail |\n", + " |-----------|----------|------------|---------|----------|\n", + " | #id | BYTES | PRIMARY_ID | None | |\n", + "\n", + "\n", + "Edge Sets:\n", + " edgeset_1: (Source: nodeset_1, Target: nodeset_1)\n", + " (No features)\n", + "\n", + " edgeset_2: (Source: nodeset_1, Target: nodeset_2)\n", + " (No features)\n", + "\n" + ] + } + ], + "source": [ + "dgf.print.schema(schema)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "ykRurJ1dnIYy" + }, + "source": [ + "Let's create the graph data." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "colab": { + "height": 240 }, - { - "cell_type": "markdown", - "source": [ - "Let's create the graph data." - ], - "metadata": { - "id": "ykRurJ1dnIYy" - } + "execution": { + "iopub.execute_input": "2026-09-23T18:16:15.762426Z", + "iopub.status.busy": "2026-09-23T18:16:15.762097Z", + "iopub.status.idle": "2026-09-23T18:16:15.808543Z", + "shell.execute_reply": "2026-09-23T18:16:15.807954Z" + }, + "executionInfo": { + "elapsed": 48, + "status": "ok", + "timestamp": 1790187375809.84, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 }, + "id": "kUWXGckKL-rx", + "outputId": "44733275-f940-44df-b06d-2704c935ef79" + }, + "outputs": [ { - "cell_type": "code", - "source": [ - "graph = dgf.data.InMemoryGraph(\n", - " node_sets={\n", - " \"nodeset_1\": dgf.data.InMemoryNodeSet(\n", - " num_nodes=3,\n", - " features={\n", - " \"#id\": np.array([b\"a\", b\"b\", b\"c\"]),\n", - " \"f1\": np.array([b\"X\", b\"Y\", b\"Y\"]),\n", - " \"f2\": np.array([0.1, 0.5, 0.2], dtype=np.float32),\n", - " },\n", - " ),\n", - " \"nodeset_2\": dgf.data.InMemoryNodeSet(\n", - " num_nodes=2,\n", - " features={\n", - " \"#id\": np.array([b\"u\", b\"v\"]),\n", - " },\n", - " ),\n", - " },\n", - " edge_sets={\n", - " \"edgeset_1\": dgf.data.InMemoryEdgeSet(\n", - " adjacency=np.array([\n", - " [0, 0, 1],\n", - " [1, 1, 2],\n", - " ])\n", - " ),\n", - " \"edgeset_2\": dgf.data.InMemoryEdgeSet(\n", - " adjacency=np.array([\n", - " [0, 1],\n", - " [0, 1],\n", - " ])\n", - " ),\n", - " },\n", - ")\n", - "\n", - "# Plot the graph\n", - "dgf.plot.plot_graph(graph, schema)" + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "nodeset_1_0\n", + "\n", + "nodeset_1_0\n", + "#id: b'a'\n", + "f1: b'X'\n", + "f2: 0.1\n", + "\n", + "\n", + "\n", + "nodeset_1_1\n", + "\n", + "nodeset_1_1\n", + "#id: b'b'\n", + "f1: b'Y'\n", + "f2: 0.5\n", + "\n", + "\n", + "\n", + "nodeset_1_0->nodeset_1_1\n", + "\n", + "\n", + "edgeset_1\n", + "\n", + "\n", + "\n", + "nodeset_1_0->nodeset_1_1\n", + "\n", + "\n", + "edgeset_1\n", + "\n", + "\n", + "\n", + "nodeset_2_0\n", + "\n", + "nodeset_2_0\n", + "#id: b'u'\n", + "\n", + "\n", + "\n", + "nodeset_1_0->nodeset_2_0\n", + "\n", + "\n", + "edgeset_2\n", + "\n", + "\n", + "\n", + "nodeset_1_2\n", + "\n", + "nodeset_1_2\n", + "#id: b'c'\n", + "f1: b'Y'\n", + "f2: 0.2\n", + "\n", + "\n", + "\n", + "nodeset_1_1->nodeset_1_2\n", + "\n", + "\n", + "edgeset_1\n", + "\n", + "\n", + "\n", + "nodeset_2_1\n", + "\n", + "nodeset_2_1\n", + "#id: b'v'\n", + "\n", + "\n", + "\n", + "nodeset_1_1->nodeset_2_1\n", + "\n", + "\n", + "edgeset_2\n", + "\n", + "\n", + "\n" ], - "metadata": { - "colab": { - "height": 240 - }, - "id": "kUWXGckKL-rx", - "executionInfo": { - "status": "ok", - "timestamp": 1777976575766, - "user_tz": -120, - "elapsed": 62, - "user": { - "displayName": "", - "userId": "" - } - }, - "outputId": "44733275-f940-44df-b06d-2704c935ef79" - }, - "execution_count": null, - "outputs": [ - { - "output_type": "execute_result", - "data": { - "image/svg+xml": "\n\n\n\n\n\n\n\n\nnodeset_1_0\n\nnodeset_1_0\n#id: b'a'\nf1: b'X'\nf2: 0.1\n\n\n\nnodeset_1_1\n\nnodeset_1_1\n#id: b'b'\nf1: b'Y'\nf2: 0.5\n\n\n\nnodeset_1_0->nodeset_1_1\n\n\nedgeset_1\n\n\n\nnodeset_1_0->nodeset_1_1\n\n\nedgeset_1\n\n\n\nnodeset_2_0\n\nnodeset_2_0\n#id: b'u'\n\n\n\nnodeset_1_0->nodeset_2_0\n\n\nedgeset_2\n\n\n\nnodeset_1_2\n\nnodeset_1_2\n#id: b'c'\nf1: b'Y'\nf2: 0.2\n\n\n\nnodeset_1_1->nodeset_1_2\n\n\nedgeset_1\n\n\n\nnodeset_2_1\n\nnodeset_2_1\n#id: b'v'\n\n\n\nnodeset_1_1->nodeset_2_1\n\n\nedgeset_2\n\n\n\n", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "execution_count": 4 - } + "text/plain": [ + "" ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "graph = dgf.data.InMemoryGraph(\n", + " node_sets={\n", + " \"nodeset_1\": dgf.data.InMemoryNodeSet(\n", + " num_nodes=3,\n", + " features={\n", + " \"#id\": np.array([b\"a\", b\"b\", b\"c\"]),\n", + " \"f1\": np.array([b\"X\", b\"Y\", b\"Y\"]),\n", + " \"f2\": np.array([0.1, 0.5, 0.2], dtype=np.float32),\n", + " },\n", + " ),\n", + " \"nodeset_2\": dgf.data.InMemoryNodeSet(\n", + " num_nodes=2,\n", + " features={\n", + " \"#id\": np.array([b\"u\", b\"v\"]),\n", + " },\n", + " ),\n", + " },\n", + " edge_sets={\n", + " \"edgeset_1\": dgf.data.InMemoryEdgeSet(\n", + " adjacency=np.array([\n", + " [0, 0, 1],\n", + " [1, 1, 2],\n", + " ])\n", + " ),\n", + " \"edgeset_2\": dgf.data.InMemoryEdgeSet(\n", + " adjacency=np.array([\n", + " [0, 1],\n", + " [0, 1],\n", + " ])\n", + " ),\n", + " },\n", + ")\n", + "\n", + "# Plot the graph\n", + "dgf.plot.plot_graph(graph, schema)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "cosZMiP7MKXq" + }, + "source": [ + "**Remark:**\n", + "\n", + "- The adjacency information for the edgesets is provided as a NumPy array of\n", + " shape `[2, number of edges]`. The first row contains the source node\n", + " indices, and the second row contains the target node indices.\n", + "- A graph is essentially a dataclass containing a numpy array. DGF requires\n", + " for features and adjacencies to be provided as numpy arrays.\n", + "- GF support other equivalent graph classes using JAX or TensorFlow arrays\n", + " (see later).\n", + "\n", + "Checking the validity of a graph made by hand is always a good idea:" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-23T18:16:15.811134Z", + "iopub.status.busy": "2026-09-23T18:16:15.810649Z", + "iopub.status.idle": "2026-09-23T18:16:15.813881Z", + "shell.execute_reply": "2026-09-23T18:16:15.813485Z" }, - { - "cell_type": "markdown", - "source": [ - "**Remark:**\n", - "\n", - "- The adjacency information for the edgesets is provided as a NumPy array of\n", - " shape `[2, number of edges]`. The first row contains the source node\n", - " indices, and the second row contains the target node indices.\n", - "- A graph is essentially a dataclass containing a numpy array. DGF requires\n", - " for features and adjacencies to be provided as numpy arrays.\n", - "- GF support other equivalent graph classes using JAX or TensorFlow arrays\n", - " (see later).\n", - "\n", - "Checking the validity of a graph made by hand is always a good idea:" - ], - "metadata": { - "id": "cosZMiP7MKXq" - } + "executionInfo": { + "elapsed": 5, + "status": "ok", + "timestamp": 1790187375815.0386, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 }, - { - "cell_type": "code", - "source": [ - "dgf.validate.validate_graph(graph, schema)" - ], - "metadata": { - "id": "iFvmkAOUMfih" - }, - "execution_count": null, - "outputs": [] + "id": "iFvmkAOUMfih" + }, + "outputs": [], + "source": [ + "dgf.validate.validate_graph(graph, schema)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "SG8bTpdZfx5J" + }, + "source": [ + "## Modifying a graph in memory" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "e21fYUDONDPi" + }, + "source": [ + "The graph and schema are simply dataclasses with no logic / functions. You can\n", + "modify them directly. Now, let's create another edgeset." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "colab": { + "height": 228 }, - { - "cell_type": "markdown", - "source": [ - "## Modifying a graph in memory" - ], - "metadata": { - "id": "SG8bTpdZfx5J" - } + "execution": { + "iopub.execute_input": "2026-09-23T18:16:15.816011Z", + "iopub.status.busy": "2026-09-23T18:16:15.815730Z", + "iopub.status.idle": "2026-09-23T18:16:15.859886Z", + "shell.execute_reply": "2026-09-23T18:16:15.859139Z" }, - { - "cell_type": "markdown", - "source": [ - "The graph and schema are simply dataclasses with no logic / functions. You can\n", - "modify them directly. Now, let's create another edgeset." - ], - "metadata": { - "id": "e21fYUDONDPi" - } + "executionInfo": { + "elapsed": 46, + "status": "ok", + "timestamp": 1790187375861.2004, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 }, + "id": "8W3sIXkYNGNR", + "outputId": "b7f8dd1f-bc06-42de-e1a8-f8293ac5d4d2" + }, + "outputs": [ { - "cell_type": "code", - "source": [ - "schema.edge_sets[\"edgeset_3\"] = dgf.data.EdgeSchema(\n", - " source=\"nodeset_2\", target=\"nodeset_1\"\n", - ")\n", - "graph.edge_sets[\"edgeset_3\"] = dgf.data.InMemoryEdgeSet(\n", - " adjacency=np.array([\n", - " [1, 0],\n", - " [0, 2],\n", - " ])\n", - ")\n", - "dgf.validate.validate_graph(graph, schema)\n", - "dgf.plot.plot_graph(graph, schema)" + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "nodeset_1_0\n", + "\n", + "nodeset_1_0\n", + "#id: b'a'\n", + "f1: b'X'\n", + "f2: 0.1\n", + "\n", + "\n", + "\n", + "nodeset_1_1\n", + "\n", + "nodeset_1_1\n", + "#id: b'b'\n", + "f1: b'Y'\n", + "f2: 0.5\n", + "\n", + "\n", + "\n", + "nodeset_1_0->nodeset_1_1\n", + "\n", + "\n", + "edgeset_1\n", + "\n", + "\n", + "\n", + "nodeset_1_0->nodeset_1_1\n", + "\n", + "\n", + "edgeset_1\n", + "\n", + "\n", + "\n", + "nodeset_2_0\n", + "\n", + "nodeset_2_0\n", + "#id: b'u'\n", + "\n", + "\n", + "\n", + "nodeset_1_0->nodeset_2_0\n", + "\n", + "\n", + "edgeset_2\n", + "\n", + "\n", + "\n", + "nodeset_1_2\n", + "\n", + "nodeset_1_2\n", + "#id: b'c'\n", + "f1: b'Y'\n", + "f2: 0.2\n", + "\n", + "\n", + "\n", + "nodeset_1_1->nodeset_1_2\n", + "\n", + "\n", + "edgeset_1\n", + "\n", + "\n", + "\n", + "nodeset_2_1\n", + "\n", + "nodeset_2_1\n", + "#id: b'v'\n", + "\n", + "\n", + "\n", + "nodeset_1_1->nodeset_2_1\n", + "\n", + "\n", + "edgeset_2\n", + "\n", + "\n", + "\n", + "nodeset_2_0->nodeset_1_2\n", + "\n", + "\n", + "edgeset_3\n", + "\n", + "\n", + "\n", + "nodeset_2_1->nodeset_1_0\n", + "\n", + "\n", + "edgeset_3\n", + "\n", + "\n", + "\n" ], - "metadata": { - "colab": { - "height": 228 - }, - "id": "8W3sIXkYNGNR", - "executionInfo": { - "status": "ok", - "timestamp": 1777976576008, - "user_tz": -120, - "elapsed": 50, - "user": { - "displayName": "", - "userId": "" - } - }, - "outputId": "b7f8dd1f-bc06-42de-e1a8-f8293ac5d4d2" - }, - "execution_count": null, - "outputs": [ - { - "output_type": "execute_result", - "data": { - "image/svg+xml": "\n\n\n\n\n\n\n\n\nnodeset_1_0\n\nnodeset_1_0\n#id: b'a'\nf1: b'X'\nf2: 0.1\n\n\n\nnodeset_1_1\n\nnodeset_1_1\n#id: b'b'\nf1: b'Y'\nf2: 0.5\n\n\n\nnodeset_1_0->nodeset_1_1\n\n\nedgeset_1\n\n\n\nnodeset_1_0->nodeset_1_1\n\n\nedgeset_1\n\n\n\nnodeset_2_0\n\nnodeset_2_0\n#id: b'u'\n\n\n\nnodeset_1_0->nodeset_2_0\n\n\nedgeset_2\n\n\n\nnodeset_1_2\n\nnodeset_1_2\n#id: b'c'\nf1: b'Y'\nf2: 0.2\n\n\n\nnodeset_1_1->nodeset_1_2\n\n\nedgeset_1\n\n\n\nnodeset_2_1\n\nnodeset_2_1\n#id: b'v'\n\n\n\nnodeset_1_1->nodeset_2_1\n\n\nedgeset_2\n\n\n\nnodeset_2_0->nodeset_1_2\n\n\nedgeset_3\n\n\n\nnodeset_2_1->nodeset_1_0\n\n\nedgeset_3\n\n\n\n", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "execution_count": 6 - } + "text/plain": [ + "" ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "schema.edge_sets[\"edgeset_3\"] = dgf.data.EdgeSchema(\n", + " source=\"nodeset_2\", target=\"nodeset_1\"\n", + ")\n", + "graph.edge_sets[\"edgeset_3\"] = dgf.data.InMemoryEdgeSet(\n", + " adjacency=np.array([\n", + " [1, 0],\n", + " [0, 2],\n", + " ])\n", + ")\n", + "dgf.validate.validate_graph(graph, schema)\n", + "dgf.plot.plot_graph(graph, schema)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "NMulQq-uf7Bs" + }, + "source": [ + "## Loading / saving graphs" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "YJMnJmjJNMJX" + }, + "source": [ + "Graphs can be saved to disk. Various formats are supported. See the \"Graph File\n", + "Format\" guide.\n", + "\n", + "The GF format is the most efficient both to store small and large graphs." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-23T18:16:15.862299Z", + "iopub.status.busy": "2026-09-23T18:16:15.862034Z", + "iopub.status.idle": "2026-09-23T18:16:15.879627Z", + "shell.execute_reply": "2026-09-23T18:16:15.879040Z" }, - { - "cell_type": "markdown", - "source": [ - "## Loading / saving graphs" - ], - "metadata": { - "id": "NMulQq-uf7Bs" - } + "executionInfo": { + "elapsed": 19, + "status": "ok", + "timestamp": 1790187375880.6555, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 }, + "id": "p_QqkZv5NR-p", + "outputId": "e04b1dae-f4f0-4f48-c5f0-2d11f50e7db8" + }, + "outputs": [ { - "cell_type": "markdown", - "source": [ - "Graphs can be saved to disk. Various formats are supported. See the \"Graph File\n", - "Format\" guide.\n", - "\n", - "The GF format is the most efficient both to store small and large graphs." - ], - "metadata": { - "id": "YJMnJmjJNMJX" - } + "name": "stdout", + "output_type": "stream", + "text": [ + "Graph written from memory in 0.01s\n" + ] + } + ], + "source": [ + "# Save the graph\n", + "dgf.io.write_graph(graph, schema, \"/tmp/my_graph\")" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": { + "colab": { + "height": 246 }, - { - "cell_type": "code", - "source": [ - "# Save the graph\n", - "dgf.io.write_graph(graph, schema, \"/tmp/my_graph\")" - ], - "metadata": { - "id": "p_QqkZv5NR-p", - "executionInfo": { - "status": "ok", - "timestamp": 1777976576149, - "user_tz": -120, - "elapsed": 18, - "user": { - "displayName": "", - "userId": "" - } - }, - "outputId": "e04b1dae-f4f0-4f48-c5f0-2d11f50e7db8" - }, - "execution_count": null, - "outputs": [ - { - "output_type": "stream", - "name": "stdout", - "text": [ - "GFGraph written from memory in 0.01 seconds\n" - ] - } - ] + "execution": { + "iopub.execute_input": "2026-09-23T18:16:15.881538Z", + "iopub.status.busy": "2026-09-23T18:16:15.881357Z", + "iopub.status.idle": "2026-09-23T18:16:16.311572Z", + "shell.execute_reply": "2026-09-23T18:16:16.311137Z" }, - { - "cell_type": "code", - "source": [ - "# Load the graph\n", - "loaded_graph, loaded_schema = dgf.io.read_graph(\"/tmp/my_graph\")\n", - "dgf.plot.plot_graph(loaded_graph, loaded_schema)" - ], - "metadata": { - "colab": { - "height": 246 - }, - "id": "CSQ_HNlxNW_F", - "executionInfo": { - "status": "ok", - "timestamp": 1777976576522, - "user_tz": -120, - "elapsed": 311, - "user": { - "displayName": "", - "userId": "" - } - }, - "outputId": "09573ef5-b2bb-469d-fa8c-b54330bdce0f" - }, - "execution_count": null, - "outputs": [ - { - "output_type": "stream", - "name": "stdout", - "text": [ - "GFGraph read in memory in 0.25 seconds\n" - ] - }, - { - "output_type": "execute_result", - "data": { - "image/svg+xml": "\n\n\n\n\n\n\n\n\nnodeset_1_0\n\nnodeset_1_0\n#id: b'a'\nf1: b'X'\nf2: 0.1\n\n\n\nnodeset_1_1\n\nnodeset_1_1\n#id: b'b'\nf1: b'Y'\nf2: 0.5\n\n\n\nnodeset_1_0->nodeset_1_1\n\n\nedgeset_1\n\n\n\nnodeset_1_0->nodeset_1_1\n\n\nedgeset_1\n\n\n\nnodeset_2_0\n\nnodeset_2_0\n#id: b'u'\n\n\n\nnodeset_1_0->nodeset_2_0\n\n\nedgeset_2\n\n\n\nnodeset_1_2\n\nnodeset_1_2\n#id: b'c'\nf1: b'Y'\nf2: 0.2\n\n\n\nnodeset_1_1->nodeset_1_2\n\n\nedgeset_1\n\n\n\nnodeset_2_1\n\nnodeset_2_1\n#id: b'v'\n\n\n\nnodeset_1_1->nodeset_2_1\n\n\nedgeset_2\n\n\n\nnodeset_2_0->nodeset_1_2\n\n\nedgeset_3\n\n\n\nnodeset_2_1->nodeset_1_0\n\n\nedgeset_3\n\n\n\n", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "execution_count": 8 - } - ] + "executionInfo": { + "elapsed": 431, + "status": "ok", + "timestamp": 1790187376312.569, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 }, + "id": "CSQ_HNlxNW_F", + "outputId": "09573ef5-b2bb-469d-fa8c-b54330bdce0f" + }, + "outputs": [ { - "cell_type": "markdown", - "source": [ - "Graphs can be saved / restored using pickle. This option is very efficient, but\n", - "not portable at all." - ], - "metadata": { - "id": "5ximSUnBg0K2" - } + "name": "stdout", + "output_type": "stream", + "text": [ + "Graph read in memory in 0.39s\n" + ] }, { - "cell_type": "code", - "source": [ - "import pickle\n", - "\n", - "with open(\"/tmp/my_graph.pkl\", \"wb\") as file:\n", - " pickle.dump((graph, schema), file)\n", - "\n", - "with open(\"/tmp/my_graph.pkl\", \"rb\") as file:\n", - " loaded_graph, loaded_schema = pickle.load(file)\n", - "\n", - "dgf.plot.plot_graph(loaded_graph, loaded_schema)" + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "nodeset_1_0\n", + "\n", + "nodeset_1_0\n", + "#id: b'a'\n", + "f1: b'X'\n", + "f2: 0.1\n", + "\n", + "\n", + "\n", + "nodeset_1_1\n", + "\n", + "nodeset_1_1\n", + "#id: b'b'\n", + "f1: b'Y'\n", + "f2: 0.5\n", + "\n", + "\n", + "\n", + "nodeset_1_0->nodeset_1_1\n", + "\n", + "\n", + "edgeset_1\n", + "\n", + "\n", + "\n", + "nodeset_1_0->nodeset_1_1\n", + "\n", + "\n", + "edgeset_1\n", + "\n", + "\n", + "\n", + "nodeset_2_0\n", + "\n", + "nodeset_2_0\n", + "#id: b'u'\n", + "\n", + "\n", + "\n", + "nodeset_1_0->nodeset_2_0\n", + "\n", + "\n", + "edgeset_2\n", + "\n", + "\n", + "\n", + "nodeset_1_2\n", + "\n", + "nodeset_1_2\n", + "#id: b'c'\n", + "f1: b'Y'\n", + "f2: 0.2\n", + "\n", + "\n", + "\n", + "nodeset_1_1->nodeset_1_2\n", + "\n", + "\n", + "edgeset_1\n", + "\n", + "\n", + "\n", + "nodeset_2_1\n", + "\n", + "nodeset_2_1\n", + "#id: b'v'\n", + "\n", + "\n", + "\n", + "nodeset_1_1->nodeset_2_1\n", + "\n", + "\n", + "edgeset_2\n", + "\n", + "\n", + "\n", + "nodeset_2_0->nodeset_1_2\n", + "\n", + "\n", + "edgeset_3\n", + "\n", + "\n", + "\n", + "nodeset_2_1->nodeset_1_0\n", + "\n", + "\n", + "edgeset_3\n", + "\n", + "\n", + "\n" ], - "metadata": { - "colab": { - "height": 228 - }, - "id": "ER2VMWCEhLZL", - "executionInfo": { - "status": "ok", - "timestamp": 1777976576696, - "user_tz": -120, - "elapsed": 63, - "user": { - "displayName": "", - "userId": "" - } - }, - "outputId": "2df98f60-116c-4d3e-8b1d-0eba83ff6334" - }, - "execution_count": null, - "outputs": [ - { - "output_type": "execute_result", - "data": { - "image/svg+xml": "\n\n\n\n\n\n\n\n\nnodeset_1_0\n\nnodeset_1_0\n#id: b'a'\nf1: b'X'\nf2: 0.1\n\n\n\nnodeset_1_1\n\nnodeset_1_1\n#id: b'b'\nf1: b'Y'\nf2: 0.5\n\n\n\nnodeset_1_0->nodeset_1_1\n\n\nedgeset_1\n\n\n\nnodeset_1_0->nodeset_1_1\n\n\nedgeset_1\n\n\n\nnodeset_2_0\n\nnodeset_2_0\n#id: b'u'\n\n\n\nnodeset_1_0->nodeset_2_0\n\n\nedgeset_2\n\n\n\nnodeset_1_2\n\nnodeset_1_2\n#id: b'c'\nf1: b'Y'\nf2: 0.2\n\n\n\nnodeset_1_1->nodeset_1_2\n\n\nedgeset_1\n\n\n\nnodeset_2_1\n\nnodeset_2_1\n#id: b'v'\n\n\n\nnodeset_1_1->nodeset_2_1\n\n\nedgeset_2\n\n\n\nnodeset_2_0->nodeset_1_2\n\n\nedgeset_3\n\n\n\nnodeset_2_1->nodeset_1_0\n\n\nedgeset_3\n\n\n\n", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "execution_count": 9 - } + "text/plain": [ + "" ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Load the graph\n", + "loaded_graph, loaded_schema = dgf.io.read_graph(\"/tmp/my_graph\")\n", + "dgf.plot.plot_graph(loaded_graph, loaded_schema)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "5ximSUnBg0K2" + }, + "source": [ + "Graphs can be saved / restored using pickle. This option is very efficient, but\n", + "not portable at all." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "colab": { + "height": 228 }, - { - "cell_type": "markdown", - "source": [ - "Schemas (and most other GF configs) can be loaded / saved to json using the\n", - "`dataclass_json` library.\n", - "\n", - "To make your life easy, we defined small utility functions:" - ], - "metadata": { - "id": "ADPhK__Ohbv-" - } + "execution": { + "iopub.execute_input": "2026-09-23T18:16:16.313411Z", + "iopub.status.busy": "2026-09-23T18:16:16.313232Z", + "iopub.status.idle": "2026-09-23T18:16:16.355453Z", + "shell.execute_reply": "2026-09-23T18:16:16.354929Z" }, - { - "cell_type": "code", - "source": [ - "# Save the schema\n", - "dgf.io.write_schema(schema, \"/tmp/my_schema.json\")" - ], - "metadata": { - "id": "WBrKMPCxh1OL" - }, - "execution_count": null, - "outputs": [] + "executionInfo": { + "elapsed": 43, + "status": "ok", + "timestamp": 1790187376356.4607, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 }, + "id": "ER2VMWCEhLZL", + "outputId": "2df98f60-116c-4d3e-8b1d-0eba83ff6334" + }, + "outputs": [ { - "cell_type": "code", - "source": [ - "!head -n 10 /tmp/my_schema.json" + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "nodeset_1_0\n", + "\n", + "nodeset_1_0\n", + "#id: b'a'\n", + "f1: b'X'\n", + "f2: 0.1\n", + "\n", + "\n", + "\n", + "nodeset_1_1\n", + "\n", + "nodeset_1_1\n", + "#id: b'b'\n", + "f1: b'Y'\n", + "f2: 0.5\n", + "\n", + "\n", + "\n", + "nodeset_1_0->nodeset_1_1\n", + "\n", + "\n", + "edgeset_1\n", + "\n", + "\n", + "\n", + "nodeset_1_0->nodeset_1_1\n", + "\n", + "\n", + "edgeset_1\n", + "\n", + "\n", + "\n", + "nodeset_2_0\n", + "\n", + "nodeset_2_0\n", + "#id: b'u'\n", + "\n", + "\n", + "\n", + "nodeset_1_0->nodeset_2_0\n", + "\n", + "\n", + "edgeset_2\n", + "\n", + "\n", + "\n", + "nodeset_1_2\n", + "\n", + "nodeset_1_2\n", + "#id: b'c'\n", + "f1: b'Y'\n", + "f2: 0.2\n", + "\n", + "\n", + "\n", + "nodeset_1_1->nodeset_1_2\n", + "\n", + "\n", + "edgeset_1\n", + "\n", + "\n", + "\n", + "nodeset_2_1\n", + "\n", + "nodeset_2_1\n", + "#id: b'v'\n", + "\n", + "\n", + "\n", + "nodeset_1_1->nodeset_2_1\n", + "\n", + "\n", + "edgeset_2\n", + "\n", + "\n", + "\n", + "nodeset_2_0->nodeset_1_2\n", + "\n", + "\n", + "edgeset_3\n", + "\n", + "\n", + "\n", + "nodeset_2_1->nodeset_1_0\n", + "\n", + "\n", + "edgeset_3\n", + "\n", + "\n", + "\n" ], - "metadata": { - "id": "O6PuedLWh_jc", - "executionInfo": { - "status": "ok", - "timestamp": 1777976576950, - "user_tz": -120, - "elapsed": 30, - "user": { - "displayName": "", - "userId": "" - } - }, - "outputId": "2d68c7a2-7900-4ed2-869b-15343c366d31" - }, - "execution_count": null, - "outputs": [ - { - "output_type": "stream", - "name": "stdout", - "text": [ - "{\n", - " \"node_sets\": {\n", - " \"nodeset_1\": {\n", - " \"features\": {\n", - " \"#id\": {\n", - " \"format\": \"BYTES\",\n", - " \"semantic\": \"PRIMARY_ID\",\n", - " \"shape\": null,\n", - " \"num_categorical_values\": null,\n", - " \"is_utf8_string\": false\n" - ] - } + "text/plain": [ + "" ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import pickle\n", + "\n", + "with open(\"/tmp/my_graph.pkl\", \"wb\") as file:\n", + " pickle.dump((graph, schema), file)\n", + "\n", + "with open(\"/tmp/my_graph.pkl\", \"rb\") as file:\n", + " loaded_graph, loaded_schema = pickle.load(file)\n", + "\n", + "dgf.plot.plot_graph(loaded_graph, loaded_schema)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "ADPhK__Ohbv-" + }, + "source": [ + "Schemas (and most other GF configs) can be loaded / saved to json using the\n", + "`dataclass_json` library.\n", + "\n", + "To make your life easy, we defined small utility functions:" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-23T18:16:16.357617Z", + "iopub.status.busy": "2026-09-23T18:16:16.357365Z", + "iopub.status.idle": "2026-09-23T18:16:16.362617Z", + "shell.execute_reply": "2026-09-23T18:16:16.361962Z" }, - { - "cell_type": "code", - "source": [ - "# Reload the schema\n", - "loaded_schema = dgf.io.read_schema(\"/tmp/my_schema.json\")\n", - "assert schema == loaded_schema" - ], - "metadata": { - "id": "XfOajTfOh3Q_" - }, - "execution_count": null, - "outputs": [] + "executionInfo": { + "elapsed": 7, + "status": "ok", + "timestamp": 1790187376363.8894, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 }, - { - "cell_type": "markdown", - "source": [ - "To load / save collections of graphs (e.g., to cache graph samples), you can use\n", - "the TF-GNN graph sampling format:" - ], - "metadata": { - "id": "O2XJMDoeiIox" - } + "id": "WBrKMPCxh1OL" + }, + "outputs": [], + "source": [ + "# Save the schema\n", + "dgf.io.write_schema(schema, \"/tmp/my_schema.json\")" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-23T18:16:16.365157Z", + "iopub.status.busy": "2026-09-23T18:16:16.364721Z", + "iopub.status.idle": "2026-09-23T18:16:16.371694Z", + "shell.execute_reply": "2026-09-23T18:16:16.371305Z" }, - { - "cell_type": "code", - "source": [ - "# A list / generator of 5 graphs\n", - "def graphs():\n", - " for _ in range(5):\n", - " yield graph\n", - "\n", - "\n", - "# Save the graphs\n", - "dgf.io.write_tfgnn_graphs(\n", - " graphs=graphs(), schema=schema, path=\"/tmp/my_graphs@*\"\n", - ")" - ], - "metadata": { - "id": "FedgFX6miSv9" - }, - "execution_count": null, - "outputs": [] + "executionInfo": { + "elapsed": 8, + "status": "ok", + "timestamp": 1790187376372.654, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 }, + "id": "O6PuedLWh_jc", + "outputId": "2d68c7a2-7900-4ed2-869b-15343c366d31" + }, + "outputs": [ { - "cell_type": "markdown", - "source": [ - "**Remarks:**\n", - "\n", - "- Instead of a generator, you can also simply provide a list." - ], - "metadata": { - "id": "Hy3YoHQ-iic3" - } + "name": "stdout", + "output_type": "stream", + "text": [ + "{\n", + " \"node_sets\": {\n", + " \"nodeset_1\": {\n", + " \"features\": {\n", + " \"#id\": {\n", + " \"format\": \"BYTES\",\n", + " \"semantic\": \"PRIMARY_ID\",\n", + " \"shape\": null,\n", + " \"num_categorical_values\": null,\n", + " \"is_utf8_string\": false,\n" + ] + } + ], + "source": [ + "!head -n 10 /tmp/my_schema.json" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-23T18:16:16.373457Z", + "iopub.status.busy": "2026-09-23T18:16:16.373282Z", + "iopub.status.idle": "2026-09-23T18:16:16.379180Z", + "shell.execute_reply": "2026-09-23T18:16:16.378818Z" }, - { - "cell_type": "code", - "source": [ - "# Load the graphs\n", - "for graph in dgf.io.read_tfgnn_graphs(path=\"/tmp/my_graphs@*\", schema=schema):\n", - " print(graph)" - ], - "metadata": { - "id": "jqd0V9lEi2Hs", - "executionInfo": { - "status": "ok", - "timestamp": 1777976579462, - "user_tz": -120, - "elapsed": 2291, - "user": { - "displayName": "", - "userId": "" - } - }, - "outputId": "e7192b11-c5b4-4849-950b-bf79b7bde4b0" - }, - "execution_count": null, - "outputs": [ - { - "output_type": "stream", - "name": "stdout", - "text": [ - "InMemoryGraph(node_sets={'nodeset_1': InMemoryNodeSet(num_nodes=3, features={'#id': array([b'a', b'b', b'c'], dtype='|S1'), 'f1': array([b'X', b'Y', b'Y'], dtype='|S1'), 'f2': array([0.1, 0.5, 0.2], dtype=float32)}), 'nodeset_2': InMemoryNodeSet(num_nodes=2, features={'#id': array([b'u', b'v'], dtype='|S1')})}, edge_sets={'edgeset_1': InMemoryEdgeSet(adjacency=array([[0, 0, 1],\n", - " [1, 1, 2]]), features={}), 'edgeset_2': InMemoryEdgeSet(adjacency=array([[0, 1],\n", - " [0, 1]]), features={}), 'edgeset_3': InMemoryEdgeSet(adjacency=array([[1, 0],\n", - " [0, 2]]), features={})})\n", - "InMemoryGraph(node_sets={'nodeset_1': InMemoryNodeSet(num_nodes=3, features={'#id': array([b'a', b'b', b'c'], dtype='|S1'), 'f1': array([b'X', b'Y', b'Y'], dtype='|S1'), 'f2': array([0.1, 0.5, 0.2], dtype=float32)}), 'nodeset_2': InMemoryNodeSet(num_nodes=2, features={'#id': array([b'u', b'v'], dtype='|S1')})}, edge_sets={'edgeset_1': InMemoryEdgeSet(adjacency=array([[0, 0, 1],\n", - " [1, 1, 2]]), features={}), 'edgeset_2': InMemoryEdgeSet(adjacency=array([[0, 1],\n", - " [0, 1]]), features={}), 'edgeset_3': InMemoryEdgeSet(adjacency=array([[1, 0],\n", - " [0, 2]]), features={})})\n", - "InMemoryGraph(node_sets={'nodeset_1': InMemoryNodeSet(num_nodes=3, features={'#id': array([b'a', b'b', b'c'], dtype='|S1'), 'f1': array([b'X', b'Y', b'Y'], dtype='|S1'), 'f2': array([0.1, 0.5, 0.2], dtype=float32)}), 'nodeset_2': InMemoryNodeSet(num_nodes=2, features={'#id': array([b'u', b'v'], dtype='|S1')})}, edge_sets={'edgeset_1': InMemoryEdgeSet(adjacency=array([[0, 0, 1],\n", - " [1, 1, 2]]), features={}), 'edgeset_2': InMemoryEdgeSet(adjacency=array([[0, 1],\n", - " [0, 1]]), features={}), 'edgeset_3': InMemoryEdgeSet(adjacency=array([[1, 0],\n", - " [0, 2]]), features={})})\n", - "InMemoryGraph(node_sets={'nodeset_1': InMemoryNodeSet(num_nodes=3, features={'#id': array([b'a', b'b', b'c'], dtype='|S1'), 'f1': array([b'X', b'Y', b'Y'], dtype='|S1'), 'f2': array([0.1, 0.5, 0.2], dtype=float32)}), 'nodeset_2': InMemoryNodeSet(num_nodes=2, features={'#id': array([b'u', b'v'], dtype='|S1')})}, edge_sets={'edgeset_1': InMemoryEdgeSet(adjacency=array([[0, 0, 1],\n", - " [1, 1, 2]]), features={}), 'edgeset_2': InMemoryEdgeSet(adjacency=array([[0, 1],\n", - " [0, 1]]), features={}), 'edgeset_3': InMemoryEdgeSet(adjacency=array([[1, 0],\n", - " [0, 2]]), features={})})\n", - "InMemoryGraph(node_sets={'nodeset_1': InMemoryNodeSet(num_nodes=3, features={'#id': array([b'a', b'b', b'c'], dtype='|S1'), 'f1': array([b'X', b'Y', b'Y'], dtype='|S1'), 'f2': array([0.1, 0.5, 0.2], dtype=float32)}), 'nodeset_2': InMemoryNodeSet(num_nodes=2, features={'#id': array([b'u', b'v'], dtype='|S1')})}, edge_sets={'edgeset_1': InMemoryEdgeSet(adjacency=array([[0, 0, 1],\n", - " [1, 1, 2]]), features={}), 'edgeset_2': InMemoryEdgeSet(adjacency=array([[0, 1],\n", - " [0, 1]]), features={}), 'edgeset_3': InMemoryEdgeSet(adjacency=array([[1, 0],\n", - " [0, 2]]), features={})})\n" - ] - } - ] + "executionInfo": { + "elapsed": 7, + "status": "ok", + "timestamp": 1790187376380.148, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 }, - { - "cell_type": "markdown", - "source": [ - "**Remarks:**\n", - "\n", - "- Unlike the GF disk graph format used above, the TF-GNN graph sample format\n", - " does not store the graph schema. You have to do it manually." - ], - "metadata": { - "id": "oLUvgYZPi8DW" - } + "id": "XfOajTfOh3Q_" + }, + "outputs": [], + "source": [ + "# Reload the schema\n", + "loaded_schema = dgf.io.read_schema(\"/tmp/my_schema.json\")\n", + "assert schema == loaded_schema" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "O2XJMDoeiIox" + }, + "source": [ + "To load / save collections of graphs (e.g., to cache graph samples), you can use\n", + "the TF-GNN graph sampling format:" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-23T18:16:16.380955Z", + "iopub.status.busy": "2026-09-23T18:16:16.380746Z", + "iopub.status.idle": "2026-09-23T18:16:16.623320Z", + "shell.execute_reply": "2026-09-23T18:16:16.622823Z" }, - { - "cell_type": "markdown", - "source": [ - "## Conversion between in-memory graph formats" - ], - "metadata": { - "id": "MEO5MAgbf908" - } + "executionInfo": { + "elapsed": 244, + "status": "ok", + "timestamp": 1790187376624.616, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 + }, + "id": "FedgFX6miSv9" + }, + "outputs": [], + "source": [ + "# A list / generator of 5 graphs\n", + "def graphs():\n", + " for _ in range(5):\n", + " yield graph\n", + "\n", + "\n", + "# Save the graphs\n", + "dgf.io.write_tfgnn_graphs(\n", + " graphs=graphs(), schema=schema, path=\"/tmp/my_graphs@*\"\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Hy3YoHQ-iic3" + }, + "source": [ + "**Remarks:**\n", + "\n", + "- Instead of a generator, you can also simply provide a list." + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-23T18:16:16.626012Z", + "iopub.status.busy": "2026-09-23T18:16:16.625363Z", + "iopub.status.idle": "2026-09-23T18:16:18.988035Z", + "shell.execute_reply": "2026-09-23T18:16:18.987479Z" + }, + "executionInfo": { + "elapsed": 2364, + "status": "ok", + "timestamp": 1790187378989.0984, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 }, + "id": "jqd0V9lEi2Hs", + "outputId": "e7192b11-c5b4-4849-950b-bf79b7bde4b0" + }, + "outputs": [ { - "cell_type": "markdown", - "source": [ - "Conversion to other graph formats is possible with the methods in\n", - "`dgf.convert.*`. For example, let's convert our numpy-based graph into a\n", - "tf-based graph." - ], - "metadata": { - "id": "zbRj_6GTNheC" - } + "name": "stdout", + "output_type": "stream", + "text": [ + "InMemoryGraph(node_sets={'nodeset_1': InMemoryNodeSet(num_nodes=3, features={'#id': array([b'a', b'b', b'c'], dtype='|S1'), 'f1': array([b'X', b'Y', b'Y'], dtype='|S1'), 'f2': array([0.1, 0.5, 0.2], dtype=float32)}), 'nodeset_2': InMemoryNodeSet(num_nodes=2, features={'#id': array([b'u', b'v'], dtype='|S1')})}, edge_sets={'edgeset_1': InMemoryEdgeSet(adjacency=array([[0, 0, 1],\n", + " [1, 1, 2]]), features={}), 'edgeset_2': InMemoryEdgeSet(adjacency=array([[0, 1],\n", + " [0, 1]]), features={}), 'edgeset_3': InMemoryEdgeSet(adjacency=array([[1, 0],\n", + " [0, 2]]), features={})}, timestamp=None)\n", + "InMemoryGraph(node_sets={'nodeset_1': InMemoryNodeSet(num_nodes=3, features={'#id': array([b'a', b'b', b'c'], dtype='|S1'), 'f1': array([b'X', b'Y', b'Y'], dtype='|S1'), 'f2': array([0.1, 0.5, 0.2], dtype=float32)}), 'nodeset_2': InMemoryNodeSet(num_nodes=2, features={'#id': array([b'u', b'v'], dtype='|S1')})}, edge_sets={'edgeset_1': InMemoryEdgeSet(adjacency=array([[0, 0, 1],\n", + " [1, 1, 2]]), features={}), 'edgeset_2': InMemoryEdgeSet(adjacency=array([[0, 1],\n", + " [0, 1]]), features={}), 'edgeset_3': InMemoryEdgeSet(adjacency=array([[1, 0],\n", + " [0, 2]]), features={})}, timestamp=None)\n", + "InMemoryGraph(node_sets={'nodeset_1': InMemoryNodeSet(num_nodes=3, features={'#id': array([b'a', b'b', b'c'], dtype='|S1'), 'f1': array([b'X', b'Y', b'Y'], dtype='|S1'), 'f2': array([0.1, 0.5, 0.2], dtype=float32)}), 'nodeset_2': InMemoryNodeSet(num_nodes=2, features={'#id': array([b'u', b'v'], dtype='|S1')})}, edge_sets={'edgeset_1': InMemoryEdgeSet(adjacency=array([[0, 0, 1],\n", + " [1, 1, 2]]), features={}), 'edgeset_2': InMemoryEdgeSet(adjacency=array([[0, 1],\n", + " [0, 1]]), features={}), 'edgeset_3': InMemoryEdgeSet(adjacency=array([[1, 0],\n", + " [0, 2]]), features={})}, timestamp=None)\n", + "InMemoryGraph(node_sets={'nodeset_1': InMemoryNodeSet(num_nodes=3, features={'#id': array([b'a', b'b', b'c'], dtype='|S1'), 'f1': array([b'X', b'Y', b'Y'], dtype='|S1'), 'f2': array([0.1, 0.5, 0.2], dtype=float32)}), 'nodeset_2': InMemoryNodeSet(num_nodes=2, features={'#id': array([b'u', b'v'], dtype='|S1')})}, edge_sets={'edgeset_1': InMemoryEdgeSet(adjacency=array([[0, 0, 1],\n", + " [1, 1, 2]]), features={}), 'edgeset_2': InMemoryEdgeSet(adjacency=array([[0, 1],\n", + " [0, 1]]), features={}), 'edgeset_3': InMemoryEdgeSet(adjacency=array([[1, 0],\n", + " [0, 2]]), features={})}, timestamp=None)\n", + "InMemoryGraph(node_sets={'nodeset_1': InMemoryNodeSet(num_nodes=3, features={'#id': array([b'a', b'b', b'c'], dtype='|S1'), 'f1': array([b'X', b'Y', b'Y'], dtype='|S1'), 'f2': array([0.1, 0.5, 0.2], dtype=float32)}), 'nodeset_2': InMemoryNodeSet(num_nodes=2, features={'#id': array([b'u', b'v'], dtype='|S1')})}, edge_sets={'edgeset_1': InMemoryEdgeSet(adjacency=array([[0, 0, 1],\n", + " [1, 1, 2]]), features={}), 'edgeset_2': InMemoryEdgeSet(adjacency=array([[0, 1],\n", + " [0, 1]]), features={}), 'edgeset_3': InMemoryEdgeSet(adjacency=array([[1, 0],\n", + " [0, 2]]), features={})}, timestamp=None)\n" + ] + } + ], + "source": [ + "# Load the graphs\n", + "for graph in dgf.io.read_tfgnn_graphs(path=\"/tmp/my_graphs@*\", schema=schema):\n", + " print(graph)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "oLUvgYZPi8DW" + }, + "source": [ + "**Remarks:**\n", + "\n", + "- Unlike the GF disk graph format used above, the TF-GNN graph sample format\n", + " does not store the graph schema. You have to do it manually." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "MEO5MAgbf908" + }, + "source": [ + "## Conversion between in-memory graph formats" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "zbRj_6GTNheC" + }, + "source": [ + "Conversion to other graph formats is possible with the methods in\n", + "`dgf.convert.*`. For example, let's convert our numpy-based graph into a\n", + "tf-based graph." + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-23T18:16:18.990126Z", + "iopub.status.busy": "2026-09-23T18:16:18.989798Z", + "iopub.status.idle": "2026-09-23T18:16:18.996381Z", + "shell.execute_reply": "2026-09-23T18:16:18.995919Z" }, + "executionInfo": { + "elapsed": 8, + "status": "ok", + "timestamp": 1790187378997.4348, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 + }, + "id": "qY5Wvo2WN50f", + "outputId": "37cce3ec-6193-43d8-ab5e-083414f20a6c" + }, + "outputs": [ { - "cell_type": "code", - "source": [ - "tf_graph = dgf.convert.graph_to_tf_graph(graph)\n", - "tf_graph" - ], - "metadata": { - "id": "qY5Wvo2WN50f", - "executionInfo": { - "status": "ok", - "timestamp": 1777976579542, - "user_tz": -120, - "elapsed": 24, - "user": { - "displayName": "", - "userId": "" - } - }, - "outputId": "37cce3ec-6193-43d8-ab5e-083414f20a6c" - }, - "execution_count": null, - "outputs": [ - { - "output_type": "execute_result", - "data": { - "text/plain": [ - "TFInMemoryGraph(node_sets=ImmutableDict({'nodeset_1': TFInMemoryNodeSet(num_nodes=, features=ImmutableDict({'#id': , 'f1': , 'f2': })), 'nodeset_2': TFInMemoryNodeSet(num_nodes=, features=ImmutableDict({'#id': }))}), edge_sets=ImmutableDict({'edgeset_1': TFInMemoryEdgeSet(adjacency=, features=ImmutableDict({})), 'edgeset_2': TFInMemoryEdgeSet(adjacency=, features=ImmutableDict({})), 'edgeset_3': TFInMemoryEdgeSet(adjacency=, features=ImmutableDict({}))}))" - ] - }, - "metadata": {}, - "execution_count": 15 - } + "data": { + "text/plain": [ + "TFInMemoryGraph(node_sets=ImmutableDict({'nodeset_1': TFInMemoryNodeSet(num_nodes=, features=ImmutableDict({'#id': , 'f1': , 'f2': })), 'nodeset_2': TFInMemoryNodeSet(num_nodes=, features=ImmutableDict({'#id': }))}), edge_sets=ImmutableDict({'edgeset_1': TFInMemoryEdgeSet(adjacency=, features=ImmutableDict({})), 'edgeset_2': TFInMemoryEdgeSet(adjacency=, features=ImmutableDict({})), 'edgeset_3': TFInMemoryEdgeSet(adjacency=, features=ImmutableDict({}))}))" ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "tf_graph = dgf.convert.graph_to_tf_graph(graph)\n", + "tf_graph" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Cpps8GbsN2oz" + }, + "source": [ + "Let's convert our numpy-based graph into a jax-based graph.\n", + "\n", + "**Note:** JAX does not support string values." + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-23T18:16:18.998384Z", + "iopub.status.busy": "2026-09-23T18:16:18.998069Z", + "iopub.status.idle": "2026-09-23T18:16:19.791622Z", + "shell.execute_reply": "2026-09-23T18:16:19.791053Z" }, - { - "cell_type": "markdown", - "source": [ - "Let's convert our numpy-based graph into a jax-based graph.\n", - "\n", - "**Note:** JAX does not support string values." - ], - "metadata": { - "id": "Cpps8GbsN2oz" - } + "executionInfo": { + "elapsed": 795, + "status": "ok", + "timestamp": 1790187379792.6946, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 }, + "id": "5ZckujKFNqwP", + "outputId": "ce5ef7c8-8f2d-4efa-d123-01797974f8c1" + }, + "outputs": [ { - "cell_type": "code", - "source": [ - "graph_without_string_features = copy.deepcopy(graph)\n", - "graph_without_string_features.node_sets[\"nodeset_1\"].features.pop(\"#id\")\n", - "graph_without_string_features.node_sets[\"nodeset_1\"].features.pop(\"f1\")\n", - "graph_without_string_features.node_sets[\"nodeset_2\"].features.pop(\"#id\")\n", - "jax_graph = dgf.convert.graph_to_jax_graph(graph_without_string_features)\n", - "jax_graph" - ], - "metadata": { - "id": "5ZckujKFNqwP", - "executionInfo": { - "status": "ok", - "timestamp": 1777976581493, - "user_tz": -120, - "elapsed": 1896, - "user": { - "displayName": "", - "userId": "" - } - }, - "outputId": "ce5ef7c8-8f2d-4efa-d123-01797974f8c1" - }, - "execution_count": null, - "outputs": [ - { - "output_type": "execute_result", - "data": { - "text/plain": [ - "JaxInMemoryGraph(node_sets={'nodeset_1': JaxInMemoryNodeSet(features={'f2': Array([0.1, 0.5, 0.2], dtype=float32)}, num_nodes=3), 'nodeset_2': JaxInMemoryNodeSet(features={}, num_nodes=2)}, edge_sets={'edgeset_1': JaxInMemoryEdgeSet(adjacency=Array([[0, 0, 1],\n", - " [1, 1, 2]], dtype=int32), features={}), 'edgeset_2': JaxInMemoryEdgeSet(adjacency=Array([[0, 1],\n", - " [0, 1]], dtype=int32), features={}), 'edgeset_3': JaxInMemoryEdgeSet(adjacency=Array([[1, 0],\n", - " [0, 2]], dtype=int32), features={})})" - ] - }, - "metadata": {}, - "execution_count": 16 - } + "data": { + "text/plain": [ + "JaxInMemoryGraph(node_sets={'nodeset_1': JaxInMemoryNodeSet(features={'f2': Array([0.1, 0.5, 0.2], dtype=float32)}, num_nodes=3), 'nodeset_2': JaxInMemoryNodeSet(features={}, num_nodes=2)}, edge_sets={'edgeset_1': JaxInMemoryEdgeSet(adjacency=Array([[0, 0, 1],\n", + " [1, 1, 2]], dtype=int32), features={}), 'edgeset_2': JaxInMemoryEdgeSet(adjacency=Array([[0, 1],\n", + " [0, 1]], dtype=int32), features={}), 'edgeset_3': JaxInMemoryEdgeSet(adjacency=Array([[1, 0],\n", + " [0, 2]], dtype=int32), features={})})" ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "graph_without_string_features = copy.deepcopy(graph)\n", + "graph_without_string_features.node_sets[\"nodeset_1\"].features.pop(\"#id\")\n", + "graph_without_string_features.node_sets[\"nodeset_1\"].features.pop(\"f1\")\n", + "graph_without_string_features.node_sets[\"nodeset_2\"].features.pop(\"#id\")\n", + "jax_graph = dgf.convert.graph_to_jax_graph(graph_without_string_features)\n", + "jax_graph" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "fOJjq8RXRtJ7" + }, + "source": [ + "GF also supports conversion from / to other framework graphs.\n", + "\n", + "Let's convert the graph to a Sparse Deferred struct." + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-23T18:16:19.793724Z", + "iopub.status.busy": "2026-09-23T18:16:19.793374Z", + "iopub.status.idle": "2026-09-23T18:16:19.797736Z", + "shell.execute_reply": "2026-09-23T18:16:19.797310Z" }, - { - "cell_type": "markdown", - "source": [ - "GF also supports conversion from / to other framework graphs.\n", - "\n", - "Let's convert the graph to a Sparse Deferred struct." - ], - "metadata": { - "id": "fOJjq8RXRtJ7" - } + "executionInfo": { + "elapsed": 6, + "status": "ok", + "timestamp": 1790187379798.7744, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 }, + "id": "tNfGQRJeRzkb", + "outputId": "93c70d16-4eb6-4807-cf8d-9e725b7d1f64" + }, + "outputs": [ { - "cell_type": "code", - "source": [ - "dgf.convert.graph_to_sparse_deferred_struct(graph, schema)" - ], - "metadata": { - "id": "tNfGQRJeRzkb", - "executionInfo": { - "status": "ok", - "timestamp": 1777976581572, - "user_tz": -120, - "elapsed": 24, - "user": { - "displayName": "", - "userId": "" - } - }, - "outputId": "93c70d16-4eb6-4807-cf8d-9e725b7d1f64" - }, - "execution_count": null, - "outputs": [ - { - "output_type": "execute_result", - "data": { - "text/plain": [ - "GraphStruct(edges={'edgeset_1': ((array([0, 0, 1]), array([1, 1, 2])), {}), 'edgeset_2': ((array([0, 1]), array([0, 1])), {}), 'edgeset_3': ((array([1, 0]), array([0, 2])), {})}, nodes={'nodeset_1': {'#id': array([b'a', b'b', b'c'], dtype='|S1'), 'f1': array([b'X', b'Y', b'Y'], dtype='|S1'), 'f2': array([0.1, 0.5, 0.2], dtype=float32)}, 'nodeset_2': {'#id': array([b'u', b'v'], dtype='|S1')}}, schema_={'edgeset_1': ({'nodeset_1': 0}, {'nodeset_1': 0}), 'edgeset_2': ({'nodeset_1': 0}, {'nodeset_2': 0}), 'edgeset_3': ({'nodeset_2': 0}, {'nodeset_1': 0})})" - ] - }, - "metadata": {}, - "execution_count": 17 - } + "data": { + "text/plain": [ + "GraphStruct(edges={'edgeset_1': ((array([0, 0, 1]), array([1, 1, 2])), {}), 'edgeset_2': ((array([0, 1]), array([0, 1])), {}), 'edgeset_3': ((array([1, 0]), array([0, 2])), {})}, nodes={'nodeset_1': {'#id': array([b'a', b'b', b'c'], dtype='|S1'), 'f1': array([b'X', b'Y', b'Y'], dtype='|S1'), 'f2': array([0.1, 0.5, 0.2], dtype=float32)}, 'nodeset_2': {'#id': array([b'u', b'v'], dtype='|S1')}}, schema_={'edgeset_1': ({'nodeset_1': 0}, {'nodeset_1': 0}), 'edgeset_2': ({'nodeset_1': 0}, {'nodeset_2': 0}), 'edgeset_3': ({'nodeset_2': 0}, {'nodeset_1': 0})})" ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "dgf.convert.graph_to_sparse_deferred_struct(graph, schema)" + ] + } + ], + "metadata": { + "colab": { + "last_runtime": { + "build_target": "", + "kind": "local" + }, + "provenance": [ + { + "file_id": "1hMr9gK0hhKcCtIs1Pro3_ksOnYcdAL2P", + "timestamp": 1771583153488 + } + ], + "toc_visible": true, + "views": { + "output_only": { + "cells": [ + { + "id": "woDLT7o2leXI" + }, + { + "id": "vsWpEbNGXFdO" + }, + { + "id": "QdunX1vTlM3G" + }, + { + "id": "Jzjqg7FWV6dq" + }, + { + "id": "RKXovz93qQAB" + }, + { + "id": "z3Tx-P_MlPuu" + }, + { + "id": "r4YYlN8uLDib" + }, + { + "id": "lh4TDnbCLSZf" + }, + { + "id": "N0KZpip-LVIn" + }, + { + "id": "6b0ih-HtnTIy" + }, + { + "id": "ykRurJ1dnIYy" + }, + { + "id": "kUWXGckKL-rx" + }, + { + "id": "cosZMiP7MKXq" + }, + { + "id": "iFvmkAOUMfih" + }, + { + "id": "SG8bTpdZfx5J" + }, + { + "id": "e21fYUDONDPi" + }, + { + "id": "8W3sIXkYNGNR" + }, + { + "id": "NMulQq-uf7Bs" + }, + { + "id": "YJMnJmjJNMJX" + }, + { + "id": "p_QqkZv5NR-p" + }, + { + "id": "CSQ_HNlxNW_F" + }, + { + "id": "5ximSUnBg0K2" + }, + { + "id": "ER2VMWCEhLZL" + }, + { + "id": "ADPhK__Ohbv-" + }, + { + "id": "WBrKMPCxh1OL" + }, + { + "id": "O6PuedLWh_jc" + }, + { + "id": "XfOajTfOh3Q_" + }, + { + "id": "O2XJMDoeiIox" + }, + { + "id": "FedgFX6miSv9" + }, + { + "id": "Hy3YoHQ-iic3" + }, + { + "id": "jqd0V9lEi2Hs" + }, + { + "id": "oLUvgYZPi8DW" + }, + { + "id": "MEO5MAgbf908" + }, + { + "id": "zbRj_6GTNheC" + }, + { + "id": "qY5Wvo2WN50f" + }, + { + "id": "Cpps8GbsN2oz" + }, + { + "id": "5ZckujKFNqwP" + }, + { + "id": "fOJjq8RXRtJ7" + }, + { + "id": "tNfGQRJeRzkb" + } + ], + "hide_code": true } - ] + } + }, + "kernelspec": { + "display_name": "Python 3", + "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.14.5" + } + }, + "nbformat": 4, + "nbformat_minor": 0 } diff --git a/doc/docs/tutorial/link_prediction.ipynb b/doc/docs/tutorial/link_prediction.ipynb index b72661a..d1c4561 100644 --- a/doc/docs/tutorial/link_prediction.ipynb +++ b/doc/docs/tutorial/link_prediction.ipynb @@ -1,1386 +1,2232 @@ { - "nbformat": 4, - "nbformat_minor": 0, - "metadata": { - "colab": { - "provenance": [ - { - "file_id": "1hMr9gK0hhKcCtIs1Pro3_ksOnYcdAL2P", - "timestamp": 1771583153488 - } - ], - "last_runtime": { - "build_target": "", - "kind": "local" - } + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "woDLT7o2leXI" + }, + "source": [ + "## Link Prediction with the Simple API\n", + "\n", + "[![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/google/distributed_graph_flow/blob/main/doc/docs/tutorial/link_prediction.ipynb)\n", + "\n", + "Link prediction Graph Neural Networks (GNNs) can predict the probability for an\n", + "edge to existe between two nodes.\n", + "\n", + "**Part 1:** This tutorial shows how to train, analyze, and evaluate a link\n", + "prediction model on the Arxiv citation graph. The objective is to predict the\n", + "probability of citation between two papers i.e. train a model \"Model(paper1,\n", + "paper2) -> probability of paper1 citying paper2\".\n", + "\n", + "**Part 2:** Running a model on each pair of papers/nodes is computationally\n", + "expensive. To improve serving scalability, we can use a decomposable model,\n", + "i.e., a model decomposable as \"Model(paper1, paper2) := Encoder1(paper1) *\n", + "Encoder2(paper2)\". By separating the model into two independent encoders, paper\n", + "representations can be pre-computed, and only the dot product is required for\n", + "the final prediction. We show this in this tutorial.\n", + "\n", + "**Part 3:** If the goal is to retrieve the most likely citation for a specific\n", + "paper, calculating the probability of all possible other paper is still\n", + "inefficient. Instead, the output of one encoder can be indexed in a vector\n", + "database. This allows us for efficient retrieval of the *closest* paper. We show\n", + "this in this tutorial.\n", + "\n", + "**[Not yet available] Part 4:** By default, GNNs use existing edges as both\n", + "features for message passing and as labels for the training objective. However,\n", + "some workflows require isolating specific edge sets so the model cannot \"see\"\n", + "them during the forward pass. We show how to handle cases where an edgeset acts\n", + "as a pure label.\n", + "\n", + "**[Not yet available] Part 5:** Finally, production models must respect temporal\n", + "constraints, ensuring a model does not access future data to predict past\n", + "events. Training a model with this restriction, known as temporal-aware\n", + "modeling, ensures performance metrics remain realistic at serving time. The last\n", + "section of this tutorial demonstrates how to implement this temporal masking." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "UkBFBelLpoNj" + }, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "peHHpZIGJnaO" + }, + "source": [ + "## Installing GF\n", + "\n", + "Make sure your machine has a GPU or TPU, otherwise training is going to take forever.\n", + "If you are using Google Colab, you can get one for free. Just go to Edit > Notebook settings and select your hardware accelerator as TPU or GPU." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-23T18:17:11.436805Z", + "iopub.status.busy": "2026-09-23T18:17:11.436630Z", + "iopub.status.idle": "2026-09-23T18:17:11.684748Z", + "shell.execute_reply": "2026-09-23T18:17:11.684254Z" + }, + "executionInfo": { + "elapsed": 290, + "status": "ok", + "timestamp": 1790187431726.6401, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 + }, + "id": "ZdP1A5AIXGSe" + }, + "outputs": [], + "source": [ + "# Install DGF (Distributed Graph Flow) and OGB (for the toy dataset).\n", + "!pip install dgf ogb -U" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "RKXovz93qQAB" + }, + "source": [ + "## Importing libraries" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-23T18:17:11.728042Z", + "iopub.status.busy": "2026-09-23T18:17:11.727759Z", + "iopub.status.idle": "2026-09-23T18:17:15.839871Z", + "shell.execute_reply": "2026-09-23T18:17:15.839240Z" + }, + "executionInfo": { + "elapsed": 4114, + "status": "ok", + "timestamp": 1790187435841.1377, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 + }, + "id": "z3Tx-P_MlPuu" + }, + "outputs": [], + "source": [ + "import dgf # Import Graph Flow\n", + "import jax\n", + "import numpy as np" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "3kshVj5GlEYT" + }, + "source": [ + "## Get Graph data\n", + "\n", + "We first fetch the Arxiv graph." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-23T18:17:15.842332Z", + "iopub.status.busy": "2026-09-23T18:17:15.841915Z", + "iopub.status.idle": "2026-09-23T18:17:15.915608Z", + "shell.execute_reply": "2026-09-23T18:17:15.915139Z" }, - "kernelspec": { - "name": "python3", - "display_name": "Python 3" + "executionInfo": { + "elapsed": 75, + "status": "ok", + "timestamp": 1790187435916.968, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 }, - "language_info": { - "name": "python" + "id": "xehdItoPlIaP", + "outputId": "baaa65be-a5bb-4483-c310-a1c12d16b8c5" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Caching arxiv graph at /tmp/gf_fetch/arxiv.cache\n", + "OGB dependency not available. Downloading graph from CNS.\n" + ] } + ], + "source": [ + "# Download the Mag graph from the OGB repo.\n", + "graph, schema = dgf.io.fetch_ogb_graph(\"arxiv\")" + ] }, - "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "d8wKxr1TlLRk" + }, + "source": [ + "Let's look at the graph structure." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-23T18:17:15.917915Z", + "iopub.status.busy": "2026-09-23T18:17:15.917732Z", + "iopub.status.idle": "2026-09-23T18:17:15.922044Z", + "shell.execute_reply": "2026-09-23T18:17:15.921659Z" + }, + "executionInfo": { + "elapsed": 6, + "status": "ok", + "timestamp": 1790187435923.0955, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 + }, + "id": "tWWsdUtblJ8i", + "outputId": "4f4427d5-ed97-4116-bc5b-fc3a48de90ca" + }, + "outputs": [ { - "cell_type": "markdown", - "source": [ - "## Link Prediction with the Simple API\n", - "\n", - "[![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/google/distributed_graph_flow/blob/main/doc/docs/tutorial/link_prediction.ipynb)\n", - "\n", - "Link prediction Graph Neural Networks (GNNs) can predict the probability for an\n", - "edge to existe between two nodes.\n", - "\n", - "**Part 1:** This tutorial shows how to train, analyze, and evaluate a link\n", - "prediction model on the Arxiv citation graph. The objective is to predict the\n", - "probability of citation between two papers i.e. train a model \"Model(paper1,\n", - "paper2) -> probability of paper1 citying paper2\".\n", - "\n", - "**Part 2:** Running a model on each pair of papers/nodes is computationally\n", - "expensive. To improve serving scalability, we can use a decomposable model,\n", - "i.e., a model decomposable as \"Model(paper1, paper2) := Encoder1(paper1) *\n", - "Encoder2(paper2)\". By separating the model into two independent encoders, paper\n", - "representations can be pre-computed, and only the dot product is required for\n", - "the final prediction. We show this in this tutorial.\n", - "\n", - "**Part 3:** If the goal is to retrieve the most likely citation for a specific\n", - "paper, calculating the probability of all possible other paper is still\n", - "inefficient. Instead, the output of one encoder can be indexed in a vector\n", - "database. This allows us for efficient retrieval of the *closest* paper. We show\n", - "this in this tutorial.\n", - "\n", - "**[Not yet available] Part 4:** By default, GNNs use existing edges as both\n", - "features for message passing and as labels for the training objective. However,\n", - "some workflows require isolating specific edge sets so the model cannot \"see\"\n", - "them during the forward pass. We show how to handle cases where an edgeset acts\n", - "as a pure label.\n", - "\n", - "**[Not yet available] Part 5:** Finally, production models must respect temporal\n", - "constraints, ensuring a model does not access future data to predict past\n", - "events. Training a model with this restriction, known as temporal-aware\n", - "modeling, ensures performance metrics remain realistic at serving time. The last\n", - "section of this tutorial demonstrates how to implement this temporal masking." - ], - "metadata": { - "id": "woDLT7o2leXI" - } + "name": "stdout", + "output_type": "stream", + "text": [ + "Graph Schema:\n", + "\n", + "Node Sets:\n", + " nodes:\n", + " | Feature | Format | Semantic | Shape | Detail |\n", + " |-----------|------------|-------------|---------|-------------|\n", + " | #id | BYTES | PRIMARY_ID | None | |\n", + " | #split | BYTES | CATEGORICAL | None | |\n", + " | feat | FLOAT_32 | EMBEDDING | (128,) | |\n", + " | labels | INTEGER_64 | CATEGORICAL | None | #num.cat:40 |\n", + " | year | INTEGER_64 | NUMERICAL | None | |\n", + "\n", + "\n", + "Edge Sets:\n", + " edges: (Source: nodes, Target: nodes)\n", + " (No features)\n", + "\n" + ] + } + ], + "source": [ + "# Show the schema\n", + "dgf.analyse.print_schema(schema)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "GnQTZpR3P0oL" + }, + "source": [ + "This graph has a \"label\" feature for node prediction. We don't need it for link\n", + "prediction, so we can mask it in the schema." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-23T18:17:15.924007Z", + "iopub.status.busy": "2026-09-23T18:17:15.923726Z", + "iopub.status.idle": "2026-09-23T18:17:15.927078Z", + "shell.execute_reply": "2026-09-23T18:17:15.926664Z" }, + "executionInfo": { + "elapsed": 5, + "status": "ok", + "timestamp": 1790187435928.1526, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 + }, + "id": "8PkikQwxPuHP", + "outputId": "44fe2708-1fe2-4852-bd52-a561ce86ace9" + }, + "outputs": [ { - "cell_type": "markdown", - "source": [ - "## Installing GF\n", - "\n", - "Make sure your machine has a GPU or TPU, otherwise training is going to take forever.\n", - "If you are using Google Colab, you can get one for free. Just go to Edit > Notebook settings and select your hardware accelerator as TPU or GPU." - ], - "metadata": { - "id": "CcpThJiJW1jY" - } + "name": "stdout", + "output_type": "stream", + "text": [ + "Graph Schema:\n", + "\n", + "Node Sets:\n", + " nodes:\n", + " | Feature | Format | Semantic | Shape | Detail |\n", + " |-----------|------------|------------|---------|----------|\n", + " | #id | BYTES | PRIMARY_ID | None | |\n", + " | feat | FLOAT_32 | EMBEDDING | (128,) | |\n", + " | year | INTEGER_64 | NUMERICAL | None | |\n", + "\n", + "\n", + "Edge Sets:\n", + " edges: (Source: nodes, Target: nodes)\n", + " (No features)\n", + "\n" + ] + } + ], + "source": [ + "del schema.node_sets[\"nodes\"].features[\"#split\"]\n", + "del schema.node_sets[\"nodes\"].features[\"labels\"]\n", + "dgf.analyse.print_schema(schema)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "F3Br8wailGDS" + }, + "source": [ + "## Part 1: Training model\n", + "\n", + "We train a model to predict edges between papers." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-23T18:17:15.929165Z", + "iopub.status.busy": "2026-09-23T18:17:15.928750Z", + "iopub.status.idle": "2026-09-23T18:18:51.483361Z", + "shell.execute_reply": "2026-09-23T18:18:51.482935Z" + }, + "executionInfo": { + "elapsed": 95556, + "status": "ok", + "timestamp": 1790187531484.3857, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 }, + "id": "eayALqcllTm0", + "outputId": "1ea1ef56-2f6e-469e-9892-9810c713c678" + }, + "outputs": [ { - "cell_type": "code", - "source": [ - "# Install DGF (Distributed Graph Flow) and OGB (for the toy dataset).\n", - "!pip install dgf ogb -U" - ], - "metadata": { - "id": "-kP_WU8wW6IZ" - }, - "execution_count": null, - "outputs": [] + "name": "stdout", + "output_type": "stream", + "text": [ + "Using gpu JAX backend\n", + "Graph input schema:\n", + "Node Sets:\n", + " nodes:\n", + " | Feature | Format | Semantic | Shape | Detail |\n", + " |-----------|------------|------------|---------|----------|\n", + " | #id | BYTES | PRIMARY_ID | None | |\n", + " | feat | FLOAT_32 | EMBEDDING | (128,) | |\n", + " | year | INTEGER_64 | NUMERICAL | None | |\n", + "\n", + "\n", + "Edge Sets:\n", + " edges: (Source: nodes, Target: nodes)\n", + " (No features)\n", + "\n", + "Preparing dataset\n" + ] }, { - "cell_type": "markdown", - "source": [ - "## Importing libraries" - ], - "metadata": { - "id": "RKXovz93qQAB" - } + "name": "stderr", + "output_type": "stream", + "text": [ + "[Warning] Validation set truncated from 116624 to 8000 nodes due to caching limits. To use more validation nodes, either increase `num_valid_steps` or set `cache_valid_dataset=False`.\n" + ] }, { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "z3Tx-P_MlPuu" - }, - "outputs": [], - "source": [ - "import os\n", - "\n", - "os.environ[\"TF_USE_LEGACY_KERAS\"] = \"1\"\n", - "\n", - "import dgf # Import Graph Flow\n", - "import jax\n", - "import numpy as np" - ] + "name": "stdout", + "output_type": "stream", + "text": [ + "Num. training seed edges: 1158243, Num. validation seed edges: 8000\n", + "Create graph sampler\n", + "Compute feature statistics\n", + " Source stats:\n", + "GraphFeatureStatistics:\n", + " Node Sets (1):\n", + " 'nodes':\n", + " '#id': count=19580, min=nan, max=nan\n", + " 'feat': count=19580, min=nan, max=nan\n", + " 'year': count=19580, min=1971.0000, max=2020.0000, quantiles=(100)[1992.0000, 2008.0000, 2009.0000, ..., 2020.0000, 2020.0000, 2020.0000]\n", + "\n", + " Target stats:\n", + "GraphFeatureStatistics:\n", + " Node Sets (1):\n", + " 'nodes':\n", + " '#id': count=1659, min=nan, max=nan\n", + " 'feat': count=1659, min=nan, max=nan\n", + " 'year': count=1659, min=1994.0000, max=2020.0000, quantiles=(100)[1994.0000, 2007.0000, 2008.0000, ..., 2020.0000, 2020.0000, 2020.0000]\n", + "\n" + ] }, { - "cell_type": "markdown", - "source": [ - "## Get Graph data\n", - "\n", - "We first fetch the Arxiv graph." - ], - "metadata": { - "id": "3kshVj5GlEYT" - } + "name": "stderr", + "output_type": "stream", + "text": [ + "[Warning] No normalizer created for node set 'nodes', feature '#id'.\n", + "[Warning] No normalizer created for node set 'nodes', feature '#id'.\n" + ] }, { - "cell_type": "code", - "source": [ - "# Download the Mag graph from the OGB repo.\n", - "graph, schema = dgf.io.fetch_ogb_graph(\"arxiv\")" - ], - "metadata": { - "id": "xehdItoPlIaP", - "executionInfo": { - "status": "ok", - "timestamp": 1779800255620, - "user_tz": -120, - "elapsed": 901, - "user": { - "displayName": "", - "userId": "" - } - }, - "outputId": "eaf3b96c-1c1e-445f-afd1-7fd9100389e8" - }, - "execution_count": null, - "outputs": [ - { - "output_type": "stream", - "name": "stdout", - "text": [ - "Caching arxiv graph at /tmp/gf_fetch/arxiv.cache\n", - "OGB dependency not available. Downloading graph from CNS.\n" - ] - } - ] + "name": "stdout", + "output_type": "stream", + "text": [ + "Compute graph statistics for padding\n", + " positive source padding: Node Sets:\n", + " nodes: 228 nodes\n", + "\n", + "Edge Sets:\n", + " edges: 221 edges\n", + " positive target padding: Node Sets:\n", + " nodes: 262 nodes\n", + "\n", + "Edge Sets:\n", + " edges: 256 edges\n", + " negative target padding: Node Sets:\n", + " nodes: 866 nodes\n", + "\n", + "Edge Sets:\n", + " edges: 804 edges\n", + "Preparing dataset finished in 2.04 seconds\n", + "Source normalizer:\n", + "Graph Normalizer:\n", + "\n", + "Node Sets:\n", + " nodes:\n", + " - feat: IdentityNormalizer\n", + " - year: SoftQuantileNormalizer\n", + "\n", + "Edge Sets:\n", + " edges: (Source: nodes, Target: nodes)\n", + " (No normalizers)\n", + "\n", + "Target normalizer:\n", + "Graph Normalizer:\n", + "\n", + "Node Sets:\n", + " nodes:\n", + " - feat: IdentityNormalizer\n", + " - year: SoftQuantileNormalizer\n", + "\n", + "Edge Sets:\n", + " edges: (Source: nodes, Target: nodes)\n", + " (No normalizers)\n", + "\n", + "Source normalized graph schema:\n", + "Node Sets:\n", + " nodes:\n", + " | Feature | Format | Semantic | Shape | Detail |\n", + " |--------------------|----------|------------|---------|----------|\n", + " | feat | FLOAT_32 | EMBEDDING | (128,) | |\n", + " | year_SOFT_QUANTILE | FLOAT_32 | EMBEDDING | () | |\n", + "\n", + "\n", + "Edge Sets:\n", + " edges: (Source: nodes, Target: nodes)\n", + " (No features)\n", + "\n", + "Target normalized graph schema:\n", + "Node Sets:\n", + " nodes:\n", + " | Feature | Format | Semantic | Shape | Detail |\n", + " |--------------------|----------|------------|---------|----------|\n", + " | feat | FLOAT_32 | EMBEDDING | (128,) | |\n", + " | year_SOFT_QUANTILE | FLOAT_32 | EMBEDDING | () | |\n", + "\n", + "\n", + "Edge Sets:\n", + " edges: (Source: nodes, Target: nodes)\n", + " (No features)\n", + "\n", + "Caching validation dataset\n" + ] }, { - "cell_type": "markdown", - "source": [ - "Let's look at the graph structure." - ], - "metadata": { - "id": "d8wKxr1TlLRk" - } + "name": "stderr", + "output_type": "stream", + "text": [ + "Caching validation dataset: 100%|██████████| 1000/1000 [00:05<00:00, 173.58it/s]" + ] }, { - "cell_type": "code", - "source": [ - "# Show the schema\n", - "dgf.analyse.print_schema(schema)" - ], - "metadata": { - "id": "tWWsdUtblJ8i", - "executionInfo": { - "status": "ok", - "timestamp": 1779800255680, - "user_tz": -120, - "elapsed": 15, - "user": { - "displayName": "", - "userId": "" - } - }, - "outputId": "2856afd6-205f-44cc-df4a-38da9878f6cb" - }, - "execution_count": null, - "outputs": [ - { - "output_type": "stream", - "name": "stdout", - "text": [ - "Graph Schema:\n", - "\n", - "Node Sets:\n", - " nodes:\n", - " | Feature | Format | Semantic | Shape | Num cat. vals |\n", - " |-----------|------------|-------------|---------|-----------------|\n", - " | #id | BYTES | PRIMARY_ID | None | None |\n", - " | #split | BYTES | CATEGORICAL | None | None |\n", - " | feat | FLOAT_32 | EMBEDDING | (128,) | None |\n", - " | labels | INTEGER_64 | CATEGORICAL | None | 40 |\n", - " | year | INTEGER_64 | NUMERICAL | None | None |\n", - "\n", - "\n", - "Edge Sets:\n", - " edges: (Source: nodes, Target: nodes)\n", - " (No features)\n", - "\n" - ] - } - ] + "name": "stdout", + "output_type": "stream", + "text": [ + "Caching validation dataset finished in 5.76 seconds\n", + "Number of cache validation batches: 1000\n", + "Training model\n", + "Generate first batch to initialize model\n" + ] }, { - "cell_type": "markdown", - "source": [ - "This graph has a \"label\" feature for node prediction. We don't need it for link\n", - "prediction, so we can mask it in the schema." - ], - "metadata": { - "id": "GnQTZpR3P0oL" - } + "name": "stderr", + "output_type": "stream", + "text": [ + "\n" + ] }, { - "cell_type": "code", - "source": [ - "del schema.node_sets[\"nodes\"].features[\"#split\"]\n", - "del schema.node_sets[\"nodes\"].features[\"labels\"]\n", - "dgf.analyse.print_schema(schema)" - ], - "metadata": { - "id": "8PkikQwxPuHP", - "executionInfo": { - "status": "ok", - "timestamp": 1779800255737, - "user_tz": -120, - "elapsed": 19, - "user": { - "displayName": "", - "userId": "" - } - }, - "outputId": "f6a931d0-4b84-4a5b-a240-9eb8c595ecf2" - }, - "execution_count": null, - "outputs": [ - { - "output_type": "stream", - "name": "stdout", - "text": [ - "Graph Schema:\n", - "\n", - "Node Sets:\n", - " nodes:\n", - " | Feature | Format | Semantic | Shape | Num cat. vals |\n", - " |-----------|------------|------------|---------|-----------------|\n", - " | #id | BYTES | PRIMARY_ID | None | None |\n", - " | feat | FLOAT_32 | EMBEDDING | (128,) | None |\n", - " | year | INTEGER_64 | NUMERICAL | None | None |\n", - "\n", - "\n", - "Edge Sets:\n", - " edges: (Source: nodes, Target: nodes)\n", - " (No features)\n", - "\n" - ] - } - ] + "name": "stdout", + "output_type": "stream", + "text": [ + "Create model variables\n", + "...Tracing model\n", + "Create model variables finished in 13.52 seconds\n", + "Will validate model every 1000 step(s)\n", + "Will checkpoint model every 1000 step(s)\n", + "Start training. The first two steps are generally slow.\n" + ] }, { - "cell_type": "markdown", - "source": [ - "## Part 1: Training model\n", - "\n", - "We train a model to predict edges between papers." - ], - "metadata": { - "id": "F3Br8wailGDS" - } + "name": "stderr", + "output_type": "stream", + "text": [ + "Training: 0%| | 0/5000 [00:00" - ], - "text/html": [ - "
\n", - "\n", - "\n", - "\n", - "
\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "
\n", - "
\n", - "

Node prediction model: Predict the value of a node feature.

\n", - "
    \n", - "
  • Target edgeset: edges
  • \n", - "
\n", - "
\n", - "
\n", - "\n", - "
    \n", - "
  • Number of training seed edges: 1158243
  • \n", - "
  • Number of validation seed edges: 8000
  • \n", - "
  • Training duration: 2m 39s
  • \n", - "
\n", - "\n", - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - "\n", - "\n", - "
\n", - " \n", - "\n", - "
\n", - "
\n", - "
num_sampling_hops=1\n",
-              "sampling_width=15\n",
-              "num_layers=2\n",
-              "batch_size=8\n",
-              "max_training_time_seconds=None\n",
-              "num_train_steps=5000\n",
-              "random_seed=42\n",
-              "node_embedding_dim=128\n",
-              "learning_rate=0.001\n",
-              "opt_weight_decay=0.0001\n",
-              "dropout=0.1\n",
-              "message_pooling='sum'\n",
-              "architecture=\n",
-              "num_negative_nodes=8\n",
-              "message_passing_on_target_edgeset=True\n",
-              "negative_edges='random'\n",
-              "random_walk_num_walks_per_negative=10
\n", - "
Raw schema
\n",
-              "Node Sets:\n",
-              "  nodes:\n",
-              "    | Feature   | Format     | Semantic   | Shape   | Num cat. vals   |\n",
-              "    |-----------|------------|------------|---------|-----------------|\n",
-              "    | #id       | BYTES      | PRIMARY_ID | None    | None            |\n",
-              "    | feat      | FLOAT_32   | EMBEDDING  | (128,)  | None            |\n",
-              "    | year      | INTEGER_64 | NUMERICAL  | None    | None            |\n",
-              "\n",
-              "\n",
-              "Edge Sets:\n",
-              "  edges: (Source: nodes, Target: nodes)\n",
-              "    (No features)\n",
-              "

\n", - "Source Normalized schema
\n",
-              "Node Sets:\n",
-              "  nodes:\n",
-              "    | Feature            | Format   | Semantic   | Shape   | Num cat. vals   |\n",
-              "    |--------------------|----------|------------|---------|-----------------|\n",
-              "    | feat               | FLOAT_32 | EMBEDDING  | (128,)  | None            |\n",
-              "    | year_SOFT_QUANTILE | FLOAT_32 | EMBEDDING  | None    | None            |\n",
-              "\n",
-              "\n",
-              "Edge Sets:\n",
-              "  edges: (Source: nodes, Target: nodes)\n",
-              "    (No features)\n",
-              "

\n", - "Target Normalized schema
\n",
-              "Node Sets:\n",
-              "  nodes:\n",
-              "    | Feature            | Format   | Semantic   | Shape   | Num cat. vals   |\n",
-              "    |--------------------|----------|------------|---------|-----------------|\n",
-              "    | feat               | FLOAT_32 | EMBEDDING  | (128,)  | None            |\n",
-              "    | year_SOFT_QUANTILE | FLOAT_32 | EMBEDDING  | None    | None            |\n",
-              "\n",
-              "\n",
-              "Edge Sets:\n",
-              "  edges: (Source: nodes, Target: nodes)\n",
-              "    (No features)\n",
-              "

\n", - "
\n", - "
Source feature statistics\n", - "
GraphFeatureStatistics:\n",
-              "  Node Sets (1):\n",
-              "    'nodes':\n",
-              "      '#id': count=19307, min=nan, max=nan\n",
-              "      'feat': count=19307, min=nan, max=nan\n",
-              "      'year': count=19307, min=1990.0000, max=2020.0000, quantiles=(100)[1990.0000, 2007.0000, 2009.0000, ..., 2020.0000, 2020.0000, 2020.0000]\n",
-              "

\n", - "Target feature statistics\n", - "
GraphFeatureStatistics:\n",
-              "  Node Sets (1):\n",
-              "    'nodes':\n",
-              "      '#id': count=1544, min=nan, max=nan\n",
-              "      'feat': count=1544, min=nan, max=nan\n",
-              "      'year': count=1544, min=1971.0000, max=2020.0000, quantiles=(100)[1971.0000, 2006.0000, 2009.0000, ..., 2020.0000, 2020.0000, 2020.0000]\n",
-              "

\n", - "
\n", - "
Source sampling plan
\n",
-              "Root: nodes\n",
-              "├── edges [width=15] ➔ nodes\n",
-              "└── edges (reversed) [width=15] ➔ nodes

\n", - "Target sampling plan
\n",
-              "Root: nodes\n",
-              "├── edges [width=15] ➔ nodes\n",
-              "└── edges (reversed) [width=15] ➔ nodes

\n", - "
\n", - "
Model Structure
EmbedGraph(cat-embedding=64)\n",
-              "Dense(128)\n",
-              "Activation(silu)\n",
-              "Norm(layer_norm)\n",
-              "Graph Convolution Block x2:\n",
-              "    X = ...\n",
-              "    MPNN:\n",
-              "      Message:\n",
-              "        Dense(128)\n",
-              "        Activation(silu)\n",
-              "        Dense(128)\n",
-              "      Update:\n",
-              "        Dense(128)\n",
-              "        Activation(silu)\n",
-              "        Dropout(0.1)\n",
-              "        Dense(128)\n",
-              "    Residual(X)\n",
-              "    # Post MPNN\n",
-              "    X = ...\n",
-              "    Norm(rms_norm)\n",
-              "    Dense(512)\n",
-              "    Activation(silu)\n",
-              "    Dense(128)\n",
-              "    Dropout(0.1)\n",
-              "    Residual(X)\n",
-              "Identity
Model Weights
{'float32': 1154048}
\n", - "
Positive Source padding
\n",
-              "Node Sets:\n",
-              "  nodes: 219 nodes\n",
-              "\n",
-              "Edge Sets:\n",
-              "  edges: 213 edges

\n", - "Positive Target padding
\n",
-              "Node Sets:\n",
-              "  nodes: 270 nodes\n",
-              "\n",
-              "Edge Sets:\n",
-              "  edges: 261 edges

\n", - "Negative Target padding
\n",
-              "Node Sets:\n",
-              "  nodes: 906 nodes\n",
-              "\n",
-              "Edge Sets:\n",
-              "  edges: 848 edges

\n", - "
\n", - "\n", - "\n", - "\n", - "
" - ] - }, - "metadata": {}, - "execution_count": 6 - } - ] + "name": "stdout", + "output_type": "stream", + "text": [ + "...Tracing model\n" + ] }, { - "cell_type": "markdown", - "source": [ - "After training, a model is generally evaluated on a test dataset/graph.\n", - "\n", - "Note: We don't have a test graph, so we use our training dataset here. In a real\n", - "pipeline, evaluating a model on a training dataset make little sense." - ], - "metadata": { - "id": "RQZkBB7PlW1B" - } + "name": "stderr", + "output_type": "stream", + "text": [ + "Training: 20%|██ | 1018/5000 [00:28<03:17, 20.15it/s, step=1000, train-auc=0.8798, train-hit_at_1=0.5737, train-hit_at_5=0.9550, train-loss=0.2400, train-mrr=0.7325]" + ] }, { - "cell_type": "code", - "source": [ - "model.evaluate(graph)" - ], - "metadata": { - "id": "xDn1zgMglT37", - "colab": { - "height": 226 - }, - "executionInfo": { - "status": "ok", - "timestamp": 1779800453025, - "user_tz": -120, - "elapsed": 36862, - "user": { - "displayName": "", - "userId": "" - } - }, - "outputId": "c33737aa-8dd3-4b0a-f5bf-db3eb064b678" - }, - "execution_count": null, - "outputs": [ - { - "output_type": "stream", - "name": "stdout", - "text": [ - "Evaluating model on 10000 edges\n" - ] - }, - { - "output_type": "stream", - "name": "stderr", - "text": [ - "\rInference: 0%| | 0/11250 [00:00Evaluation\n", - "
    \n", - "
  • Num Examples: 10000
  • \n", - "
  • MRR: 0.8867838492063469
  • \n", - "
  • AUC: 0.960125
  • \n", - "
  • Hit@N:
      \n", - "
    • @1: 0.803
    • \n", - "
    • @5: 0.9937
    • \n", - "
  • \n", - "
" - ] - }, - "metadata": {}, - "execution_count": 7 - } - ] + "name": "stdout", + "output_type": "stream", + "text": [ + "Validation loop took 1.91s (only printed once)\n", + "step:1000 train-auc:0.8798 train-hit_at_1:0.5737 train-hit_at_5:0.9550 train-loss:0.2400 train-mrr:0.7325 valid-auc:0.9130 valid-hit_at_1:0.6570 valid-hit_at_5:0.9669 valid-loss:0.2873 valid-mrr:0.7922\n" + ] }, { - "cell_type": "markdown", - "source": [ - "We can make predictions for individual nodes:" - ], - "metadata": { - "id": "LMkzkPlulZUP" - } + "name": "stderr", + "output_type": "stream", + "text": [ + "Training: 28%|██▊ | 1404/5000 [00:32<00:37, 95.84it/s, step=1400, train-auc=0.9116, train-hit_at_1=0.6388, train-hit_at_5=0.9712, train-loss=0.1409, train-mrr=0.7829, valid-auc=0.9130, valid-hit_at_1=0.6570, valid-hit_at_5=0.9669, valid-loss=0.2873, valid-mrr=0.7922][Warning] Skipping batch due to padding overflow. Consider increasing num_samples_for_stats. Error: Padding for node set 'nodes' is insufficient. Required at least 877 nodes (including the sentinel node), but the padder only defines 866.\n", + "Training: 40%|████ | 2010/5000 [00:39<01:42, 29.19it/s, step=2000, train-auc=0.9302, train-hit_at_1=0.7175, train-hit_at_5=0.9725, train-loss=0.1064, train-mrr=0.8295, valid-auc=0.9130, valid-hit_at_1=0.6570, valid-hit_at_5=0.9669, valid-loss=0.2873, valid-mrr=0.7922]" + ] }, { - "cell_type": "code", - "source": [ - "# Predict the probability of an edge between nodes 0 and 1.\n", - "predictions = model.predict(graph, source_node_idxs=[0], target_node_idxs=[1])\n", - "print()\n", - "print(\"Source node id:\", graph.node_sets[\"nodes\"].features[\"#id\"][0])\n", - "print(\"Target node id:\", graph.node_sets[\"nodes\"].features[\"#id\"][1])\n", - "print(\"Probability of an edge between the two:\", predictions)" - ], - "metadata": { - "id": "pJgIjWr2lbvU", - "executionInfo": { - "status": "ok", - "timestamp": 1779800454594, - "user_tz": -120, - "elapsed": 1444, - "user": { - "displayName": "", - "userId": "" - } - }, - "outputId": "8c8f9f27-ebb9-49f2-a803-7c3684f7fb1a" - }, - "execution_count": null, - "outputs": [ - { - "output_type": "stream", - "name": "stderr", - "text": [ - "\rInference: 0%| | 0/1 [00:00\n", + "\n", + "\n", + "\n", + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
\n", + "
\n", + "\n", + " \n", + " \n", + " \n", + " \n", + "
TypeLink prediction model - Predict the probability of an edge.
Target edgesetedges
\n", + "
\n", + "
\n", + "\n", + " \n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + " \n", + "
WARNINGValidation set truncated from 116624 to 8000 nodes due to caching limits. To use more validation nodes, either increase `num_valid_steps` or set `cache_valid_dataset=False`.
WARNINGNo normalizer created for node set 'nodes', feature '#id'.
WARNINGNo normalizer created for node set 'nodes', feature '#id'.
WARNINGSkipping batch due to padding overflow. Consider increasing num_samples_for_stats. Error: Padding for node set 'nodes' is insufficient. Required at least 877 nodes (including the sentinel node), but the padder only defines 866.
WARNINGSkipping batch due to padding overflow. Consider increasing num_samples_for_stats. Error: Padding for edge set 'edges' is insufficient. Required at least 805 edges, but the padder only defines 804.
\n", + "
\n", + "
\n", + "\n", + " \n", + "\n", + " \n", + " \n", + " \n", + "\n", + " \n", + " \n", + "
Number of training seed edges1158243
Number of validation seed edges8000
Training duration1m 35s
Number of training steps (final model)5000
\n", + "
\n", + " \n", + " \n", + " \n", + " \n", + "
\n", + "
\n", + "

Note: The logs for the first training step are not shown.

\n", + "
\n", + "
\n", + "\n", + " \n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + " \n", + "
num_sampling_hops1
sampling_width15
num_layers2
batch_size8
max_training_time_secondsNone
num_train_steps5000
random_seed42
node_embedding_dim128
learning_rate0.001
opt_weight_decay0.0001
dropout0.1
message_pooling'sum'
architecture<Architecture.HETEROGENEOUS_MESSAGE_PASSING: 'HETEROGENEOUS_MESSAGE_PASSING'>
early_stopping{'patience': 5, 'min_improvement': 1e-06}
num_negative_nodes8
message_passing_on_target_edgesetTrue
negative_edges'random'
random_walk_num_walks_per_negative10
\n", + "
\n", + "
Raw schema plot\n", + "\n", + "Raw schema textual\n", + "
Node Sets:\n",
+       "  nodes:\n",
+       "    | Feature   | Format     | Semantic   | Shape   | Detail   |\n",
+       "    |-----------|------------|------------|---------|----------|\n",
+       "    | #id       | BYTES      | PRIMARY_ID | None    |          |\n",
+       "    | feat      | FLOAT_32   | EMBEDDING  | (128,)  |          |\n",
+       "    | year      | INTEGER_64 | NUMERICAL  | None    |          |\n",
+       "\n",
+       "\n",
+       "Edge Sets:\n",
+       "  edges: (Source: nodes, Target: nodes)\n",
+       "    (No features)\n",
+       "
\n", + "Source Normalized schema plot\n", + "\n", + "Source Normalized schema textual\n", + "
Node Sets:\n",
+       "  nodes:\n",
+       "    | Feature            | Format   | Semantic   | Shape   | Detail   |\n",
+       "    |--------------------|----------|------------|---------|----------|\n",
+       "    | feat               | FLOAT_32 | EMBEDDING  | (128,)  |          |\n",
+       "    | year_SOFT_QUANTILE | FLOAT_32 | EMBEDDING  | ()      |          |\n",
+       "\n",
+       "\n",
+       "Edge Sets:\n",
+       "  edges: (Source: nodes, Target: nodes)\n",
+       "    (No features)\n",
+       "
\n", + "Target Normalized schema plot\n", + "\n", + "Target Normalized schema textual\n", + "
Node Sets:\n",
+       "  nodes:\n",
+       "    | Feature            | Format   | Semantic   | Shape   | Detail   |\n",
+       "    |--------------------|----------|------------|---------|----------|\n",
+       "    | feat               | FLOAT_32 | EMBEDDING  | (128,)  |          |\n",
+       "    | year_SOFT_QUANTILE | FLOAT_32 | EMBEDDING  | ()      |          |\n",
+       "\n",
+       "\n",
+       "Edge Sets:\n",
+       "  edges: (Source: nodes, Target: nodes)\n",
+       "    (No features)\n",
+       "
\n", + "
\n", + "
Source feature statistics\n", + "
GraphFeatureStatistics:\n",
+       "  Node Sets (1):\n",
+       "    'nodes':\n",
+       "      '#id': count=19580, min=nan, max=nan\n",
+       "      'feat': count=19580, min=nan, max=nan\n",
+       "      'year': count=19580, min=1971.0000, max=2020.0000, quantiles=(100)[1992.0000, 2008.0000, 2009.0000, ..., 2020.0000, 2020.0000, 2020.0000]\n",
+       "
\n", + "Target feature statistics\n", + "
GraphFeatureStatistics:\n",
+       "  Node Sets (1):\n",
+       "    'nodes':\n",
+       "      '#id': count=1659, min=nan, max=nan\n",
+       "      'feat': count=1659, min=nan, max=nan\n",
+       "      'year': count=1659, min=1994.0000, max=2020.0000, quantiles=(100)[1994.0000, 2007.0000, 2008.0000, ..., 2020.0000, 2020.0000, 2020.0000]\n",
+       "
\n", + "
\n", + "
Source sampling plan\n", + "
Root: nodes\n",
+       "├── edges [width=15] ➔ nodes\n",
+       "└── edges (reversed) [width=15] ➔ nodes
\n", + "Target sampling plan\n", + "
Root: nodes\n",
+       "├── edges [width=15] ➔ nodes\n",
+       "└── edges (reversed) [width=15] ➔ nodes
\n", + "
\n", + "
Model Structure\n", + "
EmbedGraph(cat-embedding=64)\n",
+       "Dense(128)\n",
+       "Activation(silu)\n",
+       "Norm(layer_norm)\n",
+       "Graph Convolution Block x2:\n",
+       "    X = ...\n",
+       "    MPNN:\n",
+       "      Message:\n",
+       "        Dense(128)\n",
+       "        Activation(silu)\n",
+       "        Dense(128)\n",
+       "      Update:\n",
+       "        Dense(128)\n",
+       "        Activation(silu)\n",
+       "        Dropout(0.1)\n",
+       "        Dense(128)\n",
+       "    Residual(X)\n",
+       "    # Post MPNN\n",
+       "    X = ...\n",
+       "    Norm(rms_norm)\n",
+       "    Dense(512)\n",
+       "    Activation(silu)\n",
+       "    Dense(128)\n",
+       "    Dropout(0.1)\n",
+       "    Residual(X)\n",
+       "Identity
\n", + "Model Weights\n", + "
{'float32': 1154048}
\n", + "
Positive Source padding\n", + "
Node Sets:\n",
+       "  nodes: 228 nodes\n",
+       "\n",
+       "Edge Sets:\n",
+       "  edges: 221 edges
\n", + "Positive Target padding\n", + "
Node Sets:\n",
+       "  nodes: 262 nodes\n",
+       "\n",
+       "Edge Sets:\n",
+       "  edges: 256 edges
\n", + "Negative Target padding\n", + "
Node Sets:\n",
+       "  nodes: 866 nodes\n",
+       "\n",
+       "Edge Sets:\n",
+       "  edges: 804 edges
\n", + "
\n", + "\n", + "\n", + "\n", + "" ], - "metadata": { - "id": "qRy2Yqrqkh6q", - "executionInfo": { - "status": "ok", - "timestamp": 1779800457628, - "user_tz": -120, - "elapsed": 1508, - "user": { - "displayName": "", - "userId": "" - } - }, - "outputId": "fffc351b-f84b-48c8-c405-0f1357f0c193" - }, - "execution_count": null, - "outputs": [ - { - "output_type": "stream", - "name": "stderr", - "text": [ - "Embedding (source): 100%|██████████| 1/1 [00:00<00:00, 1.76it/s]\n", - "Embedding (target): 100%|██████████| 1/1 [00:00<00:00, 1.74it/s]" - ] - }, - { - "output_type": "stream", - "name": "stdout", - "text": [ - "\n", - "source_embedding: (1, 128)\n", - "target_embedding: (5, 128)\n" - ] - }, - { - "output_type": "stream", - "name": "stderr", - "text": [ - "\n" - ] - } + "text/plain": [ + "" ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "model.describe()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "RQZkBB7PlW1B" + }, + "source": [ + "After training, a model is generally evaluated on a test dataset/graph.\n", + "\n", + "Note: We don't have a test graph, so we use our training dataset here. In a real\n", + "pipeline, evaluating a model on a training dataset make little sense." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "colab": { + "height": 240 + }, + "execution": { + "iopub.execute_input": "2026-09-23T18:18:51.952539Z", + "iopub.status.busy": "2026-09-23T18:18:51.952360Z", + "iopub.status.idle": "2026-09-23T18:19:26.859921Z", + "shell.execute_reply": "2026-09-23T18:19:26.859385Z" }, + "executionInfo": { + "elapsed": 34909, + "status": "ok", + "timestamp": 1790187566860.8662, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 + }, + "id": "xDn1zgMglT37", + "outputId": "a4198b46-61e4-40c9-e219-3d5b3a5f1725" + }, + "outputs": [ { - "cell_type": "markdown", - "source": [ - "With the embedding, we can re-compute the probabilities predicted by `predict`.\n", - "\n", - "**Note:** GNN inference is stocastic. The values will not exactly match." - ], - "metadata": { - "id": "hAZ48ZGslEJt" - } + "name": "stdout", + "output_type": "stream", + "text": [ + "Evaluating model on 10000 edges\n" + ] }, { - "cell_type": "code", - "source": [ - "jax.nn.sigmoid(np.sum(source_embedding * target_embedding, axis=1))" + "name": "stderr", + "output_type": "stream", + "text": [ + "Inference: 0%| | 0/11250 [00:00\n", + "\n", + " .dgf-table {\n", + " border-collapse: collapse;\n", + " width: 100%;\n", + " margin-bottom: 20px;\n", + " font-size: inherit;\n", + " font-family: 'Roboto', sans-serif;\n", + " }\n", + " .dgf-table td {\n", + " padding: 4px 8px;\n", + " text-align: left;\n", + " }\n", + " .dgf-table td:first-child {\n", + " white-space: nowrap;\n", + " }\n", + " .dgf-table td:last-child {\n", + " width: 100%;\n", + " }\n", + " .dgf-table tr:nth-child(odd) {\n", + " background-color: #f8f9fa;\n", + " }\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
Num Examples10000
MRR0.8906611904761876
AUC0.9604125
Hit@N:
    \n", + "
  • @1: 0.8117
  • \n", + "
  • @5: 0.9919
  • \n", + "
" ], - "metadata": { - "id": "zygtTYnElTFj", - "executionInfo": { - "status": "ok", - "timestamp": 1779800457694, - "user_tz": -120, - "elapsed": 25, - "user": { - "displayName": "", - "userId": "" - } - }, - "outputId": "43bf7fa2-ea6b-4571-d34b-6d982c18cca6" - }, - "execution_count": null, - "outputs": [ - { - "output_type": "execute_result", - "data": { - "text/plain": [ - "Array([0.26822478, 0.4816262 , 0.34422588, 0.28125998, 0.06113339], dtype=float32)" - ] - }, - "metadata": {}, - "execution_count": 11 - } + "text/plain": [ + "Evaluation(loss=None, accuracy=None, rmse=None, r2=None, num_examples=10000, num_examples_weighted=None, mrr=0.8906611904761876, auc=0.9604125, hit_at={1: 0.8117, 5: 0.9919}, user_metrics={}, per_classes=[])" ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "model.evaluate(graph)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "LMkzkPlulZUP" + }, + "source": [ + "We can make predictions for individual nodes:" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-23T18:19:26.861846Z", + "iopub.status.busy": "2026-09-23T18:19:26.861663Z", + "iopub.status.idle": "2026-09-23T18:19:27.826066Z", + "shell.execute_reply": "2026-09-23T18:19:27.825445Z" + }, + "executionInfo": { + "elapsed": 965, + "status": "ok", + "timestamp": 1790187567827.0159, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 }, + "id": "pJgIjWr2lbvU", + "outputId": "d51dec49-7c56-4f77-d090-782f699d0297" + }, + "outputs": [ { - "cell_type": "markdown", - "source": [ - "## Part 3: Indexing embeddings for fast retrieval" - ], - "metadata": { - "id": "jkq-dT2oX6Oy" - } + "name": "stderr", + "output_type": "stream", + "text": [ + "Inference: 0%| | 0/1 [00:00 np.ndarray:\n", - " # Compute similarities\n", - " similarities = np.dot(embeddings, query.T).squeeze()\n", - " # Get top k indices (highest similarity)\n", - " top_idxs = np.argsort(similarities)[-n_neighbors:][::-1]\n", - " # Convert similarity to probabilities\n", - " top_probas = sigmoid(similarities[top_idxs])\n", - " return top_idxs, top_probas\n", - "\n", - " return find_closest_nodes\n", - "\n", - "\n", - "# Query the index\n", - "index = index_embeddings(target_embeddings)" - ], - "metadata": { - "id": "fewKjcclCwsK" - }, - "execution_count": null, - "outputs": [] + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "source_embedding: (1, 128)\n", + "target_embedding: (5, 128)\n" + ] }, { - "cell_type": "markdown", - "metadata": { - "id": "95f54634" - }, - "source": [ - "Finally, we can take the `source_embedding` we computed in Part 2 and query the\n", - "index to retrieve the most likely connections instantly, without having to run\n", - "the model on all pairs." + "name": "stderr", + "output_type": "stream", + "text": [ + "\n" + ] + } + ], + "source": [ + "source_embedding = model.predict_embedding(\n", + " graph, node_idxs=[0], encoder=\"source\"\n", + ")\n", + "target_embedding = model.predict_embedding(\n", + " graph, node_idxs=[1, 2, 3, 4, 5], encoder=\"target\"\n", + ")\n", + "print()\n", + "print(\"source_embedding:\", source_embedding.shape)\n", + "print(\"target_embedding:\", target_embedding.shape)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "hAZ48ZGslEJt" + }, + "source": [ + "With the embedding, we can re-compute the probabilities predicted by `predict`.\n", + "\n", + "**Note:** GNN inference is stocastic. The values will not exactly match." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-23T18:19:29.956386Z", + "iopub.status.busy": "2026-09-23T18:19:29.956135Z", + "iopub.status.idle": "2026-09-23T18:19:29.960143Z", + "shell.execute_reply": "2026-09-23T18:19:29.959833Z" + }, + "executionInfo": { + "elapsed": 5, + "status": "ok", + "timestamp": 1790187569961.0774, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 + }, + "id": "zygtTYnElTFj", + "outputId": "90d96fe4-604a-4802-9ada-8bcb66d5bec1" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Array([0.13553204, 0.62513316, 0.21613662, 0.1015342 , 0.05255956], dtype=float32)" ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "jax.nn.sigmoid(np.sum(source_embedding * target_embedding, axis=1))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "jkq-dT2oX6Oy" + }, + "source": [ + "## Part 3: Indexing embeddings for fast retrieval" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "cd9def3b" + }, + "source": [ + "First, let's compute target embeddings and index them using a vector database\n", + "library. We will use [Faiss](https://faiss.ai/index.html).\n", + "" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-23T18:19:29.961953Z", + "iopub.status.busy": "2026-09-23T18:19:29.961617Z", + "iopub.status.idle": "2026-09-23T18:20:00.928646Z", + "shell.execute_reply": "2026-09-23T18:20:00.928103Z" + }, + "executionInfo": { + "elapsed": 30968, + "status": "ok", + "timestamp": 1790187600929.6611, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 + }, + "id": "XTT0EtHqDEng", + "outputId": "aadd19c7-ea45-42b8-dc6a-4493d0a7da25" + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Embedding (target): 100%|██████████| 21168/21168 [00:30<00:00, 689.11it/s]\n" + ] + } + ], + "source": [ + "# Compute all the embeddings.\n", + "num_target_nodes = graph.node_sets[\"nodes\"].num_nodes\n", + "target_embeddings = model.predict_embedding(\n", + " graph, node_idxs=range(num_target_nodes), encoder=\"target\"\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "B2cqpmApDGYu" + }, + "source": [ + "Now, let's index them.\n", + "\n", + "**Note:** As a temporary solution, we index values with a simple (but inefficient)\n", + "NumPy-based solution." + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-23T18:20:00.930690Z", + "iopub.status.busy": "2026-09-23T18:20:00.930494Z", + "iopub.status.idle": "2026-09-23T18:20:01.295760Z", + "shell.execute_reply": "2026-09-23T18:20:01.295192Z" }, + "executionInfo": { + "elapsed": 367, + "status": "ok", + "timestamp": 1790187601297.0388, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 + }, + "id": "fewKjcclCwsK" + }, + "outputs": [], + "source": [ + "# As a temporary solution, we index values with a simple (but inefficient) NumPy-based solution.\n", + "# TODO: Update code when Faiss is available.\n", + "def index_embeddings(embeddings: np.ndarray):\n", + "\n", + " def sigmoid(x):\n", + " return 1 / (1 + np.exp(-x))\n", + "\n", + " def find_closest_nodes(query: np.ndarray, n_neighbors: int = 5) -> np.ndarray:\n", + " # Compute similarities\n", + " similarities = np.dot(embeddings, query.T).squeeze()\n", + " # Get top k indices (highest similarity)\n", + " top_idxs = np.argsort(similarities)[-n_neighbors:][::-1]\n", + " # Convert similarity to probabilities\n", + " top_probas = sigmoid(similarities[top_idxs])\n", + " return top_idxs, top_probas\n", + "\n", + " return find_closest_nodes\n", + "\n", + "\n", + "# Query the index\n", + "index = index_embeddings(target_embeddings)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "95f54634" + }, + "source": [ + "Finally, we can take the `source_embedding` we computed in Part 2 and query the\n", + "index to retrieve the most likely connections instantly, without having to run\n", + "the model on all pairs." + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-23T18:20:01.298223Z", + "iopub.status.busy": "2026-09-23T18:20:01.297902Z", + "iopub.status.idle": "2026-09-23T18:20:01.320885Z", + "shell.execute_reply": "2026-09-23T18:20:01.320400Z" + }, + "executionInfo": { + "elapsed": 24, + "status": "ok", + "timestamp": 1790187601321.821, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 + }, + "id": "027ffd14", + "outputId": "ec42f60a-0b3e-4bd5-fcc8-2af0aa64b86f" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "5 most likely connections to query node:\n", + "\tid=b'n149847' idx=149847 proba=0.9998799562454224\n", + "\tid=b'n93487' idx=93487 proba=0.9995434880256653\n", + "\tid=b'n0' idx=0 proba=0.9994799494743347\n", + "\tid=b'n157579' idx=157579 proba=0.9993922710418701\n", + "\tid=b'n80242' idx=80242 proba=0.9990492463111877\n" + ] + } + ], + "source": [ + "# Query the index using our source embedding\n", + "idxs, probas = index(source_embedding[0])\n", + "\n", + "print(\"5 most likely connections to query node:\")\n", + "for idx, proba in zip(idxs, probas):\n", + " id = graph.node_sets[\"nodes\"].features[\"#id\"][idx]\n", + " print(f\"\\tid={id} idx={idx} proba={proba}\")" + ] + } + ], + "metadata": { + "colab": { + "last_runtime": { + "build_target": "", + "kind": "local" + }, + "provenance": [ { - "cell_type": "code", - "metadata": { - "id": "027ffd14", - "executionInfo": { - "status": "ok", - "timestamp": 1779800487906, - "user_tz": -120, - "elapsed": 37, - "user": { - "displayName": "", - "userId": "" - } - }, - "outputId": "bbe6f6e3-e690-4fc5-fd2a-0dea5fe86c5e" + "file_id": "1hMr9gK0hhKcCtIs1Pro3_ksOnYcdAL2P", + "timestamp": 1771583153488 + } + ], + "views": { + "output_only": { + "cells": [ + { + "id": "woDLT7o2leXI" }, - "source": [ - "# Query the index using our source embedding\n", - "idxs, probas = index(source_embedding[0])\n", - "\n", - "print(\"5 most likely connections to query node:\")\n", - "for idx, proba in zip(idxs, probas):\n", - " id = graph.node_sets[\"nodes\"].features[\"#id\"][idx]\n", - " print(f\"\\tid={id} idx={idx} proba={proba}\")" - ], - "execution_count": null, - "outputs": [ - { - "output_type": "stream", - "name": "stdout", - "text": [ - "5 most likely connections to query node:\n", - "\tid=b'n157579' idx=157579 proba=0.9983875751495361\n", - "\tid=b'n112608' idx=112608 proba=0.9982813596725464\n", - "\tid=b'n121245' idx=121245 proba=0.9981396198272705\n", - "\tid=b'n4697' idx=4697 proba=0.9980858564376831\n", - "\tid=b'n50028' idx=50028 proba=0.9974484443664551\n" - ] - } - ] + { + "id": "UkBFBelLpoNj" + }, + { + "id": "peHHpZIGJnaO" + }, + { + "id": "ZdP1A5AIXGSe" + }, + { + "id": "RKXovz93qQAB" + }, + { + "id": "z3Tx-P_MlPuu" + }, + { + "id": "3kshVj5GlEYT" + }, + { + "id": "xehdItoPlIaP" + }, + { + "id": "d8wKxr1TlLRk" + }, + { + "id": "tWWsdUtblJ8i" + }, + { + "id": "GnQTZpR3P0oL" + }, + { + "id": "8PkikQwxPuHP" + }, + { + "id": "F3Br8wailGDS" + }, + { + "id": "eayALqcllTm0" + }, + { + "id": "DfuAUJFZlVO1" + }, + { + "id": "rbCwC52KlRRT" + }, + { + "id": "RQZkBB7PlW1B" + }, + { + "id": "xDn1zgMglT37" + }, + { + "id": "LMkzkPlulZUP" + }, + { + "id": "pJgIjWr2lbvU" + }, + { + "id": "DSLDMkKojl4U" + }, + { + "id": "y39jq6HfjlnN" + }, + { + "id": "la1YWnXzX4jT" + }, + { + "id": "qRy2Yqrqkh6q" + }, + { + "id": "hAZ48ZGslEJt" + }, + { + "id": "zygtTYnElTFj" + }, + { + "id": "jkq-dT2oX6Oy" + }, + { + "id": "cd9def3b" + }, + { + "id": "XTT0EtHqDEng" + }, + { + "id": "B2cqpmApDGYu" + }, + { + "id": "fewKjcclCwsK" + }, + { + "id": "95f54634" + }, + { + "id": "027ffd14" + } + ], + "hide_code": true } - ] + } + }, + "kernelspec": { + "display_name": "Python 3", + "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.14.5" + } + }, + "nbformat": 4, + "nbformat_minor": 0 } diff --git a/doc/docs/tutorial/node_prediction.ipynb b/doc/docs/tutorial/node_prediction.ipynb index 6b0cb4f..43d7132 100644 --- a/doc/docs/tutorial/node_prediction.ipynb +++ b/doc/docs/tutorial/node_prediction.ipynb @@ -1,1778 +1,4034 @@ { - "nbformat": 4, - "nbformat_minor": 0, - "metadata": { - "colab": { - "provenance": [ - { - "file_id": "1hMr9gK0hhKcCtIs1Pro3_ksOnYcdAL2P", - "timestamp": 1771583153488 - } - ], - "last_runtime": { - "build_target": "", - "kind": "local" - } + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "woDLT7o2leXI" + }, + "source": [ + "## Node Prediction with the Simple API\n", + "\n", + "[![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/google/distributed_graph_flow/blob/main/doc/docs/tutorial/node_prediction.ipynb)\n", + "\n", + "Node prediction GNNs use supervised learning to predict node feature values\n", + "(categorical labels for classification, and numerical labels for regression).\n", + "\n", + "**Part 1:** This tutorial shows how to train, analyze, and evaluate a model to\n", + "predict the label feature in the MAG citation graph. In this graph, authors,\n", + "papers, fields of study, and institutions constitute distinct node sets.\n", + "Relationships—such as authors writing papers, author affiliations with\n", + "institutions, paper topics, and citations between papers—are defined by specific\n", + "edge sets. The objective is to predict the domain of a paper stored in the\n", + "`labels` feature.\n", + "\n", + "**Part 2:** The first part of the tutorial trains a GNN with access to all\n", + "articles and citations. The second part addresses real-world constraints where\n", + "data availability depends on the time of publication. This model is trained and\n", + "tested using only historical data for each node's prediction." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "a6rCwd3CU7Dv" + }, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "cvhNQvg3TVQc" + }, + "source": [ + "## Installing GF\n", + "\n", + "Make sure your machine has a GPU or TPU, otherwise training is going to take forever.\n", + "If you are using Google Colab, you can get one for free. Just go to Edit > Notebook settings and select your hardware accelerator as TPU or GPU." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-23T18:20:59.106395Z", + "iopub.status.busy": "2026-09-23T18:20:59.106205Z", + "iopub.status.idle": "2026-09-23T18:20:59.362449Z", + "shell.execute_reply": "2026-09-23T18:20:59.362013Z" + }, + "executionInfo": { + "elapsed": 300, + "status": "ok", + "timestamp": 1790187659405.7815, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 }, - "kernelspec": { - "name": "python3", - "display_name": "Python 3" + "id": "KbZ6I1MAMKCF" + }, + "outputs": [], + "source": [ + "# Install DGF (Distributed Graph Flow) and OGB (for the toy dataset).\n", + "!pip install dgf ogb -U" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "RKXovz93qQAB" + }, + "source": [ + "## Importing libraries" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-23T18:20:59.407385Z", + "iopub.status.busy": "2026-09-23T18:20:59.407001Z", + "iopub.status.idle": "2026-09-23T18:21:03.635087Z", + "shell.execute_reply": "2026-09-23T18:21:03.634558Z" + }, + "executionInfo": { + "elapsed": 4230, + "status": "ok", + "timestamp": 1790187663636.455, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 + }, + "id": "z3Tx-P_MlPuu" + }, + "outputs": [], + "source": [ + "import dgf # Import Graph Flow" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "tWUr99pPz-Jg" + }, + "source": [ + "## Getting the graph data\n", + "\n", + "We first fetch the MAG graph." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-23T18:21:03.637564Z", + "iopub.status.busy": "2026-09-23T18:21:03.637166Z", + "iopub.status.idle": "2026-09-23T18:22:01.328180Z", + "shell.execute_reply": "2026-09-23T18:22:01.327645Z" }, - "language_info": { - "name": "python" + "executionInfo": { + "elapsed": 57692, + "status": "ok", + "timestamp": 1790187721329.4785, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 + }, + "id": "5SSkwb_f0FsB", + "outputId": "74a914a3-7ad7-43f5-a302-7cdd84da941e" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Caching mag graph at /tmp/gf_fetch/mag.cache\n", + "OGB dependency not available. Downloading graph from CNS.\n", + "Graph read in memory in 55.88s\n" + ] } + ], + "source": [ + "# Download the Mag graph from the OGB repo.\n", + "graph, schema = dgf.io.fetch_ogb_graph(\"mag\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "oJr9unOlit08" + }, + "source": [ + "Let's look at the graph structure." + ] }, - "cells": [ + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-23T18:22:01.330426Z", + "iopub.status.busy": "2026-09-23T18:22:01.330215Z", + "iopub.status.idle": "2026-09-23T18:22:01.724063Z", + "shell.execute_reply": "2026-09-23T18:22:01.723594Z" + }, + "executionInfo": { + "elapsed": 395, + "status": "ok", + "timestamp": 1790187721725.1536, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 + }, + "id": "jjYyiwfY0IfB", + "outputId": "4c20ce8a-eaa5-4163-a070-8fd36b2a73ba" + }, + "outputs": [ { - "cell_type": "markdown", - "source": [ - "## Node Prediction with the Simple API\n", - "\n", - "[![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/google/distributed_graph_flow/blob/main/doc/docs/tutorial/node_prediction.ipynb)\n", - "\n", - "Node prediction GNNs use supervised learning to predict node feature values\n", - "(categorical labels for classification, and numerical labels for regression).\n", - "\n", - "**Part 1:** This tutorial shows how to train, analyze, and evaluate a model to\n", - "predict the label feature in the MAG citation graph. In this graph, authors,\n", - "papers, fields of study, and institutions constitute distinct node sets.\n", - "Relationships—such as authors writing papers, author affiliations with\n", - "institutions, paper topics, and citations between papers—are defined by specific\n", - "edge sets. The objective is to predict the domain of a paper stored in the\n", - "`labels` feature.\n", - "\n", - "**Part 2:** The first part of the tutorial trains a GNN with access to all\n", - "articles and citations. The second part addresses real-world constraints where\n", - "data availability depends on the time of publication. This model is trained and\n", - "tested using only historical data for each node's prediction." - ], - "metadata": { - "id": "woDLT7o2leXI" - } + "name": "stdout", + "output_type": "stream", + "text": [ + "Graph Schema:\n", + "\n", + "Node Sets:\n", + " author:\n", + " | Feature | Format | Semantic | Shape | Detail |\n", + " |-----------|----------|------------|---------|----------|\n", + " | #id | BYTES | PRIMARY_ID | None | |\n", + "\n", + " field_of_study:\n", + " | Feature | Format | Semantic | Shape | Detail |\n", + " |-----------|----------|------------|---------|----------|\n", + " | #id | BYTES | PRIMARY_ID | None | |\n", + "\n", + " institution:\n", + " | Feature | Format | Semantic | Shape | Detail |\n", + " |-----------|----------|------------|---------|----------|\n", + " | #id | BYTES | PRIMARY_ID | None | |\n", + "\n", + " paper:\n", + " | Feature | Format | Semantic | Shape | Detail |\n", + " |-----------|------------|-------------|---------|----------|\n", + " | #id | BYTES | PRIMARY_ID | None | |\n", + " | #split | BYTES | CATEGORICAL | None | |\n", + " | feat | FLOAT_32 | EMBEDDING | (128,) | |\n", + " | labels | INTEGER_64 | CATEGORICAL | None | |\n", + " | year | INTEGER_64 | NUMERICAL | None | |\n", + "\n", + "\n", + "Edge Sets:\n", + " affiliated_with: (Source: author, Target: institution)\n", + " (No features)\n", + "\n", + " cites: (Source: paper, Target: paper)\n", + " (No features)\n", + "\n", + " has_topic: (Source: paper, Target: field_of_study)\n", + " (No features)\n", + "\n", + " writes: (Source: author, Target: paper)\n", + " (No features)\n", + "\n" + ] + } + ], + "source": [ + "# Show the schema\n", + "dgf.analyse.print_schema(schema)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "-EBR8nvv0I3P" + }, + "source": [ + "## Part 1: Training a time-agnostic model\n", + "\n", + "We train a model to predict the `labels` feature of the `paper` nodeset." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-23T18:22:01.726042Z", + "iopub.status.busy": "2026-09-23T18:22:01.725861Z", + "iopub.status.idle": "2026-09-23T18:24:00.773044Z", + "shell.execute_reply": "2026-09-23T18:24:00.772501Z" + }, + "executionInfo": { + "elapsed": 119049, + "status": "ok", + "timestamp": 1790187840774.4211, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 }, + "id": "_8QZI2ja0ORe", + "outputId": "b2d69682-e739-40c8-d692-675816ce8e66" + }, + "outputs": [ { - "cell_type": "markdown", - "source": [ - "## Installing GF\n", - "\n", - "Make sure your machine has a GPU or TPU, otherwise training is going to take forever.\n", - "If you are using Google Colab, you can get one for free. Just go to Edit > Notebook settings and select your hardware accelerator as TPU or GPU." - ], - "metadata": { - "id": "VVTcfUsKW0s6" - } + "name": "stdout", + "output_type": "stream", + "text": [ + "Using gpu JAX backend\n", + "Graph input schema:\n", + "Node Sets:\n", + " author:\n", + " | Feature | Format | Semantic | Shape | Detail |\n", + " |-----------|----------|------------|---------|----------|\n", + " | #id | BYTES | PRIMARY_ID | None | |\n", + "\n", + " field_of_study:\n", + " | Feature | Format | Semantic | Shape | Detail |\n", + " |-----------|----------|------------|---------|----------|\n", + " | #id | BYTES | PRIMARY_ID | None | |\n", + "\n", + " institution:\n", + " | Feature | Format | Semantic | Shape | Detail |\n", + " |-----------|----------|------------|---------|----------|\n", + " | #id | BYTES | PRIMARY_ID | None | |\n", + "\n", + " paper:\n", + " | Feature | Format | Semantic | Shape | Detail |\n", + " |-----------|------------|-------------|---------|----------|\n", + " | #id | BYTES | PRIMARY_ID | None | |\n", + " | #split | BYTES | CATEGORICAL | None | |\n", + " | feat | FLOAT_32 | EMBEDDING | (128,) | |\n", + " | labels | INTEGER_64 | CATEGORICAL | None | |\n", + " | year | INTEGER_64 | NUMERICAL | None | |\n", + "\n", + "\n", + "Edge Sets:\n", + " affiliated_with: (Source: author, Target: institution)\n", + " (No features)\n", + "\n", + " cites: (Source: paper, Target: paper)\n", + " (No features)\n", + "\n", + " has_topic: (Source: paper, Target: field_of_study)\n", + " (No features)\n", + "\n", + " writes: (Source: author, Target: paper)\n", + " (No features)\n", + "\n", + "Preparing dataset\n" + ] }, { - "cell_type": "code", - "source": [ - "# Install DGF (Distributed Graph Flow) and OGB (for the toy dataset).\n", - "!pip install dgf ogb -U" - ], - "metadata": { - "id": "WsNizTyxW5Uy" - }, - "execution_count": null, - "outputs": [] + "name": "stderr", + "output_type": "stream", + "text": [ + "[Warning] Validation set truncated from 73638 to 32000 nodes due to caching limits. To use more validation nodes, either increase `num_valid_steps` or set `cache_valid_dataset=False`.\n" + ] }, { - "cell_type": "markdown", - "source": [ - "## Importing libraries" - ], - "metadata": { - "id": "RKXovz93qQAB" - } + "name": "stdout", + "output_type": "stream", + "text": [ + "Num. training seed nodes: 704389, Num. validation seed nodes: 32000\n", + "Create graph sampler\n", + "Compute feature statistics\n", + " GraphFeatureStatistics:\n", + " Node Sets (4):\n", + " 'author':\n", + " '#id': count=46959, min=nan, max=nan\n", + " 'field_of_study':\n", + " '#id': count=101612, min=nan, max=nan\n", + " 'institution':\n", + " '#id': count=0, min=nan, max=nan\n", + " 'paper':\n", + " '#id': count=113130, min=nan, max=nan\n", + " '#split': count=113130, min=nan, max=nan, dictionary=(3)['train': 95832, 'valid': 10052, 'test': 7246]\n", + " 'feat': count=113130, min=nan, max=nan\n", + " 'labels': count=113130, min=0.0000, max=348.0000\n", + " 'year': count=113130, min=2010.0000, max=2019.0000, quantiles=(100)[2010.0000, 2010.0000, 2010.0000, ..., 2019.0000, 2019.0000, 2019.0000]\n", + "\n", + "Compute graph statistics for padding\n", + " padding: Node Sets:\n", + " author: 228 nodes\n", + " field_of_study: 386 nodes\n", + " institution: 2 nodes\n", + " paper: 525 nodes\n", + "\n", + "Edge Sets:\n", + " affiliated_with: 2 edges\n", + " cites: 493 edges\n", + " has_topic: 386 edges\n", + " writes: 228 edges\n" + ] }, { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "z3Tx-P_MlPuu" - }, - "outputs": [], - "source": [ - "import os\n", - "\n", - "os.environ[\"TF_USE_LEGACY_KERAS\"] = \"1\"\n", - "\n", - "import dgf # Import Graph Flow" - ] + "name": "stderr", + "output_type": "stream", + "text": [ + "[Warning] No normalizer created for node set 'author', feature '#id'.\n", + "[Warning] No normalizer created for node set 'paper', feature '#id'.\n", + "[Warning] No normalizer created for node set 'field_of_study', feature '#id'.\n", + "[Warning] No normalizer created for node set 'institution', feature '#id'.\n" + ] }, { - "cell_type": "markdown", - "source": [ - "## Getting the graph data\n", - "\n", - "We first fetch the MAG graph." - ], - "metadata": { - "id": "tWUr99pPz-Jg" - } + "name": "stdout", + "output_type": "stream", + "text": [ + "Preparing dataset finished in 4.21 seconds\n", + "Normalizer:\n", + "Graph Normalizer:\n", + "\n", + "Node Sets:\n", + " author:\n", + " (No normalizers)\n", + "\n", + " field_of_study:\n", + " (No normalizers)\n", + "\n", + " institution:\n", + " (No normalizers)\n", + "\n", + " paper:\n", + " - #split: DictionaryIndexNormalizer\n", + " - feat: IdentityNormalizer\n", + " - labels: IdentityNormalizer\n", + " - year: SoftQuantileNormalizer\n", + "\n", + "Edge Sets:\n", + " affiliated_with: (Source: author, Target: institution)\n", + " (No normalizers)\n", + "\n", + " cites: (Source: paper, Target: paper)\n", + " (No normalizers)\n", + "\n", + " has_topic: (Source: paper, Target: field_of_study)\n", + " (No normalizers)\n", + "\n", + " writes: (Source: author, Target: paper)\n", + " (No normalizers)\n", + "\n", + "Normalized graph schema:\n", + "Node Sets:\n", + " author:\n", + " (No features)\n", + "\n", + " field_of_study:\n", + " (No features)\n", + "\n", + " institution:\n", + " (No features)\n", + "\n", + " paper:\n", + " | Feature | Format | Semantic | Shape | Detail |\n", + " |--------------------|------------|-------------|---------|--------------|\n", + " | #split_INDEX | INTEGER_64 | CATEGORICAL | () | #num.cat:4 |\n", + " | feat | FLOAT_32 | EMBEDDING | (128,) | |\n", + " | labels | INTEGER_64 | CATEGORICAL | None | #num.cat:349 |\n", + " | year_SOFT_QUANTILE | FLOAT_32 | EMBEDDING | () | |\n", + "\n", + "\n", + "Edge Sets:\n", + " affiliated_with: (Source: author, Target: institution)\n", + " (No features)\n", + "\n", + " cites: (Source: paper, Target: paper)\n", + " (No features)\n", + "\n", + " has_topic: (Source: paper, Target: field_of_study)\n", + " (No features)\n", + "\n", + " writes: (Source: author, Target: paper)\n", + " (No features)\n", + "\n", + "Core model config:\n", + " EmbedGraph(cat-embedding=64)\n", + " Dense(128)\n", + " Activation(silu)\n", + " Norm(layer_norm)\n", + " Graph Convolution Block x2:\n", + " X = ...\n", + " MPNN:\n", + " Message:\n", + " Dense(128)\n", + " Activation(silu)\n", + " Dense(128)\n", + " Update:\n", + " Dense(128)\n", + " Activation(silu)\n", + " Dropout(0.1)\n", + " Dense(128)\n", + " Residual(X)\n", + " # Post MPNN\n", + " X = ...\n", + " Norm(rms_norm)\n", + " Dense(512)\n", + " Activation(silu)\n", + " Dense(128)\n", + " Dropout(0.1)\n", + " Residual(X)\n", + " Identity\n", + " Dense(349) # Classification head\n", + "Normalized input features:\n", + "Node Sets:\n", + " author:\n", + " (No features)\n", + "\n", + " field_of_study:\n", + " (No features)\n", + "\n", + " institution:\n", + " (No features)\n", + "\n", + " paper:\n", + " | Feature | Format | Semantic | Shape | Detail |\n", + " |--------------------|------------|-------------|---------|------------|\n", + " | #split_INDEX | INTEGER_64 | CATEGORICAL | () | #num.cat:4 |\n", + " | feat | FLOAT_32 | EMBEDDING | (128,) | |\n", + " | year_SOFT_QUANTILE | FLOAT_32 | EMBEDDING | () | |\n", + "\n", + "\n", + "Edge Sets:\n", + " affiliated_with: (Source: author, Target: institution)\n", + " (No features)\n", + "\n", + " cites: (Source: paper, Target: paper)\n", + " (No features)\n", + "\n", + " has_topic: (Source: paper, Target: field_of_study)\n", + " (No features)\n", + "\n", + " writes: (Source: author, Target: paper)\n", + " (No features)\n", + "\n", + "Caching validation dataset\n" + ] }, { - "cell_type": "code", - "source": [ - "# Download the Mag graph from the OGB repo.\n", - "graph, schema = dgf.io.fetch_ogb_graph(\"mag\")" - ], - "metadata": { - "id": "5SSkwb_f0FsB", - "executionInfo": { - "status": "ok", - "timestamp": 1779799879203, - "user_tz": -120, - "elapsed": 3580, - "user": { - "displayName": "", - "userId": "" - } - }, - "outputId": "66c7c5ae-372e-4254-9978-a6f2a3488eea" - }, - "execution_count": null, - "outputs": [ - { - "output_type": "stream", - "name": "stdout", - "text": [ - "Caching mag graph at /tmp/gf_fetch/mag.cache\n", - "OGB dependency not available. Downloading graph from CNS.\n" - ] - } - ] + "name": "stderr", + "output_type": "stream", + "text": [ + "Caching validation dataset: 100%|██████████| 1000/1000 [00:05<00:00, 199.81it/s]" + ] }, { - "cell_type": "markdown", - "source": [ - "Let's look at the graph structure." - ], - "metadata": { - "id": "oJr9unOlit08" - } + "name": "stdout", + "output_type": "stream", + "text": [ + "Caching validation dataset finished in 5.01 seconds\n", + "Number of cache validation batches: 1000\n", + "Training model\n", + "Generate first batch to initialize model\n" + ] }, { - "cell_type": "code", - "source": [ - "# Show the schema\n", - "dgf.analyse.print_schema(schema)" - ], - "metadata": { - "id": "jjYyiwfY0IfB", - "executionInfo": { - "status": "ok", - "timestamp": 1779799879270, - "user_tz": -120, - "elapsed": 22, - "user": { - "displayName": "", - "userId": "" - } - }, - "outputId": "71df79c4-c81a-492d-b3cd-d22d34aaa4f3" - }, - "execution_count": null, - "outputs": [ - { - "output_type": "stream", - "name": "stdout", - "text": [ - "Graph Schema:\n", - "\n", - "Node Sets:\n", - " author:\n", - " | Feature | Format | Semantic | Shape | Num cat. vals |\n", - " |-----------|----------|------------|---------|-----------------|\n", - " | #id | BYTES | PRIMARY_ID | None | None |\n", - "\n", - " field_of_study:\n", - " | Feature | Format | Semantic | Shape | Num cat. vals |\n", - " |-----------|----------|------------|---------|-----------------|\n", - " | #id | BYTES | PRIMARY_ID | None | None |\n", - "\n", - " institution:\n", - " | Feature | Format | Semantic | Shape | Num cat. vals |\n", - " |-----------|----------|------------|---------|-----------------|\n", - " | #id | BYTES | PRIMARY_ID | None | None |\n", - "\n", - " paper:\n", - " | Feature | Format | Semantic | Shape | Num cat. vals |\n", - " |-----------|------------|-------------|---------|-----------------|\n", - " | #id | BYTES | PRIMARY_ID | None | None |\n", - " | #split | BYTES | CATEGORICAL | None | None |\n", - " | feat | FLOAT_32 | EMBEDDING | (128,) | None |\n", - " | labels | INTEGER_64 | CATEGORICAL | None | None |\n", - " | year | INTEGER_64 | NUMERICAL | None | None |\n", - "\n", - "\n", - "Edge Sets:\n", - " affiliated_with: (Source: author, Target: institution)\n", - " (No features)\n", - "\n", - " cites: (Source: paper, Target: paper)\n", - " (No features)\n", - "\n", - " has_topic: (Source: paper, Target: field_of_study)\n", - " (No features)\n", - "\n", - " writes: (Source: author, Target: paper)\n", - " (No features)\n", - "\n" - ] - } - ] + "name": "stderr", + "output_type": "stream", + "text": [ + "\n" + ] }, { - "cell_type": "markdown", - "source": [ - "## Part 1: Training a time-agnostic model\n", - "\n", - "We train a model to predict the `labels` feature of the `paper` nodeset." - ], - "metadata": { - "id": "-EBR8nvv0I3P" - } + "name": "stdout", + "output_type": "stream", + "text": [ + "Create model variables\n", + "...Tracing model\n", + "Create model variables finished in 16.33 seconds\n", + "Will validate model every 1000 step(s)\n", + "Will checkpoint model every 1000 step(s)\n", + "Start training. The first two steps are generally slow.\n" + ] }, { - "cell_type": "code", - "source": [ - "model = dgf.learning.train_node_model(\n", - " graph=graph,\n", - " schema=schema,\n", - " target_nodeset=\"paper\",\n", - " target_column=\"labels\",\n", - " # Reduce the number of hops and train steps, for the demo to be faster.\n", - " num_sampling_hops=1,\n", - " num_train_steps=5000,\n", - ")" - ], - "metadata": { - "id": "_8QZI2ja0ORe", - "executionInfo": { - "status": "ok", - "timestamp": 1779800060977, - "user_tz": -120, - "elapsed": 181663, - "user": { - "displayName": "", - "userId": "" - } - }, - "outputId": "06053aa1-65f5-46f6-c696-12864fe4a928" - }, - "execution_count": null, - "outputs": [ - { - "output_type": "stream", - "name": "stdout", - "text": [ - "Using gpu JAX backend\n", - "Graph input schema:\n", - "Node Sets:\n", - " author:\n", - " | Feature | Format | Semantic | Shape | Num cat. vals |\n", - " |-----------|----------|------------|---------|-----------------|\n", - " | #id | BYTES | PRIMARY_ID | None | None |\n", - "\n", - " field_of_study:\n", - " | Feature | Format | Semantic | Shape | Num cat. vals |\n", - " |-----------|----------|------------|---------|-----------------|\n", - " | #id | BYTES | PRIMARY_ID | None | None |\n", - "\n", - " institution:\n", - " | Feature | Format | Semantic | Shape | Num cat. vals |\n", - " |-----------|----------|------------|---------|-----------------|\n", - " | #id | BYTES | PRIMARY_ID | None | None |\n", - "\n", - " paper:\n", - " | Feature | Format | Semantic | Shape | Num cat. vals |\n", - " |-----------|------------|-------------|---------|-----------------|\n", - " | #id | BYTES | PRIMARY_ID | None | None |\n", - " | #split | BYTES | CATEGORICAL | None | None |\n", - " | feat | FLOAT_32 | EMBEDDING | (128,) | None |\n", - " | labels | INTEGER_64 | CATEGORICAL | None | None |\n", - " | year | INTEGER_64 | NUMERICAL | None | None |\n", - "\n", - "\n", - "Edge Sets:\n", - " affiliated_with: (Source: author, Target: institution)\n", - " (No features)\n", - "\n", - " cites: (Source: paper, Target: paper)\n", - " (No features)\n", - "\n", - " has_topic: (Source: paper, Target: field_of_study)\n", - " (No features)\n", - "\n", - " writes: (Source: author, Target: paper)\n", - " (No features)\n", - "\n", - "Preparing dataset\n", - "Num. training seed nodes: 704389, Num. validation seed nodes: 32000\n", - "Create graph sampler\n", - "Compute feature statistics\n", - " GraphFeatureStatistics:\n", - " Node Sets (4):\n", - " 'author':\n", - " '#id': count=46985, min=nan, max=nan\n", - " 'field_of_study':\n", - " '#id': count=101920, min=nan, max=nan\n", - " 'institution':\n", - " '#id': count=0, min=nan, max=nan\n", - " 'paper':\n", - " '#id': count=111535, min=nan, max=nan\n", - " '#split': count=111535, min=nan, max=nan, dictionary=(3)['train': 94675, 'valid': 9821, 'test': 7039]\n", - " 'feat': count=111535, min=nan, max=nan\n", - " 'labels': count=111535, min=0.0000, max=348.0000\n", - " 'year': count=111535, min=2010.0000, max=2019.0000, quantiles=(100)[2010.0000, 2010.0000, 2010.0000, ..., 2019.0000, 2019.0000, 2019.0000]\n", - "\n" - ] - }, - { - "output_type": "stream", - "name": "stderr", - "text": [ - "[Warning] No normalizer created for node set 'author', feature '#id'.\n", - "[Warning] No normalizer created for node set 'paper', feature '#id'.\n", - "[Warning] No normalizer created for node set 'field_of_study', feature '#id'.\n", - "[Warning] No normalizer created for node set 'institution', feature '#id'.\n" - ] - }, - { - "output_type": "stream", - "name": "stdout", - "text": [ - "Compute graph statistics for padding\n", - " padding: Node Sets:\n", - " author: 256 nodes\n", - " field_of_study: 384 nodes\n", - " institution: 2 nodes\n", - " paper: 542 nodes\n", - "\n", - "Edge Sets:\n", - " affiliated_with: 2 edges\n", - " cites: 507 edges\n", - " has_topic: 384 edges\n", - " writes: 256 edges\n", - "Preparing dataset finished in 4.40 seconds\n", - "Normalizer:\n", - "Graph Normalizer:\n", - "\n", - "Node Sets:\n", - " author:\n", - " (No normalizers)\n", - "\n", - " field_of_study:\n", - " (No normalizers)\n", - "\n", - " institution:\n", - " (No normalizers)\n", - "\n", - " paper:\n", - " - #split: DictionaryIndexNormalizer\n", - " - feat: IdentityNormalizer\n", - " - labels: IdentityNormalizer\n", - " - year: SoftQuantileNormalizer\n", - "\n", - "Edge Sets:\n", - " affiliated_with: (Source: author, Target: institution)\n", - " (No normalizers)\n", - "\n", - " cites: (Source: paper, Target: paper)\n", - " (No normalizers)\n", - "\n", - " has_topic: (Source: paper, Target: field_of_study)\n", - " (No normalizers)\n", - "\n", - " writes: (Source: author, Target: paper)\n", - " (No normalizers)\n", - "\n", - "Normalized graph schema:\n", - "Node Sets:\n", - " author:\n", - " (No features)\n", - "\n", - " field_of_study:\n", - " (No features)\n", - "\n", - " institution:\n", - " (No features)\n", - "\n", - " paper:\n", - " | Feature | Format | Semantic | Shape | Num cat. vals |\n", - " |--------------------|------------|-------------|---------|-----------------|\n", - " | #split_INDEX | INTEGER_64 | CATEGORICAL | None | 4 |\n", - " | feat | FLOAT_32 | EMBEDDING | (128,) | None |\n", - " | labels | INTEGER_64 | CATEGORICAL | None | 349 |\n", - " | year_SOFT_QUANTILE | FLOAT_32 | EMBEDDING | None | None |\n", - "\n", - "\n", - "Edge Sets:\n", - " affiliated_with: (Source: author, Target: institution)\n", - " (No features)\n", - "\n", - " cites: (Source: paper, Target: paper)\n", - " (No features)\n", - "\n", - " has_topic: (Source: paper, Target: field_of_study)\n", - " (No features)\n", - "\n", - " writes: (Source: author, Target: paper)\n", - " (No features)\n", - "\n", - "Core model config:\n", - " EmbedGraph(cat-embedding=64)\n", - " Dense(128)\n", - " Activation(silu)\n", - " Norm(layer_norm)\n", - " Graph Convolution Block x2:\n", - " X = ...\n", - " MPNN:\n", - " Message:\n", - " Dense(128)\n", - " Activation(silu)\n", - " Dense(128)\n", - " Update:\n", - " Dense(128)\n", - " Activation(silu)\n", - " Dropout(0.1)\n", - " Dense(128)\n", - " Residual(X)\n", - " # Post MPNN\n", - " X = ...\n", - " Norm(rms_norm)\n", - " Dense(512)\n", - " Activation(silu)\n", - " Dense(128)\n", - " Dropout(0.1)\n", - " Residual(X)\n", - " Identity\n", - " Dense(349) # Classification head\n", - "Normalized input features:\n", - "Node Sets:\n", - " author:\n", - " (No features)\n", - "\n", - " field_of_study:\n", - " (No features)\n", - "\n", - " institution:\n", - " (No features)\n", - "\n", - " paper:\n", - " | Feature | Format | Semantic | Shape | Num cat. vals |\n", - " |--------------------|------------|-------------|---------|-----------------|\n", - " | #split_INDEX | INTEGER_64 | CATEGORICAL | None | 4 |\n", - " | feat | FLOAT_32 | EMBEDDING | (128,) | None |\n", - " | year_SOFT_QUANTILE | FLOAT_32 | EMBEDDING | None | None |\n", - "\n", - "\n", - "Edge Sets:\n", - " affiliated_with: (Source: author, Target: institution)\n", - " (No features)\n", - "\n", - " cites: (Source: paper, Target: paper)\n", - " (No features)\n", - "\n", - " has_topic: (Source: paper, Target: field_of_study)\n", - " (No features)\n", - "\n", - " writes: (Source: author, Target: paper)\n", - " (No features)\n", - "\n", - "Caching validation dataset\n" - ] - }, - { - "output_type": "stream", - "name": "stderr", - "text": [ - "Caching validation dataset: 100%|██████████| 1000/1000 [00:06<00:00, 148.22it/s]" - ] - }, - { - "output_type": "stream", - "name": "stdout", - "text": [ - "Caching validation dataset finished in 6.75 seconds\n", - "Number of cache validation batches: 1000\n", - "Training model\n", - "Generate first batch to initialize model\n", - "Create model variables\n" - ] - }, - { - "output_type": "stream", - "name": "stderr", - "text": [ - "\n" - ] - }, - { - "output_type": "stream", - "name": "stdout", - "text": [ - "...Tracing model\n", - "Create model variables finished in 20.42 seconds\n", - "Will validate model every 1000 step(s)\n", - "Will checkpoint model every 1000 step(s)\n", - "Start training. The first two steps are generally slow.\n" - ] - }, - { - "output_type": "stream", - "name": "stderr", - "text": [ - "\rTraining: 0%| | 0/10000 [00:00" - ], - "text/html": [ - "
\n", - "\n", - "\n", - "\n", - "
\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "
\n", - "
\n", - "

Node prediction model: Predict the value of a node feature.

\n", - "
    \n", - "
  • Target nodeset: paper
  • \n", - "
  • Target column: labels
  • \n", - "
  • Number of label classes: 349
  • \n", - "
\n", - "
\n", - "
\n", - "\n", - "
    \n", - "
  • Number of training seed nodes: 704389
  • \n", - "
  • Number of validation seed nodes: 32000
  • \n", - "
  • Training duration: 3m 1s
  • \n", - "
\n", - "\n", - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - "\n", - "\n", - "
\n", - " \n", - "\n", - "
\n", - "
\n", - "
num_sampling_hops=1\n",
-              "sampling_width=15\n",
-              "num_layers=2\n",
-              "batch_size=32\n",
-              "max_training_time_seconds=None\n",
-              "num_train_steps=10000\n",
-              "random_seed=42\n",
-              "node_embedding_dim=128\n",
-              "learning_rate=0.001\n",
-              "opt_weight_decay=0.0001\n",
-              "dropout=0.1\n",
-              "message_pooling='sum'\n",
-              "architecture=
\n", - "
Raw schema
\n",
-              "Node Sets:\n",
-              "  author:\n",
-              "    | Feature   | Format   | Semantic   | Shape   | Num cat. vals   |\n",
-              "    |-----------|----------|------------|---------|-----------------|\n",
-              "    | #id       | BYTES    | PRIMARY_ID | None    | None            |\n",
-              "\n",
-              "  field_of_study:\n",
-              "    | Feature   | Format   | Semantic   | Shape   | Num cat. vals   |\n",
-              "    |-----------|----------|------------|---------|-----------------|\n",
-              "    | #id       | BYTES    | PRIMARY_ID | None    | None            |\n",
-              "\n",
-              "  institution:\n",
-              "    | Feature   | Format   | Semantic   | Shape   | Num cat. vals   |\n",
-              "    |-----------|----------|------------|---------|-----------------|\n",
-              "    | #id       | BYTES    | PRIMARY_ID | None    | None            |\n",
-              "\n",
-              "  paper:\n",
-              "    | Feature   | Format     | Semantic    | Shape   | Num cat. vals   |\n",
-              "    |-----------|------------|-------------|---------|-----------------|\n",
-              "    | #id       | BYTES      | PRIMARY_ID  | None    | None            |\n",
-              "    | #split    | BYTES      | CATEGORICAL | None    | None            |\n",
-              "    | feat      | FLOAT_32   | EMBEDDING   | (128,)  | None            |\n",
-              "    | labels    | INTEGER_64 | CATEGORICAL | None    | None            |\n",
-              "    | year      | INTEGER_64 | NUMERICAL   | None    | None            |\n",
-              "\n",
-              "\n",
-              "Edge Sets:\n",
-              "  affiliated_with: (Source: author, Target: institution)\n",
-              "    (No features)\n",
-              "\n",
-              "  cites: (Source: paper, Target: paper)\n",
-              "    (No features)\n",
-              "\n",
-              "  has_topic: (Source: paper, Target: field_of_study)\n",
-              "    (No features)\n",
-              "\n",
-              "  writes: (Source: author, Target: paper)\n",
-              "    (No features)\n",
-              "

\n", - "Normalized schema
\n",
-              "Node Sets:\n",
-              "  author:\n",
-              "    (No features)\n",
-              "\n",
-              "  field_of_study:\n",
-              "    (No features)\n",
-              "\n",
-              "  institution:\n",
-              "    (No features)\n",
-              "\n",
-              "  paper:\n",
-              "    | Feature            | Format     | Semantic    | Shape   | Num cat. vals   |\n",
-              "    |--------------------|------------|-------------|---------|-----------------|\n",
-              "    | #split_INDEX       | INTEGER_64 | CATEGORICAL | None    | 4               |\n",
-              "    | feat               | FLOAT_32   | EMBEDDING   | (128,)  | None            |\n",
-              "    | labels             | INTEGER_64 | CATEGORICAL | None    | 349             |\n",
-              "    | year_SOFT_QUANTILE | FLOAT_32   | EMBEDDING   | None    | None            |\n",
-              "\n",
-              "\n",
-              "Edge Sets:\n",
-              "  affiliated_with: (Source: author, Target: institution)\n",
-              "    (No features)\n",
-              "\n",
-              "  cites: (Source: paper, Target: paper)\n",
-              "    (No features)\n",
-              "\n",
-              "  has_topic: (Source: paper, Target: field_of_study)\n",
-              "    (No features)\n",
-              "\n",
-              "  writes: (Source: author, Target: paper)\n",
-              "    (No features)\n",
-              "

\n", - "
\n", - "
Default feature statistics\n", - "
GraphFeatureStatistics:\n",
-              "  Node Sets (4):\n",
-              "    'author':\n",
-              "      '#id': count=46985, min=nan, max=nan\n",
-              "    'field_of_study':\n",
-              "      '#id': count=101920, min=nan, max=nan\n",
-              "    'institution':\n",
-              "      '#id': count=0, min=nan, max=nan\n",
-              "    'paper':\n",
-              "      '#id': count=111535, min=nan, max=nan\n",
-              "      '#split': count=111535, min=nan, max=nan, dictionary=(3)['train': 94675, 'valid': 9821, 'test': 7039]\n",
-              "      'feat': count=111535, min=nan, max=nan\n",
-              "      'labels': count=111535, min=0.0000, max=348.0000\n",
-              "      'year': count=111535, min=2010.0000, max=2019.0000, quantiles=(100)[2010.0000, 2010.0000, 2010.0000, ..., 2019.0000, 2019.0000, 2019.0000]\n",
-              "

\n", - "
\n", - "
Default sampling plan
\n",
-              "Root: paper\n",
-              "├── cites [width=15] ➔ paper\n",
-              "├── cites (reversed) [width=15] ➔ paper\n",
-              "├── has_topic [width=15] ➔ field_of_study\n",
-              "└── writes (reversed) [width=15] ➔ author

\n", - "
\n", - "
Model Structure
EmbedGraph(cat-embedding=64)\n",
-              "Dense(128)\n",
-              "Activation(silu)\n",
-              "Norm(layer_norm)\n",
-              "Graph Convolution Block x2:\n",
-              "    X = ...\n",
-              "    MPNN:\n",
-              "      Message:\n",
-              "        Dense(128)\n",
-              "        Activation(silu)\n",
-              "        Dense(128)\n",
-              "      Update:\n",
-              "        Dense(128)\n",
-              "        Activation(silu)\n",
-              "        Dropout(0.1)\n",
-              "        Dense(128)\n",
-              "    Residual(X)\n",
-              "    # Post MPNN\n",
-              "    X = ...\n",
-              "    Norm(rms_norm)\n",
-              "    Dense(512)\n",
-              "    Activation(silu)\n",
-              "    Dense(128)\n",
-              "    Dropout(0.1)\n",
-              "    Residual(X)\n",
-              "Identity\n",
-              "Dense(349) # Classification head
Model Weights
{'float32': 2312413}
\n", - "
Default padding
\n",
-              "Node Sets:\n",
-              "  author: 256 nodes\n",
-              "  field_of_study: 384 nodes\n",
-              "  institution: 2 nodes\n",
-              "  paper: 542 nodes\n",
-              "\n",
-              "Edge Sets:\n",
-              "  affiliated_with: 2 edges\n",
-              "  cites: 507 edges\n",
-              "  has_topic: 384 edges\n",
-              "  writes: 256 edges

\n", - "
\n", - "\n", - "\n", - "\n", - "
" - ] - }, - "metadata": {}, - "execution_count": 5 - } - ] + "name": "stderr", + "output_type": "stream", + "text": [ + "Training: 20%|█▉ | 999/5000 [00:31<00:48, 82.32it/s, step=1000, train-accuracy=0.2034, train-loss=3.6074]" + ] }, { - "cell_type": "markdown", - "source": [ - "After training, a model is generally evaluated on a test dataset/graph.\n", - "\n", - "**Note:** We don't have a test graph, so we use our training dataset here. In a\n", - "real pipeline, evaluating a model on a training dataset make little sense." - ], - "metadata": { - "id": "hlAMxAfmjINa" - } + "name": "stdout", + "output_type": "stream", + "text": [ + "...Tracing model\n" + ] }, { - "cell_type": "code", - "source": [ - "model.evaluate(graph)" - ], - "metadata": { - "id": "kBjFFXRg0Vm4", - "colab": { - "height": 133 - }, - "executionInfo": { - "status": "ok", - "timestamp": 1779800068701, - "user_tz": -120, - "elapsed": 6893, - "user": { - "displayName": "", - "userId": "" - } - }, - "outputId": "583c2016-0ac4-40c5-f29c-cb6820f25432" - }, - "execution_count": null, - "outputs": [ - { - "output_type": "stream", - "name": "stdout", - "text": [ - "Evaluating model on 10000 samples\n" - ] - }, - { - "output_type": "stream", - "name": "stderr", - "text": [ - "\rInference: 0%| | 0/625 [00:00Evaluation\n", - "
    \n", - "
  • Accuracy: 0.2723
  • \n", - "
  • Num Examples: 10000
  • \n", - "
" - ] - }, - "metadata": {}, - "execution_count": 6 - } - ] + "name": "stderr", + "output_type": "stream", + "text": [ + "Training: 20%|██ | 1008/5000 [00:33<05:54, 11.27it/s, step=1000, train-accuracy=0.2034, train-loss=3.6074]" + ] }, { - "cell_type": "markdown", - "source": [ - "We can make predictions for individual nodes:" - ], - "metadata": { - "id": "1jUyBIeUkbd1" - } + "name": "stdout", + "output_type": "stream", + "text": [ + "Validation loop took 2.29s (only printed once)\n", + "step:1000 train-accuracy:0.2034 train-loss:3.6074 valid-accuracy:0.2091 valid-loss:3.5008\n" + ] }, { - "cell_type": "code", - "source": [ - "# Predict the probability of each label class for the nodes 0 and 1.\n", - "predictions = model.predict(graph, seed_node_idxs=[0, 1])\n", - "print(\"\\npredictions's shape [node idx, class idx]:\", predictions.shape)" - ], - "metadata": { - "id": "E-Y0ofuL0Tf-", - "executionInfo": { - "status": "ok", - "timestamp": 1779800071967, - "user_tz": -120, - "elapsed": 3170, - "user": { - "displayName": "", - "userId": "" - } - }, - "outputId": "6978c7cf-1390-47cb-9bff-c0c888634a0b" - }, - "execution_count": null, - "outputs": [ - { - "output_type": "stream", - "name": "stderr", - "text": [ - "\rInference: 0%| | 0/1 [00:00\n", + "\n", + "\n", + "\n", + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
\n", + "
\n", + "

Node prediction model: Predict the value of a node feature.

\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
TypeNode prediction model - Predict the value of a node feature.
Target nodesetpaper
Target columnlabels
Number of label classes349
\n", + "
\n", + "
\n", + "\n", + " \n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + " \n", + "
WARNINGValidation set truncated from 73638 to 32000 nodes due to caching limits. To use more validation nodes, either increase `num_valid_steps` or set `cache_valid_dataset=False`.
WARNINGNo normalizer created for node set 'author', feature '#id'.
WARNINGNo normalizer created for node set 'paper', feature '#id'.
WARNINGNo normalizer created for node set 'field_of_study', feature '#id'.
WARNINGNo normalizer created for node set 'institution', feature '#id'.
\n", + "
\n", + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
Accuracy0.2571
Num Examples10000
AUC0.9203134776666986
Per Class Metrics [349]:
    \n", + "
  • Class 0: AUC=0.9496, PR-AUC=0.0682
  • \n", + "
  • Class 1: AUC=0.9513, PR-AUC=0.4043
  • \n", + "
  • Class 2: AUC=0.9821, PR-AUC=0.0197
  • \n", + "
  • ... (344 omitted) ...
  • \n", + "
  • Class 347: AUC=0.9749, PR-AUC=0.0401
  • \n", + "
  • Class 348: AUC=0.9014, PR-AUC=0.0015
  • \n", + "
\n", + "

*Showing plots for the first 20 classes out of 349 total classes.

\n", + "\n", + " \n", + " \n", + " \n", + " \n", + "
\n", + "
\n", + "
\n", + "\n", + " \n", + "\n", + " \n", + " \n", + " \n", + "\n", + " \n", + " \n", + "
Number of training seed nodes704389
Number of validation seed nodes32000
Training duration1m 53s
Number of training steps (final model)5000
\n", + "
\n", + " \n", + " \n", + " \n", + " \n", + "
\n", + "
\n", + "

Note: The logs for the first training step are not shown.

\n", + "
\n", + "
\n", + "\n", + " \n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + " \n", + "
num_sampling_hops1
sampling_width15
num_layers2
batch_size32
max_training_time_secondsNone
num_train_steps5000
random_seed42
node_embedding_dim128
learning_rate0.001
opt_weight_decay0.0001
dropout0.1
message_pooling'sum'
architecture<Architecture.HETEROGENEOUS_MESSAGE_PASSING: 'HETEROGENEOUS_MESSAGE_PASSING'>
early_stopping{'patience': 5, 'min_improvement': 1e-06}
\n", + "
\n", + "
Raw schema plot\n", + "\n", + "Raw schema textual\n", + "
Node Sets:\n",
+       "  author:\n",
+       "    | Feature   | Format   | Semantic   | Shape   | Detail   |\n",
+       "    |-----------|----------|------------|---------|----------|\n",
+       "    | #id       | BYTES    | PRIMARY_ID | None    |          |\n",
+       "\n",
+       "  field_of_study:\n",
+       "    | Feature   | Format   | Semantic   | Shape   | Detail   |\n",
+       "    |-----------|----------|------------|---------|----------|\n",
+       "    | #id       | BYTES    | PRIMARY_ID | None    |          |\n",
+       "\n",
+       "  institution:\n",
+       "    | Feature   | Format   | Semantic   | Shape   | Detail   |\n",
+       "    |-----------|----------|------------|---------|----------|\n",
+       "    | #id       | BYTES    | PRIMARY_ID | None    |          |\n",
+       "\n",
+       "  paper:\n",
+       "    | Feature   | Format     | Semantic    | Shape   | Detail   |\n",
+       "    |-----------|------------|-------------|---------|----------|\n",
+       "    | #id       | BYTES      | PRIMARY_ID  | None    |          |\n",
+       "    | #split    | BYTES      | CATEGORICAL | None    |          |\n",
+       "    | feat      | FLOAT_32   | EMBEDDING   | (128,)  |          |\n",
+       "    | labels    | INTEGER_64 | CATEGORICAL | None    |          |\n",
+       "    | year      | INTEGER_64 | NUMERICAL   | None    |          |\n",
+       "\n",
+       "\n",
+       "Edge Sets:\n",
+       "  affiliated_with: (Source: author, Target: institution)\n",
+       "    (No features)\n",
+       "\n",
+       "  cites: (Source: paper, Target: paper)\n",
+       "    (No features)\n",
+       "\n",
+       "  has_topic: (Source: paper, Target: field_of_study)\n",
+       "    (No features)\n",
+       "\n",
+       "  writes: (Source: author, Target: paper)\n",
+       "    (No features)\n",
+       "
\n", + "Normalized schema plot\n", + "\n", + "Normalized schema textual\n", + "
Node Sets:\n",
+       "  author:\n",
+       "    (No features)\n",
+       "\n",
+       "  field_of_study:\n",
+       "    (No features)\n",
+       "\n",
+       "  institution:\n",
+       "    (No features)\n",
+       "\n",
+       "  paper:\n",
+       "    | Feature            | Format     | Semantic    | Shape   | Detail       |\n",
+       "    |--------------------|------------|-------------|---------|--------------|\n",
+       "    | #split_INDEX       | INTEGER_64 | CATEGORICAL | ()      | #num.cat:4   |\n",
+       "    | feat               | FLOAT_32   | EMBEDDING   | (128,)  |              |\n",
+       "    | labels             | INTEGER_64 | CATEGORICAL | None    | #num.cat:349 |\n",
+       "    | year_SOFT_QUANTILE | FLOAT_32   | EMBEDDING   | ()      |              |\n",
+       "\n",
+       "\n",
+       "Edge Sets:\n",
+       "  affiliated_with: (Source: author, Target: institution)\n",
+       "    (No features)\n",
+       "\n",
+       "  cites: (Source: paper, Target: paper)\n",
+       "    (No features)\n",
+       "\n",
+       "  has_topic: (Source: paper, Target: field_of_study)\n",
+       "    (No features)\n",
+       "\n",
+       "  writes: (Source: author, Target: paper)\n",
+       "    (No features)\n",
+       "
\n", + "
\n", + "
Default feature statistics\n", + "
GraphFeatureStatistics:\n",
+       "  Node Sets (4):\n",
+       "    'author':\n",
+       "      '#id': count=46959, min=nan, max=nan\n",
+       "    'field_of_study':\n",
+       "      '#id': count=101612, min=nan, max=nan\n",
+       "    'institution':\n",
+       "      '#id': count=0, min=nan, max=nan\n",
+       "    'paper':\n",
+       "      '#id': count=113130, min=nan, max=nan\n",
+       "      '#split': count=113130, min=nan, max=nan, dictionary=(3)['train': 95832, 'valid': 10052, 'test': 7246]\n",
+       "      'feat': count=113130, min=nan, max=nan\n",
+       "      'labels': count=113130, min=0.0000, max=348.0000\n",
+       "      'year': count=113130, min=2010.0000, max=2019.0000, quantiles=(100)[2010.0000, 2010.0000, 2010.0000, ..., 2019.0000, 2019.0000, 2019.0000]\n",
+       "
\n", + "
\n", + "
Default sampling plan\n", + "
Root: paper\n",
+       "├── cites [width=15] ➔ paper\n",
+       "├── cites (reversed) [width=15] ➔ paper\n",
+       "├── has_topic [width=15] ➔ field_of_study\n",
+       "└── writes (reversed) [width=15] ➔ author
\n", + "
\n", + "
Model Structure\n", + "
EmbedGraph(cat-embedding=64)\n",
+       "Dense(128)\n",
+       "Activation(silu)\n",
+       "Norm(layer_norm)\n",
+       "Graph Convolution Block x2:\n",
+       "    X = ...\n",
+       "    MPNN:\n",
+       "      Message:\n",
+       "        Dense(128)\n",
+       "        Activation(silu)\n",
+       "        Dense(128)\n",
+       "      Update:\n",
+       "        Dense(128)\n",
+       "        Activation(silu)\n",
+       "        Dropout(0.1)\n",
+       "        Dense(128)\n",
+       "    Residual(X)\n",
+       "    # Post MPNN\n",
+       "    X = ...\n",
+       "    Norm(rms_norm)\n",
+       "    Dense(512)\n",
+       "    Activation(silu)\n",
+       "    Dense(128)\n",
+       "    Dropout(0.1)\n",
+       "    Residual(X)\n",
+       "Identity\n",
+       "Dense(349) # Classification head
\n", + "Model Weights\n", + "
{'float32': 2312413}
\n", + "
Default padding\n", + "
Node Sets:\n",
+       "  author: 228 nodes\n",
+       "  field_of_study: 386 nodes\n",
+       "  institution: 2 nodes\n",
+       "  paper: 525 nodes\n",
+       "\n",
+       "Edge Sets:\n",
+       "  affiliated_with: 2 edges\n",
+       "  cites: 493 edges\n",
+       "  has_topic: 386 edges\n",
+       "  writes: 228 edges
\n", + "
\n", + "\n", + "\n", + "\n", + "" ], - "metadata": { - "id": "x2EOyOyOHpSX", - "executionInfo": { - "status": "ok", - "timestamp": 1779800238919, - "user_tz": -120, - "elapsed": 166795, - "user": { - "displayName": "", - "userId": "" - } - }, - "outputId": "0b5af56b-6aed-4008-aaa7-384921e41084" - }, - "execution_count": null, - "outputs": [ - { - "output_type": "stream", - "name": "stdout", - "text": [ - "Preparing dataset\n", - "Num. training seed nodes: 704389, Num. validation seed nodes: 32000\n" - ] - }, - { - "output_type": "stream", - "name": "stderr", - "text": [ - "[Warning] No normalizer created for node set 'author', feature '#id'.\n", - "[Warning] No normalizer created for node set 'paper', feature '#id'.\n", - "[Warning] No normalizer created for node set 'paper', feature 'year'.\n", - "[Warning] No normalizer created for node set 'field_of_study', feature '#id'.\n", - "[Warning] No normalizer created for node set 'institution', feature '#id'.\n" - ] - }, - { - "output_type": "stream", - "name": "stdout", - "text": [ - "Preparing dataset finished in 4.46 seconds\n", - "Caching validation dataset\n", - "Caching validation dataset finished in 4.06 seconds\n", - "Number of cache validation batches: 1000\n", - "Training model\n", - "Generate first batch to initialize model\n", - "Create model variables\n", - "...Tracing model\n", - "Create model variables finished in 15.80 seconds\n", - "Will validate model every 1000 step(s)\n", - "Will checkpoint model every 1000 step(s)\n", - "Start training. The first two steps are generally slow.\n" - ] - }, - { - "output_type": "stream", - "name": "stderr", - "text": [ - "\rTraining: 0%| | 0/10000 [00:00" ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "model.describe()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "hlAMxAfmjINa" + }, + "source": [ + "After training, a model is generally evaluated on a test dataset/graph.\n", + "\n", + "**Note:** We don't have a test graph, so we use our training dataset here. In a\n", + "real pipeline, evaluating a model on a training dataset make little sense." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "colab": { + "height": 667 }, + "execution": { + "iopub.execute_input": "2026-09-23T18:24:01.329587Z", + "iopub.status.busy": "2026-09-23T18:24:01.329370Z", + "iopub.status.idle": "2026-09-23T18:24:06.130328Z", + "shell.execute_reply": "2026-09-23T18:24:06.129845Z" + }, + "executionInfo": { + "elapsed": 4804, + "status": "ok", + "timestamp": 1790187846132.923, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 + }, + "id": "kBjFFXRg0Vm4", + "outputId": "77e78731-d49a-4b97-e6fd-cee927b13351" + }, + "outputs": [ { - "cell_type": "markdown", - "source": [ - "We look at the model:" - ], - "metadata": { - "id": "0roFRwctMAxC" - } + "name": "stdout", + "output_type": "stream", + "text": [ + "Evaluating model on 10000 samples\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Inference: 100%|██████████| 625/625 [00:03<00:00, 201.54it/s]\n" + ] }, { - "cell_type": "code", - "source": [ - "model.describe()" + "data": { + "text/html": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
Accuracy0.2658
Num Examples10000
AUC0.9296022042243456
Per Class Metrics [349]:
    \n", + "
  • Class 0: AUC=0.9395, PR-AUC=0.1180
  • \n", + "
  • Class 1: AUC=0.9500, PR-AUC=0.3878
  • \n", + "
  • Class 2: AUC=0.9821, PR-AUC=0.0276
  • \n", + "
  • ... (344 omitted) ...
  • \n", + "
  • Class 347: AUC=0.9837, PR-AUC=0.3648
  • \n", + "
  • Class 348: AUC=0.8282, PR-AUC=0.0071
  • \n", + "
\n", + "

*Showing plots for the first 20 classes out of 349 total classes.

\n", + "\n", + " \n", + " \n", + " \n", + " \n", + "
\n", + " " ], - "metadata": { - "colab": { - "height": 219 - }, - "id": "HEwEDGM6MCFq", - "executionInfo": { - "status": "ok", - "timestamp": 1779800239493, - "user_tz": -120, - "elapsed": 527, - "user": { - "displayName": "", - "userId": "" - } - }, - "outputId": "197d18f8-1056-4d47-e360-9e19df3b4c25" - }, - "execution_count": null, - "outputs": [ - { - "output_type": "execute_result", - "data": { - "text/plain": [ - "" - ], - "text/html": [ - "
\n", - "\n", - "\n", - "\n", - "
\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "
\n", - "
\n", - "

Node prediction model: Predict the value of a node feature.

\n", - "
    \n", - "
  • Target nodeset: paper
  • \n", - "
  • Target column: labels
  • \n", - "
  • Number of label classes: 349
  • \n", - "
\n", - "
\n", - "
\n", - "\n", - "
    \n", - "
  • Number of training seed nodes: 704389
  • \n", - "
  • Number of validation seed nodes: 32000
  • \n", - "
  • Training duration: 2m 46s
  • \n", - "
\n", - "\n", - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - "\n", - "\n", - "
\n", - " \n", - "\n", - "
\n", - "
\n", - "
num_sampling_hops=1\n",
-              "sampling_width=15\n",
-              "num_layers=2\n",
-              "batch_size=32\n",
-              "max_training_time_seconds=None\n",
-              "num_train_steps=10000\n",
-              "random_seed=42\n",
-              "node_embedding_dim=128\n",
-              "learning_rate=0.001\n",
-              "opt_weight_decay=0.0001\n",
-              "dropout=0.1\n",
-              "message_pooling='sum'\n",
-              "architecture=
\n", - "
Raw schema
\n",
-              "Node Sets:\n",
-              "  author:\n",
-              "    | Feature   | Format   | Semantic   | Shape   | Num cat. vals   |\n",
-              "    |-----------|----------|------------|---------|-----------------|\n",
-              "    | #id       | BYTES    | PRIMARY_ID | None    | None            |\n",
-              "\n",
-              "  field_of_study:\n",
-              "    | Feature   | Format   | Semantic   | Shape   | Num cat. vals   |\n",
-              "    |-----------|----------|------------|---------|-----------------|\n",
-              "    | #id       | BYTES    | PRIMARY_ID | None    | None            |\n",
-              "\n",
-              "  institution:\n",
-              "    | Feature   | Format   | Semantic   | Shape   | Num cat. vals   |\n",
-              "    |-----------|----------|------------|---------|-----------------|\n",
-              "    | #id       | BYTES    | PRIMARY_ID | None    | None            |\n",
-              "\n",
-              "  paper:\n",
-              "    | Feature   | Format     | Semantic    | Shape   | Num cat. vals   |\n",
-              "    |-----------|------------|-------------|---------|-----------------|\n",
-              "    | #id       | BYTES      | PRIMARY_ID  | None    | None            |\n",
-              "    | #split    | BYTES      | CATEGORICAL | None    | None            |\n",
-              "    | feat      | FLOAT_32   | EMBEDDING   | (128,)  | None            |\n",
-              "    | labels    | INTEGER_64 | CATEGORICAL | None    | None            |\n",
-              "    | year      | INTEGER_64 | TIMESTAMP   | None    | None            |\n",
-              "\n",
-              "\n",
-              "Edge Sets:\n",
-              "  affiliated_with: (Source: author, Target: institution)\n",
-              "    (No features)\n",
-              "\n",
-              "  cites: (Source: paper, Target: paper)\n",
-              "    | Feature   | Format     | Semantic   | Shape   | Num cat. vals   |\n",
-              "    |-----------|------------|------------|---------|-----------------|\n",
-              "    | year      | INTEGER_64 | TIMESTAMP  | None    | None            |\n",
-              "\n",
-              "  has_topic: (Source: paper, Target: field_of_study)\n",
-              "    | Feature   | Format     | Semantic   | Shape   | Num cat. vals   |\n",
-              "    |-----------|------------|------------|---------|-----------------|\n",
-              "    | year      | INTEGER_64 | TIMESTAMP  | None    | None            |\n",
-              "\n",
-              "  writes: (Source: author, Target: paper)\n",
-              "    | Feature   | Format     | Semantic   | Shape   | Num cat. vals   |\n",
-              "    |-----------|------------|------------|---------|-----------------|\n",
-              "    | year      | INTEGER_64 | TIMESTAMP  | None    | None            |\n",
-              "

\n", - "Normalized schema
\n",
-              "Node Sets:\n",
-              "  author:\n",
-              "    (No features)\n",
-              "\n",
-              "  field_of_study:\n",
-              "    (No features)\n",
-              "\n",
-              "  institution:\n",
-              "    (No features)\n",
-              "\n",
-              "  paper:\n",
-              "    | Feature      | Format     | Semantic    | Shape   | Num cat. vals   |\n",
-              "    |--------------|------------|-------------|---------|-----------------|\n",
-              "    | #split_INDEX | INTEGER_64 | CATEGORICAL | None    | 4               |\n",
-              "    | feat         | FLOAT_32   | EMBEDDING   | (128,)  | None            |\n",
-              "    | labels       | INTEGER_64 | CATEGORICAL | None    | 349             |\n",
-              "\n",
-              "\n",
-              "Edge Sets:\n",
-              "  affiliated_with: (Source: author, Target: institution)\n",
-              "    (No features)\n",
-              "\n",
-              "  cites: (Source: paper, Target: paper)\n",
-              "    (No features)\n",
-              "\n",
-              "  has_topic: (Source: paper, Target: field_of_study)\n",
-              "    (No features)\n",
-              "\n",
-              "  writes: (Source: author, Target: paper)\n",
-              "    (No features)\n",
-              "

\n", - "
\n", - "
Default feature statistics\n", - "
GraphFeatureStatistics:\n",
-              "  Node Sets (4):\n",
-              "    'author':\n",
-              "      '#id': count=47004, min=nan, max=nan\n",
-              "    'field_of_study':\n",
-              "      '#id': count=101860, min=nan, max=nan\n",
-              "    'institution':\n",
-              "      '#id': count=0, min=nan, max=nan\n",
-              "    'paper':\n",
-              "      '#id': count=72371, min=nan, max=nan\n",
-              "      '#split': count=72371, min=nan, max=nan, dictionary=(3)['train': 68044, 'valid': 3061, 'test': 1266]\n",
-              "      'feat': count=72371, min=nan, max=nan\n",
-              "      'labels': count=72371, min=0.0000, max=348.0000\n",
-              "      'year': count=72371, min=2010.0000, max=2019.0000\n",
-              "

\n", - "
\n", - "
Default sampling plan
\n",
-              "Root: paper\n",
-              "├── cites [width=15] ➔ paper\n",
-              "├── cites (reversed) [width=15] ➔ paper\n",
-              "├── has_topic [width=15] ➔ field_of_study\n",
-              "└── writes (reversed) [width=15] ➔ author\n",
-              "\n",
-              "Temporal Features: {'has_topic': 'year', 'cites': 'year', 'writes': 'year'}

\n", - "
\n", - "
Model Structure
EmbedGraph(cat-embedding=64)\n",
-              "Dense(128)\n",
-              "Activation(silu)\n",
-              "Norm(layer_norm)\n",
-              "Graph Convolution Block x2:\n",
-              "    X = ...\n",
-              "    MPNN:\n",
-              "      Message:\n",
-              "        Dense(128)\n",
-              "        Activation(silu)\n",
-              "        Dense(128)\n",
-              "      Update:\n",
-              "        Dense(128)\n",
-              "        Activation(silu)\n",
-              "        Dropout(0.1)\n",
-              "        Dense(128)\n",
-              "    Residual(X)\n",
-              "    # Post MPNN\n",
-              "    X = ...\n",
-              "    Norm(rms_norm)\n",
-              "    Dense(512)\n",
-              "    Activation(silu)\n",
-              "    Dense(128)\n",
-              "    Dropout(0.1)\n",
-              "    Residual(X)\n",
-              "Identity\n",
-              "Dense(349) # Classification head
Model Weights
{'float32': 2312285}
\n", - "
Default padding
\n",
-              "Node Sets:\n",
-              "  author: 233 nodes\n",
-              "  field_of_study: 384 nodes\n",
-              "  institution: 2 nodes\n",
-              "  paper: 369 nodes\n",
-              "\n",
-              "Edge Sets:\n",
-              "  affiliated_with: 2 edges\n",
-              "  cites: 335 edges\n",
-              "  has_topic: 384 edges\n",
-              "  writes: 233 edges

\n", - "
\n", - "\n", - "\n", - "\n", - "
" - ] - }, - "metadata": {}, - "execution_count": 10 - } + "text/plain": [ + "Evaluation(loss=None, accuracy=0.2658, rmse=None, r2=None, num_examples=10000, num_examples_weighted=None, mrr=None, auc=np.float64(0.9296022042243456), hit_at={}, user_metrics={}, per_classes=[PerClass(auc_value=0.9395357417037633, pr_auc_value=0.11799180147856969, tp=array([ 0, 0, 0, ..., 37, 37, 37], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 5213, 6217, 9963],\n", + " shape=(10001,), dtype=uint64), tn=array([9963, 9963, 9963, ..., 4750, 3746, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([37, 37, 37, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.949990138410312, pr_auc_value=0.3877808597494682, tp=array([ 0, 0, 0, ..., 423, 423, 423], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 6286, 7322, 9577],\n", + " shape=(10001,), dtype=uint64), tn=array([9577, 9577, 9577, ..., 3291, 2255, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([423, 423, 423, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9820678271308524, pr_auc_value=0.027582443230608947, tp=array([0, 0, 0, ..., 4, 4, 4], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 2273, 3250, 9996],\n", + " shape=(10001,), dtype=uint64), tn=array([9996, 9996, 9996, ..., 7723, 6746, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([4, 4, 4, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9654396318895668, pr_auc_value=0.00828107055665006, tp=array([0, 0, 0, ..., 3, 3, 3], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 2713, 3449, 9997],\n", + " shape=(10001,), dtype=uint64), tn=array([9997, 9997, 9997, ..., 7284, 6548, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([3, 3, 3, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.0, pr_auc_value=0.0, tp=array([0, 0, 0, ..., 0, 0, 0], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 3138, 4209, 10000],\n", + " shape=(10001,), dtype=uint64), tn=array([10000, 10000, 10000, ..., 6862, 5791, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([0, 0, 0, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9727260179296038, pr_auc_value=0.36128805247526824, tp=array([ 0, 0, 0, ..., 101, 101, 101], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 5462, 6413, 9899],\n", + " shape=(10001,), dtype=uint64), tn=array([9899, 9899, 9899, ..., 4437, 3486, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([101, 101, 101, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9016968732101773, pr_auc_value=0.08900510547839821, tp=array([ 0, 0, 0, ..., 31, 31, 31], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 5553, 6714, 9969],\n", + " shape=(10001,), dtype=uint64), tn=array([9969, 9969, 9969, ..., 4416, 3255, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([31, 31, 31, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9906250648929524, pr_auc_value=0.5774620631966784, tp=array([ 0, 0, 0, ..., 63, 63, 63], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 4097, 5344, 9937],\n", + " shape=(10001,), dtype=uint64), tn=array([9937, 9937, 9937, ..., 5840, 4593, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([63, 63, 63, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9874962488746624, pr_auc_value=0.011058570257613365, tp=array([0, 0, 0, ..., 3, 3, 3], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 1851, 2715, 9997],\n", + " shape=(10001,), dtype=uint64), tn=array([9997, 9997, 9997, ..., 8146, 7282, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([3, 3, 3, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9406881983385594, pr_auc_value=0.3095643630387298, tp=array([ 0, 0, 0, ..., 186, 186, 186], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 7164, 7813, 9814],\n", + " shape=(10001,), dtype=uint64), tn=array([9814, 9814, 9814, ..., 2650, 2001, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([186, 186, 186, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.996088044022011, pr_auc_value=0.0640913119264166, tp=array([0, 0, 0, ..., 5, 5, 5], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 885, 1378, 9995],\n", + " shape=(10001,), dtype=uint64), tn=array([9995, 9995, 9995, ..., 9110, 8617, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([5, 5, 5, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.967517764211369, pr_auc_value=0.27063969700699186, tp=array([0, 0, 0, ..., 8, 8, 8], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 3887, 5386, 9992],\n", + " shape=(10001,), dtype=uint64), tn=array([9992, 9992, 9992, ..., 6105, 4606, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([8, 8, 8, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9718365744453356, pr_auc_value=0.12536873158272654, tp=array([ 0, 0, 0, ..., 24, 24, 24], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 4810, 5973, 9976],\n", + " shape=(10001,), dtype=uint64), tn=array([9976, 9976, 9976, ..., 5166, 4003, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([24, 24, 24, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9013336007219492, pr_auc_value=0.019343668780545907, tp=array([ 0, 0, 0, ..., 27, 27, 27], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 6005, 7046, 9973],\n", + " shape=(10001,), dtype=uint64), tn=array([9973, 9973, 9973, ..., 3968, 2927, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([27, 27, 27, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9827877877877877, pr_auc_value=0.22727541862755093, tp=array([ 0, 0, 0, ..., 10, 10, 10], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 2423, 3708, 9990],\n", + " shape=(10001,), dtype=uint64), tn=array([9990, 9990, 9990, ..., 7567, 6282, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([10, 10, 10, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9961734693877551, pr_auc_value=0.30709334155877244, tp=array([0, 0, 0, ..., 4, 4, 4], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 1320, 1978, 9996],\n", + " shape=(10001,), dtype=uint64), tn=array([9996, 9996, 9996, ..., 8676, 8018, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([4, 4, 4, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9565052992137368, pr_auc_value=0.029557907698060645, tp=array([0, 0, 0, ..., 9, 9, 9], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 3523, 4604, 9991],\n", + " shape=(10001,), dtype=uint64), tn=array([9991, 9991, 9991, ..., 6468, 5387, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([9, 9, 9, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.982303796744013, pr_auc_value=0.15563740588116604, tp=array([ 0, 0, 0, ..., 14, 14, 14], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 3274, 4068, 9986],\n", + " shape=(10001,), dtype=uint64), tn=array([9986, 9986, 9986, ..., 6712, 5918, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([14, 14, 14, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9879436902966316, pr_auc_value=0.6641489085670891, tp=array([ 0, 0, 0, ..., 55, 55, 55], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 3439, 4433, 9945],\n", + " shape=(10001,), dtype=uint64), tn=array([9945, 9945, 9945, ..., 6506, 5512, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([55, 55, 55, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9665526723201701, pr_auc_value=0.047544681370123666, tp=array([ 0, 0, 0, ..., 13, 13, 13], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 3438, 4476, 9987],\n", + " shape=(10001,), dtype=uint64), tn=array([9987, 9987, 9987, ..., 6549, 5511, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([13, 13, 13, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.954945275011361, pr_auc_value=0.018377323665679984, tp=array([ 0, 0, 0, ..., 13, 13, 13], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 3087, 3945, 9987],\n", + " shape=(10001,), dtype=uint64), tn=array([9987, 9987, 9987, ..., 6900, 6042, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([13, 13, 13, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9829046379216617, pr_auc_value=0.2341415581374044, tp=array([ 0, 0, 0, ..., 14, 14, 14], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 1733, 2234, 9986],\n", + " shape=(10001,), dtype=uint64), tn=array([9986, 9986, 9986, ..., 8253, 7752, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([14, 14, 14, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9619620346791491, pr_auc_value=0.38129507837297943, tp=array([ 0, 0, 0, ..., 37, 37, 38], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 4122, 5439, 9962],\n", + " shape=(10001,), dtype=uint64), tn=array([9962, 9962, 9962, ..., 5840, 4523, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([38, 38, 38, ..., 1, 1, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9846535149208893, pr_auc_value=0.19818780258643956, tp=array([ 0, 0, 0, ..., 14, 14, 14], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 2056, 2630, 9986],\n", + " shape=(10001,), dtype=uint64), tn=array([9986, 9986, 9986, ..., 7930, 7356, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([14, 14, 14, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.960117023404681, pr_auc_value=0.04513531901074935, tp=array([0, 0, 0, ..., 2, 2, 2], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 1318, 1821, 9998],\n", + " shape=(10001,), dtype=uint64), tn=array([9998, 9998, 9998, ..., 8680, 8177, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([2, 2, 2, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9991262512474344, pr_auc_value=0.7705515479052577, tp=array([ 0, 0, 0, ..., 23, 23, 23], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 3067, 4261, 9977],\n", + " shape=(10001,), dtype=uint64), tn=array([9977, 9977, 9977, ..., 6910, 5716, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([23, 23, 23, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9494515021173019, pr_auc_value=0.004030173820974545, tp=array([0, 0, 0, ..., 3, 3, 3], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 2936, 4003, 9997],\n", + " shape=(10001,), dtype=uint64), tn=array([9997, 9997, 9997, ..., 7061, 5994, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([3, 3, 3, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9779600546830716, pr_auc_value=0.032640143464271944, tp=array([0, 0, 0, ..., 3, 3, 3], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 2348, 3192, 9997],\n", + " shape=(10001,), dtype=uint64), tn=array([9997, 9997, 9997, ..., 7649, 6805, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([3, 3, 3, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9100832441232883, pr_auc_value=0.05154743524105645, tp=array([ 0, 0, 0, ..., 35, 36, 36], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 4993, 6067, 9964],\n", + " shape=(10001,), dtype=uint64), tn=array([9964, 9964, 9964, ..., 4971, 3897, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([36, 36, 36, ..., 1, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9873376948735202, pr_auc_value=0.13806810626827365, tp=array([ 0, 0, 0, ..., 21, 21, 21], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 1795, 2300, 9979],\n", + " shape=(10001,), dtype=uint64), tn=array([9979, 9979, 9979, ..., 8184, 7679, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([21, 21, 21, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9181587809676236, pr_auc_value=0.0017457037788929652, tp=array([0, 0, 0, ..., 3, 3, 3], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 2064, 2744, 9997],\n", + " shape=(10001,), dtype=uint64), tn=array([9997, 9997, 9997, ..., 7933, 7253, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([3, 3, 3, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9697812212724142, pr_auc_value=0.2022669728926565, tp=array([ 0, 0, 0, ..., 38, 38, 38], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 3565, 4740, 9962],\n", + " shape=(10001,), dtype=uint64), tn=array([9962, 9962, 9962, ..., 6397, 5222, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([38, 38, 38, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9056119108687448, pr_auc_value=0.017424685716078997, tp=array([ 0, 0, 0, ..., 13, 13, 13], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 3585, 4640, 9987],\n", + " shape=(10001,), dtype=uint64), tn=array([9987, 9987, 9987, ..., 6402, 5347, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([13, 13, 13, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9719774133218242, pr_auc_value=0.23961707247603103, tp=array([ 0, 0, 0, ..., 70, 70, 70], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 3865, 4919, 9930],\n", + " shape=(10001,), dtype=uint64), tn=array([9930, 9930, 9930, ..., 6065, 5011, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([70, 70, 70, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9205929902048839, pr_auc_value=0.06156393477685579, tp=array([ 0, 0, 0, ..., 38, 38, 38], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 5666, 6482, 9962],\n", + " shape=(10001,), dtype=uint64), tn=array([9962, 9962, 9962, ..., 4296, 3480, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([38, 38, 38, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.8333827657050263, pr_auc_value=0.05761761307339716, tp=array([ 0, 0, 0, ..., 126, 126, 126], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 8712, 9235, 9874],\n", + " shape=(10001,), dtype=uint64), tn=array([9874, 9874, 9874, ..., 1162, 639, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([126, 126, 126, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9511520122703477, pr_auc_value=0.016596452773488757, tp=array([0, 0, 0, ..., 3, 3, 3], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 1199, 1661, 9997],\n", + " shape=(10001,), dtype=uint64), tn=array([9997, 9997, 9997, ..., 8798, 8336, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([3, 3, 3, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9577463981076625, pr_auc_value=0.08562816383934484, tp=array([ 0, 0, 0, ..., 35, 35, 35], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 4401, 5267, 9965],\n", + " shape=(10001,), dtype=uint64), tn=array([9965, 9965, 9965, ..., 5564, 4698, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([35, 35, 35, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9641031428836986, pr_auc_value=0.09221885259248727, tp=array([ 0, 0, 0, ..., 53, 53, 53], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 4249, 5002, 9947],\n", + " shape=(10001,), dtype=uint64), tn=array([9947, 9947, 9947, ..., 5698, 4945, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([53, 53, 53, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9539157831566313, pr_auc_value=0.0020164542506534433, tp=array([0, 0, 0, ..., 2, 2, 2], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 2026, 3114, 9998],\n", + " shape=(10001,), dtype=uint64), tn=array([9998, 9998, 9998, ..., 7972, 6884, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([2, 2, 2, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9600965386154461, pr_auc_value=0.0059460571967788756, tp=array([0, 0, 0, ..., 4, 4, 4], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 1792, 2516, 9996],\n", + " shape=(10001,), dtype=uint64), tn=array([9996, 9996, 9996, ..., 8204, 7480, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([4, 4, 4, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.8172620239764242, pr_auc_value=0.005706814388871657, tp=array([ 0, 0, 0, ..., 14, 14, 14], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 7606, 8225, 9986],\n", + " shape=(10001,), dtype=uint64), tn=array([9986, 9986, 9986, ..., 2380, 1761, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([14, 14, 14, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9960240360540811, pr_auc_value=0.2651562314881741, tp=array([ 0, 0, 0, ..., 15, 15, 15], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 1631, 2233, 9985],\n", + " shape=(10001,), dtype=uint64), tn=array([9985, 9985, 9985, ..., 8354, 7752, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([15, 15, 15, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9726289434151227, pr_auc_value=0.2704286509413702, tp=array([ 0, 0, 0, ..., 15, 15, 15], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 4360, 5318, 9985],\n", + " shape=(10001,), dtype=uint64), tn=array([9985, 9985, 9985, ..., 5625, 4667, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([15, 15, 15, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9794293367925374, pr_auc_value=0.06778391525733153, tp=array([ 0, 0, 0, ..., 19, 19, 19], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 2041, 2697, 9981],\n", + " shape=(10001,), dtype=uint64), tn=array([9981, 9981, 9981, ..., 7940, 7284, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([19, 19, 19, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9458403675399821, pr_auc_value=0.23009643976667776, tp=array([ 0, 0, 0, ..., 93, 94, 94], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 6748, 7594, 9906],\n", + " shape=(10001,), dtype=uint64), tn=array([9906, 9906, 9906, ..., 3158, 2312, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([94, 94, 94, ..., 1, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.99874949979992, pr_auc_value=0.1374367302450088, tp=array([0, 0, 0, ..., 4, 4, 4], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 1620, 2346, 9996],\n", + " shape=(10001,), dtype=uint64), tn=array([9996, 9996, 9996, ..., 8376, 7650, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([4, 4, 4, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.7800310062012403, pr_auc_value=0.0007939762602860966, tp=array([0, 0, 0, ..., 1, 2, 2], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 3327, 4659, 9998],\n", + " shape=(10001,), dtype=uint64), tn=array([9998, 9998, 9998, ..., 6671, 5339, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([2, 2, 2, ..., 1, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.7835079643038012, pr_auc_value=0.043237756679753374, tp=array([ 0, 0, 0, ..., 73, 73, 73], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 8242, 8672, 9927],\n", + " shape=(10001,), dtype=uint64), tn=array([9927, 9927, 9927, ..., 1685, 1255, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([73, 73, 73, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9099167405796686, pr_auc_value=0.026756824766579944, tp=array([ 0, 0, 0, ..., 14, 14, 14], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 5596, 7037, 9986],\n", + " shape=(10001,), dtype=uint64), tn=array([9986, 9986, 9986, ..., 4390, 2949, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([14, 14, 14, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9628275746953764, pr_auc_value=0.059306956168211925, tp=array([ 0, 0, 0, ..., 15, 15, 15], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 3192, 4381, 9985],\n", + " shape=(10001,), dtype=uint64), tn=array([9985, 9985, 9985, ..., 6793, 5604, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([15, 15, 15, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9079157387884675, pr_auc_value=0.09233676453721489, tp=array([ 0, 0, 0, ..., 68, 68, 68], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 6511, 7441, 9932],\n", + " shape=(10001,), dtype=uint64), tn=array([9932, 9932, 9932, ..., 3421, 2491, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([68, 68, 68, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9416791386127622, pr_auc_value=0.1614509530512002, tp=array([ 0, 0, 0, ..., 79, 79, 79], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 6804, 7731, 9921],\n", + " shape=(10001,), dtype=uint64), tn=array([9921, 9921, 9921, ..., 3117, 2190, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([79, 79, 79, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9274837418709354, pr_auc_value=0.003350574215675373, tp=array([0, 0, 0, ..., 5, 5, 5], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 2377, 3113, 9995],\n", + " shape=(10001,), dtype=uint64), tn=array([9995, 9995, 9995, ..., 7618, 6882, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([5, 5, 5, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9945043941739646, pr_auc_value=0.14496748316603292, tp=array([ 0, 0, 0, ..., 13, 13, 13], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 2454, 3314, 9987],\n", + " shape=(10001,), dtype=uint64), tn=array([9987, 9987, 9987, ..., 7533, 6673, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([13, 13, 13, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9938148111099997, pr_auc_value=0.03131703291852972, tp=array([0, 0, 0, ..., 3, 3, 3], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 2271, 3195, 9997],\n", + " shape=(10001,), dtype=uint64), tn=array([9997, 9997, 9997, ..., 7726, 6802, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([3, 3, 3, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9269345684836529, pr_auc_value=0.006082545320063566, tp=array([0, 0, 0, ..., 7, 7, 7], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 4778, 6299, 9993],\n", + " shape=(10001,), dtype=uint64), tn=array([9993, 9993, 9993, ..., 5215, 3694, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([7, 7, 7, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9430672899601149, pr_auc_value=0.008225325217214221, tp=array([0, 0, 0, ..., 7, 7, 7], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 3947, 5195, 9993],\n", + " shape=(10001,), dtype=uint64), tn=array([9993, 9993, 9993, ..., 6046, 4798, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([7, 7, 7, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9717349904780996, pr_auc_value=0.13206332609910224, tp=array([ 0, 0, 0, ..., 23, 23, 23], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 3361, 4380, 9977],\n", + " shape=(10001,), dtype=uint64), tn=array([9977, 9977, 9977, ..., 6616, 5597, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([23, 23, 23, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.8690345172586293, pr_auc_value=0.0018424871046238051, tp=array([0, 0, 0, ..., 5, 5, 5], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 3477, 4924, 9995],\n", + " shape=(10001,), dtype=uint64), tn=array([9995, 9995, 9995, ..., 6518, 5071, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([5, 5, 5, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9980327431562802, pr_auc_value=0.06729784813698103, tp=array([0, 0, 0, ..., 3, 3, 3], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 2467, 3575, 9997],\n", + " shape=(10001,), dtype=uint64), tn=array([9997, 9997, 9997, ..., 7530, 6422, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([3, 3, 3, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9844994994994996, pr_auc_value=0.04729956541661604, tp=array([ 0, 0, 0, ..., 10, 10, 10], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 1918, 2464, 9990],\n", + " shape=(10001,), dtype=uint64), tn=array([9990, 9990, 9990, ..., 8072, 7526, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([10, 10, 10, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9968993798759752, pr_auc_value=0.08086248376160166, tp=array([0, 0, 0, ..., 2, 2, 2], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 2186, 3057, 9998],\n", + " shape=(10001,), dtype=uint64), tn=array([9998, 9998, 9998, ..., 7812, 6941, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([2, 2, 2, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9834417208604302, pr_auc_value=0.13006466662006333, tp=array([0, 0, 0, ..., 5, 5, 5], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 1781, 2630, 9995],\n", + " shape=(10001,), dtype=uint64), tn=array([9995, 9995, 9995, ..., 8214, 7365, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([5, 5, 5, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.979011176067349, pr_auc_value=0.1940377065555458, tp=array([ 0, 0, 0, ..., 13, 13, 13], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 2753, 3817, 9987],\n", + " shape=(10001,), dtype=uint64), tn=array([9987, 9987, 9987, ..., 7234, 6170, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([13, 13, 13, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9715193038607721, pr_auc_value=0.003275514485029163, tp=array([0, 0, 0, ..., 2, 2, 2], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 2240, 3330, 9998],\n", + " shape=(10001,), dtype=uint64), tn=array([9998, 9998, 9998, ..., 7758, 6668, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([2, 2, 2, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.873003003003003, pr_auc_value=0.013902750948256399, tp=array([ 0, 0, 0, ..., 9, 10, 10], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 6256, 7450, 9990],\n", + " shape=(10001,), dtype=uint64), tn=array([9990, 9990, 9990, ..., 3734, 2540, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([10, 10, 10, ..., 1, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9317101840736295, pr_auc_value=0.005384516917893406, tp=array([0, 0, 0, ..., 4, 4, 4], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 2624, 3447, 9996],\n", + " shape=(10001,), dtype=uint64), tn=array([9996, 9996, 9996, ..., 7372, 6549, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([4, 4, 4, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9377942502253831, pr_auc_value=0.01814901198772264, tp=array([ 0, 0, 0, ..., 17, 17, 17], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 3938, 4968, 9983],\n", + " shape=(10001,), dtype=uint64), tn=array([9983, 9983, 9983, ..., 6045, 5015, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([17, 17, 17, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9397388433059836, pr_auc_value=0.013586219884257518, tp=array([0, 0, 0, ..., 6, 6, 6], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 2887, 3877, 9994],\n", + " shape=(10001,), dtype=uint64), tn=array([9994, 9994, 9994, ..., 7107, 6117, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([6, 6, 6, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.8576653306613227, pr_auc_value=0.011611084065466305, tp=array([ 0, 0, 0, ..., 20, 20, 20], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 6445, 7564, 9980],\n", + " shape=(10001,), dtype=uint64), tn=array([9980, 9980, 9980, ..., 3535, 2416, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([20, 20, 20, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.8214507253626814, pr_auc_value=0.0036927323669128202, tp=array([0, 0, 0, ..., 4, 5, 5], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 4314, 5506, 9995],\n", + " shape=(10001,), dtype=uint64), tn=array([9995, 9995, 9995, ..., 5681, 4489, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([5, 5, 5, ..., 1, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.982617090331904, pr_auc_value=0.46109160817723566, tp=array([ 0, 0, 0, ..., 68, 68, 68], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 5452, 6462, 9932],\n", + " shape=(10001,), dtype=uint64), tn=array([9932, 9932, 9932, ..., 4480, 3470, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([68, 68, 68, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9912807175485979, pr_auc_value=0.08296110259350627, tp=array([0, 0, 0, ..., 3, 3, 3], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 1363, 2013, 9997],\n", + " shape=(10001,), dtype=uint64), tn=array([9997, 9997, 9997, ..., 8634, 7984, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([3, 3, 3, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9855413247948769, pr_auc_value=0.05351281356740158, tp=array([0, 0, 0, ..., 6, 6, 6], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 1169, 1679, 9994],\n", + " shape=(10001,), dtype=uint64), tn=array([9994, 9994, 9994, ..., 8825, 8315, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([6, 6, 6, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.7692882850120385, pr_auc_value=0.008424215534429846, tp=array([ 0, 0, 0, ..., 32, 32, 32], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 8132, 8737, 9968],\n", + " shape=(10001,), dtype=uint64), tn=array([9968, 9968, 9968, ..., 1836, 1231, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([32, 32, 32, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9684968496849685, pr_auc_value=0.0015741241904212444, tp=array([0, 0, 0, ..., 1, 1, 1], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 3483, 4465, 9999],\n", + " shape=(10001,), dtype=uint64), tn=array([9999, 9999, 9999, ..., 6516, 5534, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([1, 1, 1, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9501087840192017, pr_auc_value=0.12227353974447822, tp=array([ 0, 0, 0, ..., 65, 65, 65], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 4922, 6132, 9935],\n", + " shape=(10001,), dtype=uint64), tn=array([9935, 9935, 9935, ..., 5013, 3803, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([65, 65, 65, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9528300751879699, pr_auc_value=0.04317874106581413, tp=array([ 0, 0, 0, ..., 25, 25, 25], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 3772, 4790, 9975],\n", + " shape=(10001,), dtype=uint64), tn=array([9975, 9975, 9975, ..., 6203, 5185, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([25, 25, 25, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9966285528422739, pr_auc_value=0.32085894144854277, tp=array([0, 0, 0, ..., 8, 8, 8], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 1077, 1692, 9992],\n", + " shape=(10001,), dtype=uint64), tn=array([9992, 9992, 9992, ..., 8915, 8300, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([8, 8, 8, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.8430043398246142, pr_auc_value=0.011398676447028544, tp=array([ 0, 0, 0, ..., 18, 19, 19], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 6070, 7319, 9981],\n", + " shape=(10001,), dtype=uint64), tn=array([9981, 9981, 9981, ..., 3911, 2662, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([19, 19, 19, ..., 1, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9864914914914915, pr_auc_value=0.03919042696446624, tp=array([ 0, 0, 0, ..., 10, 10, 10], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 2202, 3014, 9990],\n", + " shape=(10001,), dtype=uint64), tn=array([9990, 9990, 9990, ..., 7788, 6976, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([10, 10, 10, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.8799088087805678, pr_auc_value=0.012764964860238988, tp=array([ 0, 0, 0, ..., 11, 11, 11], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 5386, 6669, 9989],\n", + " shape=(10001,), dtype=uint64), tn=array([9989, 9989, 9989, ..., 4603, 3320, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([11, 11, 11, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9291733523920298, pr_auc_value=0.052333662945583684, tp=array([ 0, 0, 0, ..., 43, 43, 43], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 5595, 6530, 9957],\n", + " shape=(10001,), dtype=uint64), tn=array([9957, 9957, 9957, ..., 4362, 3427, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([43, 43, 43, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9574553002807532, pr_auc_value=0.34278346447797653, tp=array([ 0, 0, 0, ..., 159, 159, 159], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 6707, 7781, 9841],\n", + " shape=(10001,), dtype=uint64), tn=array([9841, 9841, 9841, ..., 3134, 2060, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([159, 159, 159, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.8897513309071452, pr_auc_value=0.01812413291856919, tp=array([ 0, 0, 0, ..., 33, 33, 33], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 6144, 7037, 9967],\n", + " shape=(10001,), dtype=uint64), tn=array([9967, 9967, 9967, ..., 3823, 2930, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([33, 33, 33, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9513588899782875, pr_auc_value=0.117798317048682, tp=array([ 0, 0, 0, ..., 31, 31, 31], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 4018, 4761, 9969],\n", + " shape=(10001,), dtype=uint64), tn=array([9969, 9969, 9969, ..., 5951, 5208, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([31, 31, 31, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.8306150356880796, pr_auc_value=0.004081886495333116, tp=array([0, 0, 0, ..., 5, 5, 6], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 3169, 4439, 9994],\n", + " shape=(10001,), dtype=uint64), tn=array([9994, 9994, 9994, ..., 6825, 5555, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([6, 6, 6, ..., 1, 1, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9150330066013203, pr_auc_value=0.0019804808371351457, tp=array([0, 0, 0, ..., 2, 2, 2], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 3218, 4410, 9998],\n", + " shape=(10001,), dtype=uint64), tn=array([9998, 9998, 9998, ..., 6780, 5588, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([2, 2, 2, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9856866444997406, pr_auc_value=0.11999470170561405, tp=array([ 0, 0, 0, ..., 23, 23, 23], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 4214, 5453, 9977],\n", + " shape=(10001,), dtype=uint64), tn=array([9977, 9977, 9977, ..., 5763, 4524, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([23, 23, 23, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9720620620620621, pr_auc_value=0.019491528071429086, tp=array([ 0, 0, 0, ..., 10, 10, 10], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 2361, 3215, 9990],\n", + " shape=(10001,), dtype=uint64), tn=array([9990, 9990, 9990, ..., 7629, 6775, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([10, 10, 10, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9568467282767303, pr_auc_value=0.024121914468444108, tp=array([ 0, 0, 0, ..., 14, 14, 14], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 3117, 4022, 9986],\n", + " shape=(10001,), dtype=uint64), tn=array([9986, 9986, 9986, ..., 6869, 5964, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([14, 14, 14, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9683714847684285, pr_auc_value=0.17821431174514182, tp=array([ 0, 0, 0, ..., 19, 19, 19], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 4535, 5634, 9981],\n", + " shape=(10001,), dtype=uint64), tn=array([9981, 9981, 9981, ..., 5446, 4347, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([19, 19, 19, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9759405775329661, pr_auc_value=0.2572632243246655, tp=array([ 0, 0, 0, ..., 15, 15, 15], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 2690, 4135, 9985],\n", + " shape=(10001,), dtype=uint64), tn=array([9985, 9985, 9985, ..., 7295, 5850, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([15, 15, 15, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.823704481792717, pr_auc_value=0.0018964734439009718, tp=array([0, 0, 0, ..., 4, 4, 4], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 5711, 7513, 9996],\n", + " shape=(10001,), dtype=uint64), tn=array([9996, 9996, 9996, ..., 4285, 2483, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([4, 4, 4, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9706559975487741, pr_auc_value=0.05956322500815525, tp=array([ 0, 0, 0, ..., 17, 17, 17], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 3850, 4957, 9983],\n", + " shape=(10001,), dtype=uint64), tn=array([9983, 9983, 9983, ..., 6133, 5026, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([17, 17, 17, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9249059498155521, pr_auc_value=0.06802630260252031, tp=array([ 0, 0, 0, ..., 44, 44, 44], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 5814, 7091, 9956],\n", + " shape=(10001,), dtype=uint64), tn=array([9956, 9956, 9956, ..., 4142, 2865, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([44, 44, 44, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9438702404809619, pr_auc_value=0.03618207517094325, tp=array([ 0, 0, 0, ..., 20, 20, 20], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 4261, 5133, 9980],\n", + " shape=(10001,), dtype=uint64), tn=array([9980, 9980, 9980, ..., 5719, 4847, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([20, 20, 20, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9779951304115803, pr_auc_value=0.04399647102575495, tp=array([0, 0, 0, ..., 6, 6, 6], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 2416, 3418, 9994],\n", + " shape=(10001,), dtype=uint64), tn=array([9994, 9994, 9994, ..., 7578, 6576, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([6, 6, 6, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9330008020854221, pr_auc_value=0.021939532180794287, tp=array([ 0, 0, 0, ..., 26, 26, 26], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 5159, 6157, 9974],\n", + " shape=(10001,), dtype=uint64), tn=array([9974, 9974, 9974, ..., 4815, 3817, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([26, 26, 26, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.8812821798729522, pr_auc_value=0.015824115255841677, tp=array([ 0, 0, 0, ..., 30, 30, 30], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 4902, 5884, 9970],\n", + " shape=(10001,), dtype=uint64), tn=array([9970, 9970, 9970, ..., 5068, 4086, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([30, 30, 30, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9616988623094118, pr_auc_value=0.04031894451985956, tp=array([0, 0, 0, ..., 9, 9, 9], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 2658, 3472, 9991],\n", + " shape=(10001,), dtype=uint64), tn=array([9991, 9991, 9991, ..., 7333, 6519, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([9, 9, 9, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9658829414707355, pr_auc_value=0.006761160497757546, tp=array([0, 0, 0, ..., 5, 5, 5], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 3199, 3984, 9995],\n", + " shape=(10001,), dtype=uint64), tn=array([9995, 9995, 9995, ..., 6796, 6011, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([5, 5, 5, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.8932083166533227, pr_auc_value=0.006821041958016983, tp=array([0, 0, 0, ..., 8, 8, 8], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 3995, 5214, 9992],\n", + " shape=(10001,), dtype=uint64), tn=array([9992, 9992, 9992, ..., 5997, 4778, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([8, 8, 8, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9762476247624763, pr_auc_value=0.0020921237103692647, tp=array([0, 0, 0, ..., 1, 1, 1], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 3263, 4132, 9999],\n", + " shape=(10001,), dtype=uint64), tn=array([9999, 9999, 9999, ..., 6736, 5867, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([1, 1, 1, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.8904401002506266, pr_auc_value=0.030555644958259142, tp=array([ 0, 0, 0, ..., 25, 25, 25], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 5846, 6969, 9975],\n", + " shape=(10001,), dtype=uint64), tn=array([9975, 9975, 9975, ..., 4129, 3006, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([25, 25, 25, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9925887275300411, pr_auc_value=0.6797060829085965, tp=array([ 0, 0, 0, ..., 62, 62, 62], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 3754, 4872, 9938],\n", + " shape=(10001,), dtype=uint64), tn=array([9938, 9938, 9938, ..., 6184, 5066, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([62, 62, 62, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.8049857080822107, pr_auc_value=0.013342711331143623, tp=array([ 0, 0, 0, ..., 21, 21, 21], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 6863, 7675, 9979],\n", + " shape=(10001,), dtype=uint64), tn=array([9979, 9979, 9979, ..., 3116, 2304, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([21, 21, 21, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9949703845768729, pr_auc_value=0.21392370069521155, tp=array([ 0, 0, 0, ..., 13, 13, 13], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 1441, 2105, 9987],\n", + " shape=(10001,), dtype=uint64), tn=array([9987, 9987, 9987, ..., 8546, 7882, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([13, 13, 13, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9733241482965932, pr_auc_value=0.0651439474431629, tp=array([ 0, 0, 0, ..., 20, 20, 20], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 3255, 4551, 9980],\n", + " shape=(10001,), dtype=uint64), tn=array([9980, 9980, 9980, ..., 6725, 5429, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([20, 20, 20, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.965100215025012, pr_auc_value=0.12425105847145408, tp=array([ 0, 0, 0, ..., 27, 27, 27], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 4508, 5535, 9973],\n", + " shape=(10001,), dtype=uint64), tn=array([9973, 9973, 9973, ..., 5465, 4438, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([27, 27, 27, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9279995997198038, pr_auc_value=0.013498575016895864, tp=array([0, 0, 0, ..., 7, 7, 7], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 1993, 2506, 9993],\n", + " shape=(10001,), dtype=uint64), tn=array([9993, 9993, 9993, ..., 8000, 7487, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([7, 7, 7, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9775659308683804, pr_auc_value=0.3540969867374027, tp=array([ 0, 0, 0, ..., 123, 123, 123], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 3992, 5129, 9877],\n", + " shape=(10001,), dtype=uint64), tn=array([9877, 9877, 9877, ..., 5885, 4748, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([123, 123, 123, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9906164008225748, pr_auc_value=0.18941314478290616, tp=array([ 0, 0, 0, ..., 17, 17, 17], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 3416, 4689, 9983],\n", + " shape=(10001,), dtype=uint64), tn=array([9983, 9983, 9983, ..., 6567, 5294, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([17, 17, 17, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9863552578339253, pr_auc_value=0.20092429034208456, tp=array([ 0, 0, 0, ..., 29, 29, 29], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 4016, 5064, 9971],\n", + " shape=(10001,), dtype=uint64), tn=array([9971, 9971, 9971, ..., 5955, 4907, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([29, 29, 29, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9561885528563765, pr_auc_value=0.15891559967806496, tp=array([ 0, 0, 0, ..., 66, 67, 67], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 4364, 5534, 9933],\n", + " shape=(10001,), dtype=uint64), tn=array([9933, 9933, 9933, ..., 5569, 4399, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([67, 67, 67, ..., 1, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9958002180587038, pr_auc_value=0.5997394323387932, tp=array([ 0, 0, 0, ..., 42, 42, 42], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 2934, 3900, 9958],\n", + " shape=(10001,), dtype=uint64), tn=array([9958, 9958, 9958, ..., 7024, 6058, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([42, 42, 42, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9888977795559113, pr_auc_value=0.029364984131783345, tp=array([0, 0, 0, ..., 2, 2, 2], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 3321, 4125, 9998],\n", + " shape=(10001,), dtype=uint64), tn=array([9998, 9998, 9998, ..., 6677, 5873, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([2, 2, 2, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.928185444096582, pr_auc_value=0.0076857569359442416, tp=array([0, 0, 0, ..., 7, 7, 7], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 3631, 4570, 9993],\n", + " shape=(10001,), dtype=uint64), tn=array([9993, 9993, 9993, ..., 6362, 5423, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([7, 7, 7, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9922643459704579, pr_auc_value=0.020224480615327778, tp=array([0, 0, 0, ..., 3, 3, 3], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 3274, 4459, 9997],\n", + " shape=(10001,), dtype=uint64), tn=array([9997, 9997, 9997, ..., 6723, 5538, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([3, 3, 3, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.7511129451780711, pr_auc_value=0.0013577577385953583, tp=array([0, 0, 0, ..., 3, 3, 4], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 2907, 4135, 9996],\n", + " shape=(10001,), dtype=uint64), tn=array([9996, 9996, 9996, ..., 7089, 5861, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([4, 4, 4, ..., 1, 1, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9819909954977489, pr_auc_value=0.012625518398105839, tp=array([0, 0, 0, ..., 5, 5, 5], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 1832, 2678, 9995],\n", + " shape=(10001,), dtype=uint64), tn=array([9995, 9995, 9995, ..., 8163, 7317, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([5, 5, 5, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.8796964137212455, pr_auc_value=0.06329059623049148, tp=array([ 0, 0, 0, ..., 31, 31, 31], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 7912, 8966, 9969],\n", + " shape=(10001,), dtype=uint64), tn=array([9969, 9969, 9969, ..., 2057, 1003, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([31, 31, 31, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9534725519558787, pr_auc_value=0.2883860065568794, tp=array([ 0, 0, 0, ..., 43, 43, 44], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 5499, 6765, 9956],\n", + " shape=(10001,), dtype=uint64), tn=array([9956, 9956, 9956, ..., 4457, 3191, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([44, 44, 44, ..., 1, 1, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9771264712747159, pr_auc_value=0.10659544966749661, tp=array([0, 0, 0, ..., 3, 3, 3], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 1803, 2543, 9997],\n", + " shape=(10001,), dtype=uint64), tn=array([9997, 9997, 9997, ..., 8194, 7454, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([3, 3, 3, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9337117423484697, pr_auc_value=0.0013933361108011688, tp=array([0, 0, 0, ..., 2, 2, 2], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 1964, 2754, 9998],\n", + " shape=(10001,), dtype=uint64), tn=array([9998, 9998, 9998, ..., 8034, 7244, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([2, 2, 2, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9564627899969871, pr_auc_value=0.06416326589084198, tp=array([ 0, 0, 0, ..., 43, 43, 43], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 3363, 4287, 9957],\n", + " shape=(10001,), dtype=uint64), tn=array([9957, 9957, 9957, ..., 6594, 5670, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([43, 43, 43, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.934028821113782, pr_auc_value=0.037343524460751516, tp=array([ 0, 0, 0, ..., 16, 16, 16], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 4091, 5309, 9984],\n", + " shape=(10001,), dtype=uint64), tn=array([9984, 9984, 9984, ..., 5893, 4675, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([16, 16, 16, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9515399458700882, pr_auc_value=0.047428976882533405, tp=array([ 0, 0, 0, ..., 24, 24, 24], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 4178, 5310, 9976],\n", + " shape=(10001,), dtype=uint64), tn=array([9976, 9976, 9976, ..., 5798, 4666, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([24, 24, 24, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.8975351205974078, pr_auc_value=0.03228448517372814, tp=array([ 0, 0, 0, ..., 13, 14, 14], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 3107, 4008, 9986],\n", + " shape=(10001,), dtype=uint64), tn=array([9986, 9986, 9986, ..., 6879, 5978, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([14, 14, 14, ..., 1, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9636676467642049, pr_auc_value=0.08703685224985092, tp=array([ 0, 0, 0, ..., 17, 17, 17], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 3890, 4863, 9983],\n", + " shape=(10001,), dtype=uint64), tn=array([9983, 9983, 9983, ..., 6093, 5120, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([17, 17, 17, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9567332309267793, pr_auc_value=0.07397966270213299, tp=array([ 0, 0, 0, ..., 18, 18, 18], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 3294, 4386, 9982],\n", + " shape=(10001,), dtype=uint64), tn=array([9982, 9982, 9982, ..., 6688, 5596, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([18, 18, 18, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.8206593382040798, pr_auc_value=0.024745887759982783, tp=array([ 0, 0, 0, ..., 54, 54, 54], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 8186, 8680, 9946],\n", + " shape=(10001,), dtype=uint64), tn=array([9946, 9946, 9946, ..., 1760, 1266, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([54, 54, 54, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9584583833533413, pr_auc_value=0.02135048645192785, tp=array([0, 0, 0, ..., 4, 4, 4], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 2071, 3069, 9996],\n", + " shape=(10001,), dtype=uint64), tn=array([9996, 9996, 9996, ..., 7925, 6927, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([4, 4, 4, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9191725542169641, pr_auc_value=0.3128471529263308, tp=array([ 0, 0, 0, ..., 416, 416, 416], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 7382, 7855, 9584],\n", + " shape=(10001,), dtype=uint64), tn=array([9584, 9584, 9584, ..., 2202, 1729, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([416, 416, 416, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.8276826826826826, pr_auc_value=0.03219333489095928, tp=array([ 0, 0, 0, ..., 8, 9, 10], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 4082, 5484, 9990],\n", + " shape=(10001,), dtype=uint64), tn=array([9990, 9990, 9990, ..., 5908, 4506, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([10, 10, 10, ..., 2, 1, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9879044634108911, pr_auc_value=0.15615449643390655, tp=array([ 0, 0, 0, ..., 34, 34, 34], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 2893, 3771, 9966],\n", + " shape=(10001,), dtype=uint64), tn=array([9966, 9966, 9966, ..., 7073, 6195, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([34, 34, 34, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9928096238495399, pr_auc_value=0.024829437301391977, tp=array([0, 0, 0, ..., 4, 4, 4], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 1275, 1787, 9996],\n", + " shape=(10001,), dtype=uint64), tn=array([9996, 9996, 9996, ..., 8721, 8209, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([4, 4, 4, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9376601503759397, pr_auc_value=0.04344560144381243, tp=array([ 0, 0, 0, ..., 25, 25, 25], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 5458, 6515, 9975],\n", + " shape=(10001,), dtype=uint64), tn=array([9975, 9975, 9975, ..., 4517, 3460, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([25, 25, 25, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9660128012048194, pr_auc_value=0.12267559741318142, tp=array([ 0, 0, 0, ..., 40, 40, 40], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 5853, 6882, 9960],\n", + " shape=(10001,), dtype=uint64), tn=array([9960, 9960, 9960, ..., 4107, 3078, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([40, 40, 40, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9430438629294855, pr_auc_value=0.05695382978302018, tp=array([ 0, 0, 0, ..., 33, 33, 33], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 4955, 5991, 9967],\n", + " shape=(10001,), dtype=uint64), tn=array([9967, 9967, 9967, ..., 5012, 3976, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([33, 33, 33, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.8149485808309336, pr_auc_value=0.02837977087412348, tp=array([ 0, 0, 0, ..., 55, 55, 55], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 8479, 8976, 9945],\n", + " shape=(10001,), dtype=uint64), tn=array([9945, 9945, 9945, ..., 1466, 969, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([55, 55, 55, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9581135366184901, pr_auc_value=0.011394852667637364, tp=array([0, 0, 0, ..., 7, 7, 7], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 2735, 3520, 9993],\n", + " shape=(10001,), dtype=uint64), tn=array([9993, 9993, 9993, ..., 7258, 6473, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([7, 7, 7, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.985291174704823, pr_auc_value=0.33939368712120166, tp=array([0, 0, 0, ..., 5, 6, 6], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 661, 1013, 9994],\n", + " shape=(10001,), dtype=uint64), tn=array([9994, 9994, 9994, ..., 9333, 8981, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([6, 6, 6, ..., 1, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.8750828387022418, pr_auc_value=0.05420425102146535, tp=array([ 0, 0, 0, ..., 84, 84, 84], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 7171, 7755, 9916],\n", + " shape=(10001,), dtype=uint64), tn=array([9916, 9916, 9916, ..., 2745, 2161, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([84, 84, 84, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9487116833440759, pr_auc_value=0.27059489865879655, tp=array([ 0, 0, 0, ..., 38, 38, 38], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 5293, 6185, 9962],\n", + " shape=(10001,), dtype=uint64), tn=array([9962, 9962, 9962, ..., 4669, 3777, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([38, 38, 38, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9705049288549891, pr_auc_value=0.22950738853956157, tp=array([ 0, 0, 0, ..., 36, 36, 36], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 4053, 5186, 9964],\n", + " shape=(10001,), dtype=uint64), tn=array([9964, 9964, 9964, ..., 5911, 4778, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([36, 36, 36, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9547597597597598, pr_auc_value=0.1545174759177201, tp=array([ 0, 0, 0, ..., 10, 10, 10], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 2726, 3725, 9990],\n", + " shape=(10001,), dtype=uint64), tn=array([9990, 9990, 9990, ..., 7264, 6265, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([10, 10, 10, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.984767960859489, pr_auc_value=0.26342735684174035, tp=array([ 0, 0, 0, ..., 14, 14, 14], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 2482, 3273, 9986],\n", + " shape=(10001,), dtype=uint64), tn=array([9986, 9986, 9986, ..., 7504, 6713, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([14, 14, 14, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9946753463528626, pr_auc_value=0.45419610031392066, tp=array([ 0, 0, 0, ..., 15, 15, 15], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 3609, 4812, 9985],\n", + " shape=(10001,), dtype=uint64), tn=array([9985, 9985, 9985, ..., 6376, 5173, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([15, 15, 15, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9915983196639329, pr_auc_value=0.05823464377916299, tp=array([0, 0, 0, ..., 2, 2, 2], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 1675, 2427, 9998],\n", + " shape=(10001,), dtype=uint64), tn=array([9998, 9998, 9998, ..., 8323, 7571, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([2, 2, 2, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.7186124449779911, pr_auc_value=0.0021311121689967035, tp=array([0, 0, 0, ..., 2, 3, 4], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 2794, 4326, 9996],\n", + " shape=(10001,), dtype=uint64), tn=array([9996, 9996, 9996, ..., 7202, 5670, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([4, 4, 4, ..., 2, 1, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9990246098439376, pr_auc_value=0.15383305094959043, tp=array([0, 0, 0, ..., 4, 4, 4], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 1487, 2070, 9996],\n", + " shape=(10001,), dtype=uint64), tn=array([9996, 9996, 9996, ..., 8509, 7926, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([4, 4, 4, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.950036454089291, pr_auc_value=0.012061163610989968, tp=array([0, 0, 0, ..., 7, 7, 7], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 2167, 3531, 9993],\n", + " shape=(10001,), dtype=uint64), tn=array([9993, 9993, 9993, ..., 7826, 6462, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([7, 7, 7, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.965537705988746, pr_auc_value=0.34385645177531604, tp=array([ 0, 0, 0, ..., 48, 48, 48], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 4715, 5977, 9952],\n", + " shape=(10001,), dtype=uint64), tn=array([9952, 9952, 9952, ..., 5237, 3975, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([48, 48, 48, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9905192450983663, pr_auc_value=0.14553942173551823, tp=array([0, 0, 0, ..., 9, 9, 9], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 2493, 3468, 9991],\n", + " shape=(10001,), dtype=uint64), tn=array([9991, 9991, 9991, ..., 7498, 6523, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([9, 9, 9, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9429200881233728, pr_auc_value=0.10792292054261689, tp=array([ 0, 0, 0, ..., 13, 13, 14], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 1578, 2039, 9986],\n", + " shape=(10001,), dtype=uint64), tn=array([9986, 9986, 9986, ..., 8408, 7947, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([14, 14, 14, ..., 1, 1, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9760006227827267, pr_auc_value=0.08022910929927561, tp=array([0, 0, 0, ..., 9, 9, 9], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 3087, 4135, 9991],\n", + " shape=(10001,), dtype=uint64), tn=array([9991, 9991, 9991, ..., 6904, 5856, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([9, 9, 9, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9805941782534761, pr_auc_value=0.11151225034553995, tp=array([0, 0, 0, ..., 3, 3, 3], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 1890, 2829, 9997],\n", + " shape=(10001,), dtype=uint64), tn=array([9997, 9997, 9997, ..., 8107, 7168, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([3, 3, 3, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9542860004005608, pr_auc_value=0.04911722036427055, tp=array([ 0, 0, 0, ..., 12, 14, 14], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 1646, 2245, 9986],\n", + " shape=(10001,), dtype=uint64), tn=array([9986, 9986, 9986, ..., 8340, 7741, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([14, 14, 14, ..., 2, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9794703815261045, pr_auc_value=0.24844079939414057, tp=array([ 0, 0, 0, ..., 40, 40, 40], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 3975, 4997, 9960],\n", + " shape=(10001,), dtype=uint64), tn=array([9960, 9960, 9960, ..., 5985, 4963, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([40, 40, 40, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9733757206793014, pr_auc_value=0.11548632535014497, tp=array([ 0, 0, 0, ..., 23, 23, 23], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 2750, 3807, 9977],\n", + " shape=(10001,), dtype=uint64), tn=array([9977, 9977, 9977, ..., 7227, 6170, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([23, 23, 23, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.8176788394197099, pr_auc_value=0.062443585431305806, tp=array([0, 0, 0, ..., 5, 5, 5], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 4349, 5773, 9995],\n", + " shape=(10001,), dtype=uint64), tn=array([9995, 9995, 9995, ..., 5646, 4222, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([5, 5, 5, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.8800695004719494, pr_auc_value=0.05485481725125655, tp=array([ 0, 0, 0, ..., 18, 19, 19], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 5271, 6493, 9981],\n", + " shape=(10001,), dtype=uint64), tn=array([9981, 9981, 9981, ..., 4710, 3488, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([19, 19, 19, ..., 1, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9891562536972487, pr_auc_value=0.10319960892150518, tp=array([ 0, 0, 0, ..., 11, 11, 11], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 2098, 2958, 9989],\n", + " shape=(10001,), dtype=uint64), tn=array([9989, 9989, 9989, ..., 7891, 7031, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([11, 11, 11, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9921593142889313, pr_auc_value=0.6330115745764032, tp=array([ 0, 0, 0, ..., 86, 86, 86], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 4228, 5565, 9914],\n", + " shape=(10001,), dtype=uint64), tn=array([9914, 9914, 9914, ..., 5686, 4349, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([86, 86, 86, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.8856942295570455, pr_auc_value=0.06067171866687978, tp=array([ 0, 0, 0, ..., 28, 28, 28], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 6218, 7204, 9972],\n", + " shape=(10001,), dtype=uint64), tn=array([9972, 9972, 9972, ..., 3754, 2768, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([28, 28, 28, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.8942448442351052, pr_auc_value=0.03103719238584301, tp=array([ 0, 0, 0, ..., 34, 34, 34], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 7261, 8076, 9966],\n", + " shape=(10001,), dtype=uint64), tn=array([9966, 9966, 9966, ..., 2705, 1890, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([34, 34, 34, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9539807217663931, pr_auc_value=0.05806592321745796, tp=array([0, 0, 0, ..., 6, 6, 6], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 3833, 4777, 9994],\n", + " shape=(10001,), dtype=uint64), tn=array([9994, 9994, 9994, ..., 6161, 5217, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([6, 6, 6, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9460184877583199, pr_auc_value=0.08014234076710489, tp=array([ 0, 0, 0, ..., 19, 19, 19], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 5096, 6198, 9981],\n", + " shape=(10001,), dtype=uint64), tn=array([9981, 9981, 9981, ..., 4885, 3783, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([19, 19, 19, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9587902602854744, pr_auc_value=0.19520450581998094, tp=array([ 0, 0, 0, ..., 75, 75, 75], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 4952, 5891, 9925],\n", + " shape=(10001,), dtype=uint64), tn=array([9925, 9925, 9925, ..., 4973, 4034, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([75, 75, 75, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9759490058768182, pr_auc_value=0.17221753372722842, tp=array([ 0, 0, 0, ..., 26, 26, 26], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 4039, 5052, 9974],\n", + " shape=(10001,), dtype=uint64), tn=array([9974, 9974, 9974, ..., 5935, 4922, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([26, 26, 26, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9269861991794108, pr_auc_value=0.08502081954190435, tp=array([ 0, 0, 0, ..., 42, 42, 42], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 5554, 6636, 9958],\n", + " shape=(10001,), dtype=uint64), tn=array([9958, 9958, 9958, ..., 4404, 3322, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([42, 42, 42, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.8259857886309048, pr_auc_value=0.051393039862268466, tp=array([0, 0, 0, ..., 6, 6, 8], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 1714, 2293, 9992],\n", + " shape=(10001,), dtype=uint64), tn=array([9992, 9992, 9992, ..., 8278, 7699, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([8, 8, 8, ..., 2, 2, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9781702354746181, pr_auc_value=0.020319933146858853, tp=array([0, 0, 0, ..., 6, 6, 6], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 1660, 2370, 9994],\n", + " shape=(10001,), dtype=uint64), tn=array([9994, 9994, 9994, ..., 8334, 7624, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([6, 6, 6, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9723861930965483, pr_auc_value=0.20899940467730885, tp=array([0, 0, 0, ..., 5, 5, 5], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 2581, 3734, 9995],\n", + " shape=(10001,), dtype=uint64), tn=array([9995, 9995, 9995, ..., 7414, 6261, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([5, 5, 5, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=1.0, pr_auc_value=1.0, tp=array([0, 0, 0, ..., 2, 2, 2], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 602, 1002, 9998],\n", + " shape=(10001,), dtype=uint64), tn=array([9998, 9998, 9998, ..., 9396, 8996, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([2, 2, 2, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9441428142514011, pr_auc_value=0.010608060803545405, tp=array([0, 0, 0, ..., 8, 8, 8], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 2847, 3861, 9992],\n", + " shape=(10001,), dtype=uint64), tn=array([9992, 9992, 9992, ..., 7145, 6131, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([8, 8, 8, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.8498748748748749, pr_auc_value=0.0034252947101858717, tp=array([ 0, 0, 0, ..., 10, 10, 10], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 4977, 6453, 9990],\n", + " shape=(10001,), dtype=uint64), tn=array([9990, 9990, 9990, ..., 5013, 3537, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([10, 10, 10, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.8035089035614245, pr_auc_value=0.005296704157256163, tp=array([0, 0, 0, ..., 3, 3, 4], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 2755, 3469, 9996],\n", + " shape=(10001,), dtype=uint64), tn=array([9996, 9996, 9996, ..., 7241, 6527, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([4, 4, 4, ..., 1, 1, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9730083545291265, pr_auc_value=0.13724382104560523, tp=array([ 0, 0, 0, ..., 42, 43, 43], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 2870, 3879, 9957],\n", + " shape=(10001,), dtype=uint64), tn=array([9957, 9957, 9957, ..., 7087, 6078, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([43, 43, 43, ..., 1, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.8785818338663867, pr_auc_value=0.02359813002494996, tp=array([ 0, 0, 0, ..., 37, 37, 37], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 5470, 6518, 9963],\n", + " shape=(10001,), dtype=uint64), tn=array([9963, 9963, 9963, ..., 4493, 3445, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([37, 37, 37, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.8095293750973976, pr_auc_value=0.005672438301773192, tp=array([ 0, 0, 0, ..., 18, 18, 18], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 9220, 9716, 9982],\n", + " shape=(10001,), dtype=uint64), tn=array([9982, 9982, 9982, ..., 762, 266, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([18, 18, 18, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9891807430893419, pr_auc_value=0.20156017545728558, tp=array([ 0, 0, 0, ..., 22, 22, 22], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 2415, 3249, 9978],\n", + " shape=(10001,), dtype=uint64), tn=array([9978, 9978, 9978, ..., 7563, 6729, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([22, 22, 22, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.8717171210906175, pr_auc_value=0.013439355580537693, tp=array([ 0, 0, 0, ..., 24, 24, 24], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 5784, 6793, 9976],\n", + " shape=(10001,), dtype=uint64), tn=array([9976, 9976, 9976, ..., 4192, 3183, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([24, 24, 24, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9952471618971268, pr_auc_value=0.3912891769141768, tp=array([ 0, 0, 0, ..., 21, 21, 21], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 2186, 2862, 9979],\n", + " shape=(10001,), dtype=uint64), tn=array([9979, 9979, 9979, ..., 7793, 7117, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([21, 21, 21, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9595573710315772, pr_auc_value=0.24561219107617033, tp=array([ 0, 0, 0, ..., 101, 101, 101], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 6238, 7425, 9899],\n", + " shape=(10001,), dtype=uint64), tn=array([9899, 9899, 9899, ..., 3661, 2474, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([101, 101, 101, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.995287643821911, pr_auc_value=0.07171382406112733, tp=array([0, 0, 0, ..., 5, 5, 5], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 2838, 3864, 9995],\n", + " shape=(10001,), dtype=uint64), tn=array([9995, 9995, 9995, ..., 7157, 6131, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([5, 5, 5, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9060127964491033, pr_auc_value=0.04116646535428707, tp=array([ 0, 0, 0, ..., 34, 34, 34], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 7372, 8138, 9966],\n", + " shape=(10001,), dtype=uint64), tn=array([9966, 9966, 9966, ..., 2594, 1828, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([34, 34, 34, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9514540248520806, pr_auc_value=0.2701513669346379, tp=array([ 0, 0, 0, ..., 204, 204, 204], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 6217, 7187, 9796],\n", + " shape=(10001,), dtype=uint64), tn=array([9796, 9796, 9796, ..., 3579, 2609, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([204, 204, 204, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9296161029951304, pr_auc_value=0.05661755978299167, tp=array([0, 0, 0, ..., 6, 6, 6], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 3011, 3724, 9994],\n", + " shape=(10001,), dtype=uint64), tn=array([9994, 9994, 9994, ..., 6983, 6270, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([6, 6, 6, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.8839399904358367, pr_auc_value=0.004193106453942738, tp=array([0, 0, 0, ..., 9, 9, 9], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 5048, 5957, 9991],\n", + " shape=(10001,), dtype=uint64), tn=array([9991, 9991, 9991, ..., 4943, 4034, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([9, 9, 9, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9716674472545609, pr_auc_value=0.18991311164355218, tp=array([ 0, 0, 0, ..., 36, 36, 36], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 4039, 5247, 9964],\n", + " shape=(10001,), dtype=uint64), tn=array([9964, 9964, 9964, ..., 5925, 4717, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([36, 36, 36, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9238591309529525, pr_auc_value=0.16146492472330862, tp=array([ 0, 0, 0, ..., 133, 133, 133], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 6407, 7267, 9867],\n", + " shape=(10001,), dtype=uint64), tn=array([9867, 9867, 9867, ..., 3460, 2600, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([133, 133, 133, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.746940748891334, pr_auc_value=0.0026152908014502726, tp=array([0, 0, 0, ..., 2, 2, 3], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 2786, 3832, 9997],\n", + " shape=(10001,), dtype=uint64), tn=array([9997, 9997, 9997, ..., 7211, 6165, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([3, 3, 3, ..., 1, 1, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9746090493814811, pr_auc_value=0.013354927236678563, tp=array([0, 0, 0, ..., 3, 3, 3], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 2207, 2881, 9997],\n", + " shape=(10001,), dtype=uint64), tn=array([9997, 9997, 9997, ..., 7790, 7116, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([3, 3, 3, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9631312625250501, pr_auc_value=0.09280583069826699, tp=array([ 0, 0, 0, ..., 20, 20, 20], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 3634, 4647, 9980],\n", + " shape=(10001,), dtype=uint64), tn=array([9980, 9980, 9980, ..., 6346, 5333, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([20, 20, 20, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9950692705725255, pr_auc_value=0.42428712695939774, tp=array([ 0, 0, 0, ..., 15, 15, 15], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 1775, 2420, 9985],\n", + " shape=(10001,), dtype=uint64), tn=array([9985, 9985, 9985, ..., 8210, 7565, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([15, 15, 15, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9434104787994066, pr_auc_value=0.01406545178678001, tp=array([ 0, 0, 0, ..., 11, 11, 11], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 4116, 5535, 9989],\n", + " shape=(10001,), dtype=uint64), tn=array([9989, 9989, 9989, ..., 5873, 4454, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([11, 11, 11, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9940782297023532, pr_auc_value=0.48195405748297676, tp=array([ 0, 0, 0, ..., 18, 18, 18], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 2424, 3389, 9982],\n", + " shape=(10001,), dtype=uint64), tn=array([9982, 9982, 9982, ..., 7558, 6593, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([18, 18, 18, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.8679665598718462, pr_auc_value=0.0073027377767561335, tp=array([ 0, 0, 0, ..., 12, 12, 12], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 5871, 7041, 9988],\n", + " shape=(10001,), dtype=uint64), tn=array([9988, 9988, 9988, ..., 4117, 2947, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([12, 12, 12, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.8921368635375052, pr_auc_value=0.08379582439637037, tp=array([ 0, 0, 0, ..., 26, 26, 26], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 6252, 7533, 9974],\n", + " shape=(10001,), dtype=uint64), tn=array([9974, 9974, 9974, ..., 3722, 2441, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([26, 26, 26, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9642952438129545, pr_auc_value=0.11627047971282314, tp=array([0, 0, 0, ..., 6, 6, 6], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 2679, 3683, 9994],\n", + " shape=(10001,), dtype=uint64), tn=array([9994, 9994, 9994, ..., 7315, 6311, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([6, 6, 6, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9917784003735575, pr_auc_value=0.22243861723957611, tp=array([0, 0, 0, ..., 6, 6, 6], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 2261, 3175, 9994],\n", + " shape=(10001,), dtype=uint64), tn=array([9994, 9994, 9994, ..., 7733, 6819, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([6, 6, 6, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9736200213846566, pr_auc_value=0.17649384501565235, tp=array([ 0, 0, 0, ..., 24, 24, 24], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 3130, 3984, 9976],\n", + " shape=(10001,), dtype=uint64), tn=array([9976, 9976, 9976, ..., 6846, 5992, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([24, 24, 24, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.8009914872308463, pr_auc_value=0.01944356042210165, tp=array([ 0, 0, 0, ..., 14, 14, 15], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 6370, 7503, 9985],\n", + " shape=(10001,), dtype=uint64), tn=array([9985, 9985, 9985, ..., 3615, 2482, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([15, 15, 15, ..., 1, 1, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.0, pr_auc_value=0.0, tp=array([0, 0, 0, ..., 0, 0, 0], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 2079, 3170, 10000],\n", + " shape=(10001,), dtype=uint64), tn=array([10000, 10000, 10000, ..., 7921, 6830, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([0, 0, 0, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9763889724310777, pr_auc_value=0.15976674360679016, tp=array([ 0, 0, 0, ..., 25, 25, 25], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 4178, 5265, 9975],\n", + " shape=(10001,), dtype=uint64), tn=array([9975, 9975, 9975, ..., 5797, 4710, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([25, 25, 25, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.6485612350371777, pr_auc_value=0.0005996325044730055, tp=array([0, 0, 0, ..., 2, 2, 3], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 4786, 6095, 9997],\n", + " shape=(10001,), dtype=uint64), tn=array([9997, 9997, 9997, ..., 5211, 3902, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([3, 3, 3, ..., 1, 1, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9975865346138455, pr_auc_value=0.1813940090741446, tp=array([0, 0, 0, ..., 4, 4, 4], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 1181, 1772, 9996],\n", + " shape=(10001,), dtype=uint64), tn=array([9996, 9996, 9996, ..., 8815, 8224, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([4, 4, 4, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9563496035320678, pr_auc_value=0.03977753429626071, tp=array([0, 0, 0, ..., 9, 9, 9], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 3649, 4929, 9991],\n", + " shape=(10001,), dtype=uint64), tn=array([9991, 9991, 9991, ..., 6342, 5062, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([9, 9, 9, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9878277622097678, pr_auc_value=0.1945900483744547, tp=array([0, 0, 0, ..., 8, 8, 8], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 4421, 5550, 9992],\n", + " shape=(10001,), dtype=uint64), tn=array([9992, 9992, 9992, ..., 5571, 4442, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([8, 8, 8, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9906587635054022, pr_auc_value=0.27687706581555105, tp=array([0, 0, 0, ..., 4, 4, 4], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 1423, 2104, 9996],\n", + " shape=(10001,), dtype=uint64), tn=array([9996, 9996, 9996, ..., 8573, 7892, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([4, 4, 4, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9924492449244925, pr_auc_value=0.006550469049244612, tp=array([0, 0, 0, ..., 1, 1, 1], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 2759, 3881, 9999],\n", + " shape=(10001,), dtype=uint64), tn=array([9999, 9999, 9999, ..., 7240, 6118, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([1, 1, 1, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.904496152516271, pr_auc_value=0.08465385479918419, tp=array([ 0, 0, 0, ..., 79, 79, 79], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 6224, 6744, 9921],\n", + " shape=(10001,), dtype=uint64), tn=array([9921, 9921, 9921, ..., 3697, 3177, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([79, 79, 79, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.7452735820746224, pr_auc_value=0.0016338831053234395, tp=array([0, 0, 0, ..., 2, 2, 3], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 2351, 3392, 9997],\n", + " shape=(10001,), dtype=uint64), tn=array([9997, 9997, 9997, ..., 7646, 6605, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([3, 3, 3, ..., 1, 1, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9945989197839568, pr_auc_value=0.01706900661445876, tp=array([0, 0, 0, ..., 2, 2, 2], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 1376, 1977, 9998],\n", + " shape=(10001,), dtype=uint64), tn=array([9998, 9998, 9998, ..., 8622, 8021, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([2, 2, 2, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9178046218487395, pr_auc_value=0.2523051833866025, tp=array([0, 0, 0, ..., 4, 4, 4], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 3454, 4588, 9996],\n", + " shape=(10001,), dtype=uint64), tn=array([9996, 9996, 9996, ..., 6542, 5408, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([4, 4, 4, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9776150260625502, pr_auc_value=0.06100779758293685, tp=array([ 0, 0, 0, ..., 24, 24, 24], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 3312, 4321, 9976],\n", + " shape=(10001,), dtype=uint64), tn=array([9976, 9976, 9976, ..., 6664, 5655, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([24, 24, 24, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.8698412980926108, pr_auc_value=0.05281829887074505, tp=array([ 0, 0, 0, ..., 85, 85, 85], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 8266, 8810, 9915],\n", + " shape=(10001,), dtype=uint64), tn=array([9915, 9915, 9915, ..., 1649, 1105, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([85, 85, 85, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9812053069985208, pr_auc_value=0.2226511898467848, tp=array([0, 0, 0, ..., 9, 9, 9], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 2306, 3097, 9991],\n", + " shape=(10001,), dtype=uint64), tn=array([9991, 9991, 9991, ..., 7685, 6894, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([9, 9, 9, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9633962735994102, pr_auc_value=0.20270376938578416, tp=array([ 0, 0, 0, ..., 79, 79, 79], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 4373, 5699, 9921],\n", + " shape=(10001,), dtype=uint64), tn=array([9921, 9921, 9921, ..., 5548, 4222, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([79, 79, 79, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9917621597277823, pr_auc_value=0.0818023088290745, tp=array([0, 0, 0, ..., 8, 8, 8], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 2469, 3536, 9992],\n", + " shape=(10001,), dtype=uint64), tn=array([9992, 9992, 9992, ..., 7523, 6456, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([8, 8, 8, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9710283847901631, pr_auc_value=0.10359708735206126, tp=array([ 0, 0, 0, ..., 24, 24, 24], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 3594, 4695, 9976],\n", + " shape=(10001,), dtype=uint64), tn=array([9976, 9976, 9976, ..., 6382, 5281, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([24, 24, 24, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9914042100884997, pr_auc_value=0.08527706223675964, tp=array([ 0, 0, 0, ..., 13, 13, 13], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 3919, 4986, 9987],\n", + " shape=(10001,), dtype=uint64), tn=array([9987, 9987, 9987, ..., 6068, 5001, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([13, 13, 13, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.8633216608304153, pr_auc_value=0.0289106876839473, tp=array([0, 0, 0, ..., 4, 4, 5], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 2096, 3244, 9995],\n", + " shape=(10001,), dtype=uint64), tn=array([9995, 9995, 9995, ..., 7899, 6751, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([5, 5, 5, ..., 1, 1, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9049155473135525, pr_auc_value=0.018527449212055822, tp=array([ 0, 0, 0, ..., 24, 24, 24], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 6869, 8041, 9976],\n", + " shape=(10001,), dtype=uint64), tn=array([9976, 9976, 9976, ..., 3107, 1935, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([24, 24, 24, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9965782891445724, pr_auc_value=0.1032337859673444, tp=array([0, 0, 0, ..., 5, 5, 5], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 1466, 2081, 9995],\n", + " shape=(10001,), dtype=uint64), tn=array([9995, 9995, 9995, ..., 8529, 7914, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([5, 5, 5, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9671643286573146, pr_auc_value=0.09405425482094824, tp=array([ 0, 0, 0, ..., 20, 20, 20], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 3492, 4503, 9980],\n", + " shape=(10001,), dtype=uint64), tn=array([9980, 9980, 9980, ..., 6488, 5477, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([20, 20, 20, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.8453633292714905, pr_auc_value=0.017243261947930397, tp=array([ 0, 0, 0, ..., 26, 26, 26], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 6222, 7061, 9974],\n", + " shape=(10001,), dtype=uint64), tn=array([9974, 9974, 9974, ..., 3752, 2913, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([26, 26, 26, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9749949989998, pr_auc_value=0.027398695752911095, tp=array([0, 0, 0, ..., 2, 2, 2], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 1270, 1693, 9998],\n", + " shape=(10001,), dtype=uint64), tn=array([9998, 9998, 9998, ..., 8728, 8305, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([2, 2, 2, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9867486748674867, pr_auc_value=0.0037053505044664092, tp=array([0, 0, 0, ..., 1, 1, 1], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 999, 1533, 9999],\n", + " shape=(10001,), dtype=uint64), tn=array([9999, 9999, 9999, ..., 9000, 8466, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([1, 1, 1, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.8329291842793878, pr_auc_value=0.032079716629852914, tp=array([ 0, 0, 0, ..., 64, 64, 64], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 8403, 8934, 9936],\n", + " shape=(10001,), dtype=uint64), tn=array([9936, 9936, 9936, ..., 1533, 1002, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([64, 64, 64, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9665690314166997, pr_auc_value=0.32447555881677603, tp=array([ 0, 0, 0, ..., 58, 58, 58], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 5250, 6279, 9942],\n", + " shape=(10001,), dtype=uint64), tn=array([9942, 9942, 9942, ..., 4692, 3663, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([58, 58, 58, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9424559471365639, pr_auc_value=0.02307754513273321, tp=array([ 0, 0, 0, ..., 12, 12, 12], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 3446, 4434, 9988],\n", + " shape=(10001,), dtype=uint64), tn=array([9988, 9988, 9988, ..., 6542, 5554, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([12, 12, 12, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9668518173879432, pr_auc_value=0.04575448099303544, tp=array([ 0, 0, 0, ..., 21, 21, 21], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 4202, 5122, 9979],\n", + " shape=(10001,), dtype=uint64), tn=array([9979, 9979, 9979, ..., 5777, 4857, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([21, 21, 21, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9469032557354926, pr_auc_value=0.23417568912889694, tp=array([ 0, 0, 0, ..., 120, 120, 120], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 6779, 7647, 9880],\n", + " shape=(10001,), dtype=uint64), tn=array([9880, 9880, 9880, ..., 3101, 2233, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([120, 120, 120, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9935573458767013, pr_auc_value=0.1738664781919742, tp=array([0, 0, 0, ..., 8, 8, 8], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 2656, 3853, 9992],\n", + " shape=(10001,), dtype=uint64), tn=array([9992, 9992, 9992, ..., 7336, 6139, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([8, 8, 8, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9563665325285045, pr_auc_value=0.18535762449376242, tp=array([ 0, 0, 0, ..., 60, 60, 60], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 5540, 6725, 9940],\n", + " shape=(10001,), dtype=uint64), tn=array([9940, 9940, 9940, ..., 4400, 3215, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([60, 60, 60, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9592336934773908, pr_auc_value=0.010862716643041295, tp=array([0, 0, 0, ..., 4, 4, 4], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 1858, 2563, 9996],\n", + " shape=(10001,), dtype=uint64), tn=array([9996, 9996, 9996, ..., 8138, 7433, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([4, 4, 4, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.8468852130325815, pr_auc_value=0.0328476874890275, tp=array([ 0, 0, 0, ..., 25, 25, 25], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 5933, 7319, 9975],\n", + " shape=(10001,), dtype=uint64), tn=array([9975, 9975, 9975, ..., 4042, 2656, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([25, 25, 25, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9962143953038489, pr_auc_value=0.0669646588164478, tp=array([0, 0, 0, ..., 6, 6, 6], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 2054, 2877, 9994],\n", + " shape=(10001,), dtype=uint64), tn=array([9994, 9994, 9994, ..., 7940, 7117, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([6, 6, 6, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.968784392196098, pr_auc_value=0.010327863911832224, tp=array([0, 0, 0, ..., 5, 5, 5], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 2845, 3757, 9995],\n", + " shape=(10001,), dtype=uint64), tn=array([9995, 9995, 9995, ..., 7150, 6238, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([5, 5, 5, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9894994994994994, pr_auc_value=0.19417022229230127, tp=array([ 0, 0, 0, ..., 10, 10, 10], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 2872, 3815, 9990],\n", + " shape=(10001,), dtype=uint64), tn=array([9990, 9990, 9990, ..., 7118, 6175, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([10, 10, 10, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.8444625523580792, pr_auc_value=0.006258485904284729, tp=array([0, 0, 0, ..., 6, 6, 7], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 4829, 6216, 9993],\n", + " shape=(10001,), dtype=uint64), tn=array([9993, 9993, 9993, ..., 5164, 3777, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([7, 7, 7, ..., 1, 1, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9483500501504514, pr_auc_value=0.10024557174619338, tp=array([ 0, 0, 0, ..., 30, 30, 30], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 4262, 5174, 9970],\n", + " shape=(10001,), dtype=uint64), tn=array([9970, 9970, 9970, ..., 5708, 4796, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([30, 30, 30, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9497114935628044, pr_auc_value=0.012255666568471931, tp=array([0, 0, 0, ..., 6, 6, 6], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 2382, 3335, 9994],\n", + " shape=(10001,), dtype=uint64), tn=array([9994, 9994, 9994, ..., 7612, 6659, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([6, 6, 6, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9673233506541709, pr_auc_value=0.15468333592846492, tp=array([ 0, 0, 0, ..., 52, 52, 52], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 5658, 6961, 9948],\n", + " shape=(10001,), dtype=uint64), tn=array([9948, 9948, 9948, ..., 4290, 2987, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([52, 52, 52, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9937673109138947, pr_auc_value=0.46386472033324294, tp=array([ 0, 0, 0, ..., 33, 33, 33], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 1703, 2613, 9967],\n", + " shape=(10001,), dtype=uint64), tn=array([9967, 9967, 9967, ..., 8264, 7354, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([33, 33, 33, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9896043318490522, pr_auc_value=0.45346866309522926, tp=array([ 0, 0, 0, ..., 47, 47, 47], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 3281, 4141, 9953],\n", + " shape=(10001,), dtype=uint64), tn=array([9953, 9953, 9953, ..., 6672, 5812, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([47, 47, 47, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.6463863863863863, pr_auc_value=0.0035350198165434313, tp=array([ 0, 0, 0, ..., 9, 10, 10], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 6504, 7803, 9990],\n", + " shape=(10001,), dtype=uint64), tn=array([9990, 9990, 9990, ..., 3486, 2187, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([10, 10, 10, ..., 1, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.8997274756087094, pr_auc_value=0.017639155194176512, tp=array([ 0, 0, 0, ..., 13, 13, 14], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 3015, 3870, 9986],\n", + " shape=(10001,), dtype=uint64), tn=array([9986, 9986, 9986, ..., 6971, 6116, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([14, 14, 14, ..., 1, 1, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9452230576441102, pr_auc_value=0.03527591741217968, tp=array([ 0, 0, 0, ..., 25, 25, 25], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 4782, 5931, 9975],\n", + " shape=(10001,), dtype=uint64), tn=array([9975, 9975, 9975, ..., 5193, 4044, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([25, 25, 25, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.8970971892670373, pr_auc_value=0.06926315576568612, tp=array([ 0, 0, 0, ..., 34, 34, 34], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 6562, 7636, 9966],\n", + " shape=(10001,), dtype=uint64), tn=array([9966, 9966, 9966, ..., 3404, 2330, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([34, 34, 34, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.8935969464339324, pr_auc_value=0.0050088964515453945, tp=array([0, 0, 0, ..., 7, 7, 7], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 3331, 4276, 9993],\n", + " shape=(10001,), dtype=uint64), tn=array([9993, 9993, 9993, ..., 6662, 5717, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([7, 7, 7, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.958206458806879, pr_auc_value=0.020941003968824794, tp=array([0, 0, 0, ..., 7, 7, 7], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 3148, 4084, 9993],\n", + " shape=(10001,), dtype=uint64), tn=array([9993, 9993, 9993, ..., 6845, 5909, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([7, 7, 7, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9415464973807506, pr_auc_value=0.04077213509122727, tp=array([ 0, 0, 0, ..., 33, 33, 33], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 4677, 5540, 9967],\n", + " shape=(10001,), dtype=uint64), tn=array([9967, 9967, 9967, ..., 5290, 4427, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([33, 33, 33, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9628890612489992, pr_auc_value=0.023648998855770875, tp=array([0, 0, 0, ..., 8, 8, 8], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 2333, 3142, 9992],\n", + " shape=(10001,), dtype=uint64), tn=array([9992, 9992, 9992, ..., 7659, 6850, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([8, 8, 8, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9247281235231406, pr_auc_value=0.25800933382405916, tp=array([ 0, 0, 0, ..., 347, 347, 347], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 7149, 7994, 9653],\n", + " shape=(10001,), dtype=uint64), tn=array([9653, 9653, 9653, ..., 2504, 1659, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([347, 347, 347, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9036877490594182, pr_auc_value=0.013703191954909074, tp=array([ 0, 0, 0, ..., 18, 18, 18], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 3746, 4572, 9982],\n", + " shape=(10001,), dtype=uint64), tn=array([9982, 9982, 9982, ..., 6236, 5410, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([18, 18, 18, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9927592171663021, pr_auc_value=0.1706053052156768, tp=array([0, 0, 0, ..., 7, 7, 7], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 2690, 3807, 9993],\n", + " shape=(10001,), dtype=uint64), tn=array([9993, 9993, 9993, ..., 7303, 6186, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([7, 7, 7, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9251852222667201, pr_auc_value=0.024076029780467325, tp=array([ 0, 0, 0, ..., 12, 12, 12], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 4961, 5938, 9988],\n", + " shape=(10001,), dtype=uint64), tn=array([9988, 9988, 9988, ..., 5027, 4050, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([12, 12, 12, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.926003039513678, pr_auc_value=0.16465501287556505, tp=array([ 0, 0, 0, ..., 130, 130, 130], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 6058, 6969, 9870],\n", + " shape=(10001,), dtype=uint64), tn=array([9870, 9870, 9870, ..., 3812, 2901, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([130, 130, 130, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9814110899936648, pr_auc_value=0.008796716469701297, tp=array([0, 0, 0, ..., 3, 3, 3], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 2327, 3538, 9997],\n", + " shape=(10001,), dtype=uint64), tn=array([9997, 9997, 9997, ..., 7670, 6459, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([3, 3, 3, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9220610305152577, pr_auc_value=0.006098903785618086, tp=array([0, 0, 0, ..., 5, 5, 5], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 3206, 4494, 9995],\n", + " shape=(10001,), dtype=uint64), tn=array([9995, 9995, 9995, ..., 6789, 5501, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([5, 5, 5, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9122472288132593, pr_auc_value=0.0709997720934003, tp=array([ 0, 0, 0, ..., 34, 34, 34], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 5599, 6553, 9966],\n", + " shape=(10001,), dtype=uint64), tn=array([9966, 9966, 9966, ..., 4367, 3413, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([34, 34, 34, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9249132872290393, pr_auc_value=0.3189010156704853, tp=array([ 0, 0, 0, ..., 152, 152, 152], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 7497, 8315, 9848],\n", + " shape=(10001,), dtype=uint64), tn=array([9848, 9848, 9848, ..., 2351, 1533, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([152, 152, 152, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.969428584273704, pr_auc_value=0.07278337165077998, tp=array([ 0, 0, 0, ..., 29, 29, 29], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 3540, 4380, 9971],\n", + " shape=(10001,), dtype=uint64), tn=array([9971, 9971, 9971, ..., 6431, 5591, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([29, 29, 29, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9887387387387387, pr_auc_value=0.2398349189498105, tp=array([ 0, 0, 0, ..., 10, 10, 10], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 2586, 3553, 9990],\n", + " shape=(10001,), dtype=uint64), tn=array([9990, 9990, 9990, ..., 7404, 6437, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([10, 10, 10, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9813944183254977, pr_auc_value=0.007594813708460713, tp=array([0, 0, 0, ..., 3, 3, 3], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 2171, 3130, 9997],\n", + " shape=(10001,), dtype=uint64), tn=array([9997, 9997, 9997, ..., 7826, 6867, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([3, 3, 3, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9345863345338135, pr_auc_value=0.0092906421729569, tp=array([0, 0, 0, ..., 4, 4, 4], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 3092, 4095, 9996],\n", + " shape=(10001,), dtype=uint64), tn=array([9996, 9996, 9996, ..., 6904, 5901, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([4, 4, 4, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9944716830098059, pr_auc_value=0.1059281079710163, tp=array([0, 0, 0, ..., 6, 6, 6], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 1583, 2293, 9994],\n", + " shape=(10001,), dtype=uint64), tn=array([9994, 9994, 9994, ..., 8411, 7701, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([6, 6, 6, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9729585542329365, pr_auc_value=0.00681382872717832, tp=array([0, 0, 0, ..., 3, 3, 3], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 2240, 3013, 9997],\n", + " shape=(10001,), dtype=uint64), tn=array([9997, 9997, 9997, ..., 7757, 6984, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([3, 3, 3, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9971488595438176, pr_auc_value=0.4076325765198443, tp=array([0, 0, 0, ..., 4, 4, 4], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 1568, 2185, 9996],\n", + " shape=(10001,), dtype=uint64), tn=array([9996, 9996, 9996, ..., 8428, 7811, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([4, 4, 4, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.8667133566783393, pr_auc_value=0.03708377419548075, tp=array([0, 0, 0, ..., 4, 4, 5], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 1815, 2764, 9995],\n", + " shape=(10001,), dtype=uint64), tn=array([9995, 9995, 9995, ..., 8180, 7231, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([5, 5, 5, ..., 1, 1, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9788294147073536, pr_auc_value=0.03623839638075044, tp=array([0, 0, 0, ..., 5, 5, 5], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 1289, 1709, 9995],\n", + " shape=(10001,), dtype=uint64), tn=array([9995, 9995, 9995, ..., 8706, 8286, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([5, 5, 5, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.989137169712896, pr_auc_value=0.5303289947397544, tp=array([ 0, 0, 0, ..., 41, 41, 41], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 2302, 3141, 9959],\n", + " shape=(10001,), dtype=uint64), tn=array([9959, 9959, 9959, ..., 7657, 6818, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([41, 41, 41, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.8997419240010681, pr_auc_value=0.08210231341285064, tp=array([ 0, 0, 0, ..., 84, 85, 85], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 6127, 6914, 9915],\n", + " shape=(10001,), dtype=uint64), tn=array([9915, 9915, 9915, ..., 3788, 3001, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([85, 85, 85, ..., 1, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9728882205513785, pr_auc_value=0.06663976902851548, tp=array([ 0, 0, 0, ..., 25, 25, 25], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 3661, 4693, 9975],\n", + " shape=(10001,), dtype=uint64), tn=array([9975, 9975, 9975, ..., 6314, 5282, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([25, 25, 25, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.944599429543635, pr_auc_value=0.014396523804678249, tp=array([0, 0, 0, ..., 8, 8, 8], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 2664, 3372, 9992],\n", + " shape=(10001,), dtype=uint64), tn=array([9992, 9992, 9992, ..., 7328, 6620, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([8, 8, 8, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.8882271822417954, pr_auc_value=0.004391577335293479, tp=array([0, 0, 0, ..., 9, 9, 9], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 4713, 6128, 9991],\n", + " shape=(10001,), dtype=uint64), tn=array([9991, 9991, 9991, ..., 5278, 3863, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([9, 9, 9, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9762358507105239, pr_auc_value=0.5046676325489328, tp=array([ 0, 0, 0, ..., 248, 248, 248], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 5703, 6865, 9752],\n", + " shape=(10001,), dtype=uint64), tn=array([9752, 9752, 9752, ..., 4049, 2887, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([248, 248, 248, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.7663815526210485, pr_auc_value=0.035626114677832145, tp=array([0, 0, 0, ..., 3, 3, 4], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 2690, 3871, 9996],\n", + " shape=(10001,), dtype=uint64), tn=array([9996, 9996, 9996, ..., 7306, 6125, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([4, 4, 4, ..., 1, 1, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.897025533059683, pr_auc_value=0.2120825108635467, tp=array([ 0, 0, 0, ..., 331, 331, 331], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 7499, 7967, 9669],\n", + " shape=(10001,), dtype=uint64), tn=array([9669, 9669, 9669, ..., 2170, 1702, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([331, 331, 331, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.8395841673338671, pr_auc_value=0.0040776288050127455, tp=array([0, 0, 0, ..., 7, 8, 8], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 4414, 5878, 9992],\n", + " shape=(10001,), dtype=uint64), tn=array([9992, 9992, 9992, ..., 5578, 4114, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([8, 8, 8, ..., 1, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9826813406703351, pr_auc_value=0.015491063585405128, tp=array([0, 0, 0, ..., 5, 5, 5], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 2357, 3120, 9995],\n", + " shape=(10001,), dtype=uint64), tn=array([9995, 9995, 9995, ..., 7638, 6875, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([5, 5, 5, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.7657964055883432, pr_auc_value=0.0049229133413869215, tp=array([0, 0, 0, ..., 2, 2, 3], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 2432, 3481, 9997],\n", + " shape=(10001,), dtype=uint64), tn=array([9997, 9997, 9997, ..., 7565, 6516, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([3, 3, 3, ..., 1, 1, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9017857142857143, pr_auc_value=0.0026080488947285703, tp=array([0, 0, 0, ..., 4, 4, 4], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 4597, 6302, 9996],\n", + " shape=(10001,), dtype=uint64), tn=array([9996, 9996, 9996, ..., 5399, 3694, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([4, 4, 4, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.8141381552621048, pr_auc_value=0.005812556449917482, tp=array([0, 0, 0, ..., 3, 3, 4], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 1983, 2852, 9996],\n", + " shape=(10001,), dtype=uint64), tn=array([9996, 9996, 9996, ..., 8013, 7144, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([4, 4, 4, ..., 1, 1, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9235147538975628, pr_auc_value=0.08428285516482042, tp=array([ 0, 0, 0, ..., 79, 79, 79], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 6673, 7556, 9921],\n", + " shape=(10001,), dtype=uint64), tn=array([9921, 9921, 9921, ..., 3248, 2365, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([79, 79, 79, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9648851198866217, pr_auc_value=0.04654081743284453, tp=array([ 0, 0, 0, ..., 13, 13, 13], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 3171, 4017, 9987],\n", + " shape=(10001,), dtype=uint64), tn=array([9987, 9987, 9987, ..., 6816, 5970, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([13, 13, 13, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9914960083304407, pr_auc_value=0.5512007062995384, tp=array([ 0, 0, 0, ..., 67, 67, 67], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 4239, 5402, 9933],\n", + " shape=(10001,), dtype=uint64), tn=array([9933, 9933, 9933, ..., 5694, 4531, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([67, 67, 67, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9602152152152151, pr_auc_value=0.03465733892911165, tp=array([ 0, 0, 0, ..., 10, 10, 10], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 2206, 3020, 9990],\n", + " shape=(10001,), dtype=uint64), tn=array([9990, 9990, 9990, ..., 7784, 6970, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([10, 10, 10, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9150477908369481, pr_auc_value=0.07231106345198113, tp=array([ 0, 0, 0, ..., 21, 21, 21], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 4539, 5456, 9979],\n", + " shape=(10001,), dtype=uint64), tn=array([9979, 9979, 9979, ..., 5440, 4523, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([21, 21, 21, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9002994202713894, pr_auc_value=0.018745011290571923, tp=array([ 0, 0, 0, ..., 11, 11, 11], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 6650, 7920, 9989],\n", + " shape=(10001,), dtype=uint64), tn=array([9989, 9989, 9989, ..., 3339, 2069, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([11, 11, 11, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.8647115737497081, pr_auc_value=0.0134843725752661, tp=array([0, 0, 0, ..., 9, 9, 9], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 6812, 7928, 9991],\n", + " shape=(10001,), dtype=uint64), tn=array([9991, 9991, 9991, ..., 3179, 2063, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([9, 9, 9, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9253887999336006, pr_auc_value=0.06513574449517197, tp=array([ 0, 0, 0, ..., 28, 29, 29], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 4678, 6046, 9971],\n", + " shape=(10001,), dtype=uint64), tn=array([9971, 9971, 9971, ..., 5293, 3925, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([29, 29, 29, ..., 1, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.7730729427474545, pr_auc_value=0.004931225073193392, tp=array([ 0, 0, 0, ..., 13, 13, 15], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 5709, 7070, 9985],\n", + " shape=(10001,), dtype=uint64), tn=array([9985, 9985, 9985, ..., 4276, 2915, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([15, 15, 15, ..., 2, 2, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9909963985594237, pr_auc_value=0.06650052824324232, tp=array([0, 0, 0, ..., 4, 4, 4], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 2194, 3343, 9996],\n", + " shape=(10001,), dtype=uint64), tn=array([9996, 9996, 9996, ..., 7802, 6653, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([4, 4, 4, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.7849704616000801, pr_auc_value=0.00423817835898104, tp=array([ 0, 0, 0, ..., 13, 13, 13], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 7551, 8363, 9987],\n", + " shape=(10001,), dtype=uint64), tn=array([9987, 9987, 9987, ..., 2436, 1624, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([13, 13, 13, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9558253933696808, pr_auc_value=0.4022518695069507, tp=array([ 0, 0, 0, ..., 398, 398, 398], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 6416, 7346, 9602],\n", + " shape=(10001,), dtype=uint64), tn=array([9602, 9602, 9602, ..., 3186, 2256, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([398, 398, 398, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9641981792717087, pr_auc_value=0.009486882393768548, tp=array([0, 0, 0, ..., 4, 4, 4], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 1443, 1944, 9996],\n", + " shape=(10001,), dtype=uint64), tn=array([9996, 9996, 9996, ..., 8553, 8052, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([4, 4, 4, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9292808734939759, pr_auc_value=0.04274043225698763, tp=array([ 0, 0, 0, ..., 40, 40, 40], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 6505, 7458, 9960],\n", + " shape=(10001,), dtype=uint64), tn=array([9960, 9960, 9960, ..., 3455, 2502, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([40, 40, 40, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9825912956478239, pr_auc_value=0.015729065127556473, tp=array([0, 0, 0, ..., 5, 5, 5], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 3889, 5111, 9995],\n", + " shape=(10001,), dtype=uint64), tn=array([9995, 9995, 9995, ..., 6106, 4884, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([5, 5, 5, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.8984145392727484, pr_auc_value=0.08586546323478161, tp=array([ 0, 0, 0, ..., 84, 84, 84], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 6455, 7217, 9916],\n", + " shape=(10001,), dtype=uint64), tn=array([9916, 9916, 9916, ..., 3461, 2699, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([84, 84, 84, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9641241729538856, pr_auc_value=0.30455999224464486, tp=array([ 0, 0, 0, ..., 10, 11, 11], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 1759, 2441, 9989],\n", + " shape=(10001,), dtype=uint64), tn=array([9989, 9989, 9989, ..., 8230, 7548, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([11, 11, 11, ..., 1, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9585999144513511, pr_auc_value=0.16832461320468306, tp=array([ 0, 0, 0, ..., 11, 11, 11], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 2390, 3168, 9989],\n", + " shape=(10001,), dtype=uint64), tn=array([9989, 9989, 9989, ..., 7599, 6821, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([11, 11, 11, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9664494580402079, pr_auc_value=0.12542290658726538, tp=array([ 0, 0, 0, ..., 11, 11, 11], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 3333, 4372, 9989],\n", + " shape=(10001,), dtype=uint64), tn=array([9989, 9989, 9989, ..., 6656, 5617, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([11, 11, 11, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.8696858641241845, pr_auc_value=0.03072350582033219, tp=array([ 0, 0, 0, ..., 20, 20, 21], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 4788, 6402, 9979],\n", + " shape=(10001,), dtype=uint64), tn=array([9979, 9979, 9979, ..., 5191, 3577, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([21, 21, 21, ..., 1, 1, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9708892113690952, pr_auc_value=0.05700806909385954, tp=array([0, 0, 0, ..., 8, 8, 8], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 3277, 4261, 9992],\n", + " shape=(10001,), dtype=uint64), tn=array([9992, 9992, 9992, ..., 6715, 5731, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([8, 8, 8, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.981911911911912, pr_auc_value=0.21400539242761735, tp=array([ 0, 0, 0, ..., 10, 10, 10], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 2799, 3813, 9990],\n", + " shape=(10001,), dtype=uint64), tn=array([9990, 9990, 9990, ..., 7191, 6177, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([10, 10, 10, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.93912841025641, pr_auc_value=0.22796810965215566, tp=array([ 0, 0, 0, ..., 250, 250, 250], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 5455, 6570, 9750],\n", + " shape=(10001,), dtype=uint64), tn=array([9750, 9750, 9750, ..., 4295, 3180, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([250, 250, 250, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.980821859909762, pr_auc_value=0.3007144530195656, tp=array([ 0, 0, 0, ..., 38, 38, 38], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 3411, 4443, 9962],\n", + " shape=(10001,), dtype=uint64), tn=array([9962, 9962, 9962, ..., 6551, 5519, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([38, 38, 38, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9928540457607898, pr_auc_value=0.19878111900775883, tp=array([0, 0, 0, ..., 6, 6, 6], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 2516, 3489, 9994],\n", + " shape=(10001,), dtype=uint64), tn=array([9994, 9994, 9994, ..., 7478, 6505, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([6, 6, 6, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9803340517241379, pr_auc_value=0.18739130055703226, tp=array([ 0, 0, 0, ..., 23, 24, 24], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 2301, 3182, 9976],\n", + " shape=(10001,), dtype=uint64), tn=array([9976, 9976, 9976, ..., 7675, 6794, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([24, 24, 24, ..., 1, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9243957666132906, pr_auc_value=0.04194970941949343, tp=array([0, 0, 0, ..., 8, 8, 8], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 4371, 5334, 9992],\n", + " shape=(10001,), dtype=uint64), tn=array([9992, 9992, 9992, ..., 5621, 4658, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([8, 8, 8, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9023304660932185, pr_auc_value=0.0015666313193006265, tp=array([0, 0, 0, ..., 2, 2, 2], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 2358, 3089, 9998],\n", + " shape=(10001,), dtype=uint64), tn=array([9998, 9998, 9998, ..., 7640, 6909, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([2, 2, 2, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9874649719775821, pr_auc_value=0.11967609270870441, tp=array([0, 0, 0, ..., 8, 8, 8], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 1565, 2391, 9992],\n", + " shape=(10001,), dtype=uint64), tn=array([9992, 9992, 9992, ..., 8427, 7601, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([8, 8, 8, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9126602907749711, pr_auc_value=0.011095774287270406, tp=array([0, 0, 0, ..., 7, 7, 7], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 4284, 5525, 9993],\n", + " shape=(10001,), dtype=uint64), tn=array([9993, 9993, 9993, ..., 5709, 4468, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([7, 7, 7, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.981371274254851, pr_auc_value=0.05573055330060994, tp=array([0, 0, 0, ..., 2, 2, 2], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 3145, 4006, 9998],\n", + " shape=(10001,), dtype=uint64), tn=array([9998, 9998, 9998, ..., 6853, 5992, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([2, 2, 2, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9871838858629614, pr_auc_value=0.0865936227276276, tp=array([0, 0, 0, ..., 7, 7, 7], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 1590, 2488, 9993],\n", + " shape=(10001,), dtype=uint64), tn=array([9993, 9993, 9993, ..., 8403, 7505, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([7, 7, 7, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.6977145429085817, pr_auc_value=0.002927996217847514, tp=array([0, 0, 0, ..., 1, 1, 2], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 1420, 1913, 9998],\n", + " shape=(10001,), dtype=uint64), tn=array([9998, 9998, 9998, ..., 8578, 8085, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([2, 2, 2, ..., 1, 1, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9306099681441228, pr_auc_value=0.035632808590136504, tp=array([ 0, 0, 0, ..., 23, 23, 23], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 4562, 5315, 9977],\n", + " shape=(10001,), dtype=uint64), tn=array([9977, 9977, 9977, ..., 5415, 4662, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([23, 23, 23, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.966291884918649, pr_auc_value=0.04152941382681815, tp=array([0, 0, 0, ..., 9, 9, 9], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 3825, 4826, 9991],\n", + " shape=(10001,), dtype=uint64), tn=array([9991, 9991, 9991, ..., 6166, 5165, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([9, 9, 9, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9404308617234469, pr_auc_value=0.0732184588288861, tp=array([ 0, 0, 0, ..., 20, 20, 20], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 6436, 7564, 9980],\n", + " shape=(10001,), dtype=uint64), tn=array([9980, 9980, 9980, ..., 3544, 2416, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([20, 20, 20, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9948984695408623, pr_auc_value=0.08008195646532759, tp=array([0, 0, 0, ..., 3, 3, 3], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 1659, 2349, 9997],\n", + " shape=(10001,), dtype=uint64), tn=array([9997, 9997, 9997, ..., 8338, 7648, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([3, 3, 3, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9486012470407789, pr_auc_value=0.011714316220366672, tp=array([0, 0, 0, ..., 3, 3, 3], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 2113, 3015, 9997],\n", + " shape=(10001,), dtype=uint64), tn=array([9997, 9997, 9997, ..., 7884, 6982, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([3, 3, 3, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9695939187837567, pr_auc_value=0.008381158548034007, tp=array([0, 0, 0, ..., 2, 2, 2], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 2444, 3176, 9998],\n", + " shape=(10001,), dtype=uint64), tn=array([9998, 9998, 9998, ..., 7554, 6822, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([2, 2, 2, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.0, pr_auc_value=0.0, tp=array([0, 0, 0, ..., 0, 0, 0], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 2311, 3348, 10000],\n", + " shape=(10001,), dtype=uint64), tn=array([10000, 10000, 10000, ..., 7689, 6652, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([0, 0, 0, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9793458691738348, pr_auc_value=0.005317074588689907, tp=array([0, 0, 0, ..., 2, 2, 2], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 1785, 2746, 9998],\n", + " shape=(10001,), dtype=uint64), tn=array([9998, 9998, 9998, ..., 8213, 7252, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([2, 2, 2, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9552160432086416, pr_auc_value=0.002650415496624703, tp=array([0, 0, 0, ..., 2, 2, 2], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 2202, 3131, 9998],\n", + " shape=(10001,), dtype=uint64), tn=array([9998, 9998, 9998, ..., 7796, 6867, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([2, 2, 2, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9589043379680571, pr_auc_value=0.06617688161643996, tp=array([0, 0, 0, ..., 3, 3, 3], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 1179, 1902, 9997],\n", + " shape=(10001,), dtype=uint64), tn=array([9997, 9997, 9997, ..., 8818, 8095, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([3, 3, 3, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.8794805108199126, pr_auc_value=0.0012392021020690678, tp=array([0, 0, 0, ..., 3, 3, 3], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 2980, 4152, 9997],\n", + " shape=(10001,), dtype=uint64), tn=array([9997, 9997, 9997, ..., 7017, 5845, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([3, 3, 3, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.972297289783027, pr_auc_value=0.02047654954113096, tp=array([0, 0, 0, ..., 9, 9, 9], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 2250, 2927, 9991],\n", + " shape=(10001,), dtype=uint64), tn=array([9991, 9991, 9991, ..., 7741, 7064, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([9, 9, 9, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.8835383538353835, pr_auc_value=0.0004156829320586669, tp=array([0, 0, 0, ..., 1, 1, 1], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 3102, 4896, 9999],\n", + " shape=(10001,), dtype=uint64), tn=array([9999, 9999, 9999, ..., 6897, 5103, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([1, 1, 1, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9452502417391884, pr_auc_value=0.011516831515905426, tp=array([0, 0, 0, ..., 3, 3, 3], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 1645, 2375, 9997],\n", + " shape=(10001,), dtype=uint64), tn=array([9997, 9997, 9997, ..., 8352, 7622, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([3, 3, 3, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9751525610244098, pr_auc_value=0.012397259302058927, tp=array([0, 0, 0, ..., 4, 4, 4], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 2697, 3458, 9996],\n", + " shape=(10001,), dtype=uint64), tn=array([9996, 9996, 9996, ..., 7299, 6538, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([4, 4, 4, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9523297311720366, pr_auc_value=0.00959682984514515, tp=array([0, 0, 0, ..., 6, 6, 6], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 2009, 2561, 9994],\n", + " shape=(10001,), dtype=uint64), tn=array([9994, 9994, 9994, ..., 7985, 7433, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([6, 6, 6, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.8995865426294554, pr_auc_value=0.0039731450248407015, tp=array([0, 0, 0, ..., 3, 3, 3], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 2892, 3742, 9997],\n", + " shape=(10001,), dtype=uint64), tn=array([9997, 9997, 9997, ..., 7105, 6255, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([3, 3, 3, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9797959591918384, pr_auc_value=0.005092162309663806, tp=array([0, 0, 0, ..., 2, 2, 2], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 2226, 3044, 9998],\n", + " shape=(10001,), dtype=uint64), tn=array([9998, 9998, 9998, ..., 7772, 6954, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([2, 2, 2, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.8633578431372548, pr_auc_value=0.001413994967876829, tp=array([0, 0, 0, ..., 4, 4, 4], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 3035, 3910, 9996],\n", + " shape=(10001,), dtype=uint64), tn=array([9996, 9996, 9996, ..., 6961, 6086, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([4, 4, 4, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9998999899989999, pr_auc_value=0.3068528194400547, tp=array([0, 0, 0, ..., 1, 1, 1], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 1634, 2549, 9999],\n", + " shape=(10001,), dtype=uint64), tn=array([9999, 9999, 9999, ..., 8365, 7450, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([1, 1, 1, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9858162160194395, pr_auc_value=0.07855074782114108, tp=array([ 0, 0, 0, ..., 11, 11, 11], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 2669, 3802, 9989],\n", + " shape=(10001,), dtype=uint64), tn=array([9989, 9989, 9989, ..., 7320, 6187, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([11, 11, 11, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9973994798959792, pr_auc_value=0.1676706394801633, tp=array([0, 0, 0, ..., 2, 2, 2], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 2247, 2845, 9998],\n", + " shape=(10001,), dtype=uint64), tn=array([9998, 9998, 9998, ..., 7751, 7153, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([2, 2, 2, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9947984395318596, pr_auc_value=0.06537890713357235, tp=array([0, 0, 0, ..., 3, 3, 3], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 1475, 2162, 9997],\n", + " shape=(10001,), dtype=uint64), tn=array([9997, 9997, 9997, ..., 8522, 7835, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([3, 3, 3, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.990895447723862, pr_auc_value=0.052743610541540954, tp=array([0, 0, 0, ..., 5, 5, 5], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 2319, 3408, 9995],\n", + " shape=(10001,), dtype=uint64), tn=array([9995, 9995, 9995, ..., 7676, 6587, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([5, 5, 5, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.9874724944988997, pr_auc_value=0.009863647240081264, tp=array([0, 0, 0, ..., 2, 2, 2], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 1914, 2640, 9998],\n", + " shape=(10001,), dtype=uint64), tn=array([9998, 9998, 9998, ..., 8084, 7358, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([2, 2, 2, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.983671835917959, pr_auc_value=0.36482092755506434, tp=array([0, 0, 0, ..., 5, 5, 5], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 1825, 2442, 9995],\n", + " shape=(10001,), dtype=uint64), tn=array([9995, 9995, 9995, ..., 8170, 7553, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([5, 5, 5, ..., 0, 0, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,))), PerClass(auc_value=0.8281940970485243, pr_auc_value=0.007115016864834942, tp=array([0, 0, 0, ..., 4, 4, 5], shape=(10001,), dtype=uint64), fp=array([ 0, 0, 0, ..., 3115, 4551, 9995],\n", + " shape=(10001,), dtype=uint64), tn=array([9995, 9995, 9995, ..., 6880, 5444, 0],\n", + " shape=(10001,), dtype=uint64), fn=array([5, 5, 5, ..., 1, 1, 0], shape=(10001,), dtype=uint64), thresholds=array([1.0001e+00, 9.9990e-01, 9.9980e-01, ..., 2.0000e-04, 1.0000e-04,\n", + " 0.0000e+00], shape=(10001,)))])" ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "model.evaluate(graph)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "1jUyBIeUkbd1" + }, + "source": [ + "We can make predictions for individual nodes:" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-23T18:24:06.133827Z", + "iopub.status.busy": "2026-09-23T18:24:06.133641Z", + "iopub.status.idle": "2026-09-23T18:24:08.668757Z", + "shell.execute_reply": "2026-09-23T18:24:08.668198Z" + }, + "executionInfo": { + "elapsed": 2536, + "status": "ok", + "timestamp": 1790187848669.7666, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 + }, + "id": "E-Y0ofuL0Tf-", + "outputId": "5e5c995c-16dd-48ef-a96d-e7d014b853d1" + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Inference: 0%| | 0/1 [00:00\n", + "\n", + "\n", + "\n", + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
\n", + "
\n", + "

Node prediction model: Predict the value of a node feature.

\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
TypeNode prediction model - Predict the value of a node feature.
Target nodesetpaper
Target columnlabels
Number of label classes349
\n", + "
\n", + "
\n", + "\n", + " \n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + " \n", + "
WARNINGValidation set truncated from 73638 to 32000 nodes due to caching limits. To use more validation nodes, either increase `num_valid_steps` or set `cache_valid_dataset=False`.
WARNINGNo normalizer created for node set 'author', feature '#id'.
WARNINGNo normalizer created for node set 'paper', feature '#id'.
WARNINGNo normalizer created for node set 'field_of_study', feature '#id'.
WARNINGNo normalizer created for node set 'institution', feature '#id'.
\n", + "
\n", + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
Accuracy0.2859
Num Examples10000
AUC0.6954946353060891
Per Class Metrics [349]:
    \n", + "
  • Class 0: AUC=0.0000, PR-AUC=0.0000
  • \n", + "
  • Class 1: AUC=0.9345, PR-AUC=0.1882
  • \n", + "
  • Class 2: AUC=0.0000, PR-AUC=0.0000
  • \n", + "
  • ... (344 omitted) ...
  • \n", + "
  • Class 347: AUC=0.0000, PR-AUC=0.0000
  • \n", + "
  • Class 348: AUC=0.9510, PR-AUC=0.0020
  • \n", + "
\n", + "

*Showing plots for the first 20 classes out of 349 total classes.

\n", + "\n", + " \n", + " \n", + " \n", + " \n", + "
\n", + "
\n", + "
\n", + "\n", + " \n", + "\n", + " \n", + " \n", + " \n", + "\n", + " \n", + " \n", + "
Number of training seed nodes704389
Number of validation seed nodes32000
Training duration1m 53s
Number of training steps (final model)5000
\n", + "
\n", + " \n", + " \n", + " \n", + " \n", + "
\n", + "
\n", + "

Note: The logs for the first training step are not shown.

\n", + "
\n", + "
\n", + "\n", + " \n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + " \n", + "
num_sampling_hops1
sampling_width15
num_layers2
batch_size32
max_training_time_secondsNone
num_train_steps5000
random_seed42
node_embedding_dim128
learning_rate0.001
opt_weight_decay0.0001
dropout0.1
message_pooling'sum'
architecture<Architecture.HETEROGENEOUS_MESSAGE_PASSING: 'HETEROGENEOUS_MESSAGE_PASSING'>
early_stopping{'patience': 5, 'min_improvement': 1e-06}
\n", + "
\n", + "
Raw schema plot\n", + "\n", + "Raw schema textual\n", + "
Node Sets:\n",
+       "  author:\n",
+       "    | Feature   | Format   | Semantic   | Shape   | Detail   |\n",
+       "    |-----------|----------|------------|---------|----------|\n",
+       "    | #id       | BYTES    | PRIMARY_ID | None    |          |\n",
+       "\n",
+       "  field_of_study:\n",
+       "    | Feature   | Format   | Semantic   | Shape   | Detail   |\n",
+       "    |-----------|----------|------------|---------|----------|\n",
+       "    | #id       | BYTES    | PRIMARY_ID | None    |          |\n",
+       "\n",
+       "  institution:\n",
+       "    | Feature   | Format   | Semantic   | Shape   | Detail   |\n",
+       "    |-----------|----------|------------|---------|----------|\n",
+       "    | #id       | BYTES    | PRIMARY_ID | None    |          |\n",
+       "\n",
+       "  paper:\n",
+       "    | Feature   | Format     | Semantic    | Shape   | Detail   |\n",
+       "    |-----------|------------|-------------|---------|----------|\n",
+       "    | #id       | BYTES      | PRIMARY_ID  | None    |          |\n",
+       "    | #split    | BYTES      | CATEGORICAL | None    |          |\n",
+       "    | feat      | FLOAT_32   | EMBEDDING   | (128,)  |          |\n",
+       "    | labels    | INTEGER_64 | CATEGORICAL | None    |          |\n",
+       "    | year      | INTEGER_64 | TIMESTAMP   | None    | creation |\n",
+       "\n",
+       "\n",
+       "Edge Sets:\n",
+       "  affiliated_with: (Source: author, Target: institution)\n",
+       "    (No features)\n",
+       "\n",
+       "  cites: (Source: paper, Target: paper)\n",
+       "    | Feature   | Format     | Semantic   | Shape   | Detail   |\n",
+       "    |-----------|------------|------------|---------|----------|\n",
+       "    | year      | INTEGER_64 | TIMESTAMP  | None    | creation |\n",
+       "\n",
+       "  has_topic: (Source: paper, Target: field_of_study)\n",
+       "    | Feature   | Format     | Semantic   | Shape   | Detail   |\n",
+       "    |-----------|------------|------------|---------|----------|\n",
+       "    | year      | INTEGER_64 | TIMESTAMP  | None    | creation |\n",
+       "\n",
+       "  writes: (Source: author, Target: paper)\n",
+       "    | Feature   | Format     | Semantic   | Shape   | Detail   |\n",
+       "    |-----------|------------|------------|---------|----------|\n",
+       "    | year      | INTEGER_64 | TIMESTAMP  | None    | creation |\n",
+       "
\n", + "Normalized schema plot\n", + "\n", + "Normalized schema textual\n", + "
Node Sets:\n",
+       "  author:\n",
+       "    (No features)\n",
+       "\n",
+       "  field_of_study:\n",
+       "    (No features)\n",
+       "\n",
+       "  institution:\n",
+       "    (No features)\n",
+       "\n",
+       "  paper:\n",
+       "    | Feature                    | Format     | Semantic    | Shape   | Detail       |\n",
+       "    |----------------------------|------------|-------------|---------|--------------|\n",
+       "    | #split_INDEX               | INTEGER_64 | CATEGORICAL | ()      | #num.cat:4   |\n",
+       "    | feat                       | FLOAT_32   | EMBEDDING   | (128,)  |              |\n",
+       "    | labels                     | INTEGER_64 | CATEGORICAL | None    | #num.cat:349 |\n",
+       "    | year_day_of_month_CALENDAR | FLOAT_32   | EMBEDDING   | ()      |              |\n",
+       "    | year_day_of_week_CALENDAR  | FLOAT_32   | EMBEDDING   | ()      |              |\n",
+       "    | year_hour_CALENDAR         | FLOAT_32   | EMBEDDING   | ()      |              |\n",
+       "    | year_month_CALENDAR        | FLOAT_32   | EMBEDDING   | ()      |              |\n",
+       "    | year_seed_delta_SINUSOID   | FLOAT_32   | EMBEDDING   | (32,)   |              |\n",
+       "\n",
+       "\n",
+       "Edge Sets:\n",
+       "  affiliated_with: (Source: author, Target: institution)\n",
+       "    (No features)\n",
+       "\n",
+       "  cites: (Source: paper, Target: paper)\n",
+       "    (No features)\n",
+       "\n",
+       "  has_topic: (Source: paper, Target: field_of_study)\n",
+       "    (No features)\n",
+       "\n",
+       "  writes: (Source: author, Target: paper)\n",
+       "    (No features)\n",
+       "
\n", + "
\n", + "
Default feature statistics\n", + "
GraphFeatureStatistics:\n",
+       "  Node Sets (4):\n",
+       "    'author':\n",
+       "      '#id': count=46656, min=nan, max=nan\n",
+       "    'field_of_study':\n",
+       "      '#id': count=102084, min=nan, max=nan\n",
+       "    'institution':\n",
+       "      '#id': count=0, min=nan, max=nan\n",
+       "    'paper':\n",
+       "      '#id': count=69469, min=nan, max=nan\n",
+       "      '#split': count=69469, min=nan, max=nan, dictionary=(3)['train': 66560, 'valid': 2436, 'test': 473]\n",
+       "      'feat': count=69469, min=nan, max=nan\n",
+       "      'labels': count=69469, min=0.0000, max=348.0000\n",
+       "      'year': count=69469, min=2010.0000, max=2019.0000\n",
+       "
\n", + "
\n", + "
Default sampling plan\n", + "
Root: paper\n",
+       "├── cites [width=15] ➔ paper\n",
+       "├── cites (reversed) [width=15] ➔ paper\n",
+       "├── has_topic [width=15] ➔ field_of_study\n",
+       "└── writes (reversed) [width=15] ➔ author
\n", + "
\n", + "
Model Structure\n", + "
EmbedGraph(cat-embedding=64)\n",
+       "Dense(128)\n",
+       "Activation(silu)\n",
+       "Norm(layer_norm)\n",
+       "Graph Convolution Block x2:\n",
+       "    X = ...\n",
+       "    MPNN:\n",
+       "      Message:\n",
+       "        Dense(128)\n",
+       "        Activation(silu)\n",
+       "        Dense(128)\n",
+       "      Update:\n",
+       "        Dense(128)\n",
+       "        Activation(silu)\n",
+       "        Dropout(0.1)\n",
+       "        Dense(128)\n",
+       "    Residual(X)\n",
+       "    # Post MPNN\n",
+       "    X = ...\n",
+       "    Norm(rms_norm)\n",
+       "    Dense(512)\n",
+       "    Activation(silu)\n",
+       "    Dense(128)\n",
+       "    Dropout(0.1)\n",
+       "    Residual(X)\n",
+       "Identity\n",
+       "Dense(349) # Classification head
\n", + "Model Weights\n", + "
{'float32': 2316893}
\n", + "
Default padding\n", + "
Node Sets:\n",
+       "  author: 232 nodes\n",
+       "  field_of_study: 384 nodes\n",
+       "  institution: 2 nodes\n",
+       "  paper: 382 nodes\n",
+       "\n",
+       "Edge Sets:\n",
+       "  affiliated_with: 2 edges\n",
+       "  cites: 347 edges\n",
+       "  has_topic: 384 edges\n",
+       "  writes: 232 edges
\n", + "
\n", + "\n", + "\n", + "\n", + "" ], - "metadata": { - "id": "pWJh--ZBM2kJ" + "text/plain": [ + "" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "model.describe()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "pWJh--ZBM2kJ" + }, + "source": [ + "You can see the effect of time-aware sampling in two places:\n", + "\n", + "- In the \"graph sampling\" tab, the `edgeset_timestamp_features` field show the\n", + " edge features to time-filter on.\n", + "- In the \"padding tab\", the paper nodeset padding is smaller than before.\n", + " Because of filtering, each paper node has in average half the number of\n", + " edges, and the graph samples (used internally to train the GNN) are smaller.\n", + "\n", + "The model quality is only a little reduced. In this dataset, future information\n", + "is not critical." + ] + } + ], + "metadata": { + "colab": { + "last_runtime": { + "build_target": "", + "kind": "local" + }, + "provenance": [ + { + "file_id": "1hMr9gK0hhKcCtIs1Pro3_ksOnYcdAL2P", + "timestamp": 1771583153488 + } + ], + "views": { + "output_only": { + "cells": [ + { + "id": "woDLT7o2leXI" + }, + { + "id": "a6rCwd3CU7Dv" + }, + { + "id": "cvhNQvg3TVQc" + }, + { + "id": "KbZ6I1MAMKCF" + }, + { + "id": "RKXovz93qQAB" + }, + { + "id": "z3Tx-P_MlPuu" + }, + { + "id": "tWUr99pPz-Jg" + }, + { + "id": "5SSkwb_f0FsB" + }, + { + "id": "oJr9unOlit08" + }, + { + "id": "jjYyiwfY0IfB" + }, + { + "id": "-EBR8nvv0I3P" + }, + { + "id": "_8QZI2ja0ORe" + }, + { + "id": "Rpk-iVfVjDA0" + }, + { + "id": "sAGkWSY_0dsd" + }, + { + "id": "hlAMxAfmjINa" + }, + { + "id": "kBjFFXRg0Vm4" + }, + { + "id": "1jUyBIeUkbd1" + }, + { + "id": "E-Y0ofuL0Tf-" + }, + { + "id": "AcdssjmllmIX" + }, + { + "id": "Clpm6VTtGwqL" + }, + { + "id": "MeoDPMebL1gz" + }, + { + "id": "x2EOyOyOHpSX" + }, + { + "id": "0roFRwctMAxC" + }, + { + "id": "HEwEDGM6MCFq" + }, + { + "id": "pWJh--ZBM2kJ" } + ], + "hide_code": true } - ] + } + }, + "kernelspec": { + "display_name": "Python 3", + "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.14.5" + } + }, + "nbformat": 4, + "nbformat_minor": 0 } diff --git a/doc/docs/tutorial/normalizer.ipynb b/doc/docs/tutorial/normalizer.ipynb index b37b083..13137de 100644 --- a/doc/docs/tutorial/normalizer.ipynb +++ b/doc/docs/tutorial/normalizer.ipynb @@ -1,1074 +1,2681 @@ { - "nbformat": 4, - "nbformat_minor": 0, - "metadata": { - "colab": { - "provenance": [ - { - "file_id": "1hMr9gK0hhKcCtIs1Pro3_ksOnYcdAL2P", - "timestamp": 1771583153488 - } - ], - "last_runtime": { - "build_target": "", - "kind": "local" - }, - "toc_visible": true + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "woDLT7o2leXI" + }, + "source": [ + "## Normalizer\n", + "\n", + "[![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/google/distributed_graph_flow/blob/main/doc/docs/tutorial/normalizer.ipynb)\n", + "\n", + "This guide explains the tools available to normalize feature values for training\n", + "GNN models.\n", + "\n", + "You'll learn how to compute feature statistics either in-memory using Beam, to\n", + "use those feature statistics to initialize \"normalizers\", and finally, how to\n", + "use the \"auto-normalizer\" to automatically construct all the normalizers to\n", + "consume a graph." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "oZe5IE5sfhzY" + }, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "eoSN7nzKx5hf" + }, + "source": [ + "## Installing GF\n", + "\n", + "Make sure your machine has a GPU or TPU, otherwise training is going to take forever.\n", + "If you are using Google Colab, you can get one for free. Just go to Edit > Notebook settings and select your hardware accelerator as TPU or GPU." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-23T18:27:00.663591Z", + "iopub.status.busy": "2026-09-23T18:27:00.663276Z", + "iopub.status.idle": "2026-09-23T18:27:00.912113Z", + "shell.execute_reply": "2026-09-23T18:27:00.911473Z" }, - "kernelspec": { - "name": "python3", - "display_name": "Python 3" + "executionInfo": { + "elapsed": 289, + "status": "ok", + "timestamp": 1790188020952.075, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 }, - "language_info": { - "name": "python" - } + "id": "bZAua9oftqgv" + }, + "outputs": [], + "source": [ + "# Install DGF (Distributed Graph Flow) and OGB (for the toy dataset).\n", + "!pip install dgf ogb -U" + ] }, - "cells": [ - { - "cell_type": "markdown", - "source": [ - "## Normalizer\n", - "\n", - "[![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/google/distributed_graph_flow/blob/main/doc/docs/tutorial/normalizer.ipynb)\n", - "\n", - "This guide explains the tools available to normalize feature values for training\n", - "GNN models.\n", - "\n", - "You'll learn how to compute feature statistics either in-memory using Beam, to\n", - "use those feature statistics to initialize \"normalizers\", and finally, how to\n", - "use the \"auto-normalizer\" to automatically construct all the normalizers to\n", - "consume a graph." - ], - "metadata": { - "id": "woDLT7o2leXI" - } + { + "cell_type": "markdown", + "metadata": { + "id": "RKXovz93qQAB" + }, + "source": [ + "## Importing libraries" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-23T18:27:00.953356Z", + "iopub.status.busy": "2026-09-23T18:27:00.953133Z", + "iopub.status.idle": "2026-09-23T18:27:05.232041Z", + "shell.execute_reply": "2026-09-23T18:27:05.231414Z" }, - { - "cell_type": "markdown", - "source": [ - "## Installing GF\n", - "\n", - "Make sure your machine has a GPU or TPU, otherwise training is going to take forever.\n", - "If you are using Google Colab, you can get one for free. Just go to Edit > Notebook settings and select your hardware accelerator as TPU or GPU." - ], - "metadata": { - "id": "rMl39cGyWz1P" - } + "executionInfo": { + "elapsed": 4281, + "status": "ok", + "timestamp": 1790188025233.4937, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 }, - { - "cell_type": "code", - "source": [ - "# Install DGF (Distributed Graph Flow) and OGB (for the toy dataset).\n", - "!pip install dgf ogb -U" - ], - "metadata": { - "id": "9xBwQv7jW4vB" - }, - "execution_count": null, - "outputs": [] + "id": "z3Tx-P_MlPuu" + }, + "outputs": [], + "source": [ + "import dgf # Import Graph Flow\n", + "import numpy as np" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "mF_V1Z8wJgB7" + }, + "source": [ + "## Load some data\n", + "\n", + "Let's start by loading a toy dataset." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-23T18:27:05.234974Z", + "iopub.status.busy": "2026-09-23T18:27:05.234377Z", + "iopub.status.idle": "2026-09-23T18:27:05.665878Z", + "shell.execute_reply": "2026-09-23T18:27:05.665484Z" }, - { - "cell_type": "markdown", - "source": [ - "## Importing libraries" - ], - "metadata": { - "id": "RKXovz93qQAB" - } + "executionInfo": { + "elapsed": 433, + "status": "ok", + "timestamp": 1790188025667.3018, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 }, + "id": "QZ7ipLxDJkDD", + "outputId": "81af3456-9841-46ad-819a-47b891a2fe0f" + }, + "outputs": [ { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "z3Tx-P_MlPuu" - }, - "outputs": [], - "source": [ - "import os\n", - "\n", - "os.environ[\"TF_USE_LEGACY_KERAS\"] = \"1\"\n", - "\n", - "import dgf # Import Graph Flow\n", - "import numpy as np" - ] + "name": "stdout", + "output_type": "stream", + "text": [ + "Caching mag graph at /tmp/gf_fetch/mag.cache\n", + "OGB dependency not available. Downloading graph from CNS.\n" + ] + } + ], + "source": [ + "# Download the Mag graph from the OGB repo.\n", + "graph, schema = dgf.io.fetch_ogb_graph(\"mag\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "DaHNquKBMddz" + }, + "source": [ + "The computation of feature statistics and the normalization of features\n", + "relies extensively on both the \"format\" and \"semantic\" of the features. Make\n", + "sure those are correctly configured." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-23T18:27:05.668291Z", + "iopub.status.busy": "2026-09-23T18:27:05.668074Z", + "iopub.status.idle": "2026-09-23T18:27:05.673074Z", + "shell.execute_reply": "2026-09-23T18:27:05.672439Z" }, - { - "cell_type": "markdown", - "source": [ - "## Load some data\n", - "\n", - "Let's start by loading a toy dataset." - ], - "metadata": { - "id": "mF_V1Z8wJgB7" - } + "executionInfo": { + "elapsed": 6, + "status": "ok", + "timestamp": 1790188025674.274, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 }, + "id": "OwNRk_ThJjAd", + "outputId": "e3e295c1-98ef-4a77-9fc1-f320a246d05f" + }, + "outputs": [ { - "cell_type": "code", - "source": [ - "# Download the Mag graph from the OGB repo.\n", - "graph, schema = dgf.io.fetch_ogb_graph(\"mag\")" - ], - "metadata": { - "id": "QZ7ipLxDJkDD", - "executionInfo": { - "status": "ok", - "timestamp": 1778171979685, - "user_tz": -120, - "elapsed": 158, - "user": { - "displayName": "", - "userId": "" - } - }, - "outputId": "81af3456-9841-46ad-819a-47b891a2fe0f" - }, - "execution_count": null, - "outputs": [ - { - "output_type": "stream", - "name": "stdout", - "text": [ - "Caching mag graph at /tmp/gf_fetch/mag.cache\n", - "OGB dependency not available. Downloading graph from CNS.\n" - ] - } - ] + "name": "stdout", + "output_type": "stream", + "text": [ + "Graph Schema:\n", + "\n", + "Node Sets:\n", + " author:\n", + " | Feature | Format | Semantic | Shape | Detail |\n", + " |-----------|----------|------------|---------|----------|\n", + " | #id | BYTES | PRIMARY_ID | None | |\n", + "\n", + " field_of_study:\n", + " | Feature | Format | Semantic | Shape | Detail |\n", + " |-----------|----------|------------|---------|----------|\n", + " | #id | BYTES | PRIMARY_ID | None | |\n", + "\n", + " institution:\n", + " | Feature | Format | Semantic | Shape | Detail |\n", + " |-----------|----------|------------|---------|----------|\n", + " | #id | BYTES | PRIMARY_ID | None | |\n", + "\n", + " paper:\n", + " | Feature | Format | Semantic | Shape | Detail |\n", + " |-----------|------------|-------------|---------|----------|\n", + " | #id | BYTES | PRIMARY_ID | None | |\n", + " | #split | BYTES | CATEGORICAL | None | |\n", + " | feat | FLOAT_32 | EMBEDDING | (128,) | |\n", + " | labels | INTEGER_64 | CATEGORICAL | None | |\n", + " | year | INTEGER_64 | NUMERICAL | None | |\n", + "\n", + "\n", + "Edge Sets:\n", + " affiliated_with: (Source: author, Target: institution)\n", + " (No features)\n", + "\n", + " cites: (Source: paper, Target: paper)\n", + " (No features)\n", + "\n", + " has_topic: (Source: paper, Target: field_of_study)\n", + " (No features)\n", + "\n", + " writes: (Source: author, Target: paper)\n", + " (No features)\n", + "\n" + ] + } + ], + "source": [ + "dgf.print.schema(schema)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "V4ZTxkB2JwnY" + }, + "source": [ + "## Generate some graph samples\n", + "\n", + "First, we need to get feature statistics (e.g., quantiles, dictionaries) for the\n", + "node features. You can compute them these from the original graph or from graph\n", + "samples.\n", + "\n", + "While using the full graph is faster and easier, the results might not match the\n", + "data distribution the GNN model actually sees, and it is harder to compute\n", + "statistics on a sample of data. In this tutorial, we will show the graph sample\n", + "solution.\n", + "\n", + "First, we need to define a generator of graph samples. See the sampler tutorial\n", + "for more details." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "colab": { + "height": 1000 }, - { - "cell_type": "markdown", - "source": [ - "The computation of feature statistics and the normalization of features\n", - "relies extensively on both the \"format\" and \"semantic\" of the features. Make\n", - "sure those are correctly configured." - ], - "metadata": { - "id": "DaHNquKBMddz" - } + "execution": { + "iopub.execute_input": "2026-09-23T18:27:05.675263Z", + "iopub.status.busy": "2026-09-23T18:27:05.675053Z", + "iopub.status.idle": "2026-09-23T18:27:08.155695Z", + "shell.execute_reply": "2026-09-23T18:27:08.155195Z" + }, + "executionInfo": { + "elapsed": 2482, + "status": "ok", + "timestamp": 1790188028156.8164, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 }, + "id": "fylcFDl9JupX", + "outputId": "1e962041-cc30-44b3-f51f-b3ad2ae0a9db" + }, + "outputs": [ { - "cell_type": "code", - "source": [ - "dgf.print.schema(schema)" + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "author_0\n", + "\n", + "author_0\n", + "\n", + "\n", + "\n", + "paper_1\n", + "\n", + "paper_1\n", + "\n", + "\n", + "\n", + "author_0->paper_1\n", + "\n", + "\n", + "writes\n", + "\n", + "\n", + "\n", + "author_1\n", + "\n", + "author_1\n", + "\n", + "\n", + "\n", + "author_1->paper_1\n", + "\n", + "\n", + "writes\n", + "\n", + "\n", + "\n", + "author_2\n", + "\n", + "author_2\n", + "\n", + "\n", + "\n", + "paper_5\n", + "\n", + "paper_5\n", + "\n", + "\n", + "\n", + "author_2->paper_5\n", + "\n", + "\n", + "writes\n", + "\n", + "\n", + "\n", + "author_3\n", + "\n", + "author_3\n", + "\n", + "\n", + "\n", + "author_3->paper_5\n", + "\n", + "\n", + "writes\n", + "\n", + "\n", + "\n", + "author_4\n", + "\n", + "author_4\n", + "\n", + "\n", + "\n", + "paper_9\n", + "\n", + "paper_9\n", + "\n", + "\n", + "\n", + "author_4->paper_9\n", + "\n", + "\n", + "writes\n", + "\n", + "\n", + "\n", + "author_5\n", + "\n", + "author_5\n", + "\n", + "\n", + "\n", + "author_5->paper_9\n", + "\n", + "\n", + "writes\n", + "\n", + "\n", + "\n", + "author_6\n", + "\n", + "author_6\n", + "\n", + "\n", + "\n", + "paper_13\n", + "\n", + "paper_13\n", + "\n", + "\n", + "\n", + "author_6->paper_13\n", + "\n", + "\n", + "writes\n", + "\n", + "\n", + "\n", + "author_7\n", + "\n", + "author_7\n", + "\n", + "\n", + "\n", + "author_7->paper_13\n", + "\n", + "\n", + "writes\n", + "\n", + "\n", + "\n", + "author_8\n", + "\n", + "author_8\n", + "\n", + "\n", + "\n", + "institution_0\n", + "\n", + "institution_0\n", + "\n", + "\n", + "\n", + "author_8->institution_0\n", + "\n", + "\n", + "affiliated_with\n", + "\n", + "\n", + "\n", + "institution_1\n", + "\n", + "institution_1\n", + "\n", + "\n", + "\n", + "author_8->institution_1\n", + "\n", + "\n", + "affiliated_with\n", + "\n", + "\n", + "\n", + "paper_0\n", + "\n", + "paper_0\n", + "\n", + "\n", + "\n", + "author_8->paper_0\n", + "\n", + "\n", + "writes\n", + "\n", + "\n", + "\n", + "paper_22\n", + "\n", + "paper_22\n", + "\n", + "\n", + "\n", + "author_8->paper_22\n", + "\n", + "\n", + "writes\n", + "\n", + "\n", + "\n", + "paper_23\n", + "\n", + "paper_23\n", + "\n", + "\n", + "\n", + "author_8->paper_23\n", + "\n", + "\n", + "writes\n", + "\n", + "\n", + "\n", + "author_9\n", + "\n", + "author_9\n", + "\n", + "\n", + "\n", + "institution_2\n", + "\n", + "institution_2\n", + "\n", + "\n", + "\n", + "author_9->institution_2\n", + "\n", + "\n", + "affiliated_with\n", + "\n", + "\n", + "\n", + "institution_3\n", + "\n", + "institution_3\n", + "\n", + "\n", + "\n", + "author_9->institution_3\n", + "\n", + "\n", + "affiliated_with\n", + "\n", + "\n", + "\n", + "author_9->paper_0\n", + "\n", + "\n", + "writes\n", + "\n", + "\n", + "\n", + "paper_24\n", + "\n", + "paper_24\n", + "\n", + "\n", + "\n", + "author_9->paper_24\n", + "\n", + "\n", + "writes\n", + "\n", + "\n", + "\n", + "field_of_study_0\n", + "\n", + "field_of_study_0\n", + "\n", + "\n", + "\n", + "field_of_study_1\n", + "\n", + "field_of_study_1\n", + "\n", + "\n", + "\n", + "field_of_study_2\n", + "\n", + "field_of_study_2\n", + "\n", + "\n", + "\n", + "field_of_study_3\n", + "\n", + "field_of_study_3\n", + "\n", + "\n", + "\n", + "field_of_study_4\n", + "\n", + "field_of_study_4\n", + "\n", + "\n", + "\n", + "field_of_study_5\n", + "\n", + "field_of_study_5\n", + "\n", + "\n", + "\n", + "field_of_study_6\n", + "\n", + "field_of_study_6\n", + "\n", + "\n", + "\n", + "field_of_study_7\n", + "\n", + "field_of_study_7\n", + "\n", + "\n", + "\n", + "field_of_study_8\n", + "\n", + "field_of_study_8\n", + "\n", + "\n", + "\n", + "field_of_study_9\n", + "\n", + "field_of_study_9\n", + "\n", + "\n", + "\n", + "paper_0->field_of_study_8\n", + "\n", + "\n", + "has_topic\n", + "\n", + "\n", + "\n", + "paper_0->field_of_study_9\n", + "\n", + "\n", + "has_topic\n", + "\n", + "\n", + "\n", + "paper_0->paper_1\n", + "\n", + "\n", + "cites\n", + "\n", + "\n", + "\n", + "paper_0->paper_5\n", + "\n", + "\n", + "cites\n", + "\n", + "\n", + "\n", + "paper_1->field_of_study_0\n", + "\n", + "\n", + "has_topic\n", + "\n", + "\n", + "\n", + "paper_1->field_of_study_1\n", + "\n", + "\n", + "has_topic\n", + "\n", + "\n", + "\n", + "paper_2\n", + "\n", + "paper_2\n", + "\n", + "\n", + "\n", + "paper_1->paper_2\n", + "\n", + "\n", + "cites\n", + "\n", + "\n", + "\n", + "paper_3\n", + "\n", + "paper_3\n", + "\n", + "\n", + "\n", + "paper_3->paper_1\n", + "\n", + "\n", + "cites\n", + "\n", + "\n", + "\n", + "paper_4\n", + "\n", + "paper_4\n", + "\n", + "\n", + "\n", + "paper_4->paper_1\n", + "\n", + "\n", + "cites\n", + "\n", + "\n", + "\n", + "paper_5->field_of_study_2\n", + "\n", + "\n", + "has_topic\n", + "\n", + "\n", + "\n", + "paper_5->field_of_study_3\n", + "\n", + "\n", + "has_topic\n", + "\n", + "\n", + "\n", + "paper_6\n", + "\n", + "paper_6\n", + "\n", + "\n", + "\n", + "paper_5->paper_6\n", + "\n", + "\n", + "cites\n", + "\n", + "\n", + "\n", + "paper_7\n", + "\n", + "paper_7\n", + "\n", + "\n", + "\n", + "paper_5->paper_7\n", + "\n", + "\n", + "cites\n", + "\n", + "\n", + "\n", + "paper_8\n", + "\n", + "paper_8\n", + "\n", + "\n", + "\n", + "paper_8->paper_5\n", + "\n", + "\n", + "cites\n", + "\n", + "\n", + "\n", + "paper_9->field_of_study_4\n", + "\n", + "\n", + "has_topic\n", + "\n", + "\n", + "\n", + "paper_9->field_of_study_5\n", + "\n", + "\n", + "has_topic\n", + "\n", + "\n", + "\n", + "paper_9->paper_0\n", + "\n", + "\n", + "cites\n", + "\n", + "\n", + "\n", + "paper_9->paper_2\n", + "\n", + "\n", + "cites\n", + "\n", + "\n", + "\n", + "paper_9->paper_5\n", + "\n", + "\n", + "cites\n", + "\n", + "\n", + "\n", + "paper_10\n", + "\n", + "paper_10\n", + "\n", + "\n", + "\n", + "paper_9->paper_10\n", + "\n", + "\n", + "cites\n", + "\n", + "\n", + "\n", + "paper_11\n", + "\n", + "paper_11\n", + "\n", + "\n", + "\n", + "paper_11->paper_9\n", + "\n", + "\n", + "cites\n", + "\n", + "\n", + "\n", + "paper_12\n", + "\n", + "paper_12\n", + "\n", + "\n", + "\n", + "paper_12->paper_9\n", + "\n", + "\n", + "cites\n", + "\n", + "\n", + "\n", + "paper_13->field_of_study_6\n", + "\n", + "\n", + "has_topic\n", + "\n", + "\n", + "\n", + "paper_13->field_of_study_7\n", + "\n", + "\n", + "has_topic\n", + "\n", + "\n", + "\n", + "paper_13->paper_0\n", + "\n", + "\n", + "cites\n", + "\n", + "\n", + "\n", + "paper_14\n", + "\n", + "paper_14\n", + "\n", + "\n", + "\n", + "paper_13->paper_14\n", + "\n", + "\n", + "cites\n", + "\n", + "\n", + "\n", + "paper_15\n", + "\n", + "paper_15\n", + "\n", + "\n", + "\n", + "paper_13->paper_15\n", + "\n", + "\n", + "cites\n", + "\n", + "\n", + "\n", + "paper_16\n", + "\n", + "paper_16\n", + "\n", + "\n", + "\n", + "paper_16->paper_13\n", + "\n", + "\n", + "cites\n", + "\n", + "\n", + "\n", + "paper_17\n", + "\n", + "paper_17\n", + "\n", + "\n", + "\n", + "paper_17->paper_13\n", + "\n", + "\n", + "cites\n", + "\n", + "\n", + "\n", + "paper_18\n", + "\n", + "paper_18\n", + "\n", + "\n", + "\n", + "paper_18->field_of_study_8\n", + "\n", + "\n", + "has_topic\n", + "\n", + "\n", + "\n", + "paper_19\n", + "\n", + "paper_19\n", + "\n", + "\n", + "\n", + "paper_19->field_of_study_8\n", + "\n", + "\n", + "has_topic\n", + "\n", + "\n", + "\n", + "paper_20\n", + "\n", + "paper_20\n", + "\n", + "\n", + "\n", + "paper_20->field_of_study_9\n", + "\n", + "\n", + "has_topic\n", + "\n", + "\n", + "\n", + "paper_21\n", + "\n", + "paper_21\n", + "\n", + "\n", + "\n", + "paper_21->field_of_study_9\n", + "\n", + "\n", + "has_topic\n", + "\n", + "\n", + "\n" ], - "metadata": { - "id": "OwNRk_ThJjAd", - "executionInfo": { - "status": "ok", - "timestamp": 1778171989426, - "user_tz": -120, - "elapsed": 22, - "user": { - "displayName": "", - "userId": "" - } - }, - "outputId": "e3e295c1-98ef-4a77-9fc1-f320a246d05f" - }, - "execution_count": null, - "outputs": [ - { - "output_type": "stream", - "name": "stdout", - "text": [ - "Graph Schema:\n", - "\n", - "Node Sets:\n", - " author:\n", - " | Feature | Format | Semantic | Shape | Num cat. vals |\n", - " |-----------|----------|------------|---------|-----------------|\n", - " | #id | BYTES | PRIMARY_ID | None | None |\n", - "\n", - " field_of_study:\n", - " | Feature | Format | Semantic | Shape | Num cat. vals |\n", - " |-----------|----------|------------|---------|-----------------|\n", - " | #id | BYTES | PRIMARY_ID | None | None |\n", - "\n", - " institution:\n", - " | Feature | Format | Semantic | Shape | Num cat. vals |\n", - " |-----------|----------|------------|---------|-----------------|\n", - " | #id | BYTES | PRIMARY_ID | None | None |\n", - "\n", - " paper:\n", - " | Feature | Format | Semantic | Shape | Num cat. vals |\n", - " |-----------|------------|-------------|---------|-----------------|\n", - " | #id | BYTES | PRIMARY_ID | None | None |\n", - " | #split | BYTES | CATEGORICAL | None | None |\n", - " | feat | FLOAT_32 | EMBEDDING | (128,) | None |\n", - " | labels | INTEGER_64 | CATEGORICAL | None | None |\n", - " | year | INTEGER_64 | NUMERICAL | None | None |\n", - "\n", - "\n", - "Edge Sets:\n", - " affiliated_with: (Source: author, Target: institution)\n", - " (No features)\n", - "\n", - " cites: (Source: paper, Target: paper)\n", - " (No features)\n", - "\n", - " has_topic: (Source: paper, Target: field_of_study)\n", - " (No features)\n", - "\n", - " writes: (Source: author, Target: paper)\n", - " (No features)\n", - "\n" - ] - } + "text/plain": [ + "" ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Create a sampler\n", + "\n", + "sampler = dgf.sampling.create_sampler(\n", + " graph=graph,\n", + " schema=schema,\n", + " plan=dgf.sampling.SimpleSamplingConfig(\n", + " seed_nodeset=\"paper\",\n", + " num_hops=2,\n", + " hop_width=2,\n", + " reverse=True,\n", + " ),\n", + " batch_size=8,\n", + ")\n", + "\n", + "\n", + "# Create a generator of graph samples.\n", + "def sample_generator(num_batches: int = 50):\n", + " num_paper_nodes = graph.node_sets[\"paper\"].num_nodes\n", + " for _ in range(num_batches):\n", + " seed_node_idxs = np.random.choice(num_paper_nodes, size=8, replace=False)\n", + " samples = sampler.sample(seed_node_idxs)\n", + " for sample in samples:\n", + " yield sample\n", + "\n", + "\n", + "# Test the generate by plotting a graph sample.\n", + "for sample in sample_generator():\n", + " break\n", + "dgf.plot.plot_graph(sample, schema, features=False)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "y09HW9r7MKkO" + }, + "source": [ + "## In-memory computation of feature statistics" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-23T18:27:08.157854Z", + "iopub.status.busy": "2026-09-23T18:27:08.157539Z", + "iopub.status.idle": "2026-09-23T18:27:08.993318Z", + "shell.execute_reply": "2026-09-23T18:27:08.992858Z" }, - { - "cell_type": "markdown", - "source": [ - "## Generate some graph samples\n", - "\n", - "First, we need to get feature statistics (e.g., quantiles, dictionaries) for the\n", - "node features. You can compute them these from the original graph or from graph\n", - "samples.\n", - "\n", - "While using the full graph is faster and easier, the results might not match the\n", - "data distribution the GNN model actually sees, and it is harder to compute\n", - "statistics on a sample of data. In this tutorial, we will show the graph sample\n", - "solution.\n", - "\n", - "First, we need to define a generator of graph samples. See the sampler tutorial\n", - "for more details." - ], - "metadata": { - "id": "V4ZTxkB2JwnY" - } + "executionInfo": { + "elapsed": 837, + "status": "ok", + "timestamp": 1790188028994.3455, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 }, + "id": "GE2UXUmJMPRo", + "outputId": "b31f31fb-8193-4acd-ce96-428fcb2888e4" + }, + "outputs": [ { - "cell_type": "code", - "source": [ - "# Create a sampler\n", - "\n", - "sampler = dgf.sampling.create_sampler(\n", - " graph=graph,\n", - " schema=schema,\n", - " plan=dgf.sampling.SimpleSamplingConfig(\n", - " seed_nodeset=\"paper\",\n", - " num_hops=2,\n", - " hop_width=2,\n", - " reverse=True,\n", - " ),\n", - " batch_size=8,\n", - ")\n", - "\n", - "\n", - "# Create a generator of graph samples.\n", - "def sample_generator(num_batches: int = 50):\n", - " num_paper_nodes = graph.node_sets[\"paper\"].num_nodes\n", - " for _ in range(num_batches):\n", - " seed_node_idxs = np.random.choice(num_paper_nodes, size=8, replace=False)\n", - " samples = sampler.sample(seed_node_idxs)\n", - " for sample in samples:\n", - " yield sample\n", - "\n", - "\n", - "# Test the generate by plotting a graph sample.\n", - "for sample in sample_generator():\n", - " break\n", - "dgf.plot.plot_graph(sample, schema, features=False)" - ], - "metadata": { - "colab": { - "height": 1000 - }, - "id": "fylcFDl9JupX", - "executionInfo": { - "status": "ok", - "timestamp": 1778171994396, - "user_tz": -120, - "elapsed": 2197, - "user": { - "displayName": "", - "userId": "" - } - }, - "outputId": "1e962041-cc30-44b3-f51f-b3ad2ae0a9db" - }, - "execution_count": null, - "outputs": [ - { - "output_type": "execute_result", - "data": { - "image/svg+xml": "\n\n\n\n\n\n\n\n\nauthor_0\n\nauthor_0\n\n\n\npaper_1\n\npaper_1\n\n\n\nauthor_0->paper_1\n\n\nwrites\n\n\n\nauthor_1\n\nauthor_1\n\n\n\nauthor_1->paper_1\n\n\nwrites\n\n\n\nauthor_2\n\nauthor_2\n\n\n\npaper_6\n\npaper_6\n\n\n\nauthor_2->paper_6\n\n\nwrites\n\n\n\nauthor_3\n\nauthor_3\n\n\n\nauthor_3->paper_6\n\n\nwrites\n\n\n\nauthor_4\n\nauthor_4\n\n\n\npaper_9\n\npaper_9\n\n\n\nauthor_4->paper_9\n\n\nwrites\n\n\n\nauthor_5\n\nauthor_5\n\n\n\nauthor_5->paper_9\n\n\nwrites\n\n\n\nauthor_6\n\nauthor_6\n\n\n\ninstitution_0\n\ninstitution_0\n\n\n\nauthor_6->institution_0\n\n\naffiliated_with\n\n\n\npaper_0\n\npaper_0\n\n\n\nauthor_6->paper_0\n\n\nwrites\n\n\n\npaper_16\n\npaper_16\n\n\n\nauthor_6->paper_16\n\n\nwrites\n\n\n\nauthor_7\n\nauthor_7\n\n\n\nauthor_7->institution_0\n\n\naffiliated_with\n\n\n\nauthor_7->paper_0\n\n\nwrites\n\n\n\nfield_of_study_0\n\nfield_of_study_0\n\n\n\nfield_of_study_1\n\nfield_of_study_1\n\n\n\nfield_of_study_2\n\nfield_of_study_2\n\n\n\nfield_of_study_3\n\nfield_of_study_3\n\n\n\nfield_of_study_4\n\nfield_of_study_4\n\n\n\nfield_of_study_5\n\nfield_of_study_5\n\n\n\nfield_of_study_6\n\nfield_of_study_6\n\n\n\npaper_0->field_of_study_5\n\n\nhas_topic\n\n\n\npaper_0->field_of_study_6\n\n\nhas_topic\n\n\n\npaper_0->paper_1\n\n\ncites\n\n\n\npaper_0->paper_6\n\n\ncites\n\n\n\npaper_1->field_of_study_0\n\n\nhas_topic\n\n\n\npaper_1->field_of_study_1\n\n\nhas_topic\n\n\n\npaper_2\n\npaper_2\n\n\n\npaper_1->paper_2\n\n\ncites\n\n\n\npaper_3\n\npaper_3\n\n\n\npaper_1->paper_3\n\n\ncites\n\n\n\npaper_4\n\npaper_4\n\n\n\npaper_4->paper_1\n\n\ncites\n\n\n\npaper_5\n\npaper_5\n\n\n\npaper_5->paper_1\n\n\ncites\n\n\n\npaper_6->field_of_study_2\n\n\nhas_topic\n\n\n\npaper_6->field_of_study_3\n\n\nhas_topic\n\n\n\npaper_7\n\npaper_7\n\n\n\npaper_7->paper_6\n\n\ncites\n\n\n\npaper_8\n\npaper_8\n\n\n\npaper_8->paper_6\n\n\ncites\n\n\n\npaper_9->field_of_study_4\n\n\nhas_topic\n\n\n\npaper_9->field_of_study_5\n\n\nhas_topic\n\n\n\npaper_9->paper_0\n\n\ncites\n\n\n\npaper_10\n\npaper_10\n\n\n\npaper_9->paper_10\n\n\ncites\n\n\n\npaper_11\n\npaper_11\n\n\n\npaper_11->paper_9\n\n\ncites\n\n\n\npaper_12\n\npaper_12\n\n\n\npaper_12->field_of_study_6\n\n\nhas_topic\n\n\n\npaper_13\n\npaper_13\n\n\n\npaper_13->field_of_study_6\n\n\nhas_topic\n\n\n\npaper_14\n\npaper_14\n\n\n\npaper_14->field_of_study_5\n\n\nhas_topic\n\n\n\npaper_15\n\npaper_15\n\n\n\npaper_15->field_of_study_5\n\n\nhas_topic\n\n\n\n", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "execution_count": 4 - } + "data": { + "text/plain": [ + "GraphFeatureStatistics:\n", + " Node Sets (4):\n", + " 'author':\n", + " '#id': count=2905, min=nan, max=nan\n", + " 'field_of_study':\n", + " '#id': count=2924, min=nan, max=nan\n", + " 'institution':\n", + " '#id': count=714, min=nan, max=nan\n", + " 'paper':\n", + " '#id': count=7612, min=nan, max=nan\n", + " '#split': count=7612, min=nan, max=nan, dictionary=(3)['train': 6322, 'valid': 705, 'test': 585]\n", + " 'feat': count=7612, min=nan, max=nan\n", + " 'labels': count=7612, min=0.0000, max=348.0000\n", + " 'year': count=7612, min=2010.0000, max=2019.0000, quantiles=(100)[2010.0000, 2010.0000, 2010.0000, ..., 2019.0000, 2019.0000, 2019.0000]" ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "feature_stats = dgf.analyse.feature_statistics_from_graphs(\n", + " sample_generator(num_batches=50), schema\n", + ")\n", + "feature_stats" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "ArsJuvt-MpUP" + }, + "source": [ + "**Remark:**\n", + "\n", + "- The type of statistics gathered depends on the data type. For example,\n", + " `year` is a numerical value, so you should find its quantiles. However,\n", + " because `feat` is an embedding, you only need to find the count of\n", + " non-missing values.\n", + "- To compute feature statistics from a single graph, you can call\n", + " `dgf.analyse.feature_statistics_from_graphs([graph], schema)` (or\n", + " equivalently, `dgf.analyse.feature_statistics`)." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-23T18:27:08.995155Z", + "iopub.status.busy": "2026-09-23T18:27:08.994978Z", + "iopub.status.idle": "2026-09-23T18:27:09.720822Z", + "shell.execute_reply": "2026-09-23T18:27:09.720404Z" }, - { - "cell_type": "markdown", - "source": [ - "## In-memory computation of feature statistics" - ], - "metadata": { - "id": "y09HW9r7MKkO" - } + "executionInfo": { + "elapsed": 727, + "status": "ok", + "timestamp": 1790188029721.7666, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 }, + "id": "3tFLpSX9TH_T", + "outputId": "2c0a3f4a-e9b2-48e6-af1e-4d8801bfd003" + }, + "outputs": [ { - "cell_type": "code", - "source": [ - "feature_stats = dgf.analyse.feature_statistics_from_graphs(\n", - " sample_generator(num_batches=50), schema\n", - ")\n", - "feature_stats" - ], - "metadata": { - "id": "GE2UXUmJMPRo", - "executionInfo": { - "status": "ok", - "timestamp": 1778171997997, - "user_tz": -120, - "elapsed": 1302, - "user": { - "displayName": "", - "userId": "" - } - }, - "outputId": "b31f31fb-8193-4acd-ce96-428fcb2888e4" - }, - "execution_count": null, - "outputs": [ - { - "output_type": "execute_result", - "data": { - "text/plain": [ - "GraphFeatureStatistics:\n", - " Node Sets (4):\n", - " 'author':\n", - " '#id': count=2843, min=nan, max=nan\n", - " 'field_of_study':\n", - " '#id': count=2895, min=nan, max=nan\n", - " 'institution':\n", - " '#id': count=709, min=nan, max=nan\n", - " 'paper':\n", - " '#id': count=7428, min=nan, max=nan\n", - " '#split': count=7428, min=nan, max=nan, dictionary=(3)['train': 6173, 'valid': 674, 'test': 581]\n", - " 'feat': count=7428, min=nan, max=nan\n", - " 'labels': count=7428, min=0.0000, max=348.0000\n", - " 'year': count=7428, min=2010.0000, max=2019.0000, quantiles=(100)[2010.0000, 2010.0000, 2010.0000, ..., 2019.0000, 2019.0000, 2019.0000]" - ] - }, - "metadata": {}, - "execution_count": 5 - } + "data": { + "text/plain": [ + "GraphFeatureStatistics:\n", + " Node Sets (4):\n", + " 'author':\n", + " '#id': count=1134649, min=nan, max=nan\n", + " 'field_of_study':\n", + " '#id': count=59965, min=nan, max=nan\n", + " 'institution':\n", + " '#id': count=8740, min=nan, max=nan\n", + " 'paper':\n", + " '#id': count=736389, min=nan, max=nan\n", + " '#split': count=736389, min=nan, max=nan, dictionary=(3)['train': 629571, 'valid': 64879, 'test': 41939]\n", + " 'feat': count=736389, min=nan, max=nan\n", + " 'labels': count=736389, min=0.0000, max=348.0000\n", + " 'year': count=736389, min=2010.0000, max=2019.0000, quantiles=(100)[2010.0000, 2010.0000, 2010.0000, ..., 2019.0000, 2019.0000, 2019.0000]" ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "feature_stats = dgf.analyse.feature_statistics(graph, schema)\n", + "feature_stats" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "7ihc62ZNNF50" + }, + "source": [ + "## Create a feature normalizer\n", + "\n", + "Various feature normalizers are available in `dgf.transform`. For example,\n", + "`dgf.transform.SoftQuantileNormalizer` normalizes numerical values using the\n", + "soft-quantile method, while `dgf.transform.DictionaryIndexNormalizer` can\n", + "normalize categorical values.\n", + "\n", + "As an example, let's create a `SoftQuantileNormalizer` for the `year` feature." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-23T18:27:09.722582Z", + "iopub.status.busy": "2026-09-23T18:27:09.722407Z", + "iopub.status.idle": "2026-09-23T18:27:09.725703Z", + "shell.execute_reply": "2026-09-23T18:27:09.725082Z" }, - { - "cell_type": "markdown", - "source": [ - "**Remark:**\n", - "\n", - "- The type of statistics gathered depends on the data type. For example,\n", - " `year` is a numerical value, so you should find its quantiles. However,\n", - " because `feat` is an embedding, you only need to find the count of\n", - " non-missing values.\n", - "- To compute feature statistics from a single graph, you can call\n", - " `dgf.analyse.feature_statistics_from_graphs([graph], schema)` (or\n", - " equivalently, `dgf.analyse.feature_statistics`)." - ], - "metadata": { - "id": "ArsJuvt-MpUP" - } + "executionInfo": { + "elapsed": 4, + "status": "ok", + "timestamp": 1790188029726.655, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 }, - { - "cell_type": "code", - "source": [ - "feature_stats = dgf.analyse.feature_statistics(graph, schema)\n", - "feature_stats" - ], - "metadata": { - "id": "3tFLpSX9TH_T", - "executionInfo": { - "status": "ok", - "timestamp": 1778172000653, - "user_tz": -120, - "elapsed": 757, - "user": { - "displayName": "", - "userId": "" - } - }, - "outputId": "2c0a3f4a-e9b2-48e6-af1e-4d8801bfd003" - }, - "execution_count": null, - "outputs": [ - { - "output_type": "execute_result", - "data": { - "text/plain": [ - "GraphFeatureStatistics:\n", - " Node Sets (4):\n", - " 'author':\n", - " '#id': count=1134649, min=nan, max=nan\n", - " 'field_of_study':\n", - " '#id': count=59965, min=nan, max=nan\n", - " 'institution':\n", - " '#id': count=8740, min=nan, max=nan\n", - " 'paper':\n", - " '#id': count=736389, min=nan, max=nan\n", - " '#split': count=736389, min=nan, max=nan, dictionary=(3)['train': 629571, 'valid': 64879, 'test': 41939]\n", - " 'feat': count=736389, min=nan, max=nan\n", - " 'labels': count=736389, min=0.0000, max=348.0000\n", - " 'year': count=736389, min=2010.0000, max=2019.0000, quantiles=(100)[2010.0000, 2010.0000, 2010.0000, ..., 2019.0000, 2019.0000, 2019.0000]" - ] - }, - "metadata": {}, - "execution_count": 6 - } - ] + "id": "hHgBqvHbNfG0", + "outputId": "50adeedb-d08e-4e8c-f527-230e67f44a4d" + }, + "outputs": [], + "source": [ + "year_normalizer = dgf.transform.SoftQuantileNormalizer.create(\n", + " feature_name=\"year\",\n", + " input_schema=schema.node_sets[\"paper\"].features[\"year\"],\n", + " input_stats=feature_stats.node_sets[\"paper\"].features[\"year\"],\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "p4q8-u6CN2nT" + }, + "source": [ + "Let's normalize some values:" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-23T18:27:09.727573Z", + "iopub.status.busy": "2026-09-23T18:27:09.727323Z", + "iopub.status.idle": "2026-09-23T18:27:09.730712Z", + "shell.execute_reply": "2026-09-23T18:27:09.730399Z" }, - { - "cell_type": "markdown", - "source": [ - "## Create a feature normalizer\n", - "\n", - "Various feature normalizers are available in `dgf.transform`. For example,\n", - "`dgf.transform.SoftQuantileNormalizer` normalizes numerical values using the\n", - "soft-quantile method, while `dgf.transform.DictionaryIndexNormalizer` can\n", - "normalize categorical values.\n", - "\n", - "As an example, let's create a `SoftQuantileNormalizer` for the `year` feature." - ], - "metadata": { - "id": "7ihc62ZNNF50" - } + "executionInfo": { + "elapsed": 5, + "status": "ok", + "timestamp": 1790188029731.7095, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 }, + "id": "tRWQrs0dNIK5", + "outputId": "85f9e461-c74d-4602-93e0-ec9aa558cd0d" + }, + "outputs": [ { - "cell_type": "code", - "source": [ - "year_normalizer = dgf.transform.SoftQuantileNormalizer.create(\n", - " feature_name=\"year\",\n", - " input_schema=schema.node_sets[\"paper\"].features[\"year\"],\n", - " input_stats=feature_stats.node_sets[\"paper\"].features[\"year\"],\n", - ")" - ], - "metadata": { - "id": "hHgBqvHbNfG0", - "executionInfo": { - "status": "ok", - "timestamp": 1777985862160, - "user_tz": -120, - "elapsed": 20, - "user": { - "displayName": "", - "userId": "" - } - }, - "outputId": "50adeedb-d08e-4e8c-f527-230e67f44a4d" - }, - "execution_count": null, - "outputs": [ - { - "output_type": "execute_result", - "data": { - "text/plain": [ - "SoftQuantileNormalizer(input_feature='year', type='SoftQuantileNormalizer', output_feature_name='year_SOFT_QUANTILE', output_shape=None, quantiles=array([2010., 2011., 2012., 2013., 2014., 2015., 2016., 2017., 2018.,\n", - " 2019.], dtype=float32))" - ] - }, - "metadata": {}, - "execution_count": 23 - } + "data": { + "text/plain": [ + "array([2015, 2012, 2012, 2010, 2011, 2011, 2014, 2013, 2012, 2010])" ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Raw values\n", + "raw_values = graph.node_sets[\"paper\"].features[\"year\"][:10]\n", + "raw_values" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-23T18:27:09.732525Z", + "iopub.status.busy": "2026-09-23T18:27:09.732318Z", + "iopub.status.idle": "2026-09-23T18:27:09.735595Z", + "shell.execute_reply": "2026-09-23T18:27:09.735291Z" }, - { - "cell_type": "markdown", - "source": [ - "Let's normalize some values:" - ], - "metadata": { - "id": "p4q8-u6CN2nT" - } + "executionInfo": { + "elapsed": 4, + "status": "ok", + "timestamp": 1790188029736.4968, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 }, + "id": "2415MctXOD0G", + "outputId": "b2af3f72-7049-4a40-b21c-f4e3161cb15c" + }, + "outputs": [ { - "cell_type": "code", - "source": [ - "# Raw values\n", - "raw_values = graph.node_sets[\"paper\"].features[\"year\"][:10]\n", - "raw_values" - ], - "metadata": { - "id": "tRWQrs0dNIK5", - "executionInfo": { - "status": "ok", - "timestamp": 1777986030516, - "user_tz": -120, - "elapsed": 384, - "user": { - "displayName": "", - "userId": "" - } - }, - "outputId": "85f9e461-c74d-4602-93e0-ec9aa558cd0d" - }, - "execution_count": null, - "outputs": [ - { - "output_type": "execute_result", - "data": { - "text/plain": [ - "array([2015, 2012, 2012, 2010, 2011, 2011, 2014, 2013, 2012, 2010])" - ] - }, - "metadata": {}, - "execution_count": 29 - } + "data": { + "text/plain": [ + "{'year_SOFT_QUANTILE': array([ 0.05555558, -0.2777778 , -0.2777778 , -0.5 , -0.3888889 ,\n", + " -0.3888889 , -0.05555555, -0.16666666, -0.2777778 , -0.5 ],\n", + " dtype=float32)}" ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "year_normalizer.normalize_numpy(raw_values)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "B1cEYHKAOFkL" + }, + "source": [ + "**Remark:**\n", + "\n", + "- A normalizer might take one input feature and produce several output\n", + " features. This is why the output is a dictionary." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "3hCaaep-OPDr" + }, + "source": [ + "If you work with TensorFlow, you might use the TensorFlow version of the\n", + "normalization. This is great for serializing GNN model in TensorFlow Saved\n", + "Models:" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-23T18:27:09.737217Z", + "iopub.status.busy": "2026-09-23T18:27:09.737006Z", + "iopub.status.idle": "2026-09-23T18:27:09.831164Z", + "shell.execute_reply": "2026-09-23T18:27:09.830760Z" }, + "executionInfo": { + "elapsed": 95, + "status": "ok", + "timestamp": 1790188029832.0935, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 + }, + "id": "fDRgExHKOY31", + "outputId": "5d99925b-a8e6-4551-9d22-48d3cda3672e" + }, + "outputs": [ { - "cell_type": "code", - "source": [ - "year_normalizer.normalize_numpy(raw_values)" - ], - "metadata": { - "id": "2415MctXOD0G", - "executionInfo": { - "status": "ok", - "timestamp": 1777986033451, - "user_tz": -120, - "elapsed": 21, - "user": { - "displayName": "", - "userId": "" - } - }, - "outputId": "b2af3f72-7049-4a40-b21c-f4e3161cb15c" - }, - "execution_count": null, - "outputs": [ - { - "output_type": "execute_result", - "data": { - "text/plain": [ - "{'year_SOFT_QUANTILE': array([0.5555556 , 0.22222222, 0.22222222, 0. , 0.11111111,\n", - " 0.11111111, 0.44444445, 0.33333334, 0.22222222, 0. ],\n", - " dtype=float32)}" - ] - }, - "metadata": {}, - "execution_count": 30 - } + "data": { + "text/plain": [ + "{'year_SOFT_QUANTILE': }" ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import tensorflow as tf\n", + "\n", + "year_normalizer.normalize_tensorflow(tf.constant(raw_values))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "M-D2NXE5OkhU" + }, + "source": [ + "The normalizer can also produce the schema of its output:" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-23T18:27:09.832894Z", + "iopub.status.busy": "2026-09-23T18:27:09.832723Z", + "iopub.status.idle": "2026-09-23T18:27:09.835566Z", + "shell.execute_reply": "2026-09-23T18:27:09.835260Z" }, - { - "cell_type": "markdown", - "source": [ - "**Remark:**\n", - "\n", - "- A normalizer might take one input feature and produce several output\n", - " features. This is why the output is a dictionary." - ], - "metadata": { - "id": "B1cEYHKAOFkL" - } + "executionInfo": { + "elapsed": 4, + "status": "ok", + "timestamp": 1790188029836.4167, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 }, + "id": "T6k_DhGkOtVC", + "outputId": "51b2c388-5a40-4991-8732-db36527289b7" + }, + "outputs": [ { - "cell_type": "markdown", - "source": [ - "If you work with TensorFlow, you might use the TensorFlow version of the\n", - "normalization. This is great for serializing GNN model in TensorFlow Saved\n", - "Models:" - ], - "metadata": { - "id": "3hCaaep-OPDr" - } - }, - { - "cell_type": "code", - "source": [ - "import tensorflow as tf\n", - "\n", - "year_normalizer.normalize_tensorflow(tf.constant(raw_values))" - ], - "metadata": { - "id": "fDRgExHKOY31", - "executionInfo": { - "status": "ok", - "timestamp": 1777986051052, - "user_tz": -120, - "elapsed": 19, - "user": { - "displayName": "", - "userId": "" - } - }, - "outputId": "5d99925b-a8e6-4551-9d22-48d3cda3672e" - }, - "execution_count": null, - "outputs": [ - { - "output_type": "execute_result", - "data": { - "text/plain": [ - "{'year_SOFT_QUANTILE': }" - ] - }, - "metadata": {}, - "execution_count": 33 - } + "data": { + "text/plain": [ + "{'year_SOFT_QUANTILE': FeatureSchema(format=, semantic=, shape=(), num_categorical_values=None, is_utf8_string=False, is_timeseries=False, is_creation_time=False, group=None)}" ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "year_normalizer.output_schema()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "IAosJsb5O1Nq" + }, + "source": [ + "Like all GF configurations, normalizers can be serialized from / to json:" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": { + "colab": { + "height": 36 }, - { - "cell_type": "markdown", - "source": [ - "The normalizer can also produce the schema of its output:" - ], - "metadata": { - "id": "M-D2NXE5OkhU" - } + "execution": { + "iopub.execute_input": "2026-09-23T18:27:09.837131Z", + "iopub.status.busy": "2026-09-23T18:27:09.836930Z", + "iopub.status.idle": "2026-09-23T18:27:09.840340Z", + "shell.execute_reply": "2026-09-23T18:27:09.840042Z" }, + "executionInfo": { + "elapsed": 4, + "status": "ok", + "timestamp": 1790188029841.2395, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 + }, + "id": "DT--SmbeO8Vf", + "outputId": "43b665da-e7dd-43bb-fd7a-197746389da1" + }, + "outputs": [ { - "cell_type": "code", - "source": [ - "year_normalizer.output_schema()" - ], - "metadata": { - "id": "T6k_DhGkOtVC", - "executionInfo": { - "status": "ok", - "timestamp": 1777986104448, - "user_tz": -120, - "elapsed": 396, - "user": { - "displayName": "", - "userId": "" - } - }, - "outputId": "51b2c388-5a40-4991-8732-db36527289b7" + "data": { + "application/vnd.google.colaboratory.intrinsic+json": { + "type": "string" }, - "execution_count": null, - "outputs": [ - { - "output_type": "execute_result", - "data": { - "text/plain": [ - "{'year_SOFT_QUANTILE': FeatureSchema(format=, semantic=, shape=None, num_categorical_values=None, is_utf8_string=False)}" - ] - }, - "metadata": {}, - "execution_count": 34 - } + "text/plain": [ + "'{\"input_feature\": \"year\", \"type\": \"SoftQuantileNormalizer\", \"output_feature_name\": \"year_SOFT_QUANTILE\", \"output_shape\": [], \"quantiles\": [2010.0, 2011.0, 2012.0, 2013.0, 2014.0, 2015.0, 2016.0, 2017.0, 2018.0, 2019.0], \"is_timeseries\": false, \"group\": null}'" ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "year_normalizer.to_json()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "c9iPh5uiPIjN" + }, + "source": [ + "## Auto-normalization\n", + "\n", + "Instead of defining individual normalizers for all the features of a graph, we\n", + "can use the auto-normalizer." + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-23T18:27:09.841962Z", + "iopub.status.busy": "2026-09-23T18:27:09.841741Z", + "iopub.status.idle": "2026-09-23T18:27:09.847021Z", + "shell.execute_reply": "2026-09-23T18:27:09.846684Z" }, + "executionInfo": { + "elapsed": 6, + "status": "ok", + "timestamp": 1790188029847.884, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 + }, + "id": "FqTYbn_bPTOX", + "outputId": "85cac64d-687a-4fbb-de06-cafaf9cb3724" + }, + "outputs": [ { - "cell_type": "markdown", - "source": [ - "Like all GF configurations, normalizers can be serialized from / to json:" - ], - "metadata": { - "id": "IAosJsb5O1Nq" - } + "name": "stderr", + "output_type": "stream", + "text": [ + "[Warning] No normalizer created for node set 'author', feature '#id'.\n", + "[Warning] No normalizer created for node set 'paper', feature '#id'.\n", + "[Warning] No normalizer created for node set 'field_of_study', feature '#id'.\n", + "[Warning] No normalizer created for node set 'institution', feature '#id'.\n" + ] }, { - "cell_type": "code", - "source": [ - "year_normalizer.to_json()" - ], - "metadata": { - "colab": { - "height": 36 - }, - "id": "DT--SmbeO8Vf", - "executionInfo": { - "status": "ok", - "timestamp": 1777986172007, - "user_tz": -120, - "elapsed": 391, - "user": { - "displayName": "", - "userId": "" - } - }, - "outputId": "43b665da-e7dd-43bb-fd7a-197746389da1" - }, - "execution_count": null, - "outputs": [ - { - "output_type": "execute_result", - "data": { - "text/plain": [ - "'{\"input_feature\": \"year\", \"type\": \"SoftQuantileNormalizer\", \"output_feature_name\": \"year_SOFT_QUANTILE\", \"output_shape\": null, \"quantiles\": [2010.0, 2011.0, 2012.0, 2013.0, 2014.0, 2015.0, 2016.0, 2017.0, 2018.0, 2019.0]}'" - ], - "application/vnd.google.colaboratory.intrinsic+json": { - "type": "string" - } - }, - "metadata": {}, - "execution_count": 35 - } + "data": { + "text/plain": [ + "GraphNormalizer(config=GraphNormalizerConfig(nodesets={'author': NodeSetNormalizerConfig(normalizers=[]), 'paper': NodeSetNormalizerConfig(normalizers=[DictionaryIndexNormalizer(input_feature='#split', type='DictionaryIndexNormalizer', dictionary_map={'train': 0, 'valid': 1, 'test': 2}, out_of_vocab_value=3, output_shape=(), output_feature_name='#split_INDEX', tf_table=None), IdentityNormalizer(input_feature='labels', type='IdentityNormalizer', input_schema=FeatureSchema(format=, semantic=, shape=None, num_categorical_values=349, is_utf8_string=False, is_timeseries=False, is_creation_time=False, group=None)), SoftQuantileNormalizer(input_feature='year', type='SoftQuantileNormalizer', output_feature_name='year_SOFT_QUANTILE', output_shape=(), quantiles=array([2010., 2011., 2012., 2013., 2014., 2015., 2016., 2017., 2018.,\n", + " 2019.], dtype=float32), is_timeseries=False, group=None), IdentityNormalizer(input_feature='feat', type='IdentityNormalizer', input_schema=FeatureSchema(format=, semantic=, shape=(128,), num_categorical_values=None, is_utf8_string=False, is_timeseries=False, is_creation_time=False, group=None))]), 'field_of_study': NodeSetNormalizerConfig(normalizers=[]), 'institution': NodeSetNormalizerConfig(normalizers=[])}, edgesets={'has_topic': EdgeSetNormalizerConfig(source='paper', target='field_of_study', normalizers=[]), 'affiliated_with': EdgeSetNormalizerConfig(source='author', target='institution', normalizers=[]), 'cites': EdgeSetNormalizerConfig(source='paper', target='paper', normalizers=[]), 'writes': EdgeSetNormalizerConfig(source='author', target='paper', normalizers=[])}), _nodeset_kwargs={'author': [], 'paper': [frozenset(), frozenset(), frozenset(), frozenset()], 'field_of_study': [], 'institution': []}, _edgeset_kwargs={'has_topic': [], 'affiliated_with': [], 'cites': [], 'writes': []}, _all_accepted_kwargs=frozenset())" ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "auto_normalizer = dgf.transform.auto_normalize(schema, feature_stats)\n", + "auto_normalizer" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "mxekXNdOPdux" + }, + "source": [ + "Like for the individual normalizer, you can apply it with NumPy or TensorFlow\n", + "backend, save it, and check the output schema:" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-23T18:27:09.848574Z", + "iopub.status.busy": "2026-09-23T18:27:09.848398Z", + "iopub.status.idle": "2026-09-23T18:27:09.852174Z", + "shell.execute_reply": "2026-09-23T18:27:09.851723Z" }, - { - "cell_type": "markdown", - "source": [ - "## Auto-normalization\n", - "\n", - "Instead of defining individual normalizers for all the features of a graph, we\n", - "can use the auto-normalizer." - ], - "metadata": { - "id": "c9iPh5uiPIjN" - } + "executionInfo": { + "elapsed": 5, + "status": "ok", + "timestamp": 1790188029853.2314, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 }, + "id": "Z9tcXkv_P-TZ", + "outputId": "020efadc-335d-49b7-d6c4-97a33dce5317" + }, + "outputs": [ { - "cell_type": "code", - "source": [ - "auto_normalizer = dgf.transform.auto_normalize(schema, feature_stats)\n", - "auto_normalizer" - ], - "metadata": { - "id": "FqTYbn_bPTOX", - "executionInfo": { - "status": "ok", - "timestamp": 1777986372200, - "user_tz": -120, - "elapsed": 398, - "user": { - "displayName": "", - "userId": "" - } - }, - "outputId": "85cac64d-687a-4fbb-de06-cafaf9cb3724" - }, - "execution_count": null, - "outputs": [ - { - "output_type": "stream", - "name": "stderr", - "text": [ - "[Warning] No normalizer created for node set 'author', feature '#id'.\n", - "[Warning] No normalizer created for node set 'paper', feature '#id'.\n", - "[Warning] No normalizer created for node set 'field_of_study', feature '#id'.\n", - "[Warning] No normalizer created for node set 'institution', feature '#id'.\n" - ] - }, - { - "output_type": "execute_result", - "data": { - "text/plain": [ - "GraphNormalizer(config=GraphNormalizerConfig(nodesets={'author': NodeSetNormalizerConfig(normalizers=[]), 'paper': NodeSetNormalizerConfig(normalizers=[DictionaryIndexNormalizer(input_feature='#split', type='DictionaryIndexNormalizer', dictionary_map={'train': 0, 'valid': 1, 'test': 2}, out_of_vocab_value=3, output_shape=None, output_feature_name='#split_INDEX', tf_table=None), IdentityNormalizer(input_feature='labels', type='IdentityNormalizer', input_schema=FeatureSchema(format=, semantic=, shape=None, num_categorical_values=349, is_utf8_string=False)), SoftQuantileNormalizer(input_feature='year', type='SoftQuantileNormalizer', output_feature_name='year_SOFT_QUANTILE', output_shape=None, quantiles=array([2010., 2011., 2012., 2013., 2014., 2015., 2016., 2017., 2018.,\n", - " 2019.], dtype=float32)), IdentityNormalizer(input_feature='feat', type='IdentityNormalizer', input_schema=FeatureSchema(format=, semantic=, shape=(128,), num_categorical_values=None, is_utf8_string=False))]), 'field_of_study': NodeSetNormalizerConfig(normalizers=[]), 'institution': NodeSetNormalizerConfig(normalizers=[])}, edgesets={'has_topic': EdgeSetNormalizerConfig(source='paper', target='field_of_study', normalizers=[]), 'affiliated_with': EdgeSetNormalizerConfig(source='author', target='institution', normalizers=[]), 'cites': EdgeSetNormalizerConfig(source='paper', target='paper', normalizers=[]), 'writes': EdgeSetNormalizerConfig(source='author', target='paper', normalizers=[])}))" - ] - }, - "metadata": {}, - "execution_count": 38 - } - ] + "name": "stdout", + "output_type": "stream", + "text": [ + "Graph Schema:\n", + "\n", + "Node Sets:\n", + " author:\n", + " (No features)\n", + "\n", + " field_of_study:\n", + " (No features)\n", + "\n", + " institution:\n", + " (No features)\n", + "\n", + " paper:\n", + " | Feature | Format | Semantic | Shape | Detail |\n", + " |--------------------|------------|-------------|---------|--------------|\n", + " | #split_INDEX | INTEGER_64 | CATEGORICAL | () | #num.cat:4 |\n", + " | feat | FLOAT_32 | EMBEDDING | (128,) | |\n", + " | labels | INTEGER_64 | CATEGORICAL | None | #num.cat:349 |\n", + " | year_SOFT_QUANTILE | FLOAT_32 | EMBEDDING | () | |\n", + "\n", + "\n", + "Edge Sets:\n", + " affiliated_with: (Source: author, Target: institution)\n", + " (No features)\n", + "\n", + " cites: (Source: paper, Target: paper)\n", + " (No features)\n", + "\n", + " has_topic: (Source: paper, Target: field_of_study)\n", + " (No features)\n", + "\n", + " writes: (Source: author, Target: paper)\n", + " (No features)\n", + "\n" + ] + } + ], + "source": [ + "dgf.print.schema(auto_normalizer.output_schema())" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "WKar-IkdQK8K" + }, + "source": [ + "## Apply normalizer on graph samples\n", + "\n", + "We can now create a generator of **normalized** graph samples:" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": { + "colab": { + "height": 1000 }, - { - "cell_type": "markdown", - "source": [ - "Like for the individual normalizer, you can apply it with NumPy or TensorFlow\n", - "backend, save it, and check the output schema:" - ], - "metadata": { - "id": "mxekXNdOPdux" - } + "execution": { + "iopub.execute_input": "2026-09-23T18:27:09.854185Z", + "iopub.status.busy": "2026-09-23T18:27:09.853980Z", + "iopub.status.idle": "2026-09-23T18:27:09.930579Z", + "shell.execute_reply": "2026-09-23T18:27:09.930005Z" }, + "executionInfo": { + "elapsed": 78, + "status": "ok", + "timestamp": 1790188029931.6128, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 + }, + "id": "n2MZlXNDQWNJ", + "outputId": "8e5ef776-d36b-4c12-d8f0-518c4135638e" + }, + "outputs": [ { - "cell_type": "code", - "source": [ - "dgf.print.schema(auto_normalizer.output_schema())" + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "author_0\n", + "\n", + "author_0\n", + "\n", + "\n", + "\n", + "paper_1\n", + "\n", + "paper_1\n", + "#split_INDEX: 0\n", + "feat: [-0.204645  0.07399  -0.207171 -0.166628 -0.015243[...]\n", + "labels: 112\n", + "year_SOFT_QUANTILE: -0.16666666\n", + "\n", + "\n", + "\n", + "author_0->paper_1\n", + "\n", + "\n", + "writes\n", + "\n", + "\n", + "\n", + "author_1\n", + "\n", + "author_1\n", + "\n", + "\n", + "\n", + "paper_4\n", + "\n", + "paper_4\n", + "#split_INDEX: 0\n", + "feat: [-0.12625   0.057425 -0.188602 -0.235411  0.047472[...]\n", + "labels: 260\n", + "year_SOFT_QUANTILE: 0.16666669\n", + "\n", + "\n", + "\n", + "author_1->paper_4\n", + "\n", + "\n", + "writes\n", + "\n", + "\n", + "\n", + "author_2\n", + "\n", + "author_2\n", + "\n", + "\n", + "\n", + "author_2->paper_4\n", + "\n", + "\n", + "writes\n", + "\n", + "\n", + "\n", + "author_3\n", + "\n", + "author_3\n", + "\n", + "\n", + "\n", + "paper_2\n", + "\n", + "paper_2\n", + "#split_INDEX: 2\n", + "feat: [-7.75540e-02  1.79050e-01 -1.97274e-01 -1.78872e-[...]\n", + "labels: 33\n", + "year_SOFT_QUANTILE: 0.5\n", + "\n", + "\n", + "\n", + "author_3->paper_2\n", + "\n", + "\n", + "writes\n", + "\n", + "\n", + "\n", + "author_4\n", + "\n", + "author_4\n", + "\n", + "\n", + "\n", + "author_4->paper_2\n", + "\n", + "\n", + "writes\n", + "\n", + "\n", + "\n", + "author_5\n", + "\n", + "author_5\n", + "\n", + "\n", + "\n", + "paper_11\n", + "\n", + "paper_11\n", + "#split_INDEX: 1\n", + "feat: [-0.10656   0.055726 -0.257359 -0.190179  0.046999[...]\n", + "labels: 112\n", + "year_SOFT_QUANTILE: 0.3888889\n", + "\n", + "\n", + "\n", + "author_5->paper_11\n", + "\n", + "\n", + "writes\n", + "\n", + "\n", + "\n", + "author_6\n", + "\n", + "author_6\n", + "\n", + "\n", + "\n", + "institution_0\n", + "\n", + "institution_0\n", + "\n", + "\n", + "\n", + "author_6->institution_0\n", + "\n", + "\n", + "affiliated_with\n", + "\n", + "\n", + "\n", + "paper_0\n", + "\n", + "paper_0\n", + "#split_INDEX: 0\n", + "feat: [-0.00678   0.204157 -0.244393 -0.188915  0.01711 [...]\n", + "labels: 260\n", + "year_SOFT_QUANTILE: 0.16666669\n", + "\n", + "\n", + "\n", + "author_6->paper_0\n", + "\n", + "\n", + "writes\n", + "\n", + "\n", + "\n", + "author_6->paper_4\n", + "\n", + "\n", + "writes\n", + "\n", + "\n", + "\n", + "author_6->paper_11\n", + "\n", + "\n", + "writes\n", + "\n", + "\n", + "\n", + "paper_20\n", + "\n", + "paper_20\n", + "#split_INDEX: 0\n", + "feat: [ 0.044045  0.215125 -0.211774 -0.058018  0.02545 [...]\n", + "labels: 15\n", + "year_SOFT_QUANTILE: -0.3888889\n", + "\n", + "\n", + "\n", + "author_6->paper_20\n", + "\n", + "\n", + "writes\n", + "\n", + "\n", + "\n", + "author_7\n", + "\n", + "author_7\n", + "\n", + "\n", + "\n", + "author_7->institution_0\n", + "\n", + "\n", + "affiliated_with\n", + "\n", + "\n", + "\n", + "author_7->paper_0\n", + "\n", + "\n", + "writes\n", + "\n", + "\n", + "\n", + "paper_21\n", + "\n", + "paper_21\n", + "#split_INDEX: 1\n", + "feat: [-0.0565   -0.02678  -0.130941 -0.11861   0.055497[...]\n", + "labels: 112\n", + "year_SOFT_QUANTILE: 0.3888889\n", + "\n", + "\n", + "\n", + "author_7->paper_21\n", + "\n", + "\n", + "writes\n", + "\n", + "\n", + "\n", + "field_of_study_0\n", + "\n", + "field_of_study_0\n", + "\n", + "\n", + "\n", + "field_of_study_1\n", + "\n", + "field_of_study_1\n", + "\n", + "\n", + "\n", + "field_of_study_2\n", + "\n", + "field_of_study_2\n", + "\n", + "\n", + "\n", + "field_of_study_3\n", + "\n", + "field_of_study_3\n", + "\n", + "\n", + "\n", + "field_of_study_4\n", + "\n", + "field_of_study_4\n", + "\n", + "\n", + "\n", + "field_of_study_5\n", + "\n", + "field_of_study_5\n", + "\n", + "\n", + "\n", + "field_of_study_6\n", + "\n", + "field_of_study_6\n", + "\n", + "\n", + "\n", + "field_of_study_7\n", + "\n", + "field_of_study_7\n", + "\n", + "\n", + "\n", + "paper_0->field_of_study_3\n", + "\n", + "\n", + "has_topic\n", + "\n", + "\n", + "\n", + "paper_0->field_of_study_7\n", + "\n", + "\n", + "has_topic\n", + "\n", + "\n", + "\n", + "paper_0->paper_1\n", + "\n", + "\n", + "cites\n", + "\n", + "\n", + "\n", + "paper_0->paper_4\n", + "\n", + "\n", + "cites\n", + "\n", + "\n", + "\n", + "paper_1->field_of_study_0\n", + "\n", + "\n", + "has_topic\n", + "\n", + "\n", + "\n", + "paper_1->field_of_study_1\n", + "\n", + "\n", + "has_topic\n", + "\n", + "\n", + "\n", + "paper_2->field_of_study_4\n", + "\n", + "\n", + "has_topic\n", + "\n", + "\n", + "\n", + "paper_2->field_of_study_5\n", + "\n", + "\n", + "has_topic\n", + "\n", + "\n", + "\n", + "paper_2->paper_0\n", + "\n", + "\n", + "cites\n", + "\n", + "\n", + "\n", + "paper_2->paper_1\n", + "\n", + "\n", + "cites\n", + "\n", + "\n", + "\n", + "paper_9\n", + "\n", + "paper_9\n", + "#split_INDEX: 0\n", + "feat: [-9.70780e-02  2.44316e-01 -1.71944e-01 -1.94052e-[...]\n", + "labels: 260\n", + "year_SOFT_QUANTILE: -0.5\n", + "\n", + "\n", + "\n", + "paper_2->paper_9\n", + "\n", + "\n", + "cites\n", + "\n", + "\n", + "\n", + "paper_10\n", + "\n", + "paper_10\n", + "#split_INDEX: 0\n", + "feat: [ 3.28640e-02  5.27050e-02 -1.84524e-01 -1.45454e-[...]\n", + "labels: 221\n", + "year_SOFT_QUANTILE: 0.055555582\n", + "\n", + "\n", + "\n", + "paper_2->paper_10\n", + "\n", + "\n", + "cites\n", + "\n", + "\n", + "\n", + "paper_3\n", + "\n", + "paper_3\n", + "#split_INDEX: 1\n", + "feat: [-0.041004  0.124486 -0.230827 -0.100904  0.102041[...]\n", + "labels: 33\n", + "year_SOFT_QUANTILE: 0.3888889\n", + "\n", + "\n", + "\n", + "paper_3->paper_1\n", + "\n", + "\n", + "cites\n", + "\n", + "\n", + "\n", + "paper_4->field_of_study_2\n", + "\n", + "\n", + "has_topic\n", + "\n", + "\n", + "\n", + "paper_4->field_of_study_3\n", + "\n", + "\n", + "has_topic\n", + "\n", + "\n", + "\n", + "paper_5\n", + "\n", + "paper_5\n", + "#split_INDEX: 0\n", + "feat: [-0.135257  0.045544 -0.227687 -0.228189  0.069767[...]\n", + "labels: 260\n", + "year_SOFT_QUANTILE: 0.16666669\n", + "\n", + "\n", + "\n", + "paper_4->paper_5\n", + "\n", + "\n", + "cites\n", + "\n", + "\n", + "\n", + "paper_6\n", + "\n", + "paper_6\n", + "#split_INDEX: 0\n", + "feat: [-0.224932  0.156279 -0.205494 -0.232144  0.067358[...]\n", + "labels: 230\n", + "year_SOFT_QUANTILE: -0.2777778\n", + "\n", + "\n", + "\n", + "paper_4->paper_6\n", + "\n", + "\n", + "cites\n", + "\n", + "\n", + "\n", + "paper_7\n", + "\n", + "paper_7\n", + "#split_INDEX: 2\n", + "feat: [-2.800610e-01  1.093970e-01 -1.626060e-01 -1.6916[...]\n", + "labels: 237\n", + "year_SOFT_QUANTILE: 0.5\n", + "\n", + "\n", + "\n", + "paper_7->paper_4\n", + "\n", + "\n", + "cites\n", + "\n", + "\n", + "\n", + "paper_8\n", + "\n", + "paper_8\n", + "#split_INDEX: 1\n", + "feat: [-0.059492  0.092468 -0.251102 -0.100542  0.019542[...]\n", + "labels: 112\n", + "year_SOFT_QUANTILE: 0.3888889\n", + "\n", + "\n", + "\n", + "paper_8->paper_4\n", + "\n", + "\n", + "cites\n", + "\n", + "\n", + "\n", + "paper_11->field_of_study_2\n", + "\n", + "\n", + "has_topic\n", + "\n", + "\n", + "\n", + "paper_11->field_of_study_6\n", + "\n", + "\n", + "has_topic\n", + "\n", + "\n", + "\n", + "paper_11->paper_0\n", + "\n", + "\n", + "cites\n", + "\n", + "\n", + "\n", + "paper_12\n", + "\n", + "paper_12\n", + "#split_INDEX: 1\n", + "feat: [-0.052889  0.103502 -0.243404 -0.055129  0.06588 [...]\n", + "labels: 260\n", + "year_SOFT_QUANTILE: 0.3888889\n", + "\n", + "\n", + "\n", + "paper_11->paper_12\n", + "\n", + "\n", + "cites\n", + "\n", + "\n", + "\n", + "paper_13\n", + "\n", + "paper_13\n", + "#split_INDEX: 0\n", + "feat: [ 3.26480e-02  2.10123e-01 -2.11758e-01 -2.56450e-[...]\n", + "labels: 237\n", + "year_SOFT_QUANTILE: 0.2777778\n", + "\n", + "\n", + "\n", + "paper_11->paper_13\n", + "\n", + "\n", + "cites\n", + "\n", + "\n", + "\n", + "paper_14\n", + "\n", + "paper_14\n", + "#split_INDEX: 1\n", + "feat: [-0.077522  0.071084 -0.253487 -0.105976  0.025771[...]\n", + "labels: 112\n", + "year_SOFT_QUANTILE: 0.3888889\n", + "\n", + "\n", + "\n", + "paper_14->paper_11\n", + "\n", + "\n", + "cites\n", + "\n", + "\n", + "\n", + "paper_15\n", + "\n", + "paper_15\n", + "#split_INDEX: 2\n", + "feat: [-4.30000e-02  7.82140e-02 -2.99002e-01 -8.31200e-[...]\n", + "labels: 237\n", + "year_SOFT_QUANTILE: 0.5\n", + "\n", + "\n", + "\n", + "paper_15->paper_11\n", + "\n", + "\n", + "cites\n", + "\n", + "\n", + "\n", + "paper_16\n", + "\n", + "paper_16\n", + "#split_INDEX: 0\n", + "feat: [ 0.023793  0.172637 -0.011127 -0.044719 -0.0146  [...]\n", + "labels: 205\n", + "year_SOFT_QUANTILE: 0.2777778\n", + "\n", + "\n", + "\n", + "paper_16->field_of_study_3\n", + "\n", + "\n", + "has_topic\n", + "\n", + "\n", + "\n", + "paper_17\n", + "\n", + "paper_17\n", + "#split_INDEX: 0\n", + "feat: [-2.71744e-01 -1.52806e-01 -3.54400e-03 -1.73371e-[...]\n", + "labels: 80\n", + "year_SOFT_QUANTILE: -0.055555552\n", + "\n", + "\n", + "\n", + "paper_17->field_of_study_3\n", + "\n", + "\n", + "has_topic\n", + "\n", + "\n", + "\n", + "paper_18\n", + "\n", + "paper_18\n", + "#split_INDEX: 0\n", + "feat: [-0.036172  0.061367 -0.236012 -0.076947 -0.124253[...]\n", + "labels: 234\n", + "year_SOFT_QUANTILE: -0.16666666\n", + "\n", + "\n", + "\n", + "paper_18->field_of_study_7\n", + "\n", + "\n", + "has_topic\n", + "\n", + "\n", + "\n", + "paper_19\n", + "\n", + "paper_19\n", + "#split_INDEX: 1\n", + "feat: [-0.04277   0.134828 -0.168633 -0.054667  0.046638[...]\n", + "labels: 33\n", + "year_SOFT_QUANTILE: 0.3888889\n", + "\n", + "\n", + "\n", + "paper_19->field_of_study_7\n", + "\n", + "\n", + "has_topic\n", + "\n", + "\n", + "\n" ], - "metadata": { - "id": "Z9tcXkv_P-TZ", - "executionInfo": { - "status": "ok", - "timestamp": 1777986464455, - "user_tz": -120, - "elapsed": 20, - "user": { - "displayName": "", - "userId": "" - } - }, - "outputId": "020efadc-335d-49b7-d6c4-97a33dce5317" - }, - "execution_count": null, - "outputs": [ - { - "output_type": "stream", - "name": "stdout", - "text": [ - "Graph Schema:\n", - "\n", - "Node Sets:\n", - " author:\n", - " (No features)\n", - "\n", - " field_of_study:\n", - " (No features)\n", - "\n", - " institution:\n", - " (No features)\n", - "\n", - " paper:\n", - " | Feature | Format | Semantic | Shape | Num cat. vals |\n", - " |--------------------|------------|-------------|---------|-----------------|\n", - " | #split_INDEX | INTEGER_64 | CATEGORICAL | None | 4 |\n", - " | feat | FLOAT_32 | EMBEDDING | (128,) | None |\n", - " | labels | INTEGER_64 | CATEGORICAL | None | 349 |\n", - " | year_SOFT_QUANTILE | FLOAT_32 | EMBEDDING | None | None |\n", - "\n", - "\n", - "Edge Sets:\n", - " affiliated_with: (Source: author, Target: institution)\n", - " (No features)\n", - "\n", - " cites: (Source: paper, Target: paper)\n", - " (No features)\n", - "\n", - " has_topic: (Source: paper, Target: field_of_study)\n", - " (No features)\n", - "\n", - " writes: (Source: author, Target: paper)\n", - " (No features)\n", - "\n" - ] - } + "text/plain": [ + "" ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "def normalized_sample_generator(num_batches: int = 50):\n", + " for raw_sample in sample_generator(num_batches):\n", + " yield auto_normalizer.normalize_numpy(raw_sample)\n", + "\n", + "\n", + "# Test the generate by plotting a graph sample.\n", + "for sample in normalized_sample_generator():\n", + " break\n", + "dgf.plot.plot_graph(sample, auto_normalizer.output_schema(), features=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "S3u2S8w4QqV-" + }, + "source": [ + "## Distributed computation of feature statistics\n", + "\n", + "If the graph is too large to be loaded into memory, you can compute feature\n", + "statistics with `dgf.beam.analyse.feature_statistics` or\n", + "`dgf.beam.analyse.feature_statistics_from_graphs` instead of\n", + "`dgf.analyse.feature_statistics` or\n", + "`dgf.analyse.feature_statistics_from_graphs`.\n", + "\n", + "See `dgf/examples/feature_statistics_on_graph_samples.py` and\n", + "`dgf/examples/feature_statistics_on_hgraph.py` for full examples." + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-23T18:27:09.932445Z", + "iopub.status.busy": "2026-09-23T18:27:09.932266Z", + "iopub.status.idle": "2026-09-23T18:27:10.299319Z", + "shell.execute_reply": "2026-09-23T18:27:10.298806Z" }, - { - "cell_type": "markdown", - "source": [ - "## Apply normalizer on graph samples\n", - "\n", - "We can now create a generator of **normalized** graph samples:" - ], - "metadata": { - "id": "WKar-IkdQK8K" - } + "executionInfo": { + "elapsed": 369, + "status": "ok", + "timestamp": 1790188030300.6636, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 }, - { - "cell_type": "code", - "source": [ - "def normalized_sample_generator(num_batches: int = 50):\n", - " for raw_sample in sample_generator(num_batches):\n", - " yield auto_normalizer.normalize_numpy(raw_sample)\n", - "\n", - "\n", - "# Test the generate by plotting a graph sample.\n", - "for sample in normalized_sample_generator():\n", - " break\n", - "dgf.plot.plot_graph(sample, auto_normalizer.output_schema(), features=True)" - ], - "metadata": { - "colab": { - "height": 1000 - }, - "id": "n2MZlXNDQWNJ", - "executionInfo": { - "status": "ok", - "timestamp": 1777986602553, - "user_tz": -120, - "elapsed": 89, - "user": { - "displayName": "", - "userId": "" - } - }, - "outputId": "8e5ef776-d36b-4c12-d8f0-518c4135638e" - }, - "execution_count": null, - "outputs": [ - { - "output_type": "execute_result", - "data": { - "image/svg+xml": "\n\n\n\n\n\n\n\n\nauthor_0\n\nauthor_0\n\n\n\npaper_1\n\npaper_1\n#split_INDEX: 0\nfeat: [ 0.008037  0.096423 -0.202972 -0.234594  0.068103[...]\nlabels: 137\nyear_SOFT_QUANTILE: 0.22222222\n\n\n\nauthor_0->paper_1\n\n\nwrites\n\n\n\nauthor_1\n\nauthor_1\n\n\n\nauthor_1->paper_1\n\n\nwrites\n\n\n\nauthor_2\n\nauthor_2\n\n\n\npaper_3\n\npaper_3\n#split_INDEX: 0\nfeat: [-3.93290e-02  3.11770e-02 -1.64459e-01 -7.52610e-[...]\nlabels: 183\nyear_SOFT_QUANTILE: 0.7777778\n\n\n\nauthor_2->paper_3\n\n\nwrites\n\n\n\nauthor_3\n\nauthor_3\n\n\n\nauthor_3->paper_3\n\n\nwrites\n\n\n\nauthor_4\n\nauthor_4\n\n\n\ninstitution_0\n\ninstitution_0\n\n\n\nauthor_4->institution_0\n\n\naffiliated_with\n\n\n\npaper_0\n\npaper_0\n#split_INDEX: 0\nfeat: [-0.121266  0.086321 -0.245127 -0.184046  0.032181[...]\nlabels: 137\nyear_SOFT_QUANTILE: 0.0\n\n\n\nauthor_4->paper_0\n\n\nwrites\n\n\n\nauthor_5\n\nauthor_5\n\n\n\nauthor_5->institution_0\n\n\naffiliated_with\n\n\n\nauthor_5->paper_0\n\n\nwrites\n\n\n\npaper_11\n\npaper_11\n#split_INDEX: 0\nfeat: [ 0.034814 -0.18921  -0.093272 -0.085821  0.060141[...]\nlabels: 33\nyear_SOFT_QUANTILE: 0.44444445\n\n\n\nauthor_5->paper_11\n\n\nwrites\n\n\n\npaper_12\n\npaper_12\n#split_INDEX: 0\nfeat: [ 0.092511  0.160643 -0.134679 -0.106067  0.008377[...]\nlabels: 33\nyear_SOFT_QUANTILE: 0.7777778\n\n\n\nauthor_5->paper_12\n\n\nwrites\n\n\n\nfield_of_study_0\n\nfield_of_study_0\n\n\n\nfield_of_study_1\n\nfield_of_study_1\n\n\n\nfield_of_study_2\n\nfield_of_study_2\n\n\n\nfield_of_study_3\n\nfield_of_study_3\n\n\n\nfield_of_study_4\n\nfield_of_study_4\n\n\n\npaper_0->field_of_study_2\n\n\nhas_topic\n\n\n\npaper_0->field_of_study_4\n\n\nhas_topic\n\n\n\npaper_1->field_of_study_0\n\n\nhas_topic\n\n\n\npaper_1->field_of_study_1\n\n\nhas_topic\n\n\n\npaper_1->paper_0\n\n\ncites\n\n\n\npaper_2\n\npaper_2\n#split_INDEX: 0\nfeat: [-0.04572  -0.012771 -0.126651 -0.123901 -0.010939[...]\nlabels: 74\nyear_SOFT_QUANTILE: 0.5555556\n\n\n\npaper_2->paper_1\n\n\ncites\n\n\n\npaper_3->field_of_study_2\n\n\nhas_topic\n\n\n\npaper_3->field_of_study_3\n\n\nhas_topic\n\n\n\npaper_3->paper_0\n\n\ncites\n\n\n\npaper_4\n\npaper_4\n#split_INDEX: 0\nfeat: [ 0.006619  0.135601 -0.159768 -0.162091  0.144591[...]\nlabels: 221\nyear_SOFT_QUANTILE: 0.44444445\n\n\n\npaper_3->paper_4\n\n\ncites\n\n\n\npaper_5\n\npaper_5\n#split_INDEX: 0\nfeat: [-0.165395  0.214952 -0.273257 -0.096225 -0.008193[...]\nlabels: 152\nyear_SOFT_QUANTILE: 0.7777778\n\n\n\npaper_5->paper_3\n\n\ncites\n\n\n\npaper_6\n\npaper_6\n#split_INDEX: 1\nfeat: [-1.25115e-01  2.38000e-04 -2.00291e-01  1.29890e-[...]\nlabels: 183\nyear_SOFT_QUANTILE: 0.8888889\n\n\n\npaper_6->paper_3\n\n\ncites\n\n\n\npaper_7\n\npaper_7\n#split_INDEX: 0\nfeat: [-2.33680e-02 -7.27000e-02 -2.17372e-01 -5.84750e-[...]\nlabels: 84\nyear_SOFT_QUANTILE: 0.33333334\n\n\n\npaper_7->field_of_study_2\n\n\nhas_topic\n\n\n\npaper_8\n\npaper_8\n#split_INDEX: 0\nfeat: [-0.146798 -0.028645 -0.278634  0.028159  0.130725[...]\nlabels: 92\nyear_SOFT_QUANTILE: 0.5555556\n\n\n\npaper_8->field_of_study_2\n\n\nhas_topic\n\n\n\npaper_9\n\npaper_9\n#split_INDEX: 0\nfeat: [ 0.392276 -0.150901 -0.193575 -0.025835 -0.432227[...]\nlabels: 300\nyear_SOFT_QUANTILE: 0.33333334\n\n\n\npaper_9->field_of_study_4\n\n\nhas_topic\n\n\n\npaper_10\n\npaper_10\n#split_INDEX: 1\nfeat: [ 0.046551 -0.198425 -0.208246 -0.087059 -0.236994[...]\nlabels: 343\nyear_SOFT_QUANTILE: 0.8888889\n\n\n\npaper_10->field_of_study_4\n\n\nhas_topic\n\n\n\n", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "execution_count": 45 - } - ] + "id": "9z1GPIu4Si6p" + }, + "outputs": [], + "source": [ + "from absl import flags\n", + "import apache_beam as beam\n", + "from apache_beam.options import pipeline_options\n", + "from google3.pipeline.flume.py import runner as flume_runner\n", + "\n", + "# For the user: Comment / uncomment on of the block.\n", + "# Note: Defining the \"flume_exec_mode\" in the \"PipelineOptions\" does not work.\n", + "\n", + "# Run the execution in-process. Great for debugging / iteration on small data.\n", + "# ===\n", + "flags.FLAGS.flume_exec_mode = \"IN_PROCESS\"\n", + "options = pipeline_options.PipelineOptions()\n", + "\n", + "# Run the execution on Borg. Great for large data.\n", + "# ===\n", + "# flags.FLAGS.flume_exec_mode = \"BORG\"\n", + "# options = pipeline_options.PipelineOptions(\n", + "# flume_borg_accounting_charged_user_name=\"simple-ml-accounting\",\n", + "# flume_borg_cells=\"is\",\n", + "# flume_use_batch_scheduler=True,\n", + "# flume_batch_scheduler_strategy=\"RUN_SOON\",\n", + "# )" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "CchplnWXS5Ro" + }, + "source": [ + "Save our graph to disk. This will be the input of beam pipeline:" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-23T18:27:10.302017Z", + "iopub.status.busy": "2026-09-23T18:27:10.301332Z", + "iopub.status.idle": "2026-09-23T18:27:12.054018Z", + "shell.execute_reply": "2026-09-23T18:27:12.053559Z" }, - { - "cell_type": "markdown", - "source": [ - "## Distributed computation of feature statistics\n", - "\n", - "If the graph is too large to be loaded into memory, you can compute feature\n", - "statistics with `dgf.beam.analyse.feature_statistics` or\n", - "`dgf.beam.analyse.feature_statistics_from_graphs` instead of\n", - "`dgf.analyse.feature_statistics` or\n", - "`dgf.analyse.feature_statistics_from_graphs`.\n", - "\n", - "See `dgf/examples/feature_statistics_on_graph_samples.py` and\n", - "`dgf/examples/feature_statistics_on_hgraph.py` for full examples." - ], - "metadata": { - "id": "S3u2S8w4QqV-" - } + "executionInfo": { + "elapsed": 1754, + "status": "ok", + "timestamp": 1790188032054.943, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 }, + "id": "eaFISV4VS34M", + "outputId": "eacf11a3-44d8-4898-894a-25510c941e07" + }, + "outputs": [ { - "cell_type": "code", - "source": [ - "from absl import flags\n", - "import apache_beam as beam\n", - "from apache_beam.options import pipeline_options\n", - "from google3.pipeline.flume.py import runner as flume_runner\n", - "\n", - "# For the user: Comment / uncomment on of the block.\n", - "# Note: Defining the \"flume_exec_mode\" in the \"PipelineOptions\" does not work.\n", - "\n", - "# Run the execution in-process. Great for debugging / iteration on small data.\n", - "# ===\n", - "flags.FLAGS.flume_exec_mode = \"IN_PROCESS\"\n", - "options = pipeline_options.PipelineOptions()\n", - "\n", - "# Run the execution on Borg. Great for large data.\n", - "# ===\n", - "# flags.FLAGS.flume_exec_mode = \"BORG\"\n", - "# options = pipeline_options.PipelineOptions(\n", - "# flume_borg_accounting_charged_user_name=\"simple-ml-accounting\",\n", - "# flume_borg_cells=\"is\",\n", - "# flume_use_batch_scheduler=True,\n", - "# flume_batch_scheduler_strategy=\"RUN_SOON\",\n", - "# )" - ], - "metadata": { - "id": "9z1GPIu4Si6p" - }, - "execution_count": null, - "outputs": [] + "name": "stdout", + "output_type": "stream", + "text": [ + "Graph written from memory in 1.75s\n" + ] + } + ], + "source": [ + "graph_path = \"/tmp/my_graph\"\n", + "dgf.io.write_graph(graph, schema, path=graph_path)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "I4mWwtdnS9kW" + }, + "source": [ + "Let's compute the statistics:\n", + "\n", + "**Warning:** This can take several minutes if running locally." + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-23T18:27:12.055833Z", + "iopub.status.busy": "2026-09-23T18:27:12.055669Z", + "iopub.status.idle": "2026-09-23T18:31:43.480197Z", + "shell.execute_reply": "2026-09-23T18:31:43.479642Z" }, - { - "cell_type": "markdown", - "source": [ - "Save our graph to disk. This will be the input of beam pipeline:" - ], - "metadata": { - "id": "CchplnWXS5Ro" - } + "executionInfo": { + "elapsed": 271426, + "status": "ok", + "timestamp": 1790188303481.4236, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 }, + "id": "kHjlvBE2S9Lc" + }, + "outputs": [ { - "cell_type": "code", - "source": [ - "graph_path = \"/tmp/my_graph\"\n", - "dgf.io.write_graph(graph, schema, path=graph_path)" - ], - "metadata": { - "id": "eaFISV4VS34M", - "executionInfo": { - "status": "ok", - "timestamp": 1777987207830, - "user_tz": -120, - "elapsed": 1951, - "user": { - "displayName": "", - "userId": "" - } - }, - "outputId": "eacf11a3-44d8-4898-894a-25510c941e07" - }, - "execution_count": null, - "outputs": [ - { - "output_type": "stream", - "name": "stdout", - "text": [ - "GFGraph written from memory in 1.93 seconds\n" - ] - } + "data": { + "application/javascript": [ + "\n", + " if (typeof window.interactive_beam_jquery == 'undefined') {\n", + " var jqueryScript = document.createElement('script');\n", + " jqueryScript.src = 'https://code.jquery.com/jquery-3.4.1.slim.min.js';\n", + " jqueryScript.type = 'text/javascript';\n", + " jqueryScript.onload = function() {\n", + " var datatableScript = document.createElement('script');\n", + " datatableScript.src = 'https://cdn.datatables.net/1.10.20/js/jquery.dataTables.min.js';\n", + " datatableScript.type = 'text/javascript';\n", + " datatableScript.onload = function() {\n", + " window.interactive_beam_jquery = jQuery.noConflict(true);\n", + " window.interactive_beam_jquery(document).ready(function($){\n", + " \n", + " });\n", + " }\n", + " document.head.appendChild(datatableScript);\n", + " };\n", + " document.head.appendChild(jqueryScript);\n", + " } else {\n", + " window.interactive_beam_jquery(document).ready(function($){\n", + " \n", + " });\n", + " }" ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "with beam.Pipeline(runner=flume_runner.FlumeRunner(), options=options) as root:\n", + " # Read the graph\n", + " graph = dgf.beam.io.read_graph(root, \"/tmp/my_graph\")\n", + "\n", + " # Compute the statistics\n", + " stats = dgf.beam.analyse.feature_statistics(graph)\n", + "\n", + " # Save the statistics to a json file\n", + " dgf.beam.io.write_feature_statistics(stats, \"/tmp/stats.json\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "9jUPxSHPTKDY" + }, + "source": [ + "We can then load and inspect the statistics." + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-23T18:31:43.482331Z", + "iopub.status.busy": "2026-09-23T18:31:43.482145Z", + "iopub.status.idle": "2026-09-23T18:31:43.886582Z", + "shell.execute_reply": "2026-09-23T18:31:43.886098Z" }, - { - "cell_type": "markdown", - "source": [ - "Let's compute the statistics:\n", - "\n", - "**Warning:** This can take several minutes if running locally." - ], - "metadata": { - "id": "I4mWwtdnS9kW" - } - }, - { - "cell_type": "code", - "source": [ - "with beam.Pipeline(runner=flume_runner.FlumeRunner(), options=options) as root:\n", - " # Read the graph\n", - " graph = dgf.beam.io.read_graph(root, \"/tmp/my_graph\")\n", - "\n", - " # Compute the statistics\n", - " stats = dgf.beam.analyse.feature_statistics(graph)\n", - "\n", - " # Save the statistics to a json file\n", - " dgf.beam.io.write_feature_statistics(stats, \"/tmp/stats.json\")" - ], - "metadata": { - "id": "kHjlvBE2S9Lc" - }, - "execution_count": null, - "outputs": [] + "executionInfo": { + "elapsed": 406, + "status": "ok", + "timestamp": 1790188303887.551, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 }, + "id": "0qWyi7EzTMKr", + "outputId": "dbaa2dbd-2612-483f-9787-dccddb1cd256" + }, + "outputs": [ { - "cell_type": "markdown", - "source": [ - "We can then load and inspect the statistics." - ], - "metadata": { - "id": "9jUPxSHPTKDY" - } - }, + "data": { + "text/plain": [ + "GraphFeatureStatistics:\n", + " Node Sets (4):\n", + " 'author':\n", + " '#id': count=1134649, min=nan, max=nan\n", + " 'field_of_study':\n", + " '#id': count=59965, min=nan, max=nan\n", + " 'institution':\n", + " '#id': count=8740, min=nan, max=nan\n", + " 'paper':\n", + " '#id': count=736389, min=nan, max=nan\n", + " '#split': count=736389, min=nan, max=nan, dictionary=(3)['train': 629571, 'valid': 64879, 'test': 41939]\n", + " 'feat': count=736389, min=nan, max=nan\n", + " 'labels': count=736389, min=0.0000, max=348.0000\n", + " 'year': count=736389, min=2010.0000, max=2019.0000, quantiles=(100)[2010.0000, 2010.0000, 2010.0000, ..., 2019.0000, 2019.0000, 2019.0000]" + ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "feature_stats_from_beam = dgf.io.read_feature_statistics(\"/tmp/stats.json\")\n", + "feature_stats_from_beam" + ] + } + ], + "metadata": { + "colab": { + "last_runtime": { + "build_target": "", + "kind": "local" + }, + "provenance": [ { - "cell_type": "code", - "source": [ - "feature_stats_from_beam = dgf.io.read_feature_statistics(\"/tmp/stats.json\")\n", - "feature_stats_from_beam" - ], - "metadata": { - "id": "0qWyi7EzTMKr", - "executionInfo": { - "status": "ok", - "timestamp": 1777987630986, - "user_tz": -120, - "elapsed": 25, - "user": { - "displayName": "", - "userId": "" - } - }, - "outputId": "dbaa2dbd-2612-483f-9787-dccddb1cd256" + "file_id": "1hMr9gK0hhKcCtIs1Pro3_ksOnYcdAL2P", + "timestamp": 1771583153488 + } + ], + "toc_visible": true, + "views": { + "output_only": { + "cells": [ + { + "id": "woDLT7o2leXI" }, - "execution_count": null, - "outputs": [ - { - "output_type": "execute_result", - "data": { - "text/plain": [ - "GraphFeatureStatistics:\n", - " Node Sets (4):\n", - " 'author':\n", - " '#id': count=0, min=nan, max=nan\n", - " 'field_of_study':\n", - " '#id': count=0, min=nan, max=nan\n", - " 'institution':\n", - " '#id': count=0, min=nan, max=nan\n", - " 'paper':\n", - " '#id': count=0, min=nan, max=nan\n", - " '#split': count=736389, min=nan, max=nan, dictionary=(3)['train': 629571, 'valid': 64879, 'test': 41939]\n", - " 'feat': count=736389, min=nan, max=nan\n", - " 'labels': count=736389, min=0.0000, max=348.0000\n", - " 'year': count=736389, min=2010.0000, max=2019.0000, quantiles=(100)[2010.0000, 2010.0000, 2010.0000, ..., 2019.0000, 2019.0000, 2019.0000]" - ] - }, - "metadata": {}, - "execution_count": 51 - } - ] + { + "id": "oZe5IE5sfhzY" + }, + { + "id": "eoSN7nzKx5hf" + }, + { + "id": "bZAua9oftqgv" + }, + { + "id": "RKXovz93qQAB" + }, + { + "id": "z3Tx-P_MlPuu" + }, + { + "id": "mF_V1Z8wJgB7" + }, + { + "id": "QZ7ipLxDJkDD" + }, + { + "id": "DaHNquKBMddz" + }, + { + "id": "OwNRk_ThJjAd" + }, + { + "id": "V4ZTxkB2JwnY" + }, + { + "id": "fylcFDl9JupX" + }, + { + "id": "y09HW9r7MKkO" + }, + { + "id": "GE2UXUmJMPRo" + }, + { + "id": "ArsJuvt-MpUP" + }, + { + "id": "3tFLpSX9TH_T" + }, + { + "id": "7ihc62ZNNF50" + }, + { + "id": "hHgBqvHbNfG0" + }, + { + "id": "p4q8-u6CN2nT" + }, + { + "id": "tRWQrs0dNIK5" + }, + { + "id": "2415MctXOD0G" + }, + { + "id": "B1cEYHKAOFkL" + }, + { + "id": "3hCaaep-OPDr" + }, + { + "id": "fDRgExHKOY31" + }, + { + "id": "M-D2NXE5OkhU" + }, + { + "id": "T6k_DhGkOtVC" + }, + { + "id": "IAosJsb5O1Nq" + }, + { + "id": "DT--SmbeO8Vf" + }, + { + "id": "c9iPh5uiPIjN" + }, + { + "id": "FqTYbn_bPTOX" + }, + { + "id": "mxekXNdOPdux" + }, + { + "id": "Z9tcXkv_P-TZ" + }, + { + "id": "WKar-IkdQK8K" + }, + { + "id": "n2MZlXNDQWNJ" + }, + { + "id": "S3u2S8w4QqV-" + }, + { + "id": "9z1GPIu4Si6p" + }, + { + "id": "CchplnWXS5Ro" + }, + { + "id": "eaFISV4VS34M" + }, + { + "id": "I4mWwtdnS9kW" + }, + { + "id": "kHjlvBE2S9Lc" + }, + { + "id": "9jUPxSHPTKDY" + }, + { + "id": "0qWyi7EzTMKr" + } + ], + "hide_code": true } - ] + } + }, + "kernelspec": { + "display_name": "Python 3", + "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.14.5" + } + }, + "nbformat": 4, + "nbformat_minor": 0 } diff --git a/doc/docs/tutorial/sampler.ipynb b/doc/docs/tutorial/sampler.ipynb index 7a9c8bf..afecf7f 100644 --- a/doc/docs/tutorial/sampler.ipynb +++ b/doc/docs/tutorial/sampler.ipynb @@ -1,1123 +1,3174 @@ { - "cells": [ - { - "cell_type": "markdown", - "source": [ - "## Sampler\n", - "\n", - "[![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/google/distributed_graph_flow/blob/main/doc/docs/tutorial/sampler.ipynb)\n", - "\n", - "This tutorial shows the different options to generate graph samples.\n", - "\n", - "You'll learn how to use the in-memory sampler and the semi-distributed Beam sampler." - ], - "metadata": { - "id": "52d5d84b" - } - }, - { - "cell_type": "markdown", - "source": [ - "## Installing GF\n", - "\n", - "Make sure your machine has a GPU or TPU, otherwise training is going to take forever.\n", - "If you are using Google Colab, you can get one for free. Just go to Edit > Notebook settings and select your hardware accelerator as TPU or GPU." - ], - "metadata": { - "id": "gaz92u1mWzFu" - } + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "52d5d84b" + }, + "source": [ + "## Sampler\n", + "\n", + "[![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/google/distributed_graph_flow/blob/main/doc/docs/tutorial/sampler.ipynb)\n", + "\n", + "This tutorial shows the different options to generate graph samples.\n", + "\n", + "You'll learn how to use the in-memory sampler and the semi-distributed Beam sampler." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "EsL3MiYR9nD6" + }, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "D1I1ijy08t0v" + }, + "source": [ + "## Installing GF\n", + "\n", + "Make sure your machine has a GPU or TPU, otherwise training is going to take forever.\n", + "If you are using Google Colab, you can get one for free. Just go to Edit > Notebook settings and select your hardware accelerator as TPU or GPU." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-23T18:32:37.856620Z", + "iopub.status.busy": "2026-09-23T18:32:37.856434Z", + "iopub.status.idle": "2026-09-23T18:32:38.101388Z", + "shell.execute_reply": "2026-09-23T18:32:38.100901Z" }, - { - "cell_type": "code", - "source": [ - "# Install DGF (Distributed Graph Flow) and OGB (for the toy dataset).\n", - "!pip install dgf ogb -U" - ], - "metadata": { - "id": "-hURv5kxW4L_" - }, - "execution_count": null, - "outputs": [] + "executionInfo": { + "elapsed": 286, + "status": "ok", + "timestamp": 1790188358142.653, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 }, - { - "cell_type": "markdown", - "source": [ - "## Importing libraries" - ], - "metadata": { - "id": "71e9cf1c" - } + "id": "eASOvqWCLNMQ" + }, + "outputs": [], + "source": [ + "# Install DGF (Distributed Graph Flow) and OGB (for the toy dataset).\n", + "!pip install dgf ogb -U" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "71e9cf1c" + }, + "source": [ + "## Importing libraries" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-23T18:32:38.144052Z", + "iopub.status.busy": "2026-09-23T18:32:38.143850Z", + "iopub.status.idle": "2026-09-23T18:32:42.505988Z", + "shell.execute_reply": "2026-09-23T18:32:42.505456Z" }, - { - "cell_type": "code", - "source": [ - "import os\n", - "import dgf # Import Graph Flow\n", - "import numpy as np\n", - "import copy" - ], - "metadata": { - "id": "f5431c44", - "executionInfo": { - "status": "ok", - "timestamp": 1790065281592, - "user_tz": -120, - "elapsed": 4947, - "user": { - "displayName": "", - "userId": "" - } - } - }, - "execution_count": 1, - "outputs": [] + "executionInfo": { + "elapsed": 4364, + "status": "ok", + "timestamp": 1790188362507.4407, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 }, - { - "cell_type": "markdown", - "source": [ - "## Load some data\n", - "\n", - "Let's start by loading a toy dataset." - ], - "metadata": { - "id": "7eb84356" - } + "id": "f5431c44" + }, + "outputs": [], + "source": [ + "import dgf # Import Graph Flow\n", + "import numpy as np\n", + "import copy" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "7eb84356" + }, + "source": [ + "## Load some data\n", + "\n", + "Let's start by loading a toy dataset." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-23T18:32:42.508824Z", + "iopub.status.busy": "2026-09-23T18:32:42.508200Z", + "iopub.status.idle": "2026-09-23T18:32:42.589533Z", + "shell.execute_reply": "2026-09-23T18:32:42.589133Z" }, - { - "cell_type": "code", - "source": [ - "# Download the Arxiv graph from the OGB repo.\n", - "graph, schema = dgf.io.fetch_ogb_graph(\"arxiv\")" - ], - "metadata": { - "id": "7f9272a6", - "executionInfo": { - "status": "ok", - "timestamp": 1790065281714, - "user_tz": -120, - "elapsed": 39, - "user": { - "displayName": "", - "userId": "" - } - }, - "outputId": "baaae2ff-cab7-4d5e-fea0-e5e56d706161" - }, - "execution_count": 2, - "outputs": [ - { - "output_type": "stream", - "name": "stdout", - "text": [ - "Caching arxiv graph at /tmp/gf_fetch/arxiv.cache\n", - "OGB dependency not available. Downloading graph from CNS.\n" - ] - } - ] + "executionInfo": { + "elapsed": 83, + "status": "ok", + "timestamp": 1790188362590.8496, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 }, + "id": "7f9272a6", + "outputId": "e216c215-39c3-4e80-a711-5d4c78928767" + }, + "outputs": [ { - "cell_type": "code", - "source": [ - "dgf.print.schema(schema)" - ], - "metadata": { - "id": "d0fb3022", - "executionInfo": { - "status": "ok", - "timestamp": 1790065281805, - "user_tz": -120, - "elapsed": 37, - "user": { - "displayName": "", - "userId": "" - } - }, - "outputId": "b4e13ec1-5f98-4a8b-a891-fd25b2ae551a" - }, - "execution_count": 3, - "outputs": [ - { - "output_type": "stream", - "name": "stdout", - "text": [ - "Graph Schema:\n", - "\n", - "Node Sets:\n", - " nodes:\n", - " | Feature | Format | Semantic | Shape | Detail |\n", - " |-----------|------------|-------------|---------|-------------|\n", - " | #id | BYTES | PRIMARY_ID | None | |\n", - " | #split | BYTES | CATEGORICAL | None | |\n", - " | feat | FLOAT_32 | EMBEDDING | (128,) | |\n", - " | labels | INTEGER_64 | CATEGORICAL | None | #num.cat:40 |\n", - " | year | INTEGER_64 | NUMERICAL | None | |\n", - "\n", - "\n", - "Edge Sets:\n", - " edges: (Source: nodes, Target: nodes)\n", - " (No features)\n", - "\n" - ] - } - ] + "name": "stdout", + "output_type": "stream", + "text": [ + "Caching arxiv graph at /tmp/gf_fetch/arxiv.cache\n", + "OGB dependency not available. Downloading graph from CNS.\n" + ] + } + ], + "source": [ + "# Download the Arxiv graph from the OGB repo.\n", + "graph, schema = dgf.io.fetch_ogb_graph(\"arxiv\")" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-23T18:32:42.591860Z", + "iopub.status.busy": "2026-09-23T18:32:42.591647Z", + "iopub.status.idle": "2026-09-23T18:32:42.595939Z", + "shell.execute_reply": "2026-09-23T18:32:42.595387Z" }, - { - "cell_type": "markdown", - "source": [ - "## In-process Sampler\n", - "\n", - "GF has an in-memory graph sampler that implements GraphSAGE and other graph\n", - "sampling algorithms.\n", - "\n", - "Let's initialize a sampler." - ], - "metadata": { - "id": "2896f2d1" - } + "executionInfo": { + "elapsed": 5, + "status": "ok", + "timestamp": 1790188362596.8052, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 }, + "id": "d0fb3022", + "outputId": "c177f5ab-8b16-4c6a-fd61-c9fa5cbfcc7e" + }, + "outputs": [ { - "cell_type": "code", - "source": [ - "# Create a sampler\n", - "sampler_config = dgf.sampling.SimpleSamplingConfig(\n", - " # Start the expansion at the \"nodes\" nodeset.\n", - " # Note: This graph only has one nodeset.\n", - " seed_nodeset=\"nodes\",\n", - " # Maximum distances to consider.\n", - " num_hops=2,\n", - " # How many neighbors we consider at each hop.\n", - " hop_width=2,\n", - " # Follow the edges on both directions.\n", - " reverse=True,\n", - ")\n", - "\n", - "sampler = dgf.sampling.create_sampler(\n", - " graph=graph,\n", - " schema=schema,\n", - " plan=sampler_config,\n", - " num_threads=5,\n", - ")" - ], - "metadata": { - "id": "fba58d46", - "executionInfo": { - "status": "ok", - "timestamp": 1790065282122, - "user_tz": -120, - "elapsed": 270, - "user": { - "displayName": "", - "userId": "" - } - } - }, - "execution_count": 4, - "outputs": [] + "name": "stdout", + "output_type": "stream", + "text": [ + "Graph Schema:\n", + "\n", + "Node Sets:\n", + " nodes:\n", + " | Feature | Format | Semantic | Shape | Detail |\n", + " |-----------|------------|-------------|---------|-------------|\n", + " | #id | BYTES | PRIMARY_ID | None | |\n", + " | #split | BYTES | CATEGORICAL | None | |\n", + " | feat | FLOAT_32 | EMBEDDING | (128,) | |\n", + " | labels | INTEGER_64 | CATEGORICAL | None | #num.cat:40 |\n", + " | year | INTEGER_64 | NUMERICAL | None | |\n", + "\n", + "\n", + "Edge Sets:\n", + " edges: (Source: nodes, Target: nodes)\n", + " (No features)\n", + "\n" + ] + } + ], + "source": [ + "dgf.print.schema(schema)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "2896f2d1" + }, + "source": [ + "## In-process Sampler\n", + "\n", + "GF has an in-memory graph sampler that implements GraphSAGE and other graph\n", + "sampling algorithms.\n", + "\n", + "Let's initialize a sampler." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-23T18:32:42.597618Z", + "iopub.status.busy": "2026-09-23T18:32:42.597405Z", + "iopub.status.idle": "2026-09-23T18:32:42.801351Z", + "shell.execute_reply": "2026-09-23T18:32:42.800746Z" }, - { - "cell_type": "markdown", - "source": [ - "We can now generate and plot a graph sample." - ], - "metadata": { - "id": "4ee7378e" - } + "executionInfo": { + "elapsed": 205, + "status": "ok", + "timestamp": 1790188362802.2468, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 }, - { - "cell_type": "code", - "source": [ - "sample = sampler.sample(seed_node_idxs=0)\n", - "dgf.plot.plot_graph(sample, schema, features=False)" - ], - "metadata": { - "id": "daee962f", - "colab": { - "height": 596 - }, - "executionInfo": { - "status": "ok", - "timestamp": 1790065282226, - "user_tz": -120, - "elapsed": 38, - "user": { - "displayName": "", - "userId": "" - } - }, - "outputId": "e7b30cea-3c0f-4c09-acd9-033eb8a883d0" - }, - "execution_count": 5, - "outputs": [ - { - "output_type": "execute_result", - "data": { - "image/svg+xml": "\n\n\n\n\n\n\n\n\nnodes_0\n\nnodes_0\n\n\n\nnodes_1\n\nnodes_1\n\n\n\nnodes_0->nodes_1\n\n\nedges\n\n\n\nnodes_6\n\nnodes_6\n\n\n\nnodes_0->nodes_6\n\n\nedges\n\n\n\nnodes_2\n\nnodes_2\n\n\n\nnodes_1->nodes_2\n\n\nedges\n\n\n\nnodes_3\n\nnodes_3\n\n\n\nnodes_1->nodes_3\n\n\nedges\n\n\n\nnodes_4\n\nnodes_4\n\n\n\nnodes_4->nodes_1\n\n\nedges\n\n\n\nnodes_5\n\nnodes_5\n\n\n\nnodes_5->nodes_1\n\n\nedges\n\n\n\nnodes_7\n\nnodes_7\n\n\n\nnodes_7->nodes_6\n\n\nedges\n\n\n\nnodes_8\n\nnodes_8\n\n\n\nnodes_8->nodes_6\n\n\nedges\n\n\n\nnodes_9\n\nnodes_9\n\n\n\nnodes_9->nodes_0\n\n\nedges\n\n\n\nnodes_10\n\nnodes_10\n\n\n\nnodes_9->nodes_10\n\n\nedges\n\n\n\nnodes_11\n\nnodes_11\n\n\n\nnodes_11->nodes_9\n\n\nedges\n\n\n\nnodes_12\n\nnodes_12\n\n\n\nnodes_12->nodes_9\n\n\nedges\n\n\n\nnodes_13\n\nnodes_13\n\n\n\nnodes_13->nodes_0\n\n\nedges\n\n\n\nnodes_14\n\nnodes_14\n\n\n\nnodes_13->nodes_14\n\n\nedges\n\n\n\nnodes_15\n\nnodes_15\n\n\n\nnodes_13->nodes_15\n\n\nedges\n\n\n\nnodes_16\n\nnodes_16\n\n\n\nnodes_16->nodes_13\n\n\nedges\n\n\n\nnodes_17\n\nnodes_17\n\n\n\nnodes_17->nodes_13\n\n\nedges\n\n\n\n", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "execution_count": 5 - } - ] + "id": "fba58d46" + }, + "outputs": [], + "source": [ + "# Create a sampler\n", + "sampler_config = dgf.sampling.SimpleSamplingConfig(\n", + " # Start the expansion at the \"nodes\" nodeset.\n", + " # Note: This graph only has one nodeset.\n", + " seed_nodeset=\"nodes\",\n", + " # Maximum distances to consider.\n", + " num_hops=2,\n", + " # How many neighbors we consider at each hop.\n", + " hop_width=2,\n", + " # Follow the edges on both directions.\n", + " reverse=True,\n", + ")\n", + "\n", + "sampler = dgf.sampling.create_sampler(\n", + " graph=graph,\n", + " schema=schema,\n", + " plan=sampler_config,\n", + " num_threads=5,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "4ee7378e" + }, + "source": [ + "We can now generate and plot a graph sample." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "colab": { + "height": 596 }, - { - "cell_type": "markdown", - "source": [ - "**Remarks:**\n", - "\n", - "- `features=False` removes the features from the plot. It is great for\n", - " visualizing the topology of the graph.\n", - "- In a sample, the first node (`nodes_0`) is always the one the sample was\n", - " generated for (specified with `seed_node_idxs=0`, a.k.a. the seed node).\n", - "- Sampling one seed node at a time is not very efficient; in practice, it is\n", - " better to provide multiple seed nodes, e.g., `seed_node_idxs=[0,1,2]`.\n", - "- Since `seed_node_idxs` is an integer, `sample` returns a single sample. If\n", - " `seed_node_idxs` were a list, `sample` would return a list of samples." - ], - "metadata": { - "id": "a98cd4ee" - } + "execution": { + "iopub.execute_input": "2026-09-23T18:32:42.803404Z", + "iopub.status.busy": "2026-09-23T18:32:42.802955Z", + "iopub.status.idle": "2026-09-23T18:32:42.846004Z", + "shell.execute_reply": "2026-09-23T18:32:42.845528Z" }, - { - "cell_type": "markdown", - "source": [ - "While `SimpleSamplingConfig` offers basic options, you can use a `SamplingPlan`\n", - "for more granular control (e.g., managing individual hops).\n", - "\n", - "To see what a SamplingPlan looks like, use the\n", - "`simple_sampling_config_to_sampling_plan` method to convert your simple\n", - "configuration into a full plan." - ], - "metadata": { - "id": "2d0902f0" - } + "executionInfo": { + "elapsed": 44, + "status": "ok", + "timestamp": 1790188362847.2637, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 }, + "id": "daee962f", + "outputId": "b9b56a2a-1070-4cc1-e895-667861003202" + }, + "outputs": [ { - "cell_type": "code", - "source": [ - "sampling_plan = dgf.sampling.simple_sampling_config_to_sampling_plan(\n", - " sampler_config, schema=schema\n", - ")\n", - "sampling_plan" + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "nodes_0\n", + "\n", + "nodes_0\n", + "\n", + "\n", + "\n", + "nodes_1\n", + "\n", + "nodes_1\n", + "\n", + "\n", + "\n", + "nodes_0->nodes_1\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_6\n", + "\n", + "nodes_6\n", + "\n", + "\n", + "\n", + "nodes_0->nodes_6\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_2\n", + "\n", + "nodes_2\n", + "\n", + "\n", + "\n", + "nodes_1->nodes_2\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_3\n", + "\n", + "nodes_3\n", + "\n", + "\n", + "\n", + "nodes_1->nodes_3\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_4\n", + "\n", + "nodes_4\n", + "\n", + "\n", + "\n", + "nodes_4->nodes_1\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_5\n", + "\n", + "nodes_5\n", + "\n", + "\n", + "\n", + "nodes_5->nodes_1\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_7\n", + "\n", + "nodes_7\n", + "\n", + "\n", + "\n", + "nodes_7->nodes_6\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_8\n", + "\n", + "nodes_8\n", + "\n", + "\n", + "\n", + "nodes_8->nodes_6\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_9\n", + "\n", + "nodes_9\n", + "\n", + "\n", + "\n", + "nodes_9->nodes_0\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_10\n", + "\n", + "nodes_10\n", + "\n", + "\n", + "\n", + "nodes_9->nodes_10\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_11\n", + "\n", + "nodes_11\n", + "\n", + "\n", + "\n", + "nodes_9->nodes_11\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_12\n", + "\n", + "nodes_12\n", + "\n", + "\n", + "\n", + "nodes_12->nodes_9\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_13\n", + "\n", + "nodes_13\n", + "\n", + "\n", + "\n", + "nodes_13->nodes_9\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_14\n", + "\n", + "nodes_14\n", + "\n", + "\n", + "\n", + "nodes_14->nodes_0\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_15\n", + "\n", + "nodes_15\n", + "\n", + "\n", + "\n", + "nodes_14->nodes_15\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_16\n", + "\n", + "nodes_16\n", + "\n", + "\n", + "\n", + "nodes_14->nodes_16\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_17\n", + "\n", + "nodes_17\n", + "\n", + "\n", + "\n", + "nodes_17->nodes_14\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_18\n", + "\n", + "nodes_18\n", + "\n", + "\n", + "\n", + "nodes_18->nodes_14\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n" ], - "metadata": { - "id": "5178bcff", - "executionInfo": { - "status": "ok", - "timestamp": 1790065282444, - "user_tz": -120, - "elapsed": 20, - "user": { - "displayName": "", - "userId": "" - } - }, - "outputId": "c2e276d0-68f2-4b22-ba79-206dbfa03bb3" - }, - "execution_count": 6, - "outputs": [ - { - "output_type": "execute_result", - "data": { - "text/plain": [ - "SamplingPlan(root=PlanNode(nodeset='nodes', children=[PlanEdge(edgeset='edges', reversed=False, node=PlanNode(nodeset='nodes', children=[PlanEdge(edgeset='edges', reversed=False, node=PlanNode(nodeset='nodes', children=[]), hop_width=2), PlanEdge(edgeset='edges', reversed=True, node=PlanNode(nodeset='nodes', children=[]), hop_width=2)]), hop_width=2), PlanEdge(edgeset='edges', reversed=True, node=PlanNode(nodeset='nodes', children=[PlanEdge(edgeset='edges', reversed=False, node=PlanNode(nodeset='nodes', children=[]), hop_width=2), PlanEdge(edgeset='edges', reversed=True, node=PlanNode(nodeset='nodes', children=[]), hop_width=2)]), hop_width=2)]), with_replacement=False, temporal_sampling=False, multi_visit=True, max_timeseries_len=32, propagate_timestamp_to_edges=True)" - ] - }, - "metadata": {}, - "execution_count": 6 - } + "text/plain": [ + "" ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sample = sampler.sample(seed_node_idxs=0)\n", + "dgf.plot.plot_graph(sample, schema, features=False)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "a98cd4ee" + }, + "source": [ + "**Remarks:**\n", + "\n", + "- `features=False` removes the features from the plot. It is great for\n", + " visualizing the topology of the graph.\n", + "- In a sample, the first node (`nodes_0`) is always the one the sample was\n", + " generated for (specified with `seed_node_idxs=0`, a.k.a. the seed node).\n", + "- Sampling one seed node at a time is not very efficient; in practice, it is\n", + " better to provide multiple seed nodes, e.g., `seed_node_idxs=[0,1,2]`.\n", + "- Since `seed_node_idxs` is an integer, `sample` returns a single sample. If\n", + " `seed_node_idxs` were a list, `sample` would return a list of samples." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "2d0902f0" + }, + "source": [ + "While `SimpleSamplingConfig` offers basic options, you can use a `SamplingPlan`\n", + "for more granular control (e.g., managing individual hops).\n", + "\n", + "To see what a SamplingPlan looks like, use the\n", + "`simple_sampling_config_to_sampling_plan` method to convert your simple\n", + "configuration into a full plan." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-23T18:32:42.848313Z", + "iopub.status.busy": "2026-09-23T18:32:42.848026Z", + "iopub.status.idle": "2026-09-23T18:32:42.851406Z", + "shell.execute_reply": "2026-09-23T18:32:42.851045Z" + }, + "executionInfo": { + "elapsed": 5, + "status": "ok", + "timestamp": 1790188362852.3203, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 }, + "id": "5178bcff", + "outputId": "2c017bbd-2d89-4d09-f109-aa0de4d96bb2" + }, + "outputs": [ { - "cell_type": "code", - "source": [ - "dgf.print.sampling_plan(sampling_plan)" - ], - "metadata": { - "id": "1f9373f2", - "executionInfo": { - "status": "ok", - "timestamp": 1790065282547, - "user_tz": -120, - "elapsed": 27, - "user": { - "displayName": "", - "userId": "" - } - }, - "outputId": "b8ea2fad-fb67-4b84-b9aa-e69704a2024f" - }, - "execution_count": 7, - "outputs": [ - { - "output_type": "stream", - "name": "stdout", - "text": [ - "Sampling Plan:\n", - "\n", - "Root: nodes\n", - "├── edges [width=2] ➔ nodes\n", - "│ ├── edges [width=2] ➔ nodes\n", - "│ └── edges (reversed) [width=2] ➔ nodes\n", - "└── edges (reversed) [width=2] ➔ nodes\n", - " ├── edges [width=2] ➔ nodes\n", - " └── edges (reversed) [width=2] ➔ nodes\n" - ] - } + "data": { + "text/plain": [ + "SamplingPlan(root=PlanNode(nodeset='nodes', children=[PlanEdge(edgeset='edges', reversed=False, node=PlanNode(nodeset='nodes', children=[PlanEdge(edgeset='edges', reversed=False, node=PlanNode(nodeset='nodes', children=[]), hop_width=2), PlanEdge(edgeset='edges', reversed=True, node=PlanNode(nodeset='nodes', children=[]), hop_width=2)]), hop_width=2), PlanEdge(edgeset='edges', reversed=True, node=PlanNode(nodeset='nodes', children=[PlanEdge(edgeset='edges', reversed=False, node=PlanNode(nodeset='nodes', children=[]), hop_width=2), PlanEdge(edgeset='edges', reversed=True, node=PlanNode(nodeset='nodes', children=[]), hop_width=2)]), hop_width=2)]), with_replacement=False, temporal_sampling=False, multi_visit=True, max_timeseries_len=32, propagate_timestamp_to_edges=True)" ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sampling_plan = dgf.sampling.simple_sampling_config_to_sampling_plan(\n", + " sampler_config, schema=schema\n", + ")\n", + "sampling_plan" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-23T18:32:42.853147Z", + "iopub.status.busy": "2026-09-23T18:32:42.852869Z", + "iopub.status.idle": "2026-09-23T18:32:42.855544Z", + "shell.execute_reply": "2026-09-23T18:32:42.855087Z" }, - { - "cell_type": "markdown", - "source": [ - "To be more efficient, let's sample multiple graphs at the same time:" - ], - "metadata": { - "id": "07fe45e1" - } + "executionInfo": { + "elapsed": 4, + "status": "ok", + "timestamp": 1790188362856.455, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 }, + "id": "1f9373f2", + "outputId": "77a7549e-8b9c-4bc2-ae41-b7c140f38b3b" + }, + "outputs": [ { - "cell_type": "code", - "source": [ - "samples = sampler.sample(seed_node_idxs=[0, 1])\n", - "print(\n", - " \"Number of nodes in the first graph:\",\n", - " samples[0].node_sets[\"nodes\"].num_nodes,\n", - ")\n", - "print(\n", - " \"Number of nodes in the second graph:\",\n", - " samples[1].node_sets[\"nodes\"].num_nodes,\n", - ")" - ], - "metadata": { - "id": "8cea9faa", - "executionInfo": { - "status": "ok", - "timestamp": 1790065282641, - "user_tz": -120, - "elapsed": 14, - "user": { - "displayName": "", - "userId": "" - } - }, - "outputId": "c3c44385-bed6-4dbe-a4d6-9f2925505bd4" - }, - "execution_count": 8, - "outputs": [ - { - "output_type": "stream", - "name": "stdout", - "text": [ - "Number of nodes in the first graph: 13\n", - "Number of nodes in the second graph: 5\n" - ] - } - ] + "name": "stdout", + "output_type": "stream", + "text": [ + "Sampling Plan:\n", + "\n", + "Root: nodes\n", + "├── edges [width=2] ➔ nodes\n", + "│ ├── edges [width=2] ➔ nodes\n", + "│ └── edges (reversed) [width=2] ➔ nodes\n", + "└── edges (reversed) [width=2] ➔ nodes\n", + " ├── edges [width=2] ➔ nodes\n", + " └── edges (reversed) [width=2] ➔ nodes\n" + ] + } + ], + "source": [ + "dgf.print.sampling_plan(sampling_plan)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "07fe45e1" + }, + "source": [ + "To be more efficient, let's sample multiple graphs at the same time:" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-23T18:32:42.857234Z", + "iopub.status.busy": "2026-09-23T18:32:42.857006Z", + "iopub.status.idle": "2026-09-23T18:32:42.864538Z", + "shell.execute_reply": "2026-09-23T18:32:42.864122Z" }, - { - "cell_type": "markdown", - "source": [ - "Many GF functions use iterators / generators. Let's wrap our sampler into a\n", - "generator and show some of those functions:" - ], - "metadata": { - "id": "d67499e6" - } + "executionInfo": { + "elapsed": 8, + "status": "ok", + "timestamp": 1790188362865.455, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 }, + "id": "8cea9faa", + "outputId": "e2d60567-92ac-4c42-e04f-519e0481e04d" + }, + "outputs": [ { - "cell_type": "code", - "source": [ - "# Create a generator of graph samples.\n", - "def sample_generator(num_batches: int = 10, batch_size: int = 4):\n", - " num_nodes = graph.node_sets[\"nodes\"].num_nodes\n", - " for _ in range(num_batches):\n", - " seed_node_idxs = np.random.choice(num_nodes, size=batch_size, replace=False)\n", - " samples = sampler.sample(seed_node_idxs)\n", - " for sample in samples:\n", - " yield sample\n", - "\n", - "\n", - "# Test the generator.\n", - "for sample in sample_generator():\n", - " print(\".\", end=\"\")\n", - "print(\"Done generating\")" - ], - "metadata": { - "id": "3a98d6ce", - "executionInfo": { - "status": "ok", - "timestamp": 1790065283105, - "user_tz": -120, - "elapsed": 136, - "user": { - "displayName": "", - "userId": "" - } - }, - "outputId": "e9aacf03-ca0c-4699-8b5c-0084d4d94c41" - }, - "execution_count": 9, - "outputs": [ - { - "output_type": "stream", - "name": "stdout", - "text": [ - "........................................Done generating\n" - ] - } - ] + "name": "stdout", + "output_type": "stream", + "text": [ + "Number of nodes in the first graph: 15\n", + "Number of nodes in the second graph: 6\n" + ] + } + ], + "source": [ + "samples = sampler.sample(seed_node_idxs=[0, 1])\n", + "print(\n", + " \"Number of nodes in the first graph:\",\n", + " samples[0].node_sets[\"nodes\"].num_nodes,\n", + ")\n", + "print(\n", + " \"Number of nodes in the second graph:\",\n", + " samples[1].node_sets[\"nodes\"].num_nodes,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "d67499e6" + }, + "source": [ + "Many GF functions use iterators / generators. Let's wrap our sampler into a\n", + "generator and show some of those functions:" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-23T18:32:42.866345Z", + "iopub.status.busy": "2026-09-23T18:32:42.866157Z", + "iopub.status.idle": "2026-09-23T18:32:42.902122Z", + "shell.execute_reply": "2026-09-23T18:32:42.901663Z" }, - { - "cell_type": "markdown", - "source": [ - "For example, the `dgf.io.write_tfgnn_graphs` function takes a graph generator,\n", - "and saves the values to a TF-GNN Graph record." - ], - "metadata": { - "id": "31410d56" - } + "executionInfo": { + "elapsed": 38, + "status": "ok", + "timestamp": 1790188362903.595, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 }, + "id": "3a98d6ce", + "outputId": "da141109-12d0-4d1b-d91b-4ba1eabf0946" + }, + "outputs": [ { - "cell_type": "code", - "source": [ - "# Write the graph samples to disk.\n", - "dgf.io.write_tfgnn_graphs(\n", - " graphs=sample_generator(),\n", - " schema=schema,\n", - " path=\"/tmp/samples_graph.tfrecord.gz\",\n", - ")" - ], - "metadata": { - "id": "3efc55a0", - "executionInfo": { - "status": "ok", - "timestamp": 1790065284883, - "user_tz": -120, - "elapsed": 288, - "user": { - "displayName": "", - "userId": "" - } - } - }, - "execution_count": 10, - "outputs": [] + "name": "stdout", + "output_type": "stream", + "text": [ + "........................................Done generating\n" + ] + } + ], + "source": [ + "# Create a generator of graph samples.\n", + "def sample_generator(num_batches: int = 10, batch_size: int = 4):\n", + " num_nodes = graph.node_sets[\"nodes\"].num_nodes\n", + " for _ in range(num_batches):\n", + " seed_node_idxs = np.random.choice(num_nodes, size=batch_size, replace=False)\n", + " samples = sampler.sample(seed_node_idxs)\n", + " for sample in samples:\n", + " yield sample\n", + "\n", + "\n", + "# Test the generator.\n", + "for sample in sample_generator():\n", + " print(\".\", end=\"\")\n", + "print(\"Done generating\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "31410d56" + }, + "source": [ + "For example, the `dgf.io.write_tfgnn_graphs` function takes a graph generator,\n", + "and saves the values to a TF-GNN Graph record." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-23T18:32:42.904429Z", + "iopub.status.busy": "2026-09-23T18:32:42.904255Z", + "iopub.status.idle": "2026-09-23T18:32:43.223100Z", + "shell.execute_reply": "2026-09-23T18:32:43.222625Z" }, - { - "cell_type": "markdown", - "source": [ - "When learning GNNs, you will likely batch multiple graphs together. The `merge`\n", - "method takes a list of graphs, and returns a single merged graph." - ], - "metadata": { - "id": "c93aafb9" - } + "executionInfo": { + "elapsed": 320, + "status": "ok", + "timestamp": 1790188363224.457, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 }, - { - "cell_type": "code", - "source": [ - "samples = sampler.sample(seed_node_idxs=[0, 1])\n", - "merged_graph, offset = dgf.transform.GraphMerger(schema=schema, padding=None)(samples)\n", - "dgf.plot.plot_graph(merged_graph, schema, features=False)" - ], - "metadata": { - "id": "e34b4b65", - "colab": { - "height": 759 - }, - "executionInfo": { - "status": "ok", - "timestamp": 1790065299523, - "user_tz": -120, - "elapsed": 59, - "user": { - "displayName": "", - "userId": "" - } - }, - "outputId": "380680f6-f492-4ea6-cc79-17ce68de9efa" - }, - "execution_count": 12, - "outputs": [ - { - "output_type": "execute_result", - "data": { - "image/svg+xml": "\n\n\n\n\n\n\n\n\nnodes_0\n\nnodes_0\n\n\n\nnodes_1\n\nnodes_1\n\n\n\nnodes_0->nodes_1\n\n\nedges\n\n\n\nnodes_6\n\nnodes_6\n\n\n\nnodes_0->nodes_6\n\n\nedges\n\n\n\nnodes_2\n\nnodes_2\n\n\n\nnodes_1->nodes_2\n\n\nedges\n\n\n\nnodes_3\n\nnodes_3\n\n\n\nnodes_1->nodes_3\n\n\nedges\n\n\n\nnodes_4\n\nnodes_4\n\n\n\nnodes_4->nodes_1\n\n\nedges\n\n\n\nnodes_5\n\nnodes_5\n\n\n\nnodes_5->nodes_1\n\n\nedges\n\n\n\nnodes_7\n\nnodes_7\n\n\n\nnodes_7->nodes_6\n\n\nedges\n\n\n\nnodes_8\n\nnodes_8\n\n\n\nnodes_8->nodes_6\n\n\nedges\n\n\n\nnodes_9\n\nnodes_9\n\n\n\nnodes_9->nodes_0\n\n\nedges\n\n\n\nnodes_10\n\nnodes_10\n\n\n\nnodes_9->nodes_10\n\n\nedges\n\n\n\nnodes_11\n\nnodes_11\n\n\n\nnodes_9->nodes_11\n\n\nedges\n\n\n\nnodes_12\n\nnodes_12\n\n\n\nnodes_12->nodes_9\n\n\nedges\n\n\n\nnodes_13\n\nnodes_13\n\n\n\nnodes_13->nodes_9\n\n\nedges\n\n\n\nnodes_14\n\nnodes_14\n\n\n\nnodes_14->nodes_0\n\n\nedges\n\n\n\nnodes_15\n\nnodes_15\n\n\n\nnodes_14->nodes_15\n\n\nedges\n\n\n\nnodes_16\n\nnodes_16\n\n\n\nnodes_16->nodes_14\n\n\nedges\n\n\n\nnodes_17\n\nnodes_17\n\n\n\nnodes_17->nodes_14\n\n\nedges\n\n\n\nnodes_18\n\nnodes_18\n\n\n\nnodes_19\n\nnodes_19\n\n\n\nnodes_18->nodes_19\n\n\nedges\n\n\n\nnodes_20\n\nnodes_20\n\n\n\nnodes_19->nodes_20\n\n\nedges\n\n\n\nnodes_21\n\nnodes_21\n\n\n\nnodes_21->nodes_19\n\n\nedges\n\n\n\nnodes_22\n\nnodes_22\n\n\n\nnodes_22->nodes_19\n\n\nedges\n\n\n\nnodes_23\n\nnodes_23\n\n\n\nnodes_23->nodes_18\n\n\nedges\n\n\n\nnodes_23->nodes_19\n\n\nedges\n\n\n\n", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "execution_count": 12 - } - ] + "id": "3efc55a0" + }, + "outputs": [], + "source": [ + "# Write the graph samples to disk.\n", + "dgf.io.write_tfgnn_graphs(\n", + " graphs=sample_generator(),\n", + " schema=schema,\n", + " path=\"/tmp/samples_graph.tfrecord.gz\",\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "c93aafb9" + }, + "source": [ + "When learning GNNs, you will likely batch multiple graphs together. The `merge`\n", + "method takes a list of graphs, and returns a single merged graph." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": { + "colab": { + "height": 852 }, - { - "cell_type": "markdown", - "source": [ - "The indices of the original graph nodes in the merged graph are available with `offset`:" - ], - "metadata": { - "id": "O5j_22f5HJYn" - } + "execution": { + "iopub.execute_input": "2026-09-23T18:32:43.225613Z", + "iopub.status.busy": "2026-09-23T18:32:43.225126Z", + "iopub.status.idle": "2026-09-23T18:32:43.266862Z", + "shell.execute_reply": "2026-09-23T18:32:43.266297Z" }, - { - "cell_type": "code", - "source": [ - "offset" - ], - "metadata": { - "id": "LGu-U8SkHTMV", - "executionInfo": { - "status": "ok", - "timestamp": 1790065302547, - "user_tz": -120, - "elapsed": 23, - "user": { - "displayName": "", - "userId": "" - } - }, - "outputId": "71819742-d7ab-4b2c-b717-201aeb24980e" - }, - "execution_count": 13, - "outputs": [ - { - "output_type": "execute_result", - "data": { - "text/plain": [ - "{'nodes': array([ 0, 18, 24])}" - ] - }, - "metadata": {}, - "execution_count": 13 - } - ] + "executionInfo": { + "elapsed": 43, + "status": "ok", + "timestamp": 1790188363267.97, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 }, + "id": "e34b4b65", + "outputId": "38f50aa2-d177-46d6-f3b6-4cdb4a414307" + }, + "outputs": [ { - "cell_type": "markdown", - "source": [ - "**Remarks:**\n", - "\n", - "- The last value of the `offset` is a sentinel reporting the number of nodes. Call `merge_graph` with `sentinel_offset=False` to remove it.\n" + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "nodes_0\n", + "\n", + "nodes_0\n", + "\n", + "\n", + "\n", + "nodes_1\n", + "\n", + "nodes_1\n", + "\n", + "\n", + "\n", + "nodes_0->nodes_1\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_6\n", + "\n", + "nodes_6\n", + "\n", + "\n", + "\n", + "nodes_0->nodes_6\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_2\n", + "\n", + "nodes_2\n", + "\n", + "\n", + "\n", + "nodes_1->nodes_2\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_3\n", + "\n", + "nodes_3\n", + "\n", + "\n", + "\n", + "nodes_1->nodes_3\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_4\n", + "\n", + "nodes_4\n", + "\n", + "\n", + "\n", + "nodes_4->nodes_1\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_5\n", + "\n", + "nodes_5\n", + "\n", + "\n", + "\n", + "nodes_5->nodes_1\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_7\n", + "\n", + "nodes_7\n", + "\n", + "\n", + "\n", + "nodes_7->nodes_6\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_8\n", + "\n", + "nodes_8\n", + "\n", + "\n", + "\n", + "nodes_8->nodes_6\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_9\n", + "\n", + "nodes_9\n", + "\n", + "\n", + "\n", + "nodes_9->nodes_0\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_10\n", + "\n", + "nodes_10\n", + "\n", + "\n", + "\n", + "nodes_9->nodes_10\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_11\n", + "\n", + "nodes_11\n", + "\n", + "\n", + "\n", + "nodes_9->nodes_11\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_12\n", + "\n", + "nodes_12\n", + "\n", + "\n", + "\n", + "nodes_12->nodes_9\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_13\n", + "\n", + "nodes_13\n", + "\n", + "\n", + "\n", + "nodes_13->nodes_9\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_14\n", + "\n", + "nodes_14\n", + "\n", + "\n", + "\n", + "nodes_14->nodes_0\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_15\n", + "\n", + "nodes_15\n", + "\n", + "\n", + "\n", + "nodes_14->nodes_15\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_16\n", + "\n", + "nodes_16\n", + "\n", + "\n", + "\n", + "nodes_16->nodes_14\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_17\n", + "\n", + "nodes_17\n", + "\n", + "\n", + "\n", + "nodes_17->nodes_14\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_18\n", + "\n", + "nodes_18\n", + "\n", + "\n", + "\n", + "nodes_19\n", + "\n", + "nodes_19\n", + "\n", + "\n", + "\n", + "nodes_18->nodes_19\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_20\n", + "\n", + "nodes_20\n", + "\n", + "\n", + "\n", + "nodes_19->nodes_20\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_21\n", + "\n", + "nodes_21\n", + "\n", + "\n", + "\n", + "nodes_21->nodes_19\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_22\n", + "\n", + "nodes_22\n", + "\n", + "\n", + "\n", + "nodes_22->nodes_19\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_23\n", + "\n", + "nodes_23\n", + "\n", + "\n", + "\n", + "nodes_23->nodes_18\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_23->nodes_19\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n" ], - "metadata": { - "id": "Yxpn64EQb_0R" - } + "text/plain": [ + "" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "samples = sampler.sample(seed_node_idxs=[0, 1])\n", + "merged_graph, offset = dgf.transform.GraphMerger(schema=schema, padding=None)(samples)\n", + "dgf.plot.plot_graph(merged_graph, schema, features=False)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "O5j_22f5HJYn" + }, + "source": [ + "The indices of the original graph nodes in the merged graph are available with `offset`:" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-23T18:32:43.268897Z", + "iopub.status.busy": "2026-09-23T18:32:43.268674Z", + "iopub.status.idle": "2026-09-23T18:32:43.271866Z", + "shell.execute_reply": "2026-09-23T18:32:43.271521Z" }, - { - "cell_type": "markdown", - "source": [ - "## Subgraph extraction\n", - "\n", - "The `sample` method builds graph samples by randomly traversing edges and aggregating all visited edges and nodes. In this section, we will show the `subgraph` method that extracts all the nodes and edges in a certain radius.\n", - "\n", - "By default, `subgraph` returns a single subgraph containing all the nodes and edges at a distance less than or equal to all the seed nodes.\n" - ], - "metadata": { - "id": "OrG4j5_OZ4lB" - } + "executionInfo": { + "elapsed": 4, + "status": "ok", + "timestamp": 1790188363272.7253, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 }, + "id": "LGu-U8SkHTMV" + }, + "outputs": [ { - "cell_type": "code", - "source": [ - "sample = sampler.subgraph(seed_node_idxs=[0, 150780, 93487])\n", - "dgf.plot.plot_graph(sample, schema, features=False)" - ], - "metadata": { - "colab": { - "height": 1000 - }, - "id": "grTlK4Lnbvoq", - "executionInfo": { - "status": "ok", - "timestamp": 1790065303516, - "user_tz": -120, - "elapsed": 68, - "user": { - "displayName": "", - "userId": "" - } - }, - "outputId": "17d3526a-1b78-4571-ea21-7f83b8ab8095" - }, - "execution_count": 14, - "outputs": [ - { - "output_type": "execute_result", - "data": { - "image/svg+xml": "\n\n\n\n\n\n\n\n\nnodes_0\n\nnodes_0\n\n\n\nnodes_2\n\nnodes_2\n\n\n\nnodes_0->nodes_2\n\n\nedges\n\n\n\nnodes_3\n\nnodes_3\n\n\n\nnodes_0->nodes_3\n\n\nedges\n\n\n\nnodes_1\n\nnodes_1\n\n\n\nnodes_16\n\nnodes_16\n\n\n\nnodes_1->nodes_16\n\n\nedges\n\n\n\nnodes_21\n\nnodes_21\n\n\n\nnodes_1->nodes_21\n\n\nedges\n\n\n\nnodes_4\n\nnodes_4\n\n\n\nnodes_3->nodes_4\n\n\nedges\n\n\n\nnodes_5\n\nnodes_5\n\n\n\nnodes_3->nodes_5\n\n\nedges\n\n\n\nnodes_6\n\nnodes_6\n\n\n\nnodes_6->nodes_3\n\n\nedges\n\n\n\nnodes_7\n\nnodes_7\n\n\n\nnodes_7->nodes_2\n\n\nedges\n\n\n\nnodes_8\n\nnodes_8\n\n\n\nnodes_8->nodes_2\n\n\nedges\n\n\n\nnodes_9\n\nnodes_9\n\n\n\nnodes_9->nodes_0\n\n\nedges\n\n\n\nnodes_10\n\nnodes_10\n\n\n\nnodes_9->nodes_10\n\n\nedges\n\n\n\nnodes_11\n\nnodes_11\n\n\n\nnodes_11->nodes_9\n\n\nedges\n\n\n\nnodes_12\n\nnodes_12\n\n\n\nnodes_12->nodes_9\n\n\nedges\n\n\n\nnodes_13\n\nnodes_13\n\n\n\nnodes_13->nodes_0\n\n\nedges\n\n\n\nnodes_14\n\nnodes_14\n\n\n\nnodes_13->nodes_14\n\n\nedges\n\n\n\nnodes_15\n\nnodes_15\n\n\n\nnodes_13->nodes_15\n\n\nedges\n\n\n\nnodes_17\n\nnodes_17\n\n\n\nnodes_16->nodes_17\n\n\nedges\n\n\n\nnodes_18\n\nnodes_18\n\n\n\nnodes_16->nodes_18\n\n\nedges\n\n\n\nnodes_19\n\nnodes_19\n\n\n\nnodes_19->nodes_16\n\n\nedges\n\n\n\nnodes_20\n\nnodes_20\n\n\n\nnodes_20->nodes_16\n\n\nedges\n\n\n\nnodes_21->nodes_2\n\n\nedges\n\n\n\nnodes_22\n\nnodes_22\n\n\n\nnodes_21->nodes_22\n\n\nedges\n\n\n\nnodes_23\n\nnodes_23\n\n\n\nnodes_23->nodes_21\n\n\nedges\n\n\n\nnodes_24\n\nnodes_24\n\n\n\nnodes_24->nodes_21\n\n\nedges\n\n\n\nnodes_25\n\nnodes_25\n\n\n\nnodes_25->nodes_1\n\n\nedges\n\n\n\nnodes_26\n\nnodes_26\n\n\n\nnodes_25->nodes_26\n\n\nedges\n\n\n\nnodes_27\n\nnodes_27\n\n\n\nnodes_25->nodes_27\n\n\nedges\n\n\n\nnodes_28\n\nnodes_28\n\n\n\nnodes_28->nodes_2\n\n\nedges\n\n\n\nnodes_29\n\nnodes_29\n\n\n\nnodes_28->nodes_29\n\n\nedges\n\n\n\nnodes_30\n\nnodes_30\n\n\n\nnodes_28->nodes_30\n\n\nedges\n\n\n\nnodes_31\n\nnodes_31\n\n\n\nnodes_31->nodes_28\n\n\nedges\n\n\n\nnodes_32\n\nnodes_32\n\n\n\nnodes_32->nodes_28\n\n\nedges\n\n\n\nnodes_33\n\nnodes_33\n\n\n\nnodes_33->nodes_2\n\n\nedges\n\n\n\nnodes_34\n\nnodes_34\n\n\n\nnodes_33->nodes_34\n\n\nedges\n\n\n\nnodes_35\n\nnodes_35\n\n\n\nnodes_33->nodes_35\n\n\nedges\n\n\n\n", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "execution_count": 14 - } + "data": { + "text/plain": [ + "{'nodes': array([ 0, 18, 24])}" ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "offset" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Yxpn64EQb_0R" + }, + "source": [ + "**Remarks:**\n", + "\n", + "- The last value of the `offset` is a sentinel reporting the number of nodes. Call `merge_graph` with `sentinel_offset=False` to remove it.\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "OrG4j5_OZ4lB" + }, + "source": [ + "## Subgraph extraction\n", + "\n", + "The `sample` method builds graph samples by randomly traversing edges and aggregating all visited edges and nodes. In this section, we will show the `subgraph` method that extracts all the nodes and edges in a certain radius.\n", + "\n", + "By default, `subgraph` returns a single subgraph containing all the nodes and edges at a distance less than or equal to all the seed nodes.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-23T18:32:43.273623Z", + "iopub.status.busy": "2026-09-23T18:32:43.273293Z", + "iopub.status.idle": "2026-09-23T18:32:43.316332Z", + "shell.execute_reply": "2026-09-23T18:32:43.315862Z" }, - { - "cell_type": "markdown", - "source": [ - "**Remarks**:\n", - "\n", - "- Notice that all the nodes are connected. This is because the selected seed nodes (0, 150780, 93487)are close.\n", - "- By default, `subgraph` runs on a single thread. However, it is fully thread-safe: You can call it in parallel using Python's multi-threading.\n", - "\n", - "`multisubgraph` is related to `subgraph`: Instead of returning a single graph, it returns a separate sub-graph around each seed node:\n" - ], - "metadata": { - "id": "868fTc8JbkzT" - } + "executionInfo": { + "elapsed": 44, + "status": "ok", + "timestamp": 1790188363317.5933, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 }, + "id": "grTlK4Lnbvoq" + }, + "outputs": [ { - "cell_type": "code", - "source": [ - "samples = sampler.multisubgraph(seed_node_idxs=[0, 150780, 93487])\n", - "\n", - "# Grouping the graphs to create a single plot.\n", - "merge_graph, offset = dgf.transform.GraphMerger(schema=schema, padding=None)(samples)\n", - "dgf.plot.plot_graph(merge_graph, schema, features=False)" + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "nodes_0\n", + "\n", + "nodes_0\n", + "\n", + "\n", + "\n", + "nodes_2\n", + "\n", + "nodes_2\n", + "\n", + "\n", + "\n", + "nodes_0->nodes_2\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_3\n", + "\n", + "nodes_3\n", + "\n", + "\n", + "\n", + "nodes_0->nodes_3\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_1\n", + "\n", + "nodes_1\n", + "\n", + "\n", + "\n", + "nodes_17\n", + "\n", + "nodes_17\n", + "\n", + "\n", + "\n", + "nodes_1->nodes_17\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_22\n", + "\n", + "nodes_22\n", + "\n", + "\n", + "\n", + "nodes_1->nodes_22\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_4\n", + "\n", + "nodes_4\n", + "\n", + "\n", + "\n", + "nodes_3->nodes_4\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_5\n", + "\n", + "nodes_5\n", + "\n", + "\n", + "\n", + "nodes_3->nodes_5\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_6\n", + "\n", + "nodes_6\n", + "\n", + "\n", + "\n", + "nodes_6->nodes_3\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_7\n", + "\n", + "nodes_7\n", + "\n", + "\n", + "\n", + "nodes_7->nodes_3\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_8\n", + "\n", + "nodes_8\n", + "\n", + "\n", + "\n", + "nodes_8->nodes_2\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_9\n", + "\n", + "nodes_9\n", + "\n", + "\n", + "\n", + "nodes_9->nodes_2\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_10\n", + "\n", + "nodes_10\n", + "\n", + "\n", + "\n", + "nodes_10->nodes_0\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_11\n", + "\n", + "nodes_11\n", + "\n", + "\n", + "\n", + "nodes_10->nodes_11\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_12\n", + "\n", + "nodes_12\n", + "\n", + "\n", + "\n", + "nodes_12->nodes_0\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_12->nodes_2\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_13\n", + "\n", + "nodes_13\n", + "\n", + "\n", + "\n", + "nodes_12->nodes_13\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_14\n", + "\n", + "nodes_14\n", + "\n", + "\n", + "\n", + "nodes_12->nodes_14\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_15\n", + "\n", + "nodes_15\n", + "\n", + "\n", + "\n", + "nodes_15->nodes_12\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_16\n", + "\n", + "nodes_16\n", + "\n", + "\n", + "\n", + "nodes_16->nodes_12\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_18\n", + "\n", + "nodes_18\n", + "\n", + "\n", + "\n", + "nodes_17->nodes_18\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_19\n", + "\n", + "nodes_19\n", + "\n", + "\n", + "\n", + "nodes_17->nodes_19\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_20\n", + "\n", + "nodes_20\n", + "\n", + "\n", + "\n", + "nodes_20->nodes_17\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_21\n", + "\n", + "nodes_21\n", + "\n", + "\n", + "\n", + "nodes_21->nodes_17\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_23\n", + "\n", + "nodes_23\n", + "\n", + "\n", + "\n", + "nodes_23->nodes_22\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_24\n", + "\n", + "nodes_24\n", + "\n", + "\n", + "\n", + "nodes_24->nodes_22\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_25\n", + "\n", + "nodes_25\n", + "\n", + "\n", + "\n", + "nodes_25->nodes_1\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_26\n", + "\n", + "nodes_26\n", + "\n", + "\n", + "\n", + "nodes_25->nodes_26\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_27\n", + "\n", + "nodes_27\n", + "\n", + "\n", + "\n", + "nodes_25->nodes_27\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_28\n", + "\n", + "nodes_28\n", + "\n", + "\n", + "\n", + "nodes_28->nodes_2\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_29\n", + "\n", + "nodes_29\n", + "\n", + "\n", + "\n", + "nodes_28->nodes_29\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_30\n", + "\n", + "nodes_30\n", + "\n", + "\n", + "\n", + "nodes_30->nodes_28\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_31\n", + "\n", + "nodes_31\n", + "\n", + "\n", + "\n", + "nodes_31->nodes_28\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n" ], - "metadata": { - "colab": { - "height": 1000 - }, - "id": "dK98Ep4Mcnya", - "executionInfo": { - "status": "ok", - "timestamp": 1790065394900, - "user_tz": -120, - "elapsed": 452, - "user": { - "displayName": "", - "userId": "" - } - }, - "outputId": "510ae806-c277-42d0-9e31-512490ffcde6" - }, - "execution_count": 16, - "outputs": [ - { - "output_type": "execute_result", - "data": { - "image/svg+xml": "\n\n\n\n\n\n\n\n\nnodes_0\n\nnodes_0\n\n\n\nnodes_1\n\nnodes_1\n\n\n\nnodes_0->nodes_1\n\n\nedges\n\n\n\nnodes_6\n\nnodes_6\n\n\n\nnodes_0->nodes_6\n\n\nedges\n\n\n\nnodes_2\n\nnodes_2\n\n\n\nnodes_1->nodes_2\n\n\nedges\n\n\n\nnodes_3\n\nnodes_3\n\n\n\nnodes_1->nodes_3\n\n\nedges\n\n\n\nnodes_4\n\nnodes_4\n\n\n\nnodes_4->nodes_1\n\n\nedges\n\n\n\nnodes_5\n\nnodes_5\n\n\n\nnodes_5->nodes_1\n\n\nedges\n\n\n\nnodes_7\n\nnodes_7\n\n\n\nnodes_7->nodes_6\n\n\nedges\n\n\n\nnodes_8\n\nnodes_8\n\n\n\nnodes_8->nodes_6\n\n\nedges\n\n\n\nnodes_9\n\nnodes_9\n\n\n\nnodes_9->nodes_0\n\n\nedges\n\n\n\nnodes_10\n\nnodes_10\n\n\n\nnodes_9->nodes_10\n\n\nedges\n\n\n\nnodes_11\n\nnodes_11\n\n\n\nnodes_9->nodes_11\n\n\nedges\n\n\n\nnodes_12\n\nnodes_12\n\n\n\nnodes_12->nodes_9\n\n\nedges\n\n\n\nnodes_13\n\nnodes_13\n\n\n\nnodes_13->nodes_9\n\n\nedges\n\n\n\nnodes_14\n\nnodes_14\n\n\n\nnodes_14->nodes_0\n\n\nedges\n\n\n\nnodes_15\n\nnodes_15\n\n\n\nnodes_14->nodes_15\n\n\nedges\n\n\n\nnodes_16\n\nnodes_16\n\n\n\nnodes_14->nodes_16\n\n\nedges\n\n\n\nnodes_17\n\nnodes_17\n\n\n\nnodes_17->nodes_14\n\n\nedges\n\n\n\nnodes_18\n\nnodes_18\n\n\n\nnodes_18->nodes_14\n\n\nedges\n\n\n\nnodes_19\n\nnodes_19\n\n\n\nnodes_20\n\nnodes_20\n\n\n\nnodes_19->nodes_20\n\n\nedges\n\n\n\nnodes_25\n\nnodes_25\n\n\n\nnodes_19->nodes_25\n\n\nedges\n\n\n\nnodes_21\n\nnodes_21\n\n\n\nnodes_20->nodes_21\n\n\nedges\n\n\n\nnodes_22\n\nnodes_22\n\n\n\nnodes_20->nodes_22\n\n\nedges\n\n\n\nnodes_23\n\nnodes_23\n\n\n\nnodes_23->nodes_20\n\n\nedges\n\n\n\nnodes_24\n\nnodes_24\n\n\n\nnodes_24->nodes_20\n\n\nedges\n\n\n\nnodes_26\n\nnodes_26\n\n\n\nnodes_25->nodes_26\n\n\nedges\n\n\n\nnodes_27\n\nnodes_27\n\n\n\nnodes_25->nodes_27\n\n\nedges\n\n\n\nnodes_28\n\nnodes_28\n\n\n\nnodes_28->nodes_25\n\n\nedges\n\n\n\nnodes_29\n\nnodes_29\n\n\n\nnodes_29->nodes_25\n\n\nedges\n\n\n\nnodes_30\n\nnodes_30\n\n\n\nnodes_30->nodes_19\n\n\nedges\n\n\n\nnodes_31\n\nnodes_31\n\n\n\nnodes_30->nodes_31\n\n\nedges\n\n\n\nnodes_32\n\nnodes_32\n\n\n\nnodes_30->nodes_32\n\n\nedges\n\n\n\nnodes_33\n\nnodes_33\n\n\n\nnodes_34\n\nnodes_34\n\n\n\nnodes_34->nodes_33\n\n\nedges\n\n\n\nnodes_35\n\nnodes_35\n\n\n\nnodes_34->nodes_35\n\n\nedges\n\n\n\nnodes_36\n\nnodes_36\n\n\n\nnodes_36->nodes_34\n\n\nedges\n\n\n\nnodes_37\n\nnodes_37\n\n\n\nnodes_37->nodes_33\n\n\nedges\n\n\n\nnodes_38\n\nnodes_38\n\n\n\nnodes_37->nodes_38\n\n\nedges\n\n\n\nnodes_39\n\nnodes_39\n\n\n\nnodes_37->nodes_39\n\n\nedges\n\n\n\nnodes_40\n\nnodes_40\n\n\n\nnodes_40->nodes_37\n\n\nedges\n\n\n\nnodes_41\n\nnodes_41\n\n\n\nnodes_41->nodes_37\n\n\nedges\n\n\n\n", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "execution_count": 16 - } + "text/plain": [ + "" ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sample = sampler.subgraph(seed_node_idxs=[0, 150780, 93487])\n", + "dgf.plot.plot_graph(sample, schema, features=False)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "868fTc8JbkzT" + }, + "source": [ + "**Remarks**:\n", + "\n", + "- Notice that all the nodes are connected. This is because the selected seed nodes (0, 150780, 93487)are close.\n", + "- By default, `subgraph` runs on a single thread. However, it is fully thread-safe: You can call it in parallel using Python's multi-threading.\n", + "\n", + "`multisubgraph` is related to `subgraph`: Instead of returning a single graph, it returns a separate sub-graph around each seed node:\n" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": { + "colab": { + "height": 1000 }, - { - "cell_type": "markdown", - "source": [ - "\n", - "**Remark:**\n", - "\n", - "- Notice the 3 independent subgraphs.\n", - "- `multisubgraph` is multi-threaded (one thread per subgraphs)." - ], - "metadata": { - "id": "5c5d4YfWcpar" - } + "execution": { + "iopub.execute_input": "2026-09-23T18:32:43.318562Z", + "iopub.status.busy": "2026-09-23T18:32:43.318280Z", + "iopub.status.idle": "2026-09-23T18:32:43.365244Z", + "shell.execute_reply": "2026-09-23T18:32:43.364671Z" }, - { - "cell_type": "markdown", - "source": [ - "## Temporal masking\n", - "\n", - "Some applications require sampling graphs with temporal masking. This means only\n", - "sampling nodes and edges with a timestamp before a specific value, where the\n", - "limit changes for each sample.\n", - "\n", - "Timestamps should be indicated either on nodes or edges with \"is_creation_time\" equal to true.\n", - "\n", - "**Note:** Edge timestamps are more powerful but less common. Internally, GF converts node timestamps into edge timestamps with the `dgf.transform.propagate_timestamp_to_edges` method.\n", - "\n", - "The following example shows how to create a graph with node timestamps, convert\n", - "them to edge timestamps, and perform temporal sampling.\n", - "\n", - "Let's Arxiv graph contains `year` features on the nodes that we can use as a\n", - "timestamp, and propagate to the edges." - ], - "metadata": { - "id": "bfbc2230" - } + "executionInfo": { + "elapsed": 48, + "status": "ok", + "timestamp": 1790188363366.272, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 }, + "id": "dK98Ep4Mcnya", + "outputId": "a67644aa-b0b5-49d4-d040-59a3263aeca1" + }, + "outputs": [ { - "cell_type": "code", - "source": [ - "time_schema = copy.deepcopy(schema)\n", - "time_schema.node_sets[\"nodes\"].features[\"year\"].is_creation_time = True\n", - "dgf.print.schema(time_schema)" + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "nodes_0\n", + "\n", + "nodes_0\n", + "\n", + "\n", + "\n", + "nodes_1\n", + "\n", + "nodes_1\n", + "\n", + "\n", + "\n", + "nodes_0->nodes_1\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_6\n", + "\n", + "nodes_6\n", + "\n", + "\n", + "\n", + "nodes_0->nodes_6\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_2\n", + "\n", + "nodes_2\n", + "\n", + "\n", + "\n", + "nodes_1->nodes_2\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_3\n", + "\n", + "nodes_3\n", + "\n", + "\n", + "\n", + "nodes_1->nodes_3\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_4\n", + "\n", + "nodes_4\n", + "\n", + "\n", + "\n", + "nodes_4->nodes_1\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_5\n", + "\n", + "nodes_5\n", + "\n", + "\n", + "\n", + "nodes_5->nodes_1\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_7\n", + "\n", + "nodes_7\n", + "\n", + "\n", + "\n", + "nodes_7->nodes_6\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_8\n", + "\n", + "nodes_8\n", + "\n", + "\n", + "\n", + "nodes_8->nodes_6\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_9\n", + "\n", + "nodes_9\n", + "\n", + "\n", + "\n", + "nodes_9->nodes_0\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_10\n", + "\n", + "nodes_10\n", + "\n", + "\n", + "\n", + "nodes_9->nodes_10\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_11\n", + "\n", + "nodes_11\n", + "\n", + "\n", + "\n", + "nodes_9->nodes_11\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_12\n", + "\n", + "nodes_12\n", + "\n", + "\n", + "\n", + "nodes_12->nodes_9\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_13\n", + "\n", + "nodes_13\n", + "\n", + "\n", + "\n", + "nodes_13->nodes_0\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_14\n", + "\n", + "nodes_14\n", + "\n", + "\n", + "\n", + "nodes_13->nodes_14\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_15\n", + "\n", + "nodes_15\n", + "\n", + "\n", + "\n", + "nodes_13->nodes_15\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_16\n", + "\n", + "nodes_16\n", + "\n", + "\n", + "\n", + "nodes_16->nodes_13\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_17\n", + "\n", + "nodes_17\n", + "\n", + "\n", + "\n", + "nodes_17->nodes_13\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_18\n", + "\n", + "nodes_18\n", + "\n", + "\n", + "\n", + "nodes_19\n", + "\n", + "nodes_19\n", + "\n", + "\n", + "\n", + "nodes_18->nodes_19\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_24\n", + "\n", + "nodes_24\n", + "\n", + "\n", + "\n", + "nodes_18->nodes_24\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_20\n", + "\n", + "nodes_20\n", + "\n", + "\n", + "\n", + "nodes_19->nodes_20\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_21\n", + "\n", + "nodes_21\n", + "\n", + "\n", + "\n", + "nodes_19->nodes_21\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_22\n", + "\n", + "nodes_22\n", + "\n", + "\n", + "\n", + "nodes_22->nodes_19\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_23\n", + "\n", + "nodes_23\n", + "\n", + "\n", + "\n", + "nodes_23->nodes_19\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_25\n", + "\n", + "nodes_25\n", + "\n", + "\n", + "\n", + "nodes_24->nodes_25\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_26\n", + "\n", + "nodes_26\n", + "\n", + "\n", + "\n", + "nodes_24->nodes_26\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_27\n", + "\n", + "nodes_27\n", + "\n", + "\n", + "\n", + "nodes_27->nodes_24\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_28\n", + "\n", + "nodes_28\n", + "\n", + "\n", + "\n", + "nodes_28->nodes_24\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_29\n", + "\n", + "nodes_29\n", + "\n", + "\n", + "\n", + "nodes_29->nodes_18\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_30\n", + "\n", + "nodes_30\n", + "\n", + "\n", + "\n", + "nodes_29->nodes_30\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_31\n", + "\n", + "nodes_31\n", + "\n", + "\n", + "\n", + "nodes_29->nodes_31\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_32\n", + "\n", + "nodes_32\n", + "\n", + "\n", + "\n", + "nodes_33\n", + "\n", + "nodes_33\n", + "\n", + "\n", + "\n", + "nodes_33->nodes_32\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_34\n", + "\n", + "nodes_34\n", + "\n", + "\n", + "\n", + "nodes_33->nodes_34\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_35\n", + "\n", + "nodes_35\n", + "\n", + "\n", + "\n", + "nodes_33->nodes_35\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_36\n", + "\n", + "nodes_36\n", + "\n", + "\n", + "\n", + "nodes_36->nodes_33\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_37\n", + "\n", + "nodes_37\n", + "\n", + "\n", + "\n", + "nodes_37->nodes_33\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_38\n", + "\n", + "nodes_38\n", + "\n", + "\n", + "\n", + "nodes_38->nodes_32\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_39\n", + "\n", + "nodes_39\n", + "\n", + "\n", + "\n", + "nodes_38->nodes_39\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_40\n", + "\n", + "nodes_40\n", + "\n", + "\n", + "\n", + "nodes_38->nodes_40\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_41\n", + "\n", + "nodes_41\n", + "\n", + "\n", + "\n", + "nodes_41->nodes_38\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_42\n", + "\n", + "nodes_42\n", + "\n", + "\n", + "\n", + "nodes_42->nodes_38\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n" ], - "metadata": { - "id": "462d6c62", - "executionInfo": { - "status": "ok", - "timestamp": 1790065410820, - "user_tz": -120, - "elapsed": 28, - "user": { - "displayName": "", - "userId": "" - } - }, - "outputId": "b60df5c7-32d4-404b-f8f9-dc155e2d6b42" - }, - "execution_count": 17, - "outputs": [ - { - "output_type": "stream", - "name": "stdout", - "text": [ - "Graph Schema:\n", - "\n", - "Node Sets:\n", - " nodes:\n", - " | Feature | Format | Semantic | Shape | Detail |\n", - " |-----------|------------|-------------|---------|-------------|\n", - " | #id | BYTES | PRIMARY_ID | None | |\n", - " | #split | BYTES | CATEGORICAL | None | |\n", - " | feat | FLOAT_32 | EMBEDDING | (128,) | |\n", - " | labels | INTEGER_64 | CATEGORICAL | None | #num.cat:40 |\n", - " | year | INTEGER_64 | NUMERICAL | None | creation |\n", - "\n", - "\n", - "Edge Sets:\n", - " edges: (Source: nodes, Target: nodes)\n", - " (No features)\n", - "\n" - ] - } + "text/plain": [ + "" ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "samples = sampler.multisubgraph(seed_node_idxs=[0, 150780, 93487])\n", + "\n", + "# Grouping the graphs to create a single plot.\n", + "merge_graph, offset = dgf.transform.GraphMerger(schema=schema, padding=None)(samples)\n", + "dgf.plot.plot_graph(merge_graph, schema, features=False)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "5c5d4YfWcpar" + }, + "source": [ + "\n", + "**Remark:**\n", + "\n", + "- Notice the 3 independent subgraphs.\n", + "- `multisubgraph` is multi-threaded (one thread per subgraphs)." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "bfbc2230" + }, + "source": [ + "## Temporal masking\n", + "\n", + "Some applications require sampling graphs with temporal masking. This means only\n", + "sampling nodes and edges with a timestamp before a specific value, where the\n", + "limit changes for each sample.\n", + "\n", + "Timestamps should be indicated either on nodes or edges with \"is_creation_time\" equal to true.\n", + "\n", + "**Note:** Edge timestamps are more powerful but less common. Internally, GF converts node timestamps into edge timestamps with the `dgf.transform.propagate_timestamp_to_edges` method.\n", + "\n", + "The following example shows how to create a graph with node timestamps, convert\n", + "them to edge timestamps, and perform temporal sampling.\n", + "\n", + "Let's Arxiv graph contains `year` features on the nodes that we can use as a\n", + "timestamp, and propagate to the edges." + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-23T18:32:43.367494Z", + "iopub.status.busy": "2026-09-23T18:32:43.367068Z", + "iopub.status.idle": "2026-09-23T18:32:43.372803Z", + "shell.execute_reply": "2026-09-23T18:32:43.372352Z" }, - { - "cell_type": "markdown", - "source": [ - "The following example shows how to create a graph with node timestamps, convert\n", - "them to edge timestamps, and perform temporal sampling." - ], - "metadata": { - "id": "5ad2a4f9" - } + "executionInfo": { + "elapsed": 7, + "status": "ok", + "timestamp": 1790188363373.767, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 }, + "id": "NvZ9fNQ7LxSs", + "outputId": "f833e47c-3754-44a8-bdb8-f4b0ff6fd1e7" + }, + "outputs": [ { - "cell_type": "code", - "source": [ - "# Create a sampler\n", - "time_sampler_config = dgf.sampling.SimpleSamplingConfig(\n", - " seed_nodeset=\"nodes\",\n", - " num_hops=2,\n", - " hop_width=2,\n", - " reverse=True,\n", - " # This is the new part: the creation time of the nodes and of the edges is\n", - " # inferred from the \"is_creation_time\" features of the schema.\n", - " temporal_sampling=True,\n", - ")\n", - "\n", - "time_sampler = dgf.sampling.create_sampler(\n", - " graph=graph,\n", - " schema=time_schema,\n", - " plan=time_sampler_config,\n", - " num_threads=5,\n", - ")" - ], - "metadata": { - "id": "c6d421dc", - "executionInfo": { - "status": "ok", - "timestamp": 1790065451482, - "user_tz": -120, - "elapsed": 222, - "user": { - "displayName": "", - "userId": "" - } - } - }, - "execution_count": 21, - "outputs": [] + "name": "stdout", + "output_type": "stream", + "text": [ + "Graph Schema:\n", + "\n", + "Node Sets:\n", + " nodes:\n", + " | Feature | Format | Semantic | Shape | Detail |\n", + " |-----------|------------|-------------|---------|-------------|\n", + " | #id | BYTES | PRIMARY_ID | None | |\n", + " | #split | BYTES | CATEGORICAL | None | |\n", + " | feat | FLOAT_32 | EMBEDDING | (128,) | |\n", + " | labels | INTEGER_64 | CATEGORICAL | None | #num.cat:40 |\n", + " | year | INTEGER_64 | NUMERICAL | None | creation |\n", + "\n", + "\n", + "Edge Sets:\n", + " edges: (Source: nodes, Target: nodes)\n", + " (No features)\n", + "\n" + ] + } + ], + "source": [ + "time_schema = copy.deepcopy(schema)\n", + "time_schema.node_sets[\"nodes\"].features[\"year\"].is_creation_time = True\n", + "dgf.print.schema(time_schema)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "5ad2a4f9" + }, + "source": [ + "The following example shows how to create a graph with node timestamps, convert\n", + "them to edge timestamps, and perform temporal sampling." + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-23T18:32:43.374679Z", + "iopub.status.busy": "2026-09-23T18:32:43.374451Z", + "iopub.status.idle": "2026-09-23T18:32:43.627765Z", + "shell.execute_reply": "2026-09-23T18:32:43.627273Z" }, - { - "cell_type": "markdown", - "source": [ - "Let's look at the year of the first node:" - ], - "metadata": { - "id": "909d6d86" - } + "executionInfo": { + "elapsed": 255, + "status": "ok", + "timestamp": 1790188363628.9556, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 + }, + "id": "c6d421dc" + }, + "outputs": [], + "source": [ + "# Create a sampler\n", + "time_sampler_config = dgf.sampling.SimpleSamplingConfig(\n", + " seed_nodeset=\"nodes\",\n", + " num_hops=2,\n", + " hop_width=2,\n", + " reverse=True,\n", + " # This is the new part: the creation time of the nodes and of the edges is\n", + " # inferred from the \"is_creation_time\" features of the schema.\n", + " temporal_sampling=True,\n", + ")\n", + "\n", + "time_sampler = dgf.sampling.create_sampler(\n", + " graph=graph,\n", + " schema=time_schema,\n", + " plan=time_sampler_config,\n", + " num_threads=5,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "909d6d86" + }, + "source": [ + "Let's look at the year of the first node:" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-23T18:32:43.629903Z", + "iopub.status.busy": "2026-09-23T18:32:43.629602Z", + "iopub.status.idle": "2026-09-23T18:32:43.632730Z", + "shell.execute_reply": "2026-09-23T18:32:43.632437Z" }, + "executionInfo": { + "elapsed": 4, + "status": "ok", + "timestamp": 1790188363633.6614, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 + }, + "id": "91611d90", + "outputId": "055c8769-b8dd-4897-aba5-1d9acea0d769" + }, + "outputs": [ { - "cell_type": "code", - "source": [ - "graph.node_sets[\"nodes\"].features[\"year\"][0]" - ], - "metadata": { - "id": "91611d90", - "executionInfo": { - "status": "ok", - "timestamp": 1790065445634, - "user_tz": -120, - "elapsed": 16, - "user": { - "displayName": "", - "userId": "" - } - }, - "outputId": "65fc350c-b58e-4f8b-a52c-9d7cc24bcc92" - }, - "execution_count": 20, - "outputs": [ - { - "output_type": "execute_result", - "data": { - "text/plain": [ - "np.int64(2013)" - ] - }, - "metadata": {}, - "execution_count": 20 - } + "data": { + "text/plain": [ + "np.int64(2013)" ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "graph.node_sets[\"nodes\"].features[\"year\"][0]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "5cb0d579" + }, + "source": [ + "So, let's create a graph sample by only considering edges prior (non-strict)\n", + "to 2013." + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": { + "colab": { + "height": 395 }, - { - "cell_type": "markdown", - "source": [ - "So, let's create a graph sample by only considering edges prior (non-strict)\n", - "to 2013." - ], - "metadata": { - "id": "5cb0d579" - } + "execution": { + "iopub.execute_input": "2026-09-23T18:32:43.634340Z", + "iopub.status.busy": "2026-09-23T18:32:43.634171Z", + "iopub.status.idle": "2026-09-23T18:32:43.678957Z", + "shell.execute_reply": "2026-09-23T18:32:43.678428Z" }, + "executionInfo": { + "elapsed": 46, + "status": "ok", + "timestamp": 1790188363680.2434, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 + }, + "id": "a35660d2", + "outputId": "16028134-3b41-4be1-db5d-a239de4912d0" + }, + "outputs": [ { - "cell_type": "code", - "source": [ - "sample = time_sampler.sample(seed_node_idxs=0, seed_timestamps=2013)\n", - "dgf.plot.plot_graph(sample, time_schema, features=True)" + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "nodes_0\n", + "\n", + "nodes_0\n", + "#id: b'n0'\n", + "#split: b'train'\n", + "feat: [-0.057943 -0.05253  -0.072603 -0.026555  0.130435[...]\n", + "labels: 4\n", + "year: 2013\n", + "\n", + "\n", + "\n", + "nodes_1\n", + "\n", + "nodes_1\n", + "#id: b'n52893'\n", + "#split: b'train'\n", + "feat: [-0.055733 -0.031606 -0.292581 -0.054655  0.088294[...]\n", + "labels: 24\n", + "year: 2010\n", + "\n", + "\n", + "\n", + "nodes_0->nodes_1\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_4\n", + "\n", + "nodes_4\n", + "#id: b'n93487'\n", + "#split: b'train'\n", + "feat: [-0.215197  0.051735 -0.050193 -0.02174   0.12137 [...]\n", + "labels: 24\n", + "year: 2012\n", + "\n", + "\n", + "\n", + "nodes_0->nodes_4\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_2\n", + "\n", + "nodes_2\n", + "#id: b'n14528'\n", + "#split: b'train'\n", + "feat: [ 6.38240e-02 -4.81730e-02 -2.85770e-01 -1.85823e-[...]\n", + "labels: 24\n", + "year: 2010\n", + "\n", + "\n", + "\n", + "nodes_1->nodes_2\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_3\n", + "\n", + "nodes_3\n", + "#id: b'n71730'\n", + "#split: b'train'\n", + "feat: [-0.115429  0.011448 -0.260941  0.03005   0.145451[...]\n", + "labels: 24\n", + "year: 2009\n", + "\n", + "\n", + "\n", + "nodes_1->nodes_3\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n", + "nodes_5\n", + "\n", + "nodes_5\n", + "#id: b'n67258'\n", + "#split: b'train'\n", + "feat: [-0.149148 -0.08377  -0.191802  0.092441 -0.005551[...]\n", + "labels: 24\n", + "year: 2013\n", + "\n", + "\n", + "\n", + "nodes_5->nodes_4\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "\n" ], - "metadata": { - "colab": { - "height": 375 - }, - "id": "a35660d2", - "executionInfo": { - "status": "ok", - "timestamp": 1790065444305, - "user_tz": -120, - "elapsed": 58, - "user": { - "displayName": "", - "userId": "" - } - }, - "outputId": "79d397cf-2b74-4ca3-9873-27cb2cd86c61" - }, - "execution_count": 19, - "outputs": [ - { - "output_type": "execute_result", - "data": { - "image/svg+xml": "\n\n\n\n\n\n\n\n\nnodes_0\n\nnodes_0\n#id: b'n0'\n#split: b'train'\nfeat: [-0.057943 -0.05253  -0.072603 -0.026555  0.130435[...]\nlabels: 4\nyear: 2013\n\n\n\nnodes_1\n\nnodes_1\n#id: b'n52893'\n#split: b'train'\nfeat: [-0.055733 -0.031606 -0.292581 -0.054655  0.088294[...]\nlabels: 24\nyear: 2010\n\n\n\nnodes_0->nodes_1\n\n\nedges\n\n\n\nnodes_4\n\nnodes_4\n#id: b'n93487'\n#split: b'train'\nfeat: [-0.215197  0.051735 -0.050193 -0.02174   0.12137 [...]\nlabels: 24\nyear: 2012\n\n\n\nnodes_0->nodes_4\n\n\nedges\n\n\n\nnodes_2\n\nnodes_2\n#id: b'n14528'\n#split: b'train'\nfeat: [ 6.38240e-02 -4.81730e-02 -2.85770e-01 -1.85823e-[...]\nlabels: 24\nyear: 2010\n\n\n\nnodes_1->nodes_2\n\n\nedges\n\n\n\nnodes_3\n\nnodes_3\n#id: b'n71730'\n#split: b'train'\nfeat: [-0.115429  0.011448 -0.260941  0.03005   0.145451[...]\nlabels: 24\nyear: 2009\n\n\n\nnodes_1->nodes_3\n\n\nedges\n\n\n\nnodes_5\n\nnodes_5\n#id: b'n67258'\n#split: b'train'\nfeat: [-0.149148 -0.08377  -0.191802  0.092441 -0.005551[...]\nlabels: 24\nyear: 2013\n\n\n\nnodes_5->nodes_4\n\n\nedges\n\n\n\n", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "execution_count": 19 - } + "text/plain": [ + "" ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sample = time_sampler.sample(seed_node_idxs=0, seed_timestamps=2013)\n", + "dgf.plot.plot_graph(sample, time_schema, features=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "ef4b8f03" + }, + "source": [ + "## Semi-distributed sampler (v2)\n", + "\n", + "The semi-distributed sampler is a simple way to distribute the computation of\n", + "graph samples over multiple machines using Apache Beam. It is not a fully\n", + "distributed sampler though: Each worker will load the full graph topology (i.e.,\n", + "the edges; but not the features) in memory.\n", + "\n", + "**Note:** For a full example, check\n", + "`dgf/examples/create_graph_samples_semi_distributed_v2.py`.\n", + "\n", + "Let's configure and run the semi-distributed graph sampler:" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-23T18:32:43.681333Z", + "iopub.status.busy": "2026-09-23T18:32:43.681009Z", + "iopub.status.idle": "2026-09-23T18:32:43.906957Z", + "shell.execute_reply": "2026-09-23T18:32:43.906597Z" }, - { - "cell_type": "markdown", - "source": [ - "## Semi-distributed sampler (v2)\n", - "\n", - "The semi-distributed sampler is a simple way to distribute the computation of\n", - "graph samples over multiple machines using Apache Beam. It is not a fully\n", - "distributed sampler though: Each worker will load the full graph topology (i.e.,\n", - "the edges; but not the features) in memory.\n", - "\n", - "**Note:** For a full example, check\n", - "`dgf/examples/create_graph_samples_semi_distributed_v2.py`.\n", - "\n", - "Let's configure and run the semi-distributed graph sampler:" - ], - "metadata": { - "id": "ef4b8f03" - } + "executionInfo": { + "elapsed": 227, + "status": "ok", + "timestamp": 1790188363908.234, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 }, - { - "cell_type": "code", - "source": [ - "from absl import flags\n", - "import apache_beam as beam\n", - "from apache_beam.options import pipeline_options\n", - "from google3.pipeline.flume.py import runner as flume_runner\n", - "\n", - "# For the user: Comment / uncomment on of the block.\n", - "# Note: Defining the \"flume_exec_mode\" in the \"PipelineOptions\" does not work.\n", - "\n", - "# Run the execution in-process. Great for debugging / iteration on small data.\n", - "# ===\n", - "flags.FLAGS.flume_exec_mode = \"IN_PROCESS\"\n", - "options = pipeline_options.PipelineOptions()\n", - "\n", - "# Run the execution on Borg. Great for large data.\n", - "# ===\n", - "# flags.FLAGS.flume_exec_mode = \"BORG\"\n", - "# options = pipeline_options.PipelineOptions(\n", - "# flume_borg_accounting_charged_user_name=\"simple-ml-accounting\",\n", - "# flume_borg_cells=\"is\",\n", - "# flume_use_batch_scheduler=True,\n", - "# flume_batch_scheduler_strategy=\"RUN_SOON\",\n", - "# )" - ], - "metadata": { - "id": "4151f762" - }, - "execution_count": null, - "outputs": [] + "id": "4151f762" + }, + "outputs": [], + "source": [ + "from absl import flags\n", + "import apache_beam as beam\n", + "from apache_beam.options import pipeline_options\n", + "from google3.pipeline.flume.py import runner as flume_runner\n", + "\n", + "# For the user: Comment / uncomment on of the block.\n", + "# Note: Defining the \"flume_exec_mode\" in the \"PipelineOptions\" does not work.\n", + "\n", + "# Run the execution in-process. Great for debugging / iteration on small data.\n", + "# ===\n", + "flags.FLAGS.flume_exec_mode = \"IN_PROCESS\"\n", + "options = pipeline_options.PipelineOptions()\n", + "\n", + "# Run the execution on Borg. Great for large data.\n", + "# ===\n", + "# flags.FLAGS.flume_exec_mode = \"BORG\"\n", + "# options = pipeline_options.PipelineOptions(\n", + "# flume_borg_accounting_charged_user_name=\"simple-ml-accounting\",\n", + "# flume_borg_cells=\"is\",\n", + "# flume_use_batch_scheduler=True,\n", + "# flume_batch_scheduler_strategy=\"RUN_SOON\",\n", + "# )" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "71fc0067" + }, + "source": [ + "Save our graph to disk. This will be the input of the sampler." + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-23T18:32:43.909135Z", + "iopub.status.busy": "2026-09-23T18:32:43.908954Z", + "iopub.status.idle": "2026-09-23T18:32:44.288082Z", + "shell.execute_reply": "2026-09-23T18:32:44.287545Z" }, - { - "cell_type": "markdown", - "source": [ - "Save our graph to disk. This will be the input of the sampler." - ], - "metadata": { - "id": "71fc0067" - } + "executionInfo": { + "elapsed": 380, + "status": "ok", + "timestamp": 1790188364289.1096, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 }, + "id": "f4a3e3de" + }, + "outputs": [ { - "cell_type": "code", - "source": [ - "graph_path = \"/tmp/my_graph\"\n", - "dgf.io.write_graph(graph, schema, path=graph_path)" - ], - "metadata": { - "id": "f4a3e3de" - }, - "execution_count": null, - "outputs": [] + "name": "stdout", + "output_type": "stream", + "text": [ + "Graph written from memory in 0.38s\n" + ] + } + ], + "source": [ + "graph_path = \"/tmp/my_graph\"\n", + "dgf.io.write_graph(graph, schema, path=graph_path)" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-23T18:32:44.289991Z", + "iopub.status.busy": "2026-09-23T18:32:44.289809Z", + "iopub.status.idle": "2026-09-23T18:34:42.743424Z", + "shell.execute_reply": "2026-09-23T18:34:42.742728Z" }, - { - "cell_type": "code", - "source": [ - "with beam.Pipeline(runner=flume_runner.FlumeRunner(), options=options) as root:\n", - "\n", - " # Read only the node and the #id feature from the graph. Those\n", - " # is the nodes to seed.\n", - " seed_graph = dgf.beam.io.read_graph(\n", - " root,\n", - " graph_path,\n", - " schema_filter=dgf.data.GraphSchemaFilter(\n", - " nodeset_fn=lambda key, sch: key == \"nodes\",\n", - " edgeset_fn=lambda key, sch: False,\n", - " feature_fn=lambda key, sch: key == \"#id\",\n", - " ),\n", - " )\n", - " seed_node_ids = dgf.beam.sampling.extract_nodes_ids(seed_graph, \"nodes\")\n", - "\n", - " # Alternatively, you can do:\n", - " # seed_node_ids = beam.Create([b\"\", b\"\"])\n", - "\n", - " # Randomly select 50 seed nodes.\n", - " seed_node_ids = (\n", - " seed_node_ids\n", - " | \"Sample seeds\" >> beam.combiners.Sample.FixedSizeGlobally(50)\n", - " | beam.FlatMap(lambda xs: xs)\n", - " )\n", - "\n", - " # Generate samples\n", - " samples, output_schema = dgf.beam.sampling.semi_distributed_sampler_v2(\n", - " graph_path=graph_path,\n", - " plan=sampler_config,\n", - " seeds=seed_node_ids,\n", - " num_threads=20,\n", - " beam_feature_collection=False,\n", - " )\n", - "\n", - " # Save the samples to disk\n", - " dgf.beam.io.write_tfgnn_graphs(samples, \"/tmp/samples@*\", output_schema)" - ], - "metadata": { - "id": "ecbaa932" - }, - "execution_count": null, - "outputs": [] + "executionInfo": { + "elapsed": 118455, + "status": "ok", + "timestamp": 1790188482744.6162, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 }, + "id": "ecbaa932" + }, + "outputs": [ { - "cell_type": "markdown", - "source": [ - "**Remark:**\n", - "\n", - "- The semi-distributed sampler (v2) takes as input a graph stored on disk. It\n", - " is today, the fastest option (faster than loading the input graph from a\n", - " beam pcollection like the v1).\n", - "- The `num_threads` option control the number of threads for each worker.\n", - "- Setting `beam_feature_collection=True` tells the sampler to generate\n", - " topologies first and then collect feature values using Apache Beam. This\n", - " approach scales effectively because workers only store the graph topology in\n", - " memory, though it results in slower overall performance. Alternatively,\n", - " `beam_feature_collection=False` triggers feature collection at the same time\n", - " the sampler generates the topology. While this method is faster, it requires\n", - " workers to load all feature values into memory, which limits its ability to\n", - " scale.\n", - "- Note that all the beam method start with `dgf.beam.*`." - ], - "metadata": { - "id": "70dcd856" - } + "data": { + "application/javascript": [ + "\n", + " if (typeof window.interactive_beam_jquery == 'undefined') {\n", + " var jqueryScript = document.createElement('script');\n", + " jqueryScript.src = 'https://code.jquery.com/jquery-3.4.1.slim.min.js';\n", + " jqueryScript.type = 'text/javascript';\n", + " jqueryScript.onload = function() {\n", + " var datatableScript = document.createElement('script');\n", + " datatableScript.src = 'https://cdn.datatables.net/1.10.20/js/jquery.dataTables.min.js';\n", + " datatableScript.type = 'text/javascript';\n", + " datatableScript.onload = function() {\n", + " window.interactive_beam_jquery = jQuery.noConflict(true);\n", + " window.interactive_beam_jquery(document).ready(function($){\n", + " \n", + " });\n", + " }\n", + " document.head.appendChild(datatableScript);\n", + " };\n", + " document.head.appendChild(jqueryScript);\n", + " } else {\n", + " window.interactive_beam_jquery(document).ready(function($){\n", + " \n", + " });\n", + " }" + ] + }, + "metadata": {}, + "output_type": "display_data" }, { - "cell_type": "markdown", - "source": [ - "We can now load and plot the graph samples." - ], - "metadata": { - "id": "b39436b6" - } + "name": "stdout", + "output_type": "stream", + "text": [ + "Using override schema\n", + "Reading 1 nodeset(s), 1 edgeset(s), and 5 feature(s)\n", + "Reading metadata from /tmp/my_graph\n", + "Reading nodeset nodes from /tmp/my_graph\n", + ".concatenating values\n", + "Reading edgeset edges from /tmp/my_graph\n", + ".concatenating values\n", + "Graph read in memory in 0.52s\n" + ] + } + ], + "source": [ + "with beam.Pipeline(runner=flume_runner.FlumeRunner(), options=options) as root:\n", + "\n", + " # Read only the node and the #id feature from the graph. Those\n", + " # is the nodes to seed.\n", + " seed_graph = dgf.beam.io.read_graph(\n", + " root,\n", + " graph_path,\n", + " schema_filter=dgf.data.GraphSchemaFilter(\n", + " nodeset_fn=lambda key, sch: key == \"nodes\",\n", + " edgeset_fn=lambda key, sch: False,\n", + " feature_fn=lambda key, sch: key == \"#id\",\n", + " ),\n", + " )\n", + " seed_node_ids = dgf.beam.sampling.extract_nodes_ids(seed_graph, \"nodes\")\n", + "\n", + " # Alternatively, you can do:\n", + " # seed_node_ids = beam.Create([b\"\", b\"\"])\n", + "\n", + " # Randomly select 50 seed nodes.\n", + " seed_node_ids = (\n", + " seed_node_ids\n", + " | \"Sample seeds\" >> beam.combiners.Sample.FixedSizeGlobally(50)\n", + " | beam.FlatMap(lambda xs: xs)\n", + " )\n", + "\n", + " # Generate samples\n", + " samples, output_schema = dgf.beam.sampling.semi_distributed_sampler_v2(\n", + " graph_path=graph_path,\n", + " plan=sampler_config,\n", + " seeds=seed_node_ids,\n", + " num_threads=20,\n", + " beam_feature_collection=False,\n", + " )\n", + "\n", + " # Save the samples to disk\n", + " dgf.beam.io.write_tfgnn_graphs(samples, \"/tmp/samples@*\", output_schema)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "70dcd856" + }, + "source": [ + "**Remark:**\n", + "\n", + "- The semi-distributed sampler (v2) takes as input a graph stored on disk. It\n", + " is today, the fastest option (faster than loading the input graph from a\n", + " beam pcollection like the v1).\n", + "- The `num_threads` option control the number of threads for each worker.\n", + "- Setting `beam_feature_collection=True` tells the sampler to generate\n", + " topologies first and then collect feature values using Apache Beam. This\n", + " approach scales effectively because workers only store the graph topology in\n", + " memory, though it results in slower overall performance. Alternatively,\n", + " `beam_feature_collection=False` triggers feature collection at the same time\n", + " the sampler generates the topology. While this method is faster, it requires\n", + " workers to load all feature values into memory, which limits its ability to\n", + " scale.\n", + "- Note that all the beam method start with `dgf.beam.*`." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "b39436b6" + }, + "source": [ + "We can now load and plot the graph samples." + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-23T18:34:42.745544Z", + "iopub.status.busy": "2026-09-23T18:34:42.745327Z", + "iopub.status.idle": "2026-09-23T18:34:47.479165Z", + "shell.execute_reply": "2026-09-23T18:34:47.478595Z" }, + "executionInfo": { + "elapsed": 4735, + "status": "ok", + "timestamp": 1790188487480.6619, + "user": { + "user_id": "gbm" + }, + "user_tz": -120 + }, + "id": "d75df1fd" + }, + "outputs": [ { - "cell_type": "code", - "source": [ - "graphs = list(dgf.io.read_tfgnn_graphs(path=\"/tmp/samples@*\", schema=schema))\n", - "print(f\"Found {len(graphs)} graphs\")" - ], - "metadata": { - "id": "d75df1fd" - }, - "execution_count": null, - "outputs": [] + "name": "stdout", + "output_type": "stream", + "text": [ + "Found 50 graphs\n" + ] } - ], - "metadata": { - "colab": { - "provenance": [ - { - "file_id": "1hMr9gK0hhKcCtIs1Pro3_ksOnYcdAL2P", - "timestamp": 1771583153488 - } - ], - "last_runtime": { - "build_target": "", - "kind": "local" + ], + "source": [ + "graphs = list(dgf.io.read_tfgnn_graphs(path=\"/tmp/samples@*\", schema=schema))\n", + "print(f\"Found {len(graphs)} graphs\")" + ] + } + ], + "metadata": { + "colab": { + "last_runtime": { + "build_target": "", + "kind": "local" + }, + "provenance": [ + { + "file_id": "1hMr9gK0hhKcCtIs1Pro3_ksOnYcdAL2P", + "timestamp": 1771583153488 + } + ], + "toc_visible": true, + "views": { + "output_only": { + "cells": [ + { + "id": "52d5d84b" }, - "toc_visible": true - }, - "kernelspec": { - "name": "python3", - "display_name": "Python 3" - }, - "language_info": { - "name": "python" + { + "id": "EsL3MiYR9nD6" + }, + { + "id": "D1I1ijy08t0v" + }, + { + "id": "eASOvqWCLNMQ" + }, + { + "id": "71e9cf1c" + }, + { + "id": "f5431c44" + }, + { + "id": "7eb84356" + }, + { + "id": "7f9272a6" + }, + { + "id": "d0fb3022" + }, + { + "id": "2896f2d1" + }, + { + "id": "fba58d46" + }, + { + "id": "4ee7378e" + }, + { + "id": "daee962f" + }, + { + "id": "a98cd4ee" + }, + { + "id": "2d0902f0" + }, + { + "id": "5178bcff" + }, + { + "id": "1f9373f2" + }, + { + "id": "07fe45e1" + }, + { + "id": "8cea9faa" + }, + { + "id": "d67499e6" + }, + { + "id": "3a98d6ce" + }, + { + "id": "31410d56" + }, + { + "id": "3efc55a0" + }, + { + "id": "c93aafb9" + }, + { + "id": "e34b4b65" + }, + { + "id": "O5j_22f5HJYn" + }, + { + "id": "LGu-U8SkHTMV" + }, + { + "id": "Yxpn64EQb_0R" + }, + { + "id": "OrG4j5_OZ4lB" + }, + { + "id": "grTlK4Lnbvoq" + }, + { + "id": "868fTc8JbkzT" + }, + { + "id": "dK98Ep4Mcnya" + }, + { + "id": "5c5d4YfWcpar" + }, + { + "id": "bfbc2230" + }, + { + "id": "NvZ9fNQ7LxSs" + }, + { + "id": "5ad2a4f9" + }, + { + "id": "c6d421dc" + }, + { + "id": "909d6d86" + }, + { + "id": "91611d90" + }, + { + "id": "5cb0d579" + }, + { + "id": "a35660d2" + }, + { + "id": "ef4b8f03" + }, + { + "id": "4151f762" + }, + { + "id": "71fc0067" + }, + { + "id": "f4a3e3de" + }, + { + "id": "ecbaa932" + }, + { + "id": "70dcd856" + }, + { + "id": "b39436b6" + }, + { + "id": "d75df1fd" + } + ], + "hide_code": true } + } + }, + "kernelspec": { + "display_name": "Python 3", + "name": "python3" }, - "nbformat_minor": 0, - "nbformat": 4 + "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.14.5" + } + }, + "nbformat": 4, + "nbformat_minor": 0 } diff --git a/doc/mkdocs.yml b/doc/mkdocs.yml index 066b485..1eaaaeb 100644 --- a/doc/mkdocs.yml +++ b/doc/mkdocs.yml @@ -74,6 +74,8 @@ nav: - Tasks: - Node model: tutorial/node_prediction.ipynb - Link model: tutorial/link_prediction.ipynb + - Input features: + - Timeseries: tutorial/timeseries_node_prediction.ipynb - Advanced API: - In-memory graph: tutorial/in_memory_graph.ipynb - Sampler: tutorial/sampler.ipynb