Source: https://dev-docs.nebula-graph.io/v5.3.2/analytic/algorithms/cosine_similarity/ Documentation version: v5.3.2 # Cosine Similarity ## Algorithm introduction Cosine similarity computes pairwise similarity scores from vector directions in embedding space. Compute cosine similarity between two nodes identified by node property id using undirected 1-hop neighborhoods, counting parallel edges as multiplicity in the adjacency vector. For more information, see [Cosine Similarity](https://en.wikipedia.org/wiki/Cosine_similarity). ## Data model To implement the Cosine Similarity algorithm in GQL, the following data model is used to create a graph type: - Entities are represented by nodes of type `Node`, with properties `id` and `rating`. - Relationships are represented by edges of type `Link`, with property `weight`. For the complete definition of the graph type, see [Prepare graph data](https://dev-docs.nebula-graph.io/v5.3.2/analytic/algorithms/cosine_similarity/#1_prepare_graph_data). ## Procedure implementation[]() To compute Cosine Similarity results, a [procedure](https://dev-docs.nebula-graph.io/v5.3.2/database-gql-reference/procedures/create/) is created. For more information about the procedure, see [Procedure implementation](https://dev-docs.nebula-graph.io/v5.3.2/analytic/algorithms/cosine_similarity/#procedure_implement). The following section describes the key components of the procedure and how it works. The procedure takes the following input parameters: - `left_id`: `INT64`. ID of the first node to compare. - `right_id`: `INT64`. ID of the second node to compare. Additionally, the following variables are defined within the procedure: | Variable name | Variable type | Description | `common_cnt` | Global [SumAgg aggregator value variable](https://dev-docs.nebula-graph.io/v5.3.2/database-gql-reference/variable-definition/agg/sum_agg/) | Count of common neighbors between the left and right nodes. | `left_deg` | Global [SumAgg aggregator value variable](https://dev-docs.nebula-graph.io/v5.3.2/database-gql-reference/variable-definition/agg/sum_agg/) | Degree of the left query node. | `right_deg` | Global [SumAgg aggregator value variable](https://dev-docs.nebula-graph.io/v5.3.2/database-gql-reference/variable-definition/agg/sum_agg/) | Degree of the right query node. | `dot_val` | Global [SumAgg aggregator value variable](https://dev-docs.nebula-graph.io/v5.3.2/database-gql-reference/variable-definition/agg/sum_agg/) | Dot product of left and right node neighborhood indicator vectors. | `left_norm_sq` | Global [SumAgg aggregator value variable](https://dev-docs.nebula-graph.io/v5.3.2/database-gql-reference/variable-definition/agg/sum_agg/) | Squared L2 norm of the left node's neighborhood vector. | `right_norm_sq` | Global [SumAgg aggregator value variable](https://dev-docs.nebula-graph.io/v5.3.2/database-gql-reference/variable-definition/agg/sum_agg/) | Squared L2 norm of the right node's neighborhood vector. | `similarity` | [Primitive value variable](https://dev-docs.nebula-graph.io/v5.3.2/database-gql-reference/variable-definition/value-variable/) | Final cosine similarity score between the two nodes. | `left_cnt` | Node-attached [SumAgg aggregator value variable](https://dev-docs.nebula-graph.io/v5.3.2/database-gql-reference/variable-definition/agg/sum_agg/) | Per-node flag/count indicating membership in left node's neighborhood. | `right_cnt` | Node-attached [SumAgg aggregator value variable](https://dev-docs.nebula-graph.io/v5.3.2/database-gql-reference/variable-definition/agg/sum_agg/) | Per-node flag/count indicating membership in right node's neighborhood. The following steps outline the logic of the procedure: 1. For the left node, mark all its neighbors with `seen_left`; for the right node, mark all with `seen_right`. 2. Compute `dot_val` (number of common neighbors), `left_norm_sq` (left node degree), and `right_norm_sq` (right node degree). 3. Compute cosine similarity as `dot_val / sqrt(left_norm_sq × right_norm_sq)`. 4. Return the similarity score. ## Steps Run the following code blocks in order in the NebulaGraph Studio CONSOLE. For first-time setup, follow [Prepare algorithm sample data](https://dev-docs.nebula-graph.io/v5.3.2/analytic/algorithms/prepare-data/) to check where to run commands and how the directory is mounted. ### 1. Prepare graph data[]() 1. Create a graph type that matches your data model. ```` ''' CREATE GRAPH TYPE IF NOT EXISTS cosine_similarity_graph_type AS { NODE Node (LABEL Node {id INT PRIMARY KEY, rating INT}), EDGE Link (Node)-[LABEL Link {weight INT, MULTIEDGE KEY()}]->(Node) } ''' ```` 2. Download these CSV files and save them with their original names in `examples/algorithms/cosine_similarity/` under your deployment directory: - [cosine_similarity_node.csv](https://dev-docs.nebula-graph.io/v5.3.2/analytic/algorithms/files/cosine_similarity/cosine_similarity_node.csv): Node data. - [cosine_similarity_edge.csv](https://dev-docs.nebula-graph.io/v5.3.2/analytic/algorithms/files/cosine_similarity/cosine_similarity_edge.csv): Edge data. Preview the CSV files cosine_similarity_node.csv ```` id,rating 1,0 2,2 3,1 4,0 5,0 6,2 7,1 8,2 9,0 10,1 11,0 12,2 13,1 14,1 15,0 16,2 17,0 18,1 19,2 20,0 21,2 22,1 23,0 24,1 ```` cosine_similarity_edge.csv ```` src_id,dst_id,weight 1,2,4 1,3,2 1,4,7 1,5,9 2,3,1 2,4,6 2,5,5 2,6,2 3,4,3 3,5,4 3,6,8 4,6,2 4,7,6 4,8,5 5,6,4 5,8,7 5,9,6 6,8,1 6,9,5 6,10,6 7,9,2 7,10,9 8,9,3 8,11,6 8,12,4 9,10,4 9,12,7 9,13,2 10,12,1 10,13,8 10,14,4 11,12,2 11,14,5 11,15,7 12,14,3 12,15,6 12,16,5 13,15,2 13,16,7 14,15,4 14,17,8 14,18,2 15,16,1 15,18,5 15,19,3 16,18,3 16,19,6 17,18,2 17,20,7 17,21,5 18,19,4 18,21,9 18,22,6 19,21,1 19,22,5 20,21,3 20,23,6 20,24,8 21,22,2 21,23,4 21,24,7 22,24,4 23,24,1 ```` 3. Create a temporary graph based on the graph type and load the data files into the temporary graph. ```` CREATE TEMPORARY GRAPH IF NOT EXISTS #cosine_similarity_graph TYPED cosine_similarity_graph_type ```` ```` ''' USE #cosine_similarity_graph IMPORT INTO GRAPH { NODE (v@Node {id: id, rating: rating}) FROM DATAFILE { PATH: "file:///examples/algorithms/cosine_similarity/cosine_similarity_node.csv", FORMAT: "CSV", DELIMITER: ',' }, EDGE (id: src_id)-[e@Link {weight: weight}]->(id: dst_id) FROM DATAFILE { PATH: "file:///examples/algorithms/cosine_similarity/cosine_similarity_edge.csv", FORMAT: "CSV", DELIMITER: ',' } } ''' ```` After the import succeeds, run this query in the console to check the node count: ```` ''' USE #cosine_similarity_graph MATCH (n) RETURN count(n) AS node_count ''' ```` The expected `node_count` is `24`. If it differs, [check the files and import results](https://dev-docs.nebula-graph.io/v5.3.2/analytic/algorithms/prepare-data/#troubleshooting) before continuing. ### 2. Create a procedure[]() Create a procedure to implement the Cosine Similarity algorithm. For more information about the procedure, see [Procedure implementation](https://dev-docs.nebula-graph.io/v5.3.2/analytic/algorithms/cosine_similarity/#procedure_implement). ```` ''' CREATE OR REPLACE PROCEDURE cosine_similarity_score(left_id INT64, right_id INT64) RETURNS ( query_left_id INT64, query_right_id INT64, left_degree INT64, right_degree INT64, common_neighbor_count INT64, dot_product DOUBLE, score DOUBLE ) COMMENT "Compute cosine similarity between two vertices identified by vertex property id using undirected 1-hop neighborhoods, counting parallel edges as multiplicity in the adjacency vector." AS { VALUE common_cnt SumAgg = 0 VALUE left_deg SumAgg = 0 VALUE right_deg SumAgg = 0 VALUE dot_val SumAgg = 0.0 VALUE left_norm_sq SumAgg = 0.0 VALUE right_norm_sq SumAgg = 0.0 VALUE similarity DOUBLE = 0.0 NODE VALUE left_cnt SumAgg = 0 NODE VALUE right_cnt SumAgg = 0 MATCH (s)-[e]-(n) WHERE s.id = left_id PER PATH { SET n.@left_cnt += 1 } MATCH (s)-[e]-(n) WHERE s.id = right_id PER PATH { SET n.@right_cnt += 1 } MATCH (n) WHERE n.@left_cnt > 0 PER NODE (n) { SET @left_deg += n.@left_cnt SET @left_norm_sq += CAST(n.@left_cnt AS DOUBLE) * CAST(n.@left_cnt AS DOUBLE) } MATCH (n) WHERE n.@right_cnt > 0 PER NODE (n) { SET @right_deg += n.@right_cnt SET @right_norm_sq += CAST(n.@right_cnt AS DOUBLE) * CAST(n.@right_cnt AS DOUBLE) } MATCH (n) WHERE n.@left_cnt > 0 AND n.@right_cnt > 0 AND n.id <> left_id AND n.id <> right_id PER NODE (n) { SET @common_cnt += 1 SET @dot_val += CAST(n.@left_cnt AS DOUBLE) * CAST(n.@right_cnt AS DOUBLE) } IF left_id = right_id THEN { SET similarity = 1.0 } ELSEIF @left_deg = 0 OR @right_deg = 0 THEN { SET similarity = 0.0 } ELSE { SET similarity = @dot_val / sqrt(@left_norm_sq * @right_norm_sq) } RETURN left_id AS query_left_id, right_id AS query_right_id, @left_deg AS left_degree, @right_deg AS right_degree, @common_cnt AS common_neighbor_count, @dot_val AS dot_product, similarity AS score } ''' ```` ### 3. Call your procedure[]() Compute cosine similarity between nodes `6` and `9`. ```` USE #cosine_similarity_graph CALL cosine_similarity_score(6, 9) RETURN query_left_id, query_right_id, left_degree, right_degree, common_neighbor_count, dot_product, score ```` Returns: ```` +---------------+----------------+-------------+--------------+-----------------------+-------------+---------------------+ | query_left_id | query_right_id | left_degree | right_degree | common_neighbor_count | dot_product | score | +---------------+----------------+-------------+--------------+-----------------------+-------------+---------------------+ | 6 | 9 | 7 | 7 | 3 | 3.0 | 0.42857142857142855 | +---------------+----------------+-------------+--------------+-----------------------+-------------+---------------------+ ```` Save results to a file or graph To save the returned results as CSV files or write them to a graph, see [EXPORT](https://dev-docs.nebula-graph.io/v5.3.2/database-gql-reference/in-out/export/). Match the exported fields to the returned columns and target properties.