Skip to content

Commit

Permalink
Merge pull request #619 from graphistry/docs/metadata
Browse files Browse the repository at this point in the history
Docs/metadata
  • Loading branch information
lmeyerov authored Dec 24, 2024
2 parents 7f5b2db + 2acd391 commit c18d72a
Show file tree
Hide file tree
Showing 16 changed files with 239 additions and 14 deletions.
2 changes: 1 addition & 1 deletion .readthedocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ build:
- cp -r demos docs/source/demos
- cp README.md docs/source/README.md
- cp ARCHITECTURE.md docs/source/ARCHITECTURE.md
- cp CONTRIBUTE.md docs/source/CONTRIBUTE.md
- cp CONTRIBUTING.md docs/source/CONTRIBUTING.md
- cp DEVELOP.md docs/source/DEVELOP.md

# build html
Expand Down
2 changes: 1 addition & 1 deletion ARCHITECTURE.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Architecture

This document should help get started with modifying code. See also [develop.md](DEVELOP.md) for developer commands and [CONTRIBUTE.md](CONTRIBUTE.md) for community guidelines.
This document should help get started with modifying code. See also [develop.md](DEVELOP.md) for developer commands and [CONTRIBUTING.md](CONTRIBUTING.md) for community guidelines.

## Client/Server Wrangling Tool

Expand Down
21 changes: 21 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,27 @@ The changelog format is based on [Keep a Changelog](https://keepachangelog.com/e
This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html) and all PyGraphistry-specific breaking changes are explictly noted here.

## [Development]

## [0.35.3 - 2024-12-24]

### Docs

* Rename CONTRIBUTE.md to CONTRIBUTING.md to match OSS standards (snyk)
* setup.py: add project_urls
* Add FUNDING.md
* Add CODE_OF_CONDUCT.md

### Feat

* GFQL serialization: Edge AST node deserializes as more precise `ASTEdge` subclasses

### Fixes

* GFQL Hop: Detect #614 of node id column name colliding with edge src/dst id column name and raise `NotImplementedError`
* MyPy: Remove explicit type annotation from Engine

## [0.35.2 - 2024-12-13]

### Docs

* Python remote mode notebook: Fixed engine results
Expand Down
29 changes: 29 additions & 0 deletions CODE_OF_CONDUCT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# PyGraphistry Code of Conduct

PyGraphistry is an open-source project built on collaboration and respect. This Code of Conduct lays out how we expect everyone to interact so we can maintain a positive and productive environment.

## How We Work Together

- Treat others decently—listen, respond constructively, and keep it professional.
- Stay focused on ideas and solutions, not personal disagreements.
- Be helpful, especially to newcomers or those asking for clarification.

## What’s Not Okay

Harassment, personal attacks, or sharing private information without permission are not okay. Be respectful and avoid language or behavior that alienates or disrupts others.

## Where This Applies

This Code of Conduct applies to all spaces related to the PyGraphistry project, like GitHub repositories, pull request discussions, and private communications. If you’re representing PyGraphistry in public (e.g., social media or events), we expect the same standards of behavior.

## If There’s a Problem

If you see or experience behavior that doesn’t align with this Code of Conduct, let us know at **[[email protected]](mailto:[email protected])**. We’ll handle it privately and work together to figure out the next steps.

## What Happens Next

Breaking the rules? We’ll handle it case by case. You might get a warning, a temporary ban, or permanent removal from the project.

---

Thanks for being part of PyGraphistry!
File renamed without changes.
2 changes: 1 addition & 1 deletion DEVELOP.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Development Setup

See also [CONTRIBUTE.md](CONTRIBUTE.md) and [ARCHITECTURE.md](ARCHITECTURE.md)
See also [CONTRIBUTING.md](CONTRIBUTING.md) and [ARCHITECTURE.md](ARCHITECTURE.md)

Development is setup for local native and containerized Python coding & testing, and with automatic GitHub Actions for CI + CD. The server tests are like the local ones, except against a wider test matrix of environments.

Expand Down
32 changes: 32 additions & 0 deletions FUNDING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Supporting PyGraphistry

