Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 10 additions & 14 deletions pymathics/graph/boxes.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,51 +8,47 @@

import base64
import tempfile


from typing import Tuple

from mathics.core.element import BaseElement, BoxElementMixin


from pymathics.graph.format import png_format_graph, svg_format_graph

no_doc = True


class GraphBox(BoxElementMixin):
def __init__(self, G, **options):
self.G = G
self.options = options

def boxes_to_b64text(
self, elements: Tuple[BaseElement] = None, **options
) -> Tuple[bytes, Tuple[int, int]]:
def to_b64text(self, **options) -> Tuple[bytes, Tuple[int, int]]:
"""
Produces a base64 png representation and a tuple with the size of the pillow image
associated to the object.
"""
contents, size = self.boxes_to_png(elements, **options)
contents, size = self.to_png(**options)
encoded = base64.b64encode(contents)
encoded = b"data:image/png;base64," + encoded
return encoded, size

def boxes_to_png(self, elements=None, **options) -> Tuple[bytes, Tuple[int, int]]:
def to_png(self, **options) -> Tuple[bytes, Tuple[int, int]]:
"""
returns a tuple with the set of bytes with a png representation of the image
and the scaled size.
"""
return png_format_graph(self.G, **self.options), (800, 600)

def boxes_to_svg(self, elements=None, **options):
def to_svg(self, **options):
return svg_format_graph(self.G, **self.options), (400, 300)

def boxes_to_tex(self, elements=None, **options) -> str:
def to_tex(self, **options) -> str:
"""
Store the associated image as a png file and return
a LaTeX command for including it.
"""

data, size = self.boxes_to_png(elements, **options)
data, size = self.to_png(elements, **options)
res = 100 # pixels/cm
width_str, height_str = (str(n / res).strip() for n in size)
head = rf"\includegraphics[width={width_str}cm,height={height_str}cm]"
Expand All @@ -69,11 +65,11 @@ def boxes_to_tex(self, elements=None, **options) -> str:

return head + "{" + format(path) + "}"

def boxes_to_text(self, elements=None, **options):
def to_text(self, **options):
return "-Graph-"

def boxes_to_mathml(self, elements=None, **options):
encoded, size = self.boxes_to_b64text(elements, **options)
def to_mathml(self, **options):
encoded, size = self.to_b64text(**options)
decoded = encoded.decode("utf8")
# see https://tools.ietf.org/html/rfc2397
return f'<mglyph src="{decoded}" width="50%" height="50%" />'
1 change: 0 additions & 1 deletion pymathics/graph/format.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,6 @@ def get_png_graph() -> BytesIO:
def hierarchy_pos(
G, root=None, width=1.0, vert_gap=0.2, vert_loc=0, leaf_vs_root_factor=0.5
):

