diff --git a/ChangeLog.md b/ChangeLog.md index 17be8a1e..049f0e6a 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -3,6 +3,8 @@ Starting with v1.31.6, this file will contain a record of major features and updates made in each release of graph-notebook. ## Upcoming +- New Neptune Analytics notebook - Vector Similarity Algorithms ([Link to PR](https://github.com/aws/graph-notebook/pull/555)) + - Path: 02-Neptune-Analytics > 02-Graph-Algorithms > 06-Vector-Similarity-Algorithms - Deprecated Python 3.7 support ([Link to PR](https://github.com/aws/graph-notebook/pull/551)) ## Release 4.0.2 (Dec 14, 2023) diff --git a/src/graph_notebook/notebooks/02-Neptune-Analytics/01-Getting-Started/01-Getting-Started-With-Neptune-Analytics.ipynb b/src/graph_notebook/notebooks/02-Neptune-Analytics/01-Getting-Started/01-Getting-Started-With-Neptune-Analytics.ipynb index d2341a3e..6363f64b 100644 --- a/src/graph_notebook/notebooks/02-Neptune-Analytics/01-Getting-Started/01-Getting-Started-With-Neptune-Analytics.ipynb +++ b/src/graph_notebook/notebooks/02-Neptune-Analytics/01-Getting-Started/01-Getting-Started-With-Neptune-Analytics.ipynb @@ -77,7 +77,7 @@ }, "outputs": [], "source": [ - "%status" + "%opencypher_status" ] }, { @@ -85,13 +85,13 @@ "id": "8f3b5040-15be-474e-9b55-01b60945085d", "metadata": {}, "source": [ - "Examining the response we should see that the graph status is currently `healthy` as well as some metadata such as versions and the start time for the cluster.\n", + "Examining the response we should see that the graph returns an object that contains some information on currently running openCypher queries.\n", "\n", "## Getting Help\n", "You can get help at any time using the `--help` option.\n", "\n", "```\n", - "%status --help\n", + "%opencypher_status --help\n", "```\n", "\n", "**Note:** If you are using a cell magic the cell body needs at least one character in it for `--help` to work.\n", @@ -429,4 +429,4 @@ }, "nbformat": 4, "nbformat_minor": 5 -} \ No newline at end of file +} diff --git a/src/graph_notebook/notebooks/02-Neptune-Analytics/02-Graph-Algorithms/00-Amazon-Neptune-Analytics-Algorithm-Support.pdf b/src/graph_notebook/notebooks/02-Neptune-Analytics/02-Graph-Algorithms/00-Amazon-Neptune-Analytics-Algorithm-Support.pdf new file mode 100644 index 00000000..bb04b6e8 Binary files /dev/null and b/src/graph_notebook/notebooks/02-Neptune-Analytics/02-Graph-Algorithms/00-Amazon-Neptune-Analytics-Algorithm-Support.pdf differ diff --git a/src/graph_notebook/notebooks/02-Neptune-Analytics/02-Graph-Algorithms/02-Path-Finding-Algorithms.ipynb b/src/graph_notebook/notebooks/02-Neptune-Analytics/02-Graph-Algorithms/02-Path-Finding-Algorithms.ipynb index a83fc0ef..ec91fd64 100644 --- a/src/graph_notebook/notebooks/02-Neptune-Analytics/02-Graph-Algorithms/02-Path-Finding-Algorithms.ipynb +++ b/src/graph_notebook/notebooks/02-Neptune-Analytics/02-Graph-Algorithms/02-Path-Finding-Algorithms.ipynb @@ -544,7 +544,9 @@ "\n", "* [Community detection/clustering algorithms](./04-Community-Detection-Algorithms.ipynb)\n", "\n", - "* [Similarity algorithms](./05-Similarity-Algorithms.ipynb)" + "* [Similarity algorithms](./05-Similarity-Algorithms.ipynb)\n", + "\n", + "* [Vector Similarity algorithms](./06-Vector-Similarity-Algorithms.ipynb)" ] } ], diff --git a/src/graph_notebook/notebooks/02-Neptune-Analytics/02-Graph-Algorithms/03-Centrality-Algorithms.ipynb b/src/graph_notebook/notebooks/02-Neptune-Analytics/02-Graph-Algorithms/03-Centrality-Algorithms.ipynb index 6819e8fc..ce941c96 100644 --- a/src/graph_notebook/notebooks/02-Neptune-Analytics/02-Graph-Algorithms/03-Centrality-Algorithms.ipynb +++ b/src/graph_notebook/notebooks/02-Neptune-Analytics/02-Graph-Algorithms/03-Centrality-Algorithms.ipynb @@ -400,7 +400,9 @@ "\n", "* [Community detection/clustering algorithms](./04-Community-Detection-Algorithms.ipynb)\n", "\n", - "* [Similarity algorithms](./05-Similarity-Algorithms.ipynb)" + "* [Similarity algorithms](./05-Similarity-Algorithms.ipynb)\n", + "\n", + "* [Vector Similarity algorithms](./06-Vector-Similarity-Algorithms.ipynb)" ] } ], diff --git a/src/graph_notebook/notebooks/02-Neptune-Analytics/02-Graph-Algorithms/04-Community-Detection-Algorithms.ipynb b/src/graph_notebook/notebooks/02-Neptune-Analytics/02-Graph-Algorithms/04-Community-Detection-Algorithms.ipynb index 3f307c7d..be43d8b0 100644 --- a/src/graph_notebook/notebooks/02-Neptune-Analytics/02-Graph-Algorithms/04-Community-Detection-Algorithms.ipynb +++ b/src/graph_notebook/notebooks/02-Neptune-Analytics/02-Graph-Algorithms/04-Community-Detection-Algorithms.ipynb @@ -371,7 +371,9 @@ "\n", "* [Centrality algorithms](./03-Centrality-Algorithms.ipynb)\n", "\n", - "* [Similarity algorithms](./05-Similarity-Algorithms.ipynb)" + "* [Similarity algorithms](./05-Similarity-Algorithms.ipynb)\n", + "\n", + "* [Vector Similarity algorithms](./06-Vector-Similarity-Algorithms.ipynb)" ] } ], diff --git a/src/graph_notebook/notebooks/02-Neptune-Analytics/02-Graph-Algorithms/06-Vector-Similarity-Algorithms.ipynb b/src/graph_notebook/notebooks/02-Neptune-Analytics/02-Graph-Algorithms/06-Vector-Similarity-Algorithms.ipynb new file mode 100644 index 00000000..a4edabd4 --- /dev/null +++ b/src/graph_notebook/notebooks/02-Neptune-Analytics/02-Graph-Algorithms/06-Vector-Similarity-Algorithms.ipynb @@ -0,0 +1,388 @@ +{ + "cells": [ + { + "attachments": { + "02e14c92-e464-46c9-9dfd-0ebb4a676358.png": { + "image/png": "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zTJwXU4EREAEREAEJL76DYiACIiACIhAlglIfLMMXJcTAREQAREQgWpCIAIiIAIiIAL5QIClCH7//Xf37bffuu+++86tXr3aVa1a1W233XauXr16bquttnIbbZQfNqXENx9+cWqjCIiACBQxgV9//dV9/fXXbtKkSW727Nlu8eLF7scff3S//fabie2WW27p6tSp43beeWfXtm1bt8suu7hNN93UValSJbTUJL6hvTVqmAiIgAiIwIoVK9wXX3zhxo4d69566y03efJk98svv7g///yzBJzq1au7Zs2auS5durhDDjnE7bnnnmYRV6sWTpmroiUFS9w/fREBERABEQgBAe9iRnQfeeQRN2LECLN0y2oaItykSRN3/fXXmxDvsMMOoXRF54dzvCza2i8CIiACIlBQBBjbnTp1qrv33nvdqFGj3Nq1a1PqH8d99dVX7vLLLzdLGfd0GIvEN4x3RW0SAREQgSImgNX7008/uQceeMDGeQmsYluiEj+uy3EIMOPCTz/9tBs3blyp5yaqL1vbwukMz1bvdR0REAEREIHQEVi1apWbNm2ae++99yyq2QsvQssYbvv27S2oascdd7Sx3wULFriPP/7YffPNN1EL+Y8//nATJ050u+22mx3foEGDUPVT4huq26HGiIAIiIAIfP/99+6DDz5wy5Yts6lFnsjGG2/sdt11V3fCCSe4gw46yMZ2EVmE+vnnn3evv/66+/LLL/3h7ocffrBgLQK2JL5RLPogAiIgAiIQdgJYnWvWrDHXLeLHK9MF0Rw/fnzUiuV6WL01atRwF1xwgevRo4erXbt2tBlENteqVcutW7fOzZs3r8R5WMWI82GHHRY9PgwfNOYbhrugNoiACIhACAkgvIy3MsWHaOOZM2c6LM1MF6YSLVy40MTUX2uTTTZxdevWdV27dnXbb7+93xx9r1+/vs3xZa5vbEHIScoRtiK3c9juiNojAiIgAjkmwBxaLEhcv2PGjHHTp0839y/i17FjR9etWze3xx57ZCyRBQLvrW2PAvGtWbOm23zzzS2rld/u3xkL3nrrrc069tt4J2qaZBxhKxLfsN0RtUcEREAEckDARwnPmTPHffbZZ+7TTz+1d8ZLf/75ZwtsYjx11qxZbsaMGW733Xe3V+vWrR2BT/FRx5XpAikj4zNUIchY4UQy83AQn0aS9hOoRZR0bMFN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AiKQhMBLL71kaRL32msvWxpvyJAhtpJQklMKcheLIZBmkmxZfnWk2EUc8qHTcjvnw11SG0VABIqWACvuYN3y+vLLL20Zv08++cQWIShWKLjbWbCBVY5I4oEVzNq/jP/mS5H45sudUjtFQASKigALyHvRJakEa+fyqlWrVlFxSNTZadOmWb5oVk7C5UzaSVYukvgmoqVtIiACIiACZRJ48803TXRZjIDxzAceeMD169evzPOK6YAff/zRLN/PP//cBLdz587mhs4nBhrzzae7pbaKgAgULIHHHnvMderUyaJ4caWyKP0HH3wg4S3QO15Q4vvOO+9k7TY98cQTtrZk1i6oC4mACBQcgcWLF7vrrrvO1a1b17HqD2vWzpo1yz3//PM2l7XgOqwORQnktfgitrzIdsLkat4TlZUrVyba7Pjh476oSPn9998dr2SF/aw9uXbt2mSHaZ8IiECRERg/frw744wzHPNVX375ZXfNNde4pUuXujvuuCOvUiQW2W1La3fzUny94CK2119/vQFhojmh5oSeszAzy07NmzfPlqAi+0mPHj2iCzBPmTLFtWnTxubJ7bfffpaa7IcffrB6cPWwaDNPnpT33nvP6jjxxBPtO//wH+XOO+90V199tTv88MPt9Z///Ce6nw/9+/e3CLx//etfFoV30003ldivLyIgAsVHAKHt3r27Y4ySh//Bgwe7qVOnuosuusiRt1ileAjkVbQz7pl3333XrF0mVyO4vMcWIt/4QR999NHuhBNOcAMHDrQk3DxVMo6COPft29eNGDEi+oT53HPPufPOO8+98MILttjzP//5T/fzzz9btbiBmFMXK7633nqrCWrjxo0tyXfs9fnM3DsmwWP1MhmcOWksGM1/PNpFP3ipiIAIFD6B1atXR6OWZ86c6c466yw3ceJEt8ceexR+59XDUgnkheWLUOFWRnixVLFwEwlvbC8RvVdffdWEl+1YwhTmx3Xr1i0qvGw79thjHVFzTNpOR0F8WSQa6/i2226z9y233NLaQ/1Y6/RHApwO2qpDBMJJAKFlHJe/PXjCMAaWLFliQizhDec9y2arqmXzYuW9Fu5lhIr3a6+9ttxihQDGlz///HODlS8QQqzidGVIQWhbt25trqXY6/v2cB2El77xqkjfYuvVZxEQgfAQeOutt0xg8XThNbvvvvvcqaeeGp4GqiWhIBBay5fxXB9A5cUqHcR44nzjjTfc119/Ha1u+PDhZgn7MZftttuuxH7c0fGFydzffvttdDOi7l3VWNLDhg2zZN9cjxdjzD/99FP0eMSXfiG8CDDfVURABPKXwOOPP24P3IzpssYs8SMffvihhDd/b2lGW14to7VXoHKsXESXsdyyXMuJqufHjotn3LhxFnjFf4KGDRu6e++91w7HKn3mmWfMfb377rtb0APjxE8//XS0Op5WEUQEmnRuLVq0MMHGndy7d287jkCrXr16uQkTJth3ohcJwCLTCq5xgr1Id9azZ0/bTyaWyy+/fIOUcF50vRVckT5HG64PIiACWSXAA7jPQsWsinPOOcc99dRTJYa1stogXSxvCIRKfBGiyrphyQjDK1nBEmUuHQKJ8DLHLrYg2GSZmT17tu3bYostouLtj+M8nmwJqtp4443tONzXvuBmYoxn7ty5Nv674447+l0bvNNv/+LBQ27oDRBpgwiEigAP3YguiTEYYuLBm9SP3nsWqsZWsDF45tasWWPTMfHsUVjEgIcMvvN3MheFa9MG2kLhO1NGaSttjv07nIv2pXrN0IhvOoQ31U5zHAFZTZs2TXpKs2bNku5nZ6NGjUo9hnUmW7ZsWer++B0woPAAQvHf7Yv+EQERyDkBUj4iujycs54sMyu8NyznjUtTAxAwFixYtGiRzdogcGzdunVWO7NGiNTGq8ffvjp16tg6upkWPNqE2OJpwOChDbSFQttoI95OcivstNNObocddgi9CIdCfLH2cDfL5bpecCXA9v9K/4hAzgkwPdG7llnsoFCnCjElEoEjdoW/xzxYMMPk+++/j94DhI8hOoSO2JbDDjvMhJjhvOrVq0ePS+cHZqGwshOrGL322ms2JXTBggVm6XId9jPciDeTIT/yPHQNhi233nprezDA0Apjybn4Sng3/Fl4i1cCvCEbbRGBbBEg3sOLLjMV/KpCftpittqRjevguiXGhWBUEgwx5EbioUTTL9mGEN59992Wu+CAAw4wwdt3331t1kg624tVi7DyIDBmzBhrIw9DPCjEF9r7+uuvm7WO1/K4446z5EpYwrlykce3MfZ7TsUXkZHFG3s71n+WAK9noU8ikE0Co0ePNtFl4XqCLwnWJIajEAsiRo4D/g4jcrhvcTeXJnAwwAWMKGIhI8K8T5482bVt29YddNBBFvHN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edSoUbZkGeO5//73vyW8ZcHTfhEQgawQyIj4+pZr3NeTyOw747qrV69OehFc0kxHytfCilll/Z4IOCPanteqVavca6+95j799FNbR7dKlSr52nW1WwREoAAJZER8GfPldf311xcgsvB1iQhnxjWTFY5h3c58LAiv/y3xu4otTBW69dZbzZ1++umnu1133dUW3B46dKg79NBDYw/VZxEQAREIDYGMiC+9w/Wskj0CfnpRaVcsa39p5+V6e6zwxrbFTxWqXbu2e/TRR925555rU4Xuvfde17Zt29hD9VkEREAEQkcgY+JLT3ETluUqDB0RNSg0BPjteIvXNyp2qtD06dPdoEGD3Jw5c9xVV13lttlmG3+Y3kVABEQg1AQyJr5yPYf6vudF4xIFWDFVCJEdN26cGzt2rDv++OPzoi9qpAiIgAjEEsjYVCMugus50R/Q2AboswgkIpDod7PZZptZBDNju/vss0+i07RNBERABPKCQMYsX3rvg2Pkes6L30JoGsk47+eff75Be3799VeHqxlh1m9qAzzaIAIikEcEMiq+cMD6jR+3yyM+amqWCfgAqx9//DF6ZdzM/uU3IsAcqyICIiAC+Uggo25ngGD9Ir5YKt4SzkdQanN2CPAbif+dxFu5Pnsa7/pdZee+6CoiIALpJZAV8eWPKQIc/0c1vV1RbYVAINFvJNG2Quir+iACIlC8BDLudgatTzcpN2Hx/tDUcxEQAREQgfUEsiK+WC4a+10PXZ9EQAREQASKm0BWxBfEWL2IcKIpJMV9C9R7ERABERCBYiOQNfEFrHc/xwfQFBt09VcEREAERKC4CWRVfL37WdZvcf/o1HsREAERKHYCWRVfYMv9XOw/OfVfBERABEQg6+ILcu9+RohVREAEREAERKDYCOREfL37mbm/EuBi+8mpvyIgAiIgAhlPslEaYi+6PvWk/17a8dqemAD5jlevXu3Wrl2b+ID/2+qP++2331z16tWTHqudIiACIiACmSWQM/GlW15wJcDlv8l//vmnW7NmjZs1a5abO3eui82FnKi27777zn355ZduwYIFrkGDBm7TTTdNdJi2iYAIiIAIZIFA1UAAr8vCdUq9BC5oihdg/9026p9SCaxYscK9/vrr9gDz3nvvuZUrV7pIJFLq8Qj11KlT3bx589x2223nmjZtWuqx2iECIiACIpBZAjkXX7rnBVcCXPbNRkQ/+eQT9/DDD7tBgwa5GTNmuFWrViUVXmpFmDl36dKlZi0vXrzY1a1b12211VauatWqZV9YR4iACIiACKSNQCjEl94gwLyefPJJd9ppp1kH+a6ynsDXX3/t3nzzTff888+7N954w82cObPMsd71Z/9PgH/55Re3ZMkSt2jRIof1XK1aNbf11lu7LbbYIvZQfRYBERABEcgggdCIL31s1KiRO/XUU627soINg1msBEsxtovgYu2OHj3aLVu27H8HVODfdevWue+//96s5p9//tksX8SXNXOrVKlSgRp1igiIgAiIQHkIhEp8fcO9xYsAF7MI//HHH+ZSxrU8YMAA99BDD7np06e733//3aOq1DuRz9RH/VyrWbNmbuONNzYxlghXCq1OFgEREIGkBKoEY4GlR+kkPTU7O4kH8wJMcg6+p6OQX5pXuupLR5vi68A9PGLECPfII4+4adOmOSxgopzTXXA9E4TVpk0bd80117g999zTrOB0X0f1iYAIiIAI/I9A6MXX3yhEMt0iTI5pBDidou7bW5l3noeIYB4yZIgbO3as++qrr2wub2XqLOtcgq4233xzt8suu7iePXu6Qw891LVr105u6LLAab8IiIAIVIBA3oiv71usCLMN4cRN7V3V/rhU3hFeBJixTubJ5lqEcSdj7dIuxnc/+OADC4zKhLVbGh9EuFWrVm6//fZzBx98sPFBlDfaaKPSTtF2ERABERCBchLIO/H1/UOgeHlrmO2IJwUh5pVK8dYvxxLwNX/+/JyI8E8//WQWLhYvEd8EWBGZnKtSs2ZN17FjR9evXz+3++67u3r16pllnKv26LoiIAIiUEgE8lZ842+CH7uNFWMvwFhxFP/dn8t3BDx+icNsijAuZubpTpw40aYQvfDCC44I5DAUrGCmIV1yySWuV69erkWLFm6zzTaTKzoMN0dtEAERyGsCBSO+8XcBUeVFeffdd+3df7cvKfzj3dGMu8YLdwqnl3kI7mSCqB588EH38ssvuy+++MKs3TDFwOFu3nLLLV2XLl3cCSec4I488kgT4DI7pwNEQAREQARKJVCw4ltqj2N2eDGOt3z9IQiuH1P229L1jpt58uTJ7oknnnATJkywnMtYwGEtRENj+cIEV3TDhg2VHzqsN0vtEgERCD2BohZf7g7uasZYGev1JZOii1U7e/Zs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RNi7o8kKhgjHL3uY4DRtEgERKHICEt8i/wGo++khwLrJ3h1du3Ztc0efd9556alctYiACBQcAYlvwd1SdSiXBFauXBl1R7OoA5awRDiXd0TXFoFwEpD4hvO+qFV5TgAR9u7o7bff3kT4/PPPz/NeqfkiIALpIiDxTRdJ1SMCCQisWrUq6o5mSUgs4b/+9a8JjtQmERCBYiIg8S2mu62+5ozA6tWro+5oiXDOboMuLAKhISDxDc2tUEOKgcAvv/wSFeFtttnGLOELLrigGLquPoqACMQQkPjGwNBHERABERABEcgGgY2ycRFdQwREQAREQAREYD0Bie96FvokAiIgAiIgAlkhIPHNCmZdRAREQAREQATWE5D4rmehTyIgAgGBhx56yA0cOFAsREAEMkhA4ptBuOWt+plnnnHXXHNNeU/T8SJgBObMmeP+/e9/V5pGjRo13FZbbVXpelSBCIhA6QQkvqWzyfqeTTbZxG2++eZZv25FL3jiiSc65q+qpE5g+fLljsUYSissWbh48eLSdrtIJOIWLVrkfvvttw2O+f33393atWs32B67gfO//vrrpMcdd9xx7uijj449rcTnZG3gQH+NP//8s8R5+iICIrCeQLX1H/Up1wT69u2b6yaU6/oIL3/st9hii3KdV4wHI5g8rCCQjRo1MmaI7Ouvv244fvjhB3f66ae7efPmuTp16pjAPvfcc27XXXe1/aeccopt++OPP1yzZs3cxx9/bFZu7969bT9ekwEDBrgFCxa4zz//3LYxj3jQoEH2mX/++9//umHDhrnWrVtbXZ06dXJ/+9vfovtpI9m3aAv1Xn755dF9fCirDRzzxBNPuEcffdS1bNnSff/992727Nlu9OjR1if2q4iACPwfgeApVSWDBCZMmBAJRDXSvHlzewWLr0dWrFhR4oqfffZZpGvXrva6+eabS+zzXwJ3ou2/9957I8Ef6MgJJ5xg9d133312SK9evWz/+PHjIwcccEAkSOof2XvvvSMvvviir8Legz/+kdtvvz0S/AG28w855JDIjBkzShxT1rWCP6h2rcA1adfwbQ9SJ5aoR1/WE9h3330j77//fnQDDLkHvhxzzDGRQGz918jMmTPt/gQPOLbtu+++i9SvXz8SuJbte5C20thHTwg+TJ8+PfLPf/4zdlPSz/wuA4Hc4JhPP/00csstt2ywPZU2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y/+OqQcikISAxDcJHO0SARHIHgFEmLSV8+fPj7qja9asmb0G6EoikEUCEt8swtalREAEyibw2GOPmQjPnTvXLGGyZm2//fZln6gjQkXgjz/+cL/99pv79ddf7f333393bPvzzz+tnRtttJGrWrWq23jjjV316tUdy1XyzrZiKBLfYrjL6qMI5CGBxx9/3ER4zpw5UXe0RDicNxJBXbdunUNg165da++rV692ZDsjup33lStXmhAjxhTElhdZ0GrVquXq1atn7yzSgSBvsskm9l6tWjWHUBdakfgW2h1Vf0SgwAh4EZ49e3ZUhHfYYYcC62V+d+fnn3923J/PPvvMff75527q1Klu0aJF7pdffjFrF4s3EolEX/S2SpUq0RfWLi+WqNxpp53cLrvs4nbbbTfXoUMH16xZM1eIGdIkvvn9m1frRaBoCAwcONAs4VmzZkXHhLGYVLJPAEsXwZ00aZKbOHGimzJligXMrVixwrHe808//WRWLqJbnoIAYw2ThIU54ES/N2zY0O26666uY8eOrn379ibEhWAJS3zL88vQsSIgAjkn8MQTT5gIz5w5M2oJS4Szc1vWrFnjvv32W7NuEd5p06a5L7/80qxcxDgTBasXa7hFixauTZs2JsBYxXXq1HGbbrppJi6ZlTolvlnBrIuIgAikm4AX4RkzZkRFuHbt2um+jOoLCGDpkh6UB54JEya4t99+294Z18WdXNHC+C7WLdbuvHnzbNyYurCACb5iDJmxZK6Bm5rj99xzT9etWzd7b9mypatbt25ejglLfCv6q9F5IiACoSDw5JNPmiU8ffr0qDt6xx13DEXb8r0RiC4BVMuXL3eDBw92L7zwghs/fnxUJCvSP4QVixXBZTx3jz32MAG999573Q8//GBCSxBWgwYNHOKOG3vVqlUWMY0QUwjC6tSpkzv22GNdnz59HFPSCNDKJ3e0xLcivx6dIwIiEDoCgwYNMhEm2MfnjpYIV+42IXyffPKJu+OOO9wXX3xh4sj0oYoWRBOh7N69uzvssMPMhUzwHBHQ999/vwVs4b7eeeed3THHHGOuZsaTR44c6caOHeuYfuYLljF1tW3b1l1xxRUm4vmUK1zi6++k3kVABAqCgBdhxMKLMOODKuUjgCfhtddec0OGDHF8xvosbwBV7BXr16/v9ttvP3fccceZVYu7mMAqphVh0c4P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gAjkPwGJb/7fQ/VABERABEQgzwhIfPPshqm5IiACIiAC+U9A4pv/91A9EAEREAERyDMCEt88u2FqrgiIgAiIQP4TkPjm/z1UD0RABERABPKMgMQ3z26YmisCIiACIpD/BCS++X8P1QMREAEREIE8IyDxzbMbpuaKgAiIgAjkPwGJb/7fQ/VABERABEQgzwhIfPPshqm5IiACIiAC+U9A4pv/91A9EAEREAERyDMCEt88u2FqrgiIgAjkM4ELL7zQDR06NJ+7kJa2S3zTglGViIAIiEBxE+jZs2dKALbYYgu38cYbp3Rspg9Ktc2ZaEe1TFSqOkVABERABIqLwI8//phSh2+99daUjsvGQam2ORNtkeWbCaqqUwREQARyTGDNmjXu73//u2vTpo3baaedXPfu3d0111zjZsyYEW3ZihUr3DnnnONatGhhLz6zzZdHHnnE7b///m7IkCFu9913t3r69OlT4pjBgwfbMR988IG9czyvp556yldj74MGDYruHzlyZIl9fEnlWhw3b948Rxtq165t7bnooovcypUr2WUllXpSbbOvMyPvERUREAEREIGCI3DqqadGbr/99sgff/wR+f333yPjxo2LBCISGTt2rPWVbXvvvXckEMXIn3/+aS8+s419FN6POOKISJMmTSLffPNNZN26dZH+/ftHLrvsMtvvj/n5558jHTt2jPDuX74OfyDf2ff4449HBg4c6DdH31O51vLlyyP169ePvPjii5HVq1dHli1bFrnxxhsjhxxySLnq8W0pq83RSjPwQZZvRh5pVKkIiIAI5I7AggUL3OjRo90VV1zhNtpoI1etWjW3zz77mDVap04da9ioUaNcp06d3Mknn+yqVKliLz7vueeejn0Uzqtevbp74403XN26dV3VqlXdueee68aPH2/7/TFbbbWV22STTRzv/sW5sYXv7Ntss81iN0c/p3ItrGfa2LdvX7f55pu7HXbYwaz7VatWuc8++8zqSqUe35ay2hxtXAY+lKSTgQuoShEQAREQgewSmD17tgkUohpbTjrppOhX3M9du3aNfvcfunTp4mbOnOkOO+wwv8nVqFEj+rk08YweUMkPya41a9Ys98orr7jXXnutxFUQ36VLl5bYlqyeEgfm6IvEN0fgdVkREAERyBSBmjVrugkTJiStvl69eg4LOb4sWrTI7bjjjvGbQ/G9adOmZnlff/31oWhPZRoht3Nl6OlcERABEQghAYKsvvvuuxLu4WBc1z366KPu7rvvthYTgDVgwAALYPJdIJjpwQcfdD169PCbUn5fu3at++WXX6LHE0m8ZMmS6Pd0fDjuuOOsfVjmseXdd99106ZNi92U0udstLnUhmRgHFlVioAIiIAI5JhAIEaRDh06RALBigTjtJHevXtHrr766sgPP/wQbVkwdhvZddddI/369bMXnz/++OPofo4PxmktmGrx4sW2PRhztcCtm266KXocH55++ulI586dIxdccIEFabVu3ToSiKIdw7n77ruvBXMRvMWLwC62BYJvx6R6rY8++sjOJ1iKfvHeq1evSGCxl6seDk7WZqssg/9Uoe5SlVk7REAEREAE8pYA1u7ChQtdEPHsdt5554T9YB8WL6Vx48YWVJXwwBQ2YklileLSxvWdqeL7FUQ8O1zRBIVVtGSrzfHtk/jGE9F3ERABERABEcgwAY35ZhiwqhcBERABERCBeAIS33gi+i4CIiACIiACGSYg8c0wYFUvAiIgAiIgAvEEJL7xRPRdBERABERABDJMQOKbYcCqXgREQAREQATiCUh844nouwiIgAiIgAhkmIDEN8OAVb0IiIAIiIAIxBOQ+MYT0XcREAEREAERyDABiW+GAat6ERABERABEYgnIPGNJ6LvIiACIiACIpBhAhLfDANW9SIgAiIgAiIQT0DiG09E30VABERABEQgwwQkvhkGrOpFQAREQAREIJ6AxDeeiL6LgAiIgAiIQIYJSHwzDFjVi4AIiIAIiEA8gf8P+OtrlyF0hHwAAAAASUVORK5CYII=" + } + }, + "cell_type": "markdown", + "id": "6406bbbd-420d-4654-9d95-b31d3c91f6a3", + "metadata": { + "tags": [] + }, + "source": [ + "Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n", + "SPDX-License-Identifier: Apache-2.0\n", + "\n", + "# Vector Similarity Algorithms\n", + "\n", + "Vector similarity algorithms work by using vector based representations of data, a.k.a. embeddings, to answer questions about the data's context and its similarity and connection to other data.\n", + "\n", + "For example, a support agent could translate a question that they receive into a vector and use it to search the support knowledge base for articles that are similar to the words in the question (implicit similarity). For the most applicable articles, they could then collect metadata about the author, previous cases, runbooks, and so on so as to provide additional context when answering the question (explicit data).\n", + "\n", + "A Bioinformatics researcher, who is interested in re-purposing existing blood pressure drugs for other treatable diseases, may want to use vector similarity search over in-house knowledge graphs to find patterns in protein interaction networks.\n", + "\n", + "For another example, a large online book retailer may need to use known pirated material to quickly identify similar media in conjunction with a knowledge graph to identify patterns of deceptive listing behaviours and find malicious sellers.\n", + "\n", + "In both cases, vector search over a knowledge graph increases accuracy and speed when building the solution. In comparison to other tools available today, it provides reduced operational overhead and complexity.\n", + "\n", + "## Supported Algorithms\n", + "\n", + "Neptune Analytics supports the following vector similarity algorithms:\n", + "\n", + "* [Manage an Embedding](#Manage-an-Embedding) - This set of algorithms manages the stored embedding for a node\n", + "* [Distance](#Distance) - This algorithm computes the distance between two vectors based on their embeddings. The distance is the L2 norm of the vectors.\n", + "* [topK](#TopK) - This algorithm finds the topK nearest neighbors of an embedding based either on an embedding or by comparing against a node.\n", + "\n", + "## Creating the Graph\n", + "\n", + "To use this dataset, you need to create a graph that has a vector search index with a dimension of 1536. This step must be completed prior to running the following load commands. \n", + "\n", + "This can be done using a command similar to the one below, which can be run from a client that has the AWS CLI configured and appropriate permissions. Please refer to the [documentation](https://docs.aws.amazon.com/neptune-analytics/latest/userguide/vector-similarity.html) for the details on setting up a vector search enabled graph.\n", + "\n", + "```\n", + "aws neptune-graph create-graph --graph-name 'air-routes-embeddings' --provisioned-memory 128 --allow-from-public --replica-count 0 --vector-search '{\"dimension\": 1536}'\n", + "```\n", + " \n", + "## Loading Data\n", + "The first step in most projects is to load data to use for analysis. For demonstration purposes, we have provided a set of datasets that can be used to demonstrate the features and functionality of Neptune Analytics. \n", + "\n", + "The cell below makes it easy to load a modified version of the `air-routes` data into your graph. The `air-routes` dataset we will be loading in this notebook is supplied as part of the [Practical Gremlin](https://kelvinlawrence.net/book/Gremlin-Graph-Guide.html#air) book.\n", + "\n", + "The `air-route` graph contains several vertex types that are specified using labels. The most common ones being `airport` and `country`. There are also nodes for each of the seven continents (`continent`), in addition to a single `version` vertex that I provided as a way to test which version of the graph you are using.\n", + "\n", + "Routes between airports are modeled as edges. These edges carry the `route` label and include the distance between the two connected airport vertices as a property called `dist`. Connections between countries and airports are modelled using an edge with a `contains` label.\n", + "\n", + "Each `airport` vertex has many properties associated with it giving various details about the airport, including its IATA and ICAO codes, its description, the city it is in, and its geographic location.\n", + "\n", + "![image.png](attachment:02e14c92-e464-46c9-9dfd-0ebb4a676358.png)\n", + "\n", + "In addition to the properties of the `air-routes` dataset, the modified version used for this notebook also contains a vector embedding of a JSON version of the node properties. This was accomplished using [LangChain](https://python.langchain.com/docs/get_started/introduction) and [Amazon Bedrock's Titan Text Embedding](https://docs.aws.amazon.com/bedrock/latest/userguide/embeddings.html) model, as shown in the code snippet below.\n", + "\n", + "```\n", + "import boto3\n", + "from langchain.embeddings import BedrockEmbeddings\n", + "import pandas as pd\n", + "\n", + "df = pd.read_csv(\"air-routes-latest-nodes.csv\")\n", + "bedrock_client = boto3.client(service_name=\"bedrock-runtime\", region_name=\"\")\n", + "bedrock_embeddings = BedrockEmbeddings(\n", + " model_id=\"amazon.titan-embed-text-v1\", client=bedrock_client\n", + ")\n", + "for i in df.index:\n", + " if df.loc[i][\"~label\"] == \"airport\":\n", + " json = df.loc[i].to_json()\n", + " embedding = bedrock_embeddings.embed_query(json)\n", + " df.at[i, \"embedding:vector\"] = (\n", + " str(embedding).replace(\", \", \";\").replace(\"[\", \"\").replace(\"]\", \"\")\n", + " )\n", + " if i % 25 == 0:\n", + " print(f\"Embedding row {i}\")\n", + "\n", + "\n", + "df.to_csv(\"air-routes-latest-nodes.csv\", index=False)\n", + "```\n", + "\n", + "To load this dataset, run the two cells below. This first cell will setup a few python variables using the configuration parameters of this Neptune Notebook. The second cell will use Neptune Analytics' batch load feature to load the data from the provided S3 bucket. \n", + "\n", + "**Note:** You only need to do this once. If you have already loaded the data previously, you do not need to load it again." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f603d491-1e89-46a4-95e4-a19ee8186e05", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "import graph_notebook as gn\n", + "config = gn.configuration.get_config.get_config()\n", + "\n", + "s3_bucket = f\"s3://aws-neptune-customer-samples-{config.aws_region}/sample-datasets/gremlin/air-routes-with-embeddings/\"\n", + "region = config.aws_region" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "99e19409-67ef-4aaa-a30d-b3af18eb2d75", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "%%oc \n", + "\n", + "CALL neptune.load({format: \"csv\", \n", + " source: \"${s3_bucket}\", \n", + " region : \"${region}\"})" + ] + }, + { + "cell_type": "markdown", + "id": "91de3353-d031-4e9d-9fa8-a995b3f4b850", + "metadata": {}, + "source": [ + "## Manage an Embedding\n", + "\n", + "In Neptune Analytics, the embedding for each node is stored as a kind of \"hidden\" property that is not returned using standard openCypher. To manage these embeddings, we provide several algorithms to perform standard CRUD operations:\n", + "\n", + "- `neptune.algo.vectors.get`: This will retrieve and return the specified node's embedding.\n", + "\n", + "- `neptune.algo.vectors.upsert`: This will update or insert the provided embedding to the specified node.\n", + "\n", + "- `neptune.algo.vectors.remove`: This will remove the specified node's embedding.\n", + "\n", + "These algorithms can be combined with other openCypher clauses to perform more complex patterns, such as upserting a node with an externally generated embedding at the time of creation.\n", + "\n", + "Using our air routes data, let's show how to return the embeddings of both the Seattle and Anchorage airports." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "eb54e10f-7414-451c-831f-f132cf6960a1", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "%%oc\n", + "\n", + "MATCH (n) \n", + "WHERE n.code in ['SEA', 'ANC']\n", + "CALL neptune.algo.vectors.get(n)\n", + "YIELD node, embedding\n", + "RETURN n.code, embedding" + ] + }, + { + "cell_type": "markdown", + "id": "e689a29b-8437-4498-bc32-97fab7cb1114", + "metadata": {}, + "source": [ + "## Distance\n", + "\n", + "Vector distance is an algorithm that computes the distance between two vectors based on their embeddings. The distance is calculated from the L2 norm of the vectors and can represent the similarity or dissimilarity of the entities.\n", + "\n", + "\n", + "Here are some common uses of vector similarity distance algorithms:\n", + "\n", + "- Document/Description similarity/plagiarism detection. Comparing the vector representations of documents to find similar or duplicate content. Algorithms like SBERT may be used to generate a document embedding for comparison.\n", + "\n", + "- Recommender systems. Calculating the similarity between user/item vectors to recommend content the user may like. This includes uses like movie, product, or article recommendations.\n", + "\n", + "- Semantic search. Mapping words/documents to vectors and using similarity measures to return semantically similar search results compared to just text match.\n", + "\n", + "- Deduplication. Identifying duplicates in datasets by comparing vectors and finding entries with high similarity scores. Helps remove redundant data.\n", + "\n", + "- Anomaly detection. Identifying outliers by comparing vector deviations against expected distributions. Used for fraud or anomaly detection in finance, networks, etc.\n", + "\n", + "In general, vector similarity algorithms leverage distance comparisons in the high-dimensional vector space to understand how related or distinguishable data points are from each other. Choosing the appropriate similarity algorithm depends on the use case and data type.\n", + "\n", + "\n", + "Using our air routes data, let's find the similarity distance between the Anchorage (AK) airport and Fairbanks (AK) and Seattle (WA) airports. When looking at the results, the smaller the distance, the more similar the two nodes are." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cc4ee438-71b3-4962-9cbc-4341c64dc9bf", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "%%oc\n", + "\n", + "MATCH (n {code: 'ANC'}) \n", + "MATCH (m) WHERE m.code in ['SEA', 'FAI'] \n", + "CALL neptune.algo.vectors.distance(n, m)\n", + "YIELD distance\n", + "RETURN n.code, m.code, distance" + ] + }, + { + "cell_type": "markdown", + "id": "e32d532e-23bd-4ade-92ef-fe926a97e936", + "metadata": {}, + "source": [ + "As we see from the results, the Fairbanks airport is more similar to the Anchorage airport, which makes sense as they are both located in Alaska and have similar characteristics in their properties." + ] + }, + { + "cell_type": "markdown", + "id": "2a138e97-fc87-4045-b3b1-f8d1d6369aae", + "metadata": { + "tags": [] + }, + "source": [ + "## TopK \n", + "\n", + "TopK is a vector similarity algorithm that finds the topK nearest neighbors of a node based on the distance of their vector embeddings from a comparison embedding. In Neptune Analytics, the comparison embedding can either be provided directly using the `neptune.algos.vectors.topKByEmbedding()` algorithm, or from an input node `neptune.algos.vectors.topKByNode()`\n", + "\n", + "\n", + "Some common uses of vector similarity topK algorithms:\n", + "\n", + "1. Recommender systems - Finding the most similar items to recommend based on a user's interests or purchase history. TopK allows recommending the most relevant items.\n", + "\n", + "2. Document retrieval - Retrieving the most similar documents to a query document from a corpus. This allows finding documents on the same topic.\n", + "\n", + "3. Deduplication - Identifying duplicate or near-duplicate documents in a large collection by finding those with the greatest vector similarity.\n", + "\n", + "4. Semantic search - Finding documents that are semantically similar to a query, even if they don't contain the exact keywords. The vector similarities allow searching by meaning and concepts.\n", + "\n", + "5. Content-based image retrieval - Finding visually similar images in a database to a query image. The vector similarities capture visual features to find images depicting similar content.\n", + "\n", + "6. Fraud/anomaly detection - Identifying anomalous data points that have low vector similarity to normal data points can help detect fraud or outliers.\n", + "\n", + "So in summary, vector similarity topK algorithms are very useful for finding the closest semantic \"neighbors\" to queries in order to improve search, recommendations, retrieval, etc. Computing top similarities allows focusing only on the most relevant data.\n", + "\n", + "Using our air routes data, let's find the topK most similar nodes to the Anchorage airport. Let's first start by fetching the embedding for the Anchorage airport and saving that as a Python variable" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2b176b23-8804-4367-a5f4-00df45145e87", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "%%oc --store-to res --silent\n", + "\n", + "MATCH ( n:airport {code: 'ANC'} ) \n", + "CALL neptune.algo.vectors.get(n) YIELD embedding\n", + "RETURN embedding" + ] + }, + { + "cell_type": "markdown", + "id": "6eaff4b8-ce7e-42df-99e6-be378a7b9a80", + "metadata": {}, + "source": [ + "We can now see that we successfully retrieved the embedding by printing the length of the returned embedding, which is represented a list of floats." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1c9c54fb-7b9b-4edb-9615-cad7d410311a", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "embedding = res['results'][0]['embedding']\n", + "print(len(embedding))" + ] + }, + { + "cell_type": "markdown", + "id": "7d5c2391-7bbe-4bfd-b2fc-eab1bda421ed", + "metadata": {}, + "source": [ + "Now that we have an embedding, let's retrieve the topK most similar nodes to this embedding." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "45b69407-ece3-4820-8c04-3b53bbb6adb9", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "%%oc\n", + "\n", + "CALL neptune.algo.vectors.topKByEmbedding(\n", + " ${embedding}\n", + ")\n", + "YIELD node, score\n", + "RETURN node.code, node.desc, score" + ] + }, + { + "cell_type": "markdown", + "id": "3fc0fb9b-a201-4a37-be4a-a0260dc4cff5", + "metadata": {}, + "source": [ + "As expected, the top result returned is the Anchorage airport, which makes sense given that the embedding passed in was that of the Anchorage airport. Looking at the score, we also see that value is ~0. When looking at similarity scores, the lower the value, the more similar the two items are.\n", + "\n", + "In this example, we retrieved an embedding and then used that as the input. In most situations, you should use the `topKByEmbedding` algorithm only when you have an externally provided embedding that you want to compare against. If you want to compare against an embedding inside the graph, you should instead use the `topKByNode` algorithm. With this algorithm, we can find input nodes using openCypher, then pass that into the algorithm as input. \n", + "\n", + "Using this approach, let's find the topK most similar nodes to the Anchorage airport." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "275300eb-553c-4f42-a1a9-431ab18a672e", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "%%oc\n", + "\n", + "MATCH ( n:airport {code: 'ANC'} ) \n", + "CALL neptune.algo.vectors.topKByNode(n)\n", + "YIELD node, score\n", + "RETURN node.code, node.desc, score" + ] + }, + { + "cell_type": "markdown", + "id": "57d8ae64-f9a9-4b72-a7ce-7dfdb98919ab", + "metadata": {}, + "source": [ + "Comparing the results, we see that we get back the same topK most similar airports." + ] + }, + { + "cell_type": "markdown", + "id": "d4ec376a-48ef-4952-bd46-cd0ffc23206b", + "metadata": { + "tags": [] + }, + "source": [ + "## Next Steps\n", + "In this notebook, we have demonstrated how to use the vector similarity algorithms provided by Neptune Analytics. If you are interested in the other types of algorithms offered, please look at the other notebooks available:\n", + "\n", + "* [Path finding algorithms](./02-Path-Finding-Algorithms.ipynb)\n", + "\n", + "* [Centrality algorithms](./03-Centrality-Algorithms.ipynb)\n", + "\n", + "* [Community Detection algorithms](./04-Community-Detection-Algorithms.ipynb)\n", + "\n", + "* [Similarity algorithms](./05-Similarity-Algorithms.ipynb)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.8" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/test/unit/notebooks/test_validate_notebooks.py b/test/unit/notebooks/test_validate_notebooks.py index 87117228..acb82050 100644 --- a/test/unit/notebooks/test_validate_notebooks.py +++ b/test/unit/notebooks/test_validate_notebooks.py @@ -52,6 +52,7 @@ def test_no_extra_notebooks(self): f'{NOTEBOOK_BASE_DIR}/02-Neptune-Analytics/02-Graph-Algorithms/03-Centrality-Algorithms.ipynb', f'{NOTEBOOK_BASE_DIR}/02-Neptune-Analytics/02-Graph-Algorithms/04-Community-Detection-Algorithms.ipynb', f'{NOTEBOOK_BASE_DIR}/02-Neptune-Analytics/02-Graph-Algorithms/05-Similarity-Algorithms.ipynb', + f'{NOTEBOOK_BASE_DIR}/02-Neptune-Analytics/02-Graph-Algorithms/06-Vector-Similarity-Algorithms.ipynb', f'{NOTEBOOK_BASE_DIR}/02-Neptune-Analytics/03-Sample-Use-Cases/Overview.ipynb', f'{NOTEBOOK_BASE_DIR}/02-Neptune-Analytics/03-Sample-Use-Cases/01-FinTech/01-Fraud-Ring-Identifcation.ipynb', f'{NOTEBOOK_BASE_DIR}/02-Neptune-Analytics/03-Sample-Use-Cases/02-Investment-Analysis/01-EDGAR-Competitior-Analysis-using-Knowledge-Graph-Graph-Algorithms-and-Vector-Search.ipynb',