Skip to content

Commit 5f40400

Browse files
committed
feat: started adding support for attribute combinations, work in progres
1 parent 043324d commit 5f40400

File tree

8 files changed

+222
-13
lines changed

8 files changed

+222
-13
lines changed

src/codegen/internal_functions.py.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ from .conversion import * # noqa
88
from .enums import * # noqa
99
from .lib import * # noqa
1010
from .types import (
11+
AttributeCombinationSpecification,
1112
BoolArray,
1213
EdgeLike,
1314
EdgeSelector,

src/codegen/internal_lib.py.in

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ from .types import (
3434
igraph_bliss_info_t,
3535
igraph_error_handler_t,
3636
igraph_fatal_handler_t,
37+
igraph_function_pointer_t,
3738
igraph_hrg_t,
3839
igraph_layout_drl_options_t,
3940
igraph_maxflow_stats_t,
@@ -535,6 +536,22 @@ igraph_set_attribute_table = _lib.igraph_set_attribute_table
535536
igraph_set_attribute_table.restype = POINTER(igraph_attribute_table_t)
536537
igraph_set_attribute_table.argtypes = [POINTER(igraph_attribute_table_t)]
537538

539+
igraph_attribute_combination_init = _lib.igraph_attribute_combination_init
540+
igraph_attribute_combination_init.restype = handle_igraph_error_t
541+
igraph_attribute_combination_init.argtypes = [POINTER(igraph_attribute_combination_t)]
542+
543+
igraph_attribute_combination_destroy = _lib.igraph_attribute_combination_destroy
544+
igraph_attribute_combination_destroy.restype = None
545+
igraph_attribute_combination_destroy.argtypes = [POINTER(igraph_attribute_combination_t)]
546+
547+
igraph_attribute_combination_add = _lib.igraph_attribute_combination_add
548+
igraph_attribute_combination_add.restype = handle_igraph_error_t
549+
igraph_attribute_combination_add.argtypes = [POINTER(igraph_attribute_combination_t), c_char_p, c_int, igraph_function_pointer_t]
550+
551+
igraph_attribute_combination_query = _lib.igraph_attribute_combination_query
552+
igraph_attribute_combination_query.restype = handle_igraph_error_t
553+
igraph_attribute_combination_query.argtypes = [POINTER(igraph_attribute_combination_t), c_char_p, POINTER(c_int), POINTER(igraph_function_pointer_t)]
554+
538555
# Error handling and interruptions
539556

540557
igraph_error = _lib.igraph_error

src/codegen/types.yaml

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
# Basic types
2+
13
ERROR:
24
PY_TYPE: int
35
CTYPES_RETURN_TYPE: handle_igraph_error_t
@@ -35,6 +37,8 @@ CSTRING:
3537
OUT: "%C% = c_char_p()"
3638
OUTCONV: "%I% = bytes_to_str(%C%)"
3739

40+
# Vectors, matrices
41+
3842
VECTOR:
3943
PY_TYPE: Iterable[float]
4044
PY_RETURN_TYPE: RealArray
@@ -91,6 +95,8 @@ MATRIX_INT:
9195
OUT: "%C% = _MatrixInt.create(0)"
9296
OUTCONV: "%I% = igraph_matrix_int_t_to_numpy_array(%C%)"
9397

98+
# Graph, vertex and edge related classes
99+
94100
GRAPH:
95101
PY_TYPE: Graph
96102
INCONV:
@@ -100,9 +106,6 @@ GRAPH:
100106
%I% = _create_graph_from_boxed(%C%)
101107
INOUT: ~
102108

103-
ATTRIBUTES:
104-
PY_TYPE: ~
105-
106109
VERTEX:
107110
PY_TYPE: VertexLike
108111
PY_RETURN_TYPE: int
@@ -233,6 +236,27 @@ BIPARTITE_TYPES:
233236
INOUT: "%C% = iterable_to_igraph_vector_bool_t(%I%) if %I% is not None else None"
234237
OUT: "%C% = _VectorBool.create(0)"
235238

239+
# Attribute handling
240+
241+
ATTRIBUTES:
242+
PY_TYPE: ~
243+
244+
EDGE_ATTRIBUTE_COMBINATION:
245+
PY_TYPE: AttributeCombinationSpecification
246+
INCONV:
247+
IN: "%C% = mapping_to_attribute_combination_t(%I%)"
248+
DEFAULT:
249+
Default: "None"
250+
251+
VERTEX_ATTRIBUTE_COMBINATION:
252+
PY_TYPE: AttributeCombinationSpecification
253+
INCONV:
254+
IN: "%C% = mapping_to_attribute_combination_t(%I%)"
255+
DEFAULT:
256+
Default: "None"
257+
258+
# File I/O
259+
236260
INFILE:
237261
PY_TYPE: FileLike
238262
PY_RETURN_TYPE: c_int
@@ -247,6 +271,8 @@ OUTFILE:
247271
IN: '%C% = %S%.enter_context(any_to_file_ptr(%I%, "w"))'
248272
FLAGS: stack
249273

274+
# Enums
275+
250276
ADJACENCY_MODE:
251277
PY_TYPE: AdjacencyMode
252278
INCONV:
@@ -362,6 +388,16 @@ TRANSITIVITY_MODE:
362388
INCONV:
363389
IN: "%C% = c_int(%I%)"
364390

391+
TODIRECTED:
392+
PY_TYPE: ToDirected
393+
INCONV:
394+
IN: "%C% = c_int(%I%)"
395+
396+
TOUNDIRECTED:
397+
PY_TYPE: ToUndirected
398+
INCONV:
399+
IN: "%C% = c_int(%I%)"
400+
365401
TREE_MODE:
366402
PY_TYPE: TreeMode
367403
INCONV:

src/igraph_ctypes/_internal/conversion.py

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,23 @@
77
from contextlib import contextmanager
88
from ctypes import addressof, get_errno, memmove, POINTER
99
from os import strerror
10-
from typing import Any, IO, Iterable, Iterator, Optional, Sequence, TYPE_CHECKING
10+
from typing import (
11+
Any,
12+
Callable,
13+
IO,
14+
Iterable,
15+
Iterator,
16+
Optional,
17+
Sequence,
18+
TYPE_CHECKING,
19+
)
1120

1221
from .attributes.utils import python_type_to_igraph_attribute_type
13-
from .enums import MatrixStorage
22+
from .enums import AttributeCombinationType, MatrixStorage
1423
from .lib import (
1524
fdopen,
1625
fflush,
26+
igraph_attribute_combination_add,
1727
igraph_es_all,
1828
igraph_es_none,
1929
igraph_es_vector_copy,
@@ -62,6 +72,8 @@
6272
np_type_of_igraph_bool_t,
6373
np_type_of_igraph_integer_t,
6474
np_type_of_igraph_real_t,
75+
AttributeCombinationSpecification,
76+
AttributeCombinationSpecificationEntry,
6577
BoolArray,
6678
EdgeLike,
6779
EdgeSelector,
@@ -76,6 +88,7 @@
7688
)
7789
from .utils import bytes_to_str
7890
from .wrappers import (
91+
_AttributeCombination,
7992
_EdgeSelector,
8093
_Matrix,
8194
_MatrixInt,
@@ -130,6 +143,7 @@
130143
"iterable_to_igraph_vector_t",
131144
"iterable_to_igraph_vector_t_view",
132145
"iterable_vertex_indices_to_igraph_vector_int_t",
146+
"mapping_to_attribute_combination_t",
133147
"python_type_to_igraph_attribute_type",
134148
"sequence_to_igraph_matrix_int_t",
135149
"sequence_to_igraph_matrix_int_t_view",
@@ -442,6 +456,33 @@ def iterable_of_vertex_index_iterable_to_igraph_vector_int_list_t(
442456
return iterable_of_iterable_to_igraph_vector_int_list_t(items)
443457

444458

459+
def _any_to_attribute_combination_type_and_func(
460+
value: AttributeCombinationSpecificationEntry,
461+
) -> tuple[AttributeCombinationType, Callable | None]:
462+
if callable(value):
463+
# TODO(ntamas): need a Python-to-C wrapper around value!
464+
return AttributeCombinationType.FUNCTION, value
465+
else:
466+
return AttributeCombinationType.from_(value), None
467+
468+
469+
def mapping_to_attribute_combination_t(
470+
mapping: Optional[AttributeCombinationSpecification],
471+
) -> _AttributeCombination:
472+
"""Converts a Python mapping from attribute names to attribute combination
473+
handlers to a low-level igraph attribute combination object.
474+
"""
475+
result = _AttributeCombination.create()
476+
477+
for key, value in (mapping or {}).items():
478+
combination_type, func = _any_to_attribute_combination_type_and_func(value)
479+
igraph_attribute_combination_add(
480+
result, key.encode("utf-8"), combination_type.value, func
481+
)
482+
483+
return result
484+
485+
445486
def sequence_to_igraph_matrix_int_t(items: MatrixIntLike) -> _MatrixInt:
446487
"""Converts a sequence of sequences of Python integers to an igraph matrix
447488
of integers. Each sequence in the top-level sequence must have the same

src/igraph_ctypes/_internal/functions.py

Lines changed: 62 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from .enums import * # noqa
99
from .lib import * # noqa
1010
from .types import (
11+
AttributeCombinationSpecification,
1112
BoolArray,
1213
EdgeLike,
1314
EdgeSelector,
@@ -2391,7 +2392,17 @@ def path_length_hist(graph: Graph, directed: bool = True) -> tuple[RealArray, fl
23912392
# Construct return value
23922393
return res, unconnected
23932394

2394-
# igraph_simplify: no Python type known for type: EDGE_ATTRIBUTE_COMBINATION
2395+
2396+
def simplify(graph: Graph, remove_multiple: bool = True, remove_loops: bool = True, edge_attr_comb: Optional[AttributeCombinationSpecification] = None) -> None:
2397+
"""Type-annotated wrapper for ``igraph_simplify``."""
2398+
# Prepare input arguments
2399+
c_graph = graph
2400+
c_remove_multiple = any_to_igraph_bool_t(remove_multiple)
2401+
c_remove_loops = any_to_igraph_bool_t(remove_loops)
2402+
c_edge_attr_comb = mapping_to_attribute_combination_t(edge_attr_comb)
2403+
2404+
# Call wrapped function
2405+
igraph_simplify(c_graph, c_remove_multiple, c_remove_loops, c_edge_attr_comb)
23952406

23962407

23972408
def transitivity_undirected(graph: Graph, mode: TransitivityMode = TransitivityMode.NAN) -> float:
@@ -3167,7 +3178,16 @@ def assortativity_degree(graph: Graph, directed: bool = True) -> float:
31673178
# Construct return value
31683179
return res
31693180

3170-
# igraph_contract_vertices: no Python type known for type: VERTEX_ATTRIBUTE_COMBINATION
3181+
3182+
def contract_vertices(graph: Graph, mapping: Iterable[int], vertex_attr_comb: Optional[AttributeCombinationSpecification] = None) -> None:
3183+
"""Type-annotated wrapper for ``igraph_contract_vertices``."""
3184+
# Prepare input arguments
3185+
c_graph = graph
3186+
c_mapping = iterable_to_igraph_vector_int_t_view(mapping)
3187+
c_vertex_attr_comb = mapping_to_attribute_combination_t(vertex_attr_comb)
3188+
3189+
# Call wrapped function
3190+
igraph_contract_vertices(c_graph, c_mapping, c_vertex_attr_comb)
31713191

31723192

31733193
def eccentricity(graph: Graph, vids: VertexSelector = "all", mode: NeighborMode = NeighborMode.ALL) -> RealArray:
@@ -3837,6 +3857,9 @@ def maximal_cliques_subset(graph: Graph, subset: Iterable[VertexLike], outfile:
38373857
# Construct return value
38383858
return res, no
38393859

3860+
# Help the type checker to figure out that we never get here
3861+
assert False, "unreachable" # noqa: B011
3862+
38403863
# igraph_maximal_cliques_callback: no Python type known for type: CLIQUE_FUNC
38413864

38423865

@@ -4961,9 +4984,26 @@ def get_stochastic(graph: Graph, column_wise: bool = False, weights: Optional[It
49614984

49624985
# igraph_get_stochastic_sparse: no Python type known for type: SPARSEMAT
49634986

4964-
# igraph_to_directed: no Python type known for type: TODIRECTED
49654987

4966-
# igraph_to_undirected: no Python type known for type: TOUNDIRECTED
4988+
def to_directed(graph: Graph, mode: ToDirected = ToDirected.MUTUAL) -> None:
4989+
"""Type-annotated wrapper for ``igraph_to_directed``."""
4990+
# Prepare input arguments
4991+
c_graph = graph
4992+
c_mode = c_int(mode)
4993+
4994+
# Call wrapped function
4995+
igraph_to_directed(c_graph, c_mode)
4996+
4997+
4998+
def to_undirected(graph: Graph, mode: ToUndirected = ToUndirected.COLLAPSE, edge_attr_comb: Optional[AttributeCombinationSpecification] = None) -> None:
4999+
"""Type-annotated wrapper for ``igraph_to_undirected``."""
5000+
# Prepare input arguments
5001+
c_graph = graph
5002+
c_mode = c_int(mode)
5003+
c_edge_attr_comb = mapping_to_attribute_combination_t(edge_attr_comb)
5004+
5005+
# Call wrapped function
5006+
igraph_to_undirected(c_graph, c_mode, c_edge_attr_comb)
49675007

49685008

49695009
def read_graph_edgelist(instream: FileLike, n: int = 0, directed: bool = True) -> Graph:
@@ -4986,6 +5026,9 @@ def read_graph_edgelist(instream: FileLike, n: int = 0, directed: bool = True) -
49865026
# Construct return value
49875027
return graph
49885028

5029+
# Help the type checker to figure out that we never get here
5030+
assert False, "unreachable" # noqa: B011
5031+
49895032
# igraph_read_graph_ncol: no Python type known for type: VECTOR_STR
49905033

49915034
# igraph_read_graph_lgl: no Python type known for type: ADD_WEIGHTS
@@ -5009,6 +5052,9 @@ def read_graph_pajek(instream: FileLike) -> Graph:
50095052
# Construct return value
50105053
return graph
50115054

5055+
# Help the type checker to figure out that we never get here
5056+
assert False, "unreachable" # noqa: B011
5057+
50125058

50135059
def read_graph_graphml(instream: FileLike, index: int = 0) -> Graph:
50145060
"""Type-annotated wrapper for ``igraph_read_graph_graphml``."""
@@ -5029,6 +5075,9 @@ def read_graph_graphml(instream: FileLike, index: int = 0) -> Graph:
50295075
# Construct return value
50305076
return graph
50315077

5078+
# Help the type checker to figure out that we never get here
5079+
assert False, "unreachable" # noqa: B011
5080+
50325081
# igraph_read_graph_dimacs_flow: no Python type known for type: VECTOR_STR
50335082

50345083

@@ -5051,6 +5100,9 @@ def read_graph_graphdb(instream: FileLike, directed: bool = False) -> Graph:
50515100
# Construct return value
50525101
return graph
50535102

5103+
# Help the type checker to figure out that we never get here
5104+
assert False, "unreachable" # noqa: B011
5105+
50545106

50555107
def read_graph_gml(instream: FileLike) -> Graph:
50565108
"""Type-annotated wrapper for ``igraph_read_graph_gml``."""
@@ -5070,6 +5122,9 @@ def read_graph_gml(instream: FileLike) -> Graph:
50705122
# Construct return value
50715123
return graph
50725124

5125+
# Help the type checker to figure out that we never get here
5126+
assert False, "unreachable" # noqa: B011
5127+
50735128

50745129
def read_graph_dl(instream: FileLike, directed: bool = True) -> Graph:
50755130
"""Type-annotated wrapper for ``igraph_read_graph_dl``."""
@@ -5090,6 +5145,9 @@ def read_graph_dl(instream: FileLike, directed: bool = True) -> Graph:
50905145
# Construct return value
50915146
return graph
50925147

5148+
# Help the type checker to figure out that we never get here
5149+
assert False, "unreachable" # noqa: B011
5150+
50935151

50945152
def write_graph_edgelist(graph: Graph, outstream: FileLike) -> None:
50955153
"""Writes the graph in plain edge list format to an output stream.

src/igraph_ctypes/_internal/lib.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
igraph_bliss_info_t,
3535
igraph_error_handler_t,
3636
igraph_fatal_handler_t,
37+
igraph_function_pointer_t,
3738
igraph_hrg_t,
3839
igraph_layout_drl_options_t,
3940
igraph_maxflow_stats_t,
@@ -535,6 +536,22 @@ def _load_libc():
535536
igraph_set_attribute_table.restype = POINTER(igraph_attribute_table_t)
536537
igraph_set_attribute_table.argtypes = [POINTER(igraph_attribute_table_t)]
537538

539+
igraph_attribute_combination_init = _lib.igraph_attribute_combination_init
540+
igraph_attribute_combination_init.restype = handle_igraph_error_t
541+
igraph_attribute_combination_init.argtypes = [POINTER(igraph_attribute_combination_t)]
542+
543+
igraph_attribute_combination_destroy = _lib.igraph_attribute_combination_destroy
544+
igraph_attribute_combination_destroy.restype = None
545+
igraph_attribute_combination_destroy.argtypes = [POINTER(igraph_attribute_combination_t)]
546+
547+
igraph_attribute_combination_add = _lib.igraph_attribute_combination_add
548+
igraph_attribute_combination_add.restype = handle_igraph_error_t
549+
igraph_attribute_combination_add.argtypes = [POINTER(igraph_attribute_combination_t), c_char_p, c_int, igraph_function_pointer_t]
550+
551+
igraph_attribute_combination_query = _lib.igraph_attribute_combination_query
552+
igraph_attribute_combination_query.restype = handle_igraph_error_t
553+
igraph_attribute_combination_query.argtypes = [POINTER(igraph_attribute_combination_t), c_char_p, POINTER(c_int), POINTER(igraph_function_pointer_t)]
554+
538555
# Error handling and interruptions
539556

540557
igraph_error = _lib.igraph_error

0 commit comments

Comments
 (0)