Skip to content

Commit e808963

Browse files
committed
pymathics`Graph now working with Mathics 5.0.0
1 parent f568a01 commit e808963

File tree

3 files changed

+96
-98
lines changed

3 files changed

+96
-98
lines changed

pymathics/graph/__main__.py

Lines changed: 85 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
from mathics.builtin.base import Builtin, AtomBuiltin
1313
from mathics.builtin.box.graphics import GraphicsBox
14+
from mathics.builtin.box.inout import _BoxedString
1415
from mathics.builtin.patterns import Matcher
1516
from mathics.core.atoms import Integer, Integer0, Integer1, Real
1617
from mathics.core.convert.expression import ListExpression
@@ -27,7 +28,6 @@
2728
SymbolRGBColor,
2829
SymbolRule,
2930
)
30-
3131
from inspect import isgenerator
3232

3333
WL_MARKER_TO_NETWORKX = {
@@ -416,24 +416,39 @@ def _normalize_edges(edges):
416416

417417

418418
class Graph(Atom):
419+
class_head_name = "Pymathics`Graph"
419420

420421
options = DEFAULT_GRAPH_OPTIONS
421422

422423
def __init__(self, G, **kwargs):
423424
super(Graph, self).__init__()
424425
self.G = G
425426

427+
def __hash__(self):
428+
return hash(("Pymathics`Graph", self.G))
429+
430+
def __str__(self):
431+
return "-Graph-"
432+
433+
def atom_to_boxes(self, f, evaluation) -> _BoxedString:
434+
return _BoxedString("-Graph-")
435+
436+
def default_format(self, evaluation, form):
437+
return "-Graph-"
438+
439+
def do_format(self, evaluation, form):
440+
return self
441+
426442
@property
427443
def edges(self):
428444
return self.G.edges
429445

430-
@property
431-
def vertices(self):
432-
return self.G.nodes
433-
434446
def empty(self):
435447
return len(self.G) == 0
436448

449+
def is_loop_free(self):
450+
return not any(True for _ in nx.nodes_with_selfloops(self.G))
451+
437452
# networkx graphs can't be for mixed
438453
def is_mixed_graph(self):
439454
return False
@@ -442,100 +457,85 @@ def is_mixed_graph(self):
442457
def is_multigraph(self):
443458
return isinstance(self.G, (nx.MultiDiGraph, nx.MultiGraph))
444459

445-
def is_loop_free(self):
446-
return not any(True for _ in nx.nodes_with_selfloops(self.G))
447-
448-
def __str__(self):
449-
return "-Graph-"
450-
451-
def do_copy(self):
452-
return Graph(self.G)
453-
454460
def get_sort_key(self, pattern_sort=False):
455461
if pattern_sort:
456462
return super(Graph, self).get_sort_key(True)
457463
else:
458464
return hash(self)
459465

460-
def default_format(self, evaluation, form):
461-
return "-Graph-"
466+
@property
467+
def vertices(self):
468+
return self.G.nodes
462469

463-
def same(self, other):
464-
return isinstance(other, Graph) and self.G == other.G
465-
# FIXME
466-
# self.properties == other.properties
467-
# self.options == other.options
468-
# self.highlights == other.highlights
469470

470-
def to_python(self, *args, **kwargs):
471-
return self.G
471+
class _Collection(object):
472+
def __init__(self, expressions, properties=None, index=None):
473+
self.expressions = expressions
474+
self.properties = properties if properties else None
475+
self.index = index
472476

473-
def __hash__(self):
474-
return hash(("Graph", self.G)) # FIXME self.properties, ...
477+
def clone(self):
478+
properties = self.properties
479+
return _Collection(
480+
self.expressions[:], properties[:] if properties else None, None
481+
)
475482

476-
def atom_to_boxes(self, form, evaluation):
477-
return Expression(SymbolGraphBox, self, form)
483+
def filter(self, expressions):
484+
index = self.get_index()
485+
return [expr for expr in expressions if expr in index]
478486