"""Position nodes in tree layout. The root is at the top.

Based on Joel's answer at https://stackoverflow.com/a/29597209/2966723,
Expand Down
3 changes: 1 addition & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
[build-system]
requires = [
"setuptools", # CVE-2024-38335 recommends this
"Mathics3>9.0.0",
"mathics-core >= 9.0.1",
"Mathics3-Module-Base",
"networkx>=3.0.0",
"matplotlib",
"Mathics3-Module-Base",
]
build-backend = "setuptools.build_meta"

Expand Down
6 changes: 2 additions & 4 deletions test/helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,7 @@
# Set up a Mathics session with definitions.
# For consistency set the character encoding ASCII which is
# the lowest common denominator available on all systems.
session = MathicsSession(
character_encoding="ASCII"
)
session = MathicsSession(character_encoding="ASCII")


def reset_session(add_builtin=True, catch_interrupt=False):
Expand Down Expand Up @@ -115,7 +113,7 @@ def check_evaluation(
assert (
expected_len == got_len
), f"expected {expected_len}; got {got_len}. Messages: {outs}"
for (out, msg) in zip(outs, msgs):
for out, msg in zip(outs, msgs):
if out != msg:
print(f"out:<<{out}>>")
print(" and ")
Expand Down
65 changes: 33 additions & 32 deletions test/test_fixme.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,77 +5,78 @@
from test.helper import check_evaluation, evaluate, evaluate_value
import pytest


def setup_module(module):
"""Load pymathics.graph"""
assert evaluate_value('LoadModule["pymathics.graph"]') == "pymathics.graph"
evaluate("SortList[list_] := Sort[Map[Sort, list]]")



@pytest.mark.skip("Wrong result. Investigate me.")
def test_EdgeList():
check_evaluation(
"EdgeList[{1 -> 2, 2 <-> 3}]",
"{DirectedEdge[1, 2], UndirectedEdge[2, 3]}"
)
"EdgeList[{1 -> 2, 2 <-> 3}]", "{DirectedEdge[1, 2], UndirectedEdge[2, 3]}"
)



@pytest.mark.skip("Wrong result. Investigate me.")
def test_FindShortestPath():
check_evaluation(
(
"g = Graph[{1 -> 2, 2 -> 3, 1 -> 3}, EdgeWeight -> {0.5, a, 3}];"
"a = 0.5; FindShortestPath[g, 1, 3]'"),
"{1, 2, 3}")
check_evaluation("a = 10; FindShortestPath[g, 1, 3]", "{1, 3}")
check_evaluation("a = .;", "Null")


check_evaluation(
(
"g = Graph[{1 -> 2, 2 -> 3, 1 -> 3}, EdgeWeight -> {0.5, a, 3}];"
"a = 0.5; FindShortestPath[g, 1, 3]'"
),
"{1, 2, 3}",
)
check_evaluation("a = 10; FindShortestPath[g, 1, 3]", "{1, 3}")
check_evaluation("a = .;", "Null")


@pytest.mark.skip("This finds d<->a in the position 4 instead 2.")
def test_EdgeIndex():
check_evaluation("EdgeIndex[{c <-> d, d <-> a, a -> e}, d <-> a]",
"2")
check_evaluation("EdgeIndex[{c <-> d, d <-> a, a -> e}, d <-> a]", "2")



@pytest.mark.parametrize(
("str_expr", "str_expect"),
[
(
("g = Graph[{1 -> 2, 2 -> 3}, DirectedEdges -> True];"
"EdgeCount[g, _DirectedEdge]"),
"2"
(
"g = Graph[{1 -> 2, 2 -> 3}, DirectedEdges -> True];"
"EdgeCount[g, _DirectedEdge]"
),
"2",
),
(
(
"g = Graph[{1 -> 2, 2 -> 3}, DirectedEdges -> False];"
"EdgeCount[g, _DirectedEdge]"),
"0"
"EdgeCount[g, _DirectedEdge]"
),
"0",
),
("EdgeCount[g, _UndirectedEdge]","2")
]
("EdgeCount[g, _UndirectedEdge]", "2"),
],
)
@pytest.mark.skip("This finds d<->a in the position 4 instead 2.")
def test_edgecount(str_expr, str_expect):
check_evaluation(str_expr, str_expect)


@pytest.mark.skip("This finds d<->a in the position 4 instead 2.")
def test_EdgeIndex():
check_evaluation("EdgeIndex[{c <-> d, d <-> a, a -> e}, d <-> a]",
"2")
check_evaluation("EdgeIndex[{c <-> d, d <-> a, a -> e}, d <-> a]", "2")


@pytest.mark.skip("This is not properly evaluated. Investigate me")
def test_HITSCentrality():
check_evaluation("g = Graph[{a -> d, b -> c, d -> c, d -> a, e -> c}]; HITSCentrality[g]",
"{{0.292893, 0., 0., 0.707107, 0.}, {0., 1., 0.707107, 0., 0.707107}}")
check_evaluation(
"g = Graph[{a -> d, b -> c, d -> c, d -> a, e -> c}]; HITSCentrality[g]",
"{{0.292893, 0., 0., 0.707107, 0.}, {0., 1., 0.707107, 0., 0.707107}}",
)


@pytest.mark.skip("Investigate me.")
def test_EdgeRules():
check_evaluation(
"EdgeRules[{1 <-> 2, 2 -> 3, 3 <-> 4}]",
"{1 -> 2, 2 -> 3, 3 -> 4}"
"EdgeRules[{1 <-> 2, 2 -> 3, 3 <-> 4}]", "{1 -> 2, 2 -> 3, 3 -> 4}"
)