knit-graphs 0.0.5__py3-none-any.whl → 0.0.7__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. knit_graphs-0.0.5.dist-info/licenses/LICENSE → LICENSE +21 -21
  2. README.md +75 -0
  3. docs/Makefile +20 -0
  4. docs/make.bat +35 -0
  5. docs/source/api/knit_graphs.artin_wale_braids.rst +58 -0
  6. docs/source/api/knit_graphs.rst +74 -0
  7. docs/source/conf.py +335 -0
  8. docs/source/index.rst +71 -0
  9. docs/source/installation.rst +67 -0
  10. knit_graphs/Course.py +156 -104
  11. knit_graphs/Knit_Graph.py +249 -186
  12. knit_graphs/Knit_Graph_Visualizer.py +680 -0
  13. knit_graphs/Loop.py +141 -155
  14. knit_graphs/Pull_Direction.py +68 -23
  15. knit_graphs/Yarn.py +424 -267
  16. knit_graphs/__init__.py +3 -3
  17. knit_graphs/_base_classes.py +173 -0
  18. knit_graphs/artin_wale_braids/Crossing_Direction.py +74 -15
  19. knit_graphs/artin_wale_braids/Loop_Braid_Graph.py +95 -62
  20. knit_graphs/artin_wale_braids/Wale.py +169 -93
  21. knit_graphs/artin_wale_braids/Wale_Braid.py +50 -30
  22. knit_graphs/artin_wale_braids/Wale_Braid_Word.py +99 -54
  23. knit_graphs/artin_wale_braids/Wale_Group.py +136 -88
  24. knit_graphs/{knit_graph_generators/basic_knit_graph_generators.py → basic_knit_graph_generators.py} +302 -248
  25. knit_graphs-0.0.7.dist-info/LICENSE +21 -0
  26. {knit_graphs-0.0.5.dist-info → knit_graphs-0.0.7.dist-info}/METADATA +33 -25
  27. knit_graphs-0.0.7.dist-info/RECORD +29 -0
  28. {knit_graphs-0.0.5.dist-info → knit_graphs-0.0.7.dist-info}/WHEEL +1 -1
  29. knit_graphs/__about__.py +0 -4
  30. knit_graphs/knit_graph_generators/__init__.py +0 -0
  31. knit_graphs/knit_graph_visualizer/Stitch_Visualizer.py +0 -427
  32. knit_graphs/knit_graph_visualizer/__init__.py +0 -0
  33. knit_graphs-0.0.5.dist-info/RECORD +0 -22