479-
def boxes_to_xml(self, **options):
480-
# Figure out what to do here.
481-
return "-Graph-XML-"
487+
def extend(self, expressions, properties):
488+
if properties:
489+
if self.properties is None:
490+
self.properties = [None] * len(self.expressions)
491+
self.properties.extend(properties)
492+
self.expressions.extend(expressions)
493+
self.index = None
494+
return expressions
482495

483-
def get_property(self, element, name):
484-
if element.get_head_name() in ("System`DirectedEdge", "System`UndirectedEdge"):
485-
x = self.edges.get_property(element, name)
486-
if x is None:
487-
x = self.vertices.get_property(element, name)
488-
return x
496+
def delete(self, expressions):
497+
index = self.get_index()
498+
trash = set(index[x] for x in expressions)
499+
deleted = [self.expressions[i] for i in trash]
500+
self.expressions = [x for i, x in enumerate(self.expressions) if i not in trash]
501+
self.properties = [x for i, x in enumerate(self.properties) if i not in trash]
502+
self.index = None
503+
return deleted
489504

490-
def delete_edges(self, edges_to_delete):
491-
G = self.G.copy()
492-
directed = G.is_directed()
493-
494-
edges_to_delete = list(_normalize_edges(edges_to_delete))
495-
# FIXME: edges_to_delete is needs to be a tuple. tuples
496-
# are edges in networkx
497-
edges_to_delete = [edge for edge in self.edges if edge in edges_to_delete]
498-
499-
for edge in edges_to_delete:
500-
if edge.has_form("DirectedEdge", 2):
501-
if directed:
502-
u, v = edge.elements
503-
G.remove_edge(u, v)
504-
elif edge.has_form("UndirectedEdge", 2):
505-
u, v = edge.elements
506-
if directed:
507-
G.remove_edge(u, v)
508-
G.remove_edge(v, u)
509-
else:
510-
G.remove_edge(u, v)
511-
512-
edges = self.edges.clone()
513-
edges.delete(edges_to_delete)
514-
515-
return Graph(G)
516-
517-
def update_weights(self, evaluation):
518-
weights = None
519-
G = self.G
520-
521-
if self.is_multigraph():
522-
for u, v, k, w in G.edges.data(
523-
"System`EdgeWeight", default=None, keys=True
524-
):
525-
data = G.get_edge_data(u, v, key=k)
526-
w = data.get()
527-
if w is not None:
528-
w = w.evaluate(evaluation).to_mpmath()
529-
G[u][v][k]["WEIGHT"] = w
530-
weights = "WEIGHT"
505+
def data(self):
506+
return self.expressions, list(self.get_properties())
507+
508+
def get_index(self):
509+
index = self.index
510+
if index is None:
511+
index = dict((v, i) for i, v in enumerate(self.expressions))
512+
self.index = index
513+
return index
514+
515+
def get_properties(self):
516+
if self.properties:
517+
for p in self.properties:
518+
yield p
531519
else:
532-
for u, v, w in G.edges.data("System`EdgeWeight", default=None):
533-
if w is not None:
534-
w = w.evaluate(evaluation).to_mpmath()
535-
G[u][v]["WEIGHT"] = w
536-
weights = "WEIGHT"
520+
for _ in range(len(self.expressions)):
521+
yield None
537522

538-
return weights
523+
def get_sorted(self):
524+
index = self.get_index()
525+
return lambda c: sorted(c, key=lambda v: index[v])
526+
527+
def get_property(self, element, name):
528+
properties = self.properties
529+
if properties is None:
530+
return None
531+
index = self.get_index()
532+
i = index.get(element)
533+
if i is None:
534+
return None
535+
p = properties[i]
536+
if p is None:
537+
return None
538+
return p.get(name)
539539

540540