PyGraphistry is an open-source Python project proudly sponsored by [Graphistry](https://www.graphistry.com), a leader in GPU-accelerated visual graph analytics and AI. Your support helps us maintain and expand PyGraphistry as a free and open tool for the data science and graph analytics community.

## How to Support

If your organization uses PyGraphistry or benefits from GPU-accelerated analytics, please consider supporting this project indirectly by becoming a customer or directly such as through code contributions and sponsored development. Reach out to the Graphistry team to discuss sponsorship opportunities:

- Email: [[email protected]](mailto:[email protected])
- Website: [Graphistry Sponsorship](https://www.graphistry.com)
- Social: Say hello and stay connected via the [community Slack](https://join.slack.com/t/graphistry-community/shared_invite/zt-53ik36w2-fpP0Ibjbk7IJuVFIRSnr6g), [Twitter](https://twitter.com/graphistry) account, & [LinkedIn](https://www.linkedin.com/company/graphistry)

## Why Support PyGraphistry?

Your contributions help us:
- Keep PyGraphistry maintained, up-to-date, and secure
- Be responsive to user feedback, bug reports, and feature requests
- Advance PyGraphistry's capabilities, performance, and usability
- Ensure the project remains free and open-source
- Support the community through documentation, tutorials, and responsive issue tracking

## Corporate Sponsorship Opportunities

We welcome sponsorships from companies that depend on PyGraphistry for analytics, visualization, and AI. Sponsorships may include:
- Recognition in PyGraphistry's release notes and documentation
- Early access to new features and roadmap acceleration
- Collaboration opportunities with the Graphistry team

## Thank You

A huge thanks to the Graphistry team and community for supporting PyGraphistry, and to all our users for their continued support!

2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -207,5 +207,5 @@ Explore [10 Minutes to PyGraphistry](https://pygraphistry.readthedocs.io/en/late

## Contribute

See [CONTRIBUTE](https://pygraphistry.readthedocs.io/en/latest/CONTRIBUTE.html) and [DEVELOP](https://pygraphistry.readthedocs.io/en/latest/DEVELOP.html) for participating in PyGraphistry development, or reach out to our team
See [CONTRIBUTING](https://pygraphistry.readthedocs.io/en/latest/CONTRIBUTING.html) and [DEVELOP](https://pygraphistry.readthedocs.io/en/latest/DEVELOP.html) for participating in PyGraphistry development, or reach out to our team

2 changes: 1 addition & 1 deletion docs/docker/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ COPY docs/source /docs/source
COPY demos /docs/source/demos
COPY README.md /docs/source/README.md
COPY ARCHITECTURE.md /docs/source/ARCHITECTURE.md
COPY CONTRIBUTE.md /docs/source/CONTRIBUTE.md
COPY CONTRIBUTING.md /docs/source/CONTRIBUTING.md
COPY DEVELOP.md /docs/source/DEVELOP.md

# Step 6: Set the working directory for Sphinx to the `source/` folder
Expand Down
2 changes: 1 addition & 1 deletion docs/source/graphistry.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,6 @@
support
ecosystem
ARCHITECTURE.md
CONTRIBUTE.md
CONTRIBUTING.md
DEVELOP.md

8 changes: 4 additions & 4 deletions graphistry/Engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@


class Engine(Enum):
PANDAS : str = 'pandas'
CUDF : str = 'cudf'
DASK : str = 'dask'
DASK_CUDF : str = 'dask_cudf'
PANDAS = 'pandas'
CUDF = 'cudf'
DASK = 'dask'
DASK_CUDF = 'dask_cudf'

class EngineAbstract(Enum):
PANDAS = Engine.PANDAS.value
Expand Down
60 changes: 59 additions & 1 deletion graphistry/compute/ast.py
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,22 @@ def __init__(self,
edge_query=edge_query
)

@classmethod
def from_json(cls, d: dict) -> 'ASTEdge':
out = ASTEdgeForward(
edge_match=maybe_filter_dict_from_json(d, 'edge_match'),
hops=d['hops'] if 'hops' in d else None,
to_fixed_point=d['to_fixed_point'] if 'to_fixed_point' in d else DEFAULT_FIXED_POINT,
source_node_match=maybe_filter_dict_from_json(d, 'source_node_match'),
destination_node_match=maybe_filter_dict_from_json(d, 'destination_node_match'),
source_node_query=d['source_node_query'] if 'source_node_query' in d else None,
destination_node_query=d['destination_node_query'] if 'destination_node_query' in d else None,
edge_query=d['edge_query'] if 'edge_query' in d else None,
name=d['name'] if 'name' in d else None
)
out.validate()
return out

e_forward = ASTEdgeForward # noqa: E305

class ASTEdgeReverse(ASTEdge):
Expand Down Expand Up @@ -430,6 +446,22 @@ def __init__(self,
edge_query=edge_query
)

@classmethod
def from_json(cls, d: dict) -> 'ASTEdge':
out = ASTEdgeReverse(
edge_match=maybe_filter_dict_from_json(d, 'edge_match'),
hops=d['hops'] if 'hops' in d else None,
to_fixed_point=d['to_fixed_point'] if 'to_fixed_point' in d else DEFAULT_FIXED_POINT,
source_node_match=maybe_filter_dict_from_json(d, 'source_node_match'),
destination_node_match=maybe_filter_dict_from_json(d, 'destination_node_match'),
source_node_query=d['source_node_query'] if 'source_node_query' in d else None,
destination_node_query=d['destination_node_query'] if 'destination_node_query' in d else None,
edge_query=d['edge_query'] if 'edge_query' in d else None,
name=d['name'] if 'name' in d else None
)
out.validate()
return out

e_reverse = ASTEdgeReverse # noqa: E305

class ASTEdgeUndirected(ASTEdge):
Expand Down Expand Up @@ -460,6 +492,22 @@ def __init__(self,
edge_query=edge_query
)

@classmethod
def from_json(cls, d: dict) -> 'ASTEdge':
out = ASTEdgeUndirected(
edge_match=maybe_filter_dict_from_json(d, 'edge_match'),
hops=d['hops'] if 'hops' in d else None,
to_fixed_point=d['to_fixed_point'] if 'to_fixed_point' in d else DEFAULT_FIXED_POINT,
source_node_match=maybe_filter_dict_from_json(d, 'source_node_match'),
destination_node_match=maybe_filter_dict_from_json(d, 'destination_node_match'),
source_node_query=d['source_node_query'] if 'source_node_query' in d else None,
destination_node_query=d['destination_node_query'] if 'destination_node_query' in d else None,
edge_query=d['edge_query'] if 'edge_query' in d else None,
name=d['name'] if 'name' in d else None
)
out.validate()
return out

e_undirected = ASTEdgeUndirected # noqa: E305
e = ASTEdgeUndirected # noqa: E305

Expand All @@ -472,7 +520,17 @@ def from_json(o: JSONVal) -> Union[ASTNode, ASTEdge]:
if o['type'] == 'Node':
out = ASTNode.from_json(o)
elif o['type'] == 'Edge':
out = ASTEdge.from_json(o)
if 'direction' in o:
if o['direction'] == 'forward':
out = ASTEdgeForward.from_json(o)
elif o['direction'] == 'reverse':
out = ASTEdgeReverse.from_json(o)
elif o['direction'] == 'undirected':
out = ASTEdgeUndirected.from_json(o)
else:
raise ValueError(f'Edge has unknown direction {o["direction"]}')
else:
raise ValueError('Edge missing direction')
else:
raise ValueError(f'Unknown type {o["type"]}')
return out
5 changes: 5 additions & 0 deletions graphistry/compute/hop.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,11 @@ def hop(self: Plottable,
g2 = self.materialize_nodes(engine=EngineAbstract(engine_concrete.value))
logger.debug('materialized node/eddge types: %s, %s', type(g2._nodes), type(g2._edges))

if g2._node == g2._source:
raise NotImplementedError(f'Not supported: Node id column cannot currently have the same name as edge src column: {g2._node}')
if g2._node == g2._destination:
raise NotImplementedError(f'Not supported: Node id column cannot currently have the same name as edge dst column: {g2._node}')

starting_nodes = nodes if nodes is not None else g2._nodes

if g2._edge is None:
Expand Down
48 changes: 45 additions & 3 deletions graphistry/tests/compute/test_chain.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import os
import pandas as pd
from graphistry.compute.predicates.is_in import is_in
from graphistry.compute.predicates.numeric import gt
import pytest

from graphistry.compute.ast import ASTNode, ASTEdge, n, e, e_undirected, e_forward
from graphistry.compute.ast import ASTEdgeUndirected, ASTNode, ASTEdge, n, e, e_undirected, e_forward
from graphistry.compute.chain import Chain
from graphistry.compute.predicates.is_in import IsIn, is_in
from graphistry.compute.predicates.numeric import gt
from graphistry.tests.test_compute import CGFull


Expand Down Expand Up @@ -298,6 +298,25 @@ def test_chain_serialization_pred():
o2 = d.to_json()
assert o == o2

def test_chain_serialize_pred_is_in():

#from graphistry.compute.chain import Chain
#from graphistry import e_undirected, is_in
o = Chain([
e_undirected(
hops=1,
edge_match={"source": is_in(options=[
"Oakville Square",
"Maplewood Square"
])})
]).to_json()
d = Chain.from_json(o)
assert isinstance(d.chain[0], ASTEdgeUndirected), f'got: {type(d.chain[0])}'
assert d.chain[0].direction == 'undirected'
assert d.chain[0].hops == 1
assert isinstance(d.chain[0].edge_match['source'], IsIn)
assert d.chain[0].edge_match['source'].options == ['Oakville Square', 'Maplewood Square']

def test_chain_simple_cudf_pd():
nodes_df = pd.DataFrame({'id': [0, 1, 2], 'label': ['a', 'b', 'c']})
edges_df = pd.DataFrame({'src': [0, 1, 2], 'dst': [1, 2, 0]})
Expand Down Expand Up @@ -416,3 +435,26 @@ def test_preds_more_pd_2():
)
assert len(g2._nodes) == 2
assert set(g2._nodes[g._node].tolist()) == set(['b2', 'c2'])


def test_chain_binding_reuse():
edges_df = pd.DataFrame({'s': ['a', 'b'], 'd': ['b', 'c']})
nodes1_df = pd.DataFrame({'v': ['a', 'b', 'c']})
nodes2_df = pd.DataFrame({'s': ['a', 'b', 'c']})
nodes3_df = pd.DataFrame({'d': ['a', 'b', 'c']})

g1 = CGFull().nodes(nodes1_df, 'v').edges(edges_df, 's', 'd')
g2 = CGFull().nodes(nodes2_df, 's').edges(edges_df, 's', 'd')
g3 = CGFull().nodes(nodes3_df, 'd').edges(edges_df, 's', 'd')

try:
g1_hop = g1.chain([n(), e(), n()])
g2_hop = g2.chain([n(), e(), n()])
g3_hop = g3.chain([n(), e(), n()])
except NotImplementedError:
return

assert g1_hop._nodes.shape == g2_hop._nodes.shape
assert g1_hop._edges.shape == g2_hop._edges.shape
assert g1_hop._nodes.shape == g3_hop._nodes.shape
assert g1_hop._edges.shape == g3_hop._edges.shape
22 changes: 22 additions & 0 deletions graphistry/tests/compute/test_hop.py
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,28 @@ def test_hop_predicates_fail_destination_forward(self, g_long_forwards_chain: CG
assert g2._edges[['s', 'd']].sort_values(['s', 'd']).to_dict(orient='records') == []


def test_hop_binding_reuse():
edges_df = pd.DataFrame({'s': ['a', 'b'], 'd': ['b', 'c']})
nodes1_df = pd.DataFrame({'v': ['a', 'b', 'c']})
nodes2_df = pd.DataFrame({'s': ['a', 'b', 'c']})
nodes3_df = pd.DataFrame({'d': ['a', 'b', 'c']})

g1 = CGFull().nodes(nodes1_df, 'v').edges(edges_df, 's', 'd')
g2 = CGFull().nodes(nodes2_df, 's').edges(edges_df, 's', 'd')
g3 = CGFull().nodes(nodes3_df, 'd').edges(edges_df, 's', 'd')

try:
g1_hop = g1.hop()
g2_hop = g2.hop()
g3_hop = g3.hop()
except NotImplementedError:
return

assert g1_hop._nodes.shape == g2_hop._nodes.shape
assert g1_hop._edges.shape == g2_hop._edges.shape
assert g1_hop._nodes.shape == g3_hop._nodes.shape
assert g1_hop._edges.shape == g3_hop._edges.shape

def test_hop_simple_cudf_pd():
nodes_df = pd.DataFrame({'id': [0, 1, 2], 'label': ['a', 'b', 'c']})
edges_df = pd.DataFrame({'src': [0, 1, 2], 'dst': [1, 2, 0]})
Expand Down
16 changes: 16 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,5 +123,21 @@ def unique_flatten_dict(d):
'Topic :: Software Development :: Widget Sets',
'Topic :: System :: Distributed Computing'
],
project_urls = {
"Homepage": "https://www.graphistry.com",
"Repository": "https://github.com/graphistry/pygraphistry",
"Documentation": "https://pygraphistry.readthedocs.io/",
"Changelog": "https://github.com/graphistry/pygraphistry/blob/master/CHANGELOG.md",
"Issues": "https://github.com/graphistry/pygraphistry/issues",
"Funding": "https://www.graphistry.com",
"Download": "https://pypi.org/project/graphistry/#files",
"Slack": "https://graphistry-community.slack.com",
"Twitter": "https://twitter.com/graphistry",
"LinkedIn": "https://www.linkedin.com/company/graphistry",
"Contributing": "https://github.com/graphistry/pygraphistry/blob/master/CONTRIBUTING.md",
"License": "https://github.com/graphistry/pygraphistry/blob/main/LICENSE.txt",
"Code of Conduct": "https://github.com/graphistry/pygraphistry/blob/main/CODE_OF_CONDUCT.md",
"Support": "https://www.graphistry.com/support",
},
keywords=['cugraph', 'cudf', 'cuml', 'dask', 'Databricks', 'GFQL', 'GPU', 'Graph', 'graphviz', 'GraphX', 'Gremlin', 'igraph', 'Jupyter', 'Neo4j', 'Neptune', 'Network', 'NetworkX', 'Notebook', 'OpenSearch', 'Pandas', 'Plot', 'RAPIDS', 'RDF', 'Splunk', 'Spark', 'SQL', 'Tinkerpop', 'UMAP', 'Visualization', 'Torch', 'DGL', 'GNN']
)

0 comments on commit c18d72a

Please sign in to comment.