knit_graphs/__init__.py CHANGED
@@ -1,3 +1,3 @@
1
- # SPDX-FileCopyrightText: 2024-present m.hofmann <m.hofmann@northeastern.edu>
2
- #
3
- # SPDX-License-Identifier: MIT
1
+ # SPDX-FileCopyrightText: 2024-present m.hofmann <m.hofmann@northeastern.edu>
2
+ #
3
+ # SPDX-License-Identifier: MIT
@@ -0,0 +1,173 @@
1
+ """Private module containing base classes for Loops and Yarns. Used to resolve circular dependencies."""
2
+ from __future__ import annotations
3
+
4
+ from networkx import DiGraph
5
+
6
+
7
+ class _Base_Loop:
8
+ """Base class for Loop objects providing common functionality and interface.
9
+
10
+ This class serves as a foundation for Loop implementations and helps resolve circular dependencies between modules. It provides basic loop identification and comparison functionality.
11
+ """
12
+
13
+ def __init__(self, loop_id: int):
14
+ """Initialize a base loop with the given identifier.
15
+
16
+ Args:
17
+ loop_id (int): The unique identifier for this loop. Must be non-negative.
18
+ """
19
+ assert loop_id >= 0, f"{loop_id}: Loop_id must be non-negative"
20
+ self._loop_id: int = loop_id
21
+
22
+ @property
23
+ def loop_id(self) -> int:
24
+ """Get the unique identifier of this loop.
25
+
26
+ Returns:
27
+ int: The id of the loop.
28
+ """
29
+ return self._loop_id
30
+
31
+ def __hash__(self) -> int:
32
+ """Return hash value based on loop_id for use in sets and dictionaries.
33
+
34
+ Returns:
35
+ int: Hash value of the loop_id.
36
+ """
37
+ return self.loop_id
38
+
39
+ def __int__(self) -> int:
40
+ """Convert loop to integer representation using loop_id.
41
+
42
+ Returns:
43
+ int: The loop_id as an integer.
44
+ """
45
+ return self.loop_id
46
+
47
+ def __eq__(self, other: _Base_Loop) -> bool:
48
+ """Check equality with another base loop based on loop_id and type.
49
+
50
+ Args:
51
+ other (_Base_Loop): The other loop to compare with.
52
+
53
+ Returns:
54
+ bool: True if both loops have the same class and loop_id, False otherwise.
55
+ """
56
+ return isinstance(other, other.__class__) and self.loop_id == other.loop_id
57
+
58
+ def __lt__(self, other: _Base_Loop | int) -> bool:
59
+ """Compare loop_id with another loop or integer for ordering.
60
+
61
+ Args:
62
+ other (_Base_Loop | int): The other loop or integer to compare with.
63
+
64
+ Returns:
65
+ bool: True if this loop's id is less than the other's id.
66
+ """
67
+ return int(self.loop_id) < int(other)
68
+
69
+ def __repr__(self) -> str:
70
+ """Return string representation of the loop.
71
+
72
+ Returns:
73
+ str: String representation showing "Loop {loop_id}".
74
+ """
75
+ return f"Loop {self.loop_id}"
76
+
77
+
78
+ class _Base_Yarn:
79
+ """Base class for Yarn objects providing common functionality and interface.
80
+
81
+ This class serves as a foundation for Yarn implementations and helps resolve circular dependencies between modules. It maintains a directed graph structure for managing loop relationships.
82
+ """
83
+
84
+ def __init__(self) -> None:
85
+ """Initialize a base yarn with an empty directed graph for loop relationships."""
86
+ self.loop_graph: DiGraph = DiGraph()
87
+
88
+ def prior_loop(self, loop: _Base_Loop) -> _Base_Loop | None:
89
+ """Find the loop that precedes the given loop on this yarn.
90
+
91
+ Args:
92
+ loop (_Base_Loop): The loop to find the preceding loop of.
93
+
94
+ Returns:
95
+ _Base_Loop | None: The loop that precedes the given loop on the yarn, or None if there is no prior loop.
96
+
97
+ Raises:
98
+ NotImplementedError: This is an abstract base class which must be extended with the correct implementation.
99
+ """
100
+ raise NotImplementedError("Implemented by base class")
101
+
102
+ def next_loop(self, loop: _Base_Loop) -> _Base_Loop | None:
103
+ """Find the loop that follows the given loop on this yarn.
104
+
105
+ Args:
106
+ loop (_Base_Loop): The loop to find the next loop of.
107
+
108
+ Returns:
109
+ _Base_Loop | None: The loop that follows the given loop on the yarn, or None if there is no next loop.
110
+
111
+ Raises:
112
+ NotImplementedError: This is an abstract base class which must be extended with the correct implementation.
113
+ """
114
+ raise NotImplementedError("Implemented by base class")
115
+
116
+
117
+ class _Base_Wale:
118
+ """Base class for Wale objects providing common functionality and interface.
119
+
120
+ This class serves as a foundation for Wale implementations and maintains a directed graph structure for managing stitch relationships within a wale (vertical column of stitches).
121
+ """
122
+
123
+ def __init__(self) -> None:
124
+ """Initialize a base wale with an empty directed graph for stitch relationships."""
125
+ self.stitches: DiGraph = DiGraph()
126
+
127
+
128
+ class _Base_Knit_Graph:
129
+ """Base class for Knit Graph objects providing common functionality and interface.
130
+
131
+ This class serves as a foundation for Knit Graph implementations and maintains a directed graph structure for managing stitch relationships throughout the entire knitted structure.
132
+ """
133
+
134
+ def __init__(self) -> None:
135
+ """Initialize a base knit graph with an empty directed graph for stitch relationships."""
136
+ self.stitch_graph: DiGraph = DiGraph()
137
+
138
+ @property
139
+ def last_loop(self) -> None | _Base_Loop:
140
+ """Get the most recently added loop in the graph.
141
+
142
+ Returns:
143
+ None | _Base_Loop: The last loop added to the graph, or None if the graph contains no loops.
144
+
145
+ Raises:
146
+ NotImplementedError: This is an abstract base class which must be extended with the correct implementation.
147
+ """
148
+ raise NotImplementedError("Implemented by base class")
149
+
150
+ def add_loop(self, loop: _Base_Loop) -> None:
151
+ """Add a loop to the knit graph structure.
152
+
153
+ Args:
154
+ loop (_Base_Loop): The loop to add to the graph.
155
+
156
+ Raises:
157
+ NotImplementedError: This is an abstract base class which must be extended with the correct implementation.
158
+ """
159
+ raise NotImplementedError("Implemented by base class")
160
+
161
+ def get_wales_ending_with_loop(self, last_loop: _Base_Loop) -> list[_Base_Wale]:
162
+ """Get all wales (vertical columns) that terminate at the specified loop.
163
+
164
+ Args:
165
+ last_loop (_Base_Loop): The loop terminating the list of wales to be returned.
166
+
167
+ Returns:
168
+ list[_Base_Wale]: The list of wales that end at the given loop. This will only be multiple wales if the loop is a child of a decrease stitch.
169
+
170
+ Raises:
171
+ NotImplementedError: This is an abstract base class which must be extended with the correct implementation.
172
+ """
173
+ raise NotImplementedError
@@ -1,15 +1,74 @@
1
- """Module containing the Crossing_Direction Enum"""
2
- from enum import Enum
3
-
4
-
5
- class Crossing_Direction(Enum):
6
- """Enumeration of crossing values between loops"""
7
- Over_Right = "+"
8
- Under_Right = "-"
9
- No_Cross = "|"
10
-
11
- def __invert__(self):
12
- if self is Crossing_Direction.Over_Right:
13
- return Crossing_Direction.Under_Right
14
- else:
15
- return Crossing_Direction.Over_Right
1
+ """Module containing the Crossing_Direction Enum.
2
+
3
+ This module defines the Crossing_Direction enumeration which represents the different ways loops can cross over or under each other in cable knitting patterns.
4
+ """
5
+ from __future__ import annotations
6
+
7
+ from enum import Enum
8
+
9
+
10
+ class Crossing_Direction(Enum):
11
+ """Enumeration of crossing directions between loops in cable knitting patterns.
12
+
13
+ This enumeration represents the three possible crossing relationships between loops: crossing over to the right, crossing under to the right, or no crossing at all.
14
+ These directions are fundamental to representing cable structures in knitted fabrics.
15
+ """
16
+ Over_Right = "+"
17
+ Under_Right = "-"
18
+ No_Cross = "|"
19
+
20
+ @property
21
+ def opposite(self) -> Crossing_Direction:
22
+ """Get the opposite crossing direction of this direction.
23
+
24
+ The Over_Right and Under_Right crossing directions are opposites of each other, while No_Cross is its own opposite since there is no crossing to invert.
25
+
26
+ Returns:
27
+ Crossing_Direction: The opposite of this crossing direction. Over_Right returns Under_Right, Under_Right returns Over_Right, and No_Cross returns No_Cross.
28
+ """
29
+ if self is Crossing_Direction.Over_Right:
30
+ return Crossing_Direction.Under_Right
31
+ elif self is Crossing_Direction.Under_Right:
32
+ return Crossing_Direction.Over_Right
33
+ else:
34
+ return Crossing_Direction.No_Cross
35
+
36
+ def __invert__(self) -> Crossing_Direction:
37
+ """Get the opposite crossing direction using the bitwise inversion operator.
38
+
39
+ Returns:
40
+ Crossing_Direction: The opposite crossing direction, same as calling the opposite property.
41
+ """
42
+ return self.opposite
43
+
44
+ def __neg__(self) -> Crossing_Direction:
45
+ """Get the opposite crossing direction using the negation operator.
46
+
47
+ Returns:
48
+ Crossing_Direction: The opposite crossing direction, same as calling the opposite property.
49
+ """
50
+ return self.opposite
51
+
52
+ def __str__(self) -> str:
53
+ """Get the string representation of the crossing direction.
54
+
55
+ Returns:
56
+ str: The symbol representing the crossing direction ("+", "-", or "|").
57
+ """
58
+ return str(self.value)
59
+
60
+ def __repr__(self) -> str:
61
+ """Get the representation string of the crossing direction for debugging.
62
+
63
+ Returns:
64
+ str: The name of the crossing direction ("Over_Right", "Under_Right", or "No_Cross").
65
+ """
66
+ return str(self.name)
67
+
68
+ def __hash__(self) -> int:
69
+ """Get the hash value of the crossing direction for use in sets and dictionaries.
70
+
71
+ Returns:
72
+ int: Hash value based on the crossing direction value.
73
+ """
74
+ return hash(self.value)
@@ -1,62 +1,95 @@
1
- """Module containing the Loop Braid Graph class."""
2
- import networkx
3
-
4
- from knit_graphs.Loop import Loop
5
- from knit_graphs.artin_wale_braids.Crossing_Direction import Crossing_Direction
6
-
7
-
8
- class Loop_Braid_Graph:
9
- """
10
- Tracks crossing braid edges between loops
11
- """
12
-
13
- def __init__(self):
14
- self.loop_crossing_graph: networkx.DiGraph = networkx.DiGraph()
15
-
16
- def add_crossing(self, left_loop: Loop, right_loop: Loop, crossing_direction: Crossing_Direction):
17
- """
18
- Adds edge between loops with attribute of the crossing direction.
19
- :param left_loop: Loop on the left side of the crossing.
20
- :param right_loop: Loop on the right side of the crossing.
21
- :param crossing_direction: The crossing direction is (over, under, none) between loops.
22
- """
23
- self.loop_crossing_graph.add_edge(left_loop, right_loop, crossing=crossing_direction)
24
-
25
- def __contains__(self, item: Loop | tuple[Loop, Loop]):
26
- if isinstance(item, Loop):
27
- return item in self.loop_crossing_graph.nodes
28
- else:
29
- return self.loop_crossing_graph.has_edge(item[0], item[1])
30
-
31
- def left_crossing_loops(self, left_loop: Loop) -> list[Loop]:
32
- """
33
- :param left_loop: loop to left side of crossing
34
- :return: list of loops this crosses over on right side
35
- """
36
- if left_loop not in self:
37
- return []
38
- else:
39
- return [rl for rl in self.loop_crossing_graph.successors(left_loop)
40
- if self.get_crossing(left_loop, rl) is not Crossing_Direction.No_Cross]
41
-
42
- def right_crossing_loops(self, right_loop: Loop) -> list[Loop]:
43
- """
44
- :param right_loop: The loop to find crossings with.
45
- :return: The list of loops that leftward cross the right loop.
46
- """
47
- if right_loop not in self:
48
- return []
49
- else:
50
- return [l for l in self.loop_crossing_graph.predecessors(right_loop)
51
- if self.get_crossing(l, right_loop) is not Crossing_Direction.No_Cross]
52
-
53
- def get_crossing(self, left_loop: Loop, right_loop: Loop) -> Crossing_Direction:
54
- """
55
- Adds no-cross edge if no edge exists between loops
56
- :param left_loop: Loop on the left side of the crossing.
57
- :param right_loop: Loop on the right side of the crossing.
58
- :return: The crossing direction between left and right loop. Defaults to no crossing.
59
- """
60
- if not self.loop_crossing_graph.has_edge(left_loop, right_loop):
61
- self.add_crossing(left_loop, right_loop, Crossing_Direction.No_Cross)
62
- return self.loop_crossing_graph[left_loop][right_loop]['crossing']
1
+ """Module containing the Loop Braid Graph class.
2
+
3
+ This module provides the Loop_Braid_Graph class which tracks crossing relationships between loops in cable knitting patterns using a directed graph structure.
4
+ """
5
+ from typing import cast
6
+
7
+ from networkx import DiGraph
8
+
9
+ from knit_graphs.artin_wale_braids.Crossing_Direction import Crossing_Direction
10
+ from knit_graphs.Loop import Loop
11
+
12
+
13
+ class Loop_Braid_Graph:
14
+ """A graph structure that tracks crossing braid edges between loops in cable patterns.
15
+
16
+ This class maintains a directed graph where nodes are loops and edges represent cable crossings between those loops.
17
+ It provides methods to add crossings, query crossing relationships, and determine which loops cross with a given loop.
18
+
19
+ Attributes:
20
+ loop_crossing_graph (DiGraph): A NetworkX directed graph storing loop crossing relationships with crossing direction attributes.
21
+ """
22
+
23
+ def __init__(self) -> None:
24
+ """Initialize an empty loop braid graph with no crossings."""
25
+ self.loop_crossing_graph: DiGraph = DiGraph()
26
+
27
+ def add_crossing(self, left_loop: Loop, right_loop: Loop, crossing_direction: Crossing_Direction) -> None:
28
+ """Add a crossing edge between two loops with the specified crossing direction.
29
+
30
+ Args:
31
+ left_loop (Loop): The loop on the left side of the crossing.
32
+ right_loop (Loop): The loop on the right side of the crossing.
33
+ crossing_direction (Crossing_Direction): The direction of the crossing (over, under, or none) between the loops.
34
+ """
35
+ self.loop_crossing_graph.add_edge(left_loop, right_loop, crossing=crossing_direction)
36
+
37
+ def __contains__(self, item: Loop | tuple[Loop, Loop]) -> bool:
38
+ """Check if a loop or loop pair is contained in the braid graph.
39
+
40
+ Args:
41
+ item (Loop | tuple[Loop, Loop]): Either a single loop to check for node membership, or a tuple of loops to check for edge membership.
42
+
43
+ Returns:
44
+ bool: True if the loop is a node in the graph (for single loop) or if there is an edge between the loops (for tuple).
45
+ """
46
+ if isinstance(item, Loop):
47
+ return item in self.loop_crossing_graph.nodes
48
+ else:
49
+ return bool(self.loop_crossing_graph.has_edge(item[0], item[1]))
50
+
51
+ def left_crossing_loops(self, left_loop: Loop) -> list[Loop]:
52
+ """Get all loops that cross with the given loop when it is on the left side.
53
+
54
+ Args:
55
+ left_loop (Loop): The loop on the left side of potential crossings.
56
+
57
+ Returns:
58
+ list[Loop]: List of loops that this loop crosses over on the right side. Empty list if the loop is not in the graph or has no crossings.
59
+ """
60
+ if left_loop not in self:
61
+ return []
62
+ else:
63
+ return [rl for rl in self.loop_crossing_graph.successors(left_loop)
64
+ if self.get_crossing(left_loop, rl) is not Crossing_Direction.No_Cross]
65
+
66
+ def right_crossing_loops(self, right_loop: Loop) -> list[Loop]:
67
+ """Get all loops that cross with the given loop when it is on the right side.
68
+
69
+ Args:
70
+ right_loop (Loop): The loop on the right side of potential crossings.
71
+
72
+ Returns:
73
+ list[Loop]: List of loops that cross this loop from the left side. Empty list if the loop is not in the graph or has no crossings.
74
+ """
75
+ if right_loop not in self:
76
+ return []
77
+ else:
78
+ return [l for l in self.loop_crossing_graph.predecessors(right_loop)
79
+ if self.get_crossing(l, right_loop) is not Crossing_Direction.No_Cross]
80
+
81
+ def get_crossing(self, left_loop: Loop, right_loop: Loop) -> Crossing_Direction:
82
+ """Get the crossing direction between two loops, creating a no-cross edge if none exists.
83
+
84
+ If no edge exists between the loops, this method automatically adds a no-crossing edge to maintain consistency in the graph structure.
85
+
86
+ Args:
87
+ left_loop (Loop): The loop on the left side of the crossing.
88
+ right_loop (Loop): The loop on the right side of the crossing.
89
+
90
+ Returns:
91
+ Crossing_Direction: The crossing direction between the left and right loop. Defaults to No_Cross if no explicit crossing was previously defined.
92
+ """
93
+ if not self.loop_crossing_graph.has_edge(left_loop, right_loop):
94
+ self.add_crossing(left_loop, right_loop, Crossing_Direction.No_Cross)
95
+ return cast(Crossing_Direction, self.loop_crossing_graph[left_loop][right_loop]['crossing'])