541541
def _is_connected(G):
@@ -1754,9 +1754,7 @@ def apply(self, graph, evaluation, options):
17541754

17551755
def degrees(graph):
17561756
degrees = dict(list(graph.G.degree(graph.vertices)))
1757-
return ListExpression(
1758-
*[Integer(degrees.get(v, 0)) for v in graph.vertices]
1759-
)
1757+
return ListExpression(*[Integer(degrees.get(v, 0)) for v in graph.vertices])
17601758

17611759
return self._evaluate_atom(graph, options, degrees)
17621760

pymathics/graph/generators.py

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,12 @@
99

1010
from pymathics.graph.__main__ import (
1111
Graph,
12+
SymbolUndirectedEdge,
1213
_NetworkXBuiltin,
1314
_convert_networkx_graph,
1415
_graph_from_list,
15-
has_directed_option,
1616
_process_graph_options,
17+
has_directed_option,
1718
nx,
1819
)
1920

@@ -43,7 +44,7 @@ def graph_helper(
4344
else graph_generator_func(*args, **kwargs)
4445
)
4546
except MemoryError:
46-
evaluation.message(self.get_name(), "mem", expression)
47+
evaluation.message("Graph", "mem", evaluation)
4748
return None
4849
if graph_layout and not options["System`GraphLayout"].get_string_value():
4950
options["System`GraphLayout"] = String(graph_layout)
@@ -287,7 +288,7 @@ def apply(self, k, n, expression, evaluation, options):
287288
n_int = n.get_int_value()
288289
k_int = k.get_int_value()
289290

290-
new_n_int = int(((k_int ** n_int) - 1) / (k_int - 1))
291+
new_n_int = int(((k_int**n_int) - 1) / (k_int - 1))
291292
return f_r_t_apply(self, k, Integer(new_n_int), expression, evaluation, options)
292293

293294
# FIXME: can be done with rules?
@@ -296,7 +297,7 @@ def apply_2(self, n, expression, evaluation, options):
296297

297298
n_int = n.get_int_value()
298299

299-
new_n_int = int(2 ** n_int) - 1
300+
new_n_int = int(2**n_int) - 1
300301
return f_r_t_apply(
301302
self, Integer(2), Integer(new_n_int), expression, evaluation, options
302303
)
@@ -585,13 +586,13 @@ class PathGraph(_NetworkXBuiltin):
585586
= -Graph-
586587
"""
587588

588-
def apply(self, l, evaluation, options):
589+
def apply(self, element, evaluation, options):
589590
"PathGraph[l_List, OptionsPattern[%(name)s]]"
590-
leaves = l.leaves
591+
elements = element.elements
591592

592593
def edges():
593-
for u, v in zip(leaves, leaves[1:]):
594-
yield Expression("UndirectedEdge", u, v)
594+
for u, v in zip(elements, elements[1:]):
595+
yield Expression(SymbolUndirectedEdge, u, v)
595596

596597
g = _graph_from_list(edges(), options)
597598
g.G.graph_layout = (
@@ -617,7 +618,7 @@ def _generate(self, n, m, k, evaluation, options):
617618
py_k = k.get_int_value()
618619
is_directed = has_directed_option(options)
619620

620-
with RandomEnv(evaluation) as rand:
621+
with RandomEnv(evaluation) as _:
621622
for _ in range(py_k):
622623
# seed = rand.randint(0, 2 ** 63 - 1) # 2**63 is too large
623624
G = nx.gnm_random_graph(py_n, py_m, directed=is_directed)

pymathics/graph/version.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
1-
#!/usr/bin/env python3
21
# -*- coding: utf-8 -*-
32

43

54
# This file is suitable for sourcing inside POSIX shell as
65
# well as importing into Python. That's why there is no
76
# space around "=" below.
87
# fmt: off
9-
__version__="2.3.1.dev0"
8+
__version__="5.0.0.dev0"

0 commit comments

Comments
 (0)