diff --git a/.all-contributorsrc b/.all-contributorsrc
index 78c931202..6daaa80b3 100644
--- a/.all-contributorsrc
+++ b/.all-contributorsrc
@@ -531,6 +531,60 @@
"contributions": [
"code"
]
+ },
+ {
+ "login": "JDPowell648",
+ "name": "JDPowell648",
+ "avatar_url": "https://avatars.githubusercontent.com/u/41934552?v=4",
+ "profile": "https://github.com/JDPowell648",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "Adriankhl",
+ "name": "k.h.lai",
+ "avatar_url": "https://avatars.githubusercontent.com/u/16377650?v=4",
+ "profile": "https://github.com/Adriankhl",
+ "contributions": [
+ "code"
+ ]
+ },
+ {
+ "login": "gruebel",
+ "name": "Anton Grübel",
+ "avatar_url": "https://avatars.githubusercontent.com/u/33207684?v=4",
+ "profile": "https://github.com/gruebel",
+ "contributions": [
+ "code"
+ ]
+ },
+ {
+ "login": "flange-ipb",
+ "name": "flange-ipb",
+ "avatar_url": "https://avatars.githubusercontent.com/u/34936695?v=4",
+ "profile": "https://github.com/flange-ipb",
+ "contributions": [
+ "code"
+ ]
+ },
+ {
+ "login": "pmp-p",
+ "name": "Paul m. p. Peny",
+ "avatar_url": "https://avatars.githubusercontent.com/u/16009100?v=4",
+ "profile": "https://discuss.afpy.org/",
+ "contributions": [
+ "code"
+ ]
+ },
+ {
+ "login": "DavidRConnell",
+ "name": "David R. Connell",
+ "avatar_url": "https://avatars.githubusercontent.com/u/35470740?v=4",
+ "profile": "https://davidrconnell.github.io/",
+ "contributions": [
+ "code"
+ ]
}
],
"contributorsPerLine": 7
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index e9b7f0666..f047f808a 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -30,7 +30,7 @@ jobs:
python-version: '3.8'
- name: Build wheels (manylinux)
- uses: pypa/cibuildwheel@v2.16.5
+ uses: pypa/cibuildwheel@v2.17.0
env:
CIBW_BEFORE_BUILD: "yum install -y flex bison libxml2-devel zlib-devel cairo-devel && pip install -U cmake pip wheel && python setup.py build_c_core"
CIBW_BUILD: "*-manylinux_${{ matrix.wheel_arch }}"
@@ -39,7 +39,7 @@ jobs:
CIBW_TEST_SKIP: "cp310-manylinux_i686 cp311-manylinux_i686 cp312-manylinux_i686"
- name: Build wheels (musllinux)
- uses: pypa/cibuildwheel@v2.16.5
+ uses: pypa/cibuildwheel@v2.17.0
env:
CIBW_BEFORE_BUILD: "apk add flex bison libxml2-dev zlib-dev cairo-dev && pip install -U cmake pip wheel && python setup.py build_c_core"
CIBW_BUILD: "*-musllinux_${{ matrix.wheel_arch }}"
@@ -64,7 +64,7 @@ jobs:
uses: docker/setup-qemu-action@v3
- name: Build wheels (manylinux)
- uses: pypa/cibuildwheel@v2.16.5
+ uses: pypa/cibuildwheel@v2.17.0
env:
CIBW_BEFORE_BUILD: "yum install -y flex bison libxml2-devel zlib-devel cairo-devel && pip install -U cmake pip wheel && python setup.py build_c_core"
CIBW_ARCHS_LINUX: aarch64
@@ -89,7 +89,7 @@ jobs:
uses: docker/setup-qemu-action@v3
- name: Build wheels (musllinux)
- uses: pypa/cibuildwheel@v2.16.5
+ uses: pypa/cibuildwheel@v2.17.0
env:
CIBW_BEFORE_BUILD: "apk add flex bison libxml2-dev zlib-dev cairo-dev && pip install -U cmake pip wheel && python setup.py build_c_core"
CIBW_ARCHS_LINUX: aarch64
@@ -156,7 +156,7 @@ jobs:
cmake --install .
- name: Build wheels
- uses: pypa/cibuildwheel@v2.16.5
+ uses: pypa/cibuildwheel@v2.17.0
env:
CIBW_ARCHS_MACOS: "${{ matrix.wheel_arch }}"
CIBW_BEFORE_BUILD: "python setup.py build_c_core"
@@ -256,7 +256,7 @@ jobs:
shell: cmd
- name: Build wheels
- uses: pypa/cibuildwheel@v2.16.5
+ uses: pypa/cibuildwheel@v2.17.0
env:
CIBW_BEFORE_BUILD: "python setup.py build_c_core"
CIBW_BUILD: "*-${{ matrix.wheel_arch }}"
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 516ea19ca..2b88275d5 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -1,21 +1,14 @@
fail_fast: true
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
- rev: v4.0.1
+ rev: v4.6.0
hooks:
- - id: check-ast
- id: end-of-file-fixer
- id: trailing-whitespace
- repo: https://github.com/charliermarsh/ruff-pre-commit
- rev: v0.0.275
+ rev: v0.3.5
hooks:
- id: ruff
args: [--fix, --exit-non-zero-on-fix]
-
- - repo: https://github.com/psf/black
- rev: 22.3.0
- hooks:
- - id: black
- exclude: ^doc/examples_sphinx-gallery/
- language_version: python3
+ - id: ruff-format
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 03ca918b0..f787db23f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,21 @@
# igraph Python interface changelog
+## [0.11.5] - 2024-05-07
+
+### Added
+
+- Added a `prefixattr=...` keyword argument to `Graph.write_graphml()` that
+ allows the user to strip the `g_`, `v_` and `e_` prefixes from GraphML files
+ written by igraph.
+
+### Changed
+
+- `Graph.are_connected()` has now been renamed to `Graph.are_adjacent()`,
+ following up a similar change in the C core. The old name of the function
+ is deprecated but will be kept around until at least 0.12.0.
+
+- The C core of igraph was updated to version 0.10.12.
+
## [0.11.4]
### Added
@@ -621,7 +637,8 @@ Please refer to the commit logs at https://github.com/igraph/python-igraph for
a list of changes affecting versions up to 0.8.3. Notable changes after 0.8.3
are documented above.
-[main]: https://github.com/igraph/python-igraph/compare/0.11.4...main
+[main]: https://github.com/igraph/python-igraph/compare/0.11.5...main
+[0.11.5]: https://github.com/igraph/python-igraph/compare/0.11.4...0.11.5
[0.11.4]: https://github.com/igraph/python-igraph/compare/0.11.3...0.11.4
[0.11.3]: https://github.com/igraph/python-igraph/compare/0.11.2...0.11.3
[0.11.2]: https://github.com/igraph/python-igraph/compare/0.11.0...0.11.2
diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md
index 37b369f5d..dc02942b4 100644
--- a/CONTRIBUTORS.md
+++ b/CONTRIBUTORS.md
@@ -81,6 +81,14 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
 szcf-weiya 💻 |
 tristanlatr 💻 |
+  JDPowell648 📖 |
+  k.h.lai 💻 |
+  Anton Grübel 💻 |
+  flange-ipb 💻 |
+  Paul m. p. Peny 💻 |
+
+
+  David R. Connell 💻 |
diff --git a/README.md b/README.md
index a94f33f5a..45a127ca4 100644
--- a/README.md
+++ b/README.md
@@ -78,6 +78,10 @@ Also, when building in MSYS2, you need to set the `SETUPTOOLS_USE_DISTUTILS`
environment variable to `stdlib`; this is because MSYS2 uses a patched version
of `distutils` that conflicts with `setuptools >= 60.0`.
+> [!TIP]
+> You need the following packages:
+> `$MINGW_PACKAGE_PREFIX-python-pip $MINGW_PACKAGE_PREFIX-python-setuptools $MINGW_PACKAGE_PREFIX-cc $MINGW_PACKAGE_PREFIX-cmake`
+
### Enabling GraphML
By default, GraphML is disabled, because `libxml2` is not available on Windows in
@@ -158,8 +162,16 @@ the packaged igraph library instead of bringing its own copy.
It is also useful on macOS if you want to link to the igraph library installed
from Homebrew.
-Due to the lack of support of `pkg-config` on Windows, it is currently not
-possible to build against an external library on Windows.
+Due to the lack of support of `pkg-config` on MSVC, it is currently not
+possible to build against an external library on MSVC.
+
+In case you are already using a MSYS2/[MinGW](https://www.mingw-w64.org/) and already have
+[mingw-w64-igraph](https://packages.msys2.org/base/mingw-w64-igraph) installed,
+simply type:
+```
+IGRAPH_USE_PKG_CONFIG=1 SETUPTOOLS_USE_DISTUTILS=stdlib pip install igraph
+```
+to build.
**Warning:** the Python interface is guaranteed to work only with the same
version of the C core that is vendored inside the `vendor/source/igraph`
diff --git a/doc/source/analysis.rst b/doc/source/analysis.rst
index 943f719cc..bb911efad 100644
--- a/doc/source/analysis.rst
+++ b/doc/source/analysis.rst
@@ -63,7 +63,7 @@ To get the vertices at the two ends of an edge, use :attr:`Edge.source` and :att
>>> v1, v2 = e.source, e.target
Vice versa, to get the edge if from the source and target vertices, you can use :meth:`Graph.get_eid` or, for multiple pairs of source/targets,
-:meth:`Graph.get_eids`. The boolean version, asking whether two vertices are directly connected, is :meth:`Graph.are_connected`.
+:meth:`Graph.get_eids`. The boolean version, asking whether two vertices are directly connected, is :meth:`Graph.are_adjacent`.
To get the edges incident on a vertex, you can use :meth:`Vertex.incident`, :meth:`Vertex.out_edges` and
:meth:`Vertex.in_edges`. The three are equivalent on undirected graphs but not directed ones of course::
diff --git a/pyproject.toml b/pyproject.toml
index 0d5932b7b..0ded74ac4 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -3,6 +3,5 @@ requires = ["setuptools", "wheel"]
build-backend = "setuptools.build_meta"
[tool.ruff]
-ignore = ["B905", "C901", "E402", "E501"]
-line-length = 80
-select = ["B", "C", "E", "F", "W"]
+lint.ignore = ["B905", "C901", "E402", "E501"]
+lint.select = ["B", "C", "E", "F", "W"]
diff --git a/src/_igraph/graphobject.c b/src/_igraph/graphobject.c
index 4315a6df0..44b617cf4 100644
--- a/src/_igraph/graphobject.c
+++ b/src/_igraph/graphobject.c
@@ -1445,10 +1445,10 @@ PyObject *igraphmodule_Graph_is_biconnected(igraphmodule_GraphObject *self, PyOb
/** \ingroup python_interface_graph
* \brief Decides whether there is an edge from a given vertex to an other one.
* \return Py_True if the vertices are directly connected, Py_False otherwise
- * \sa igraph_are_connected
+ * \sa igraph_are_adjacent
*/
-PyObject *igraphmodule_Graph_are_connected(igraphmodule_GraphObject * self,
- PyObject * args, PyObject * kwds)
+PyObject *igraphmodule_Graph_are_adjacent(igraphmodule_GraphObject * self,
+ PyObject * args, PyObject * kwds)
{
static char *kwlist[] = { "v1", "v2", NULL };
PyObject *v1, *v2;
@@ -1464,7 +1464,7 @@ PyObject *igraphmodule_Graph_are_connected(igraphmodule_GraphObject * self,
if (igraphmodule_PyObject_to_vid(v2, &idx2, &self->g))
return NULL;
- if (igraph_are_connected(&self->g, idx1, idx2, &res))
+ if (igraph_are_adjacent(&self->g, idx1, idx2, &res))
return igraphmodule_handle_igraph_error();
if (res)
@@ -9728,18 +9728,19 @@ PyObject *igraphmodule_Graph_write_pajek(igraphmodule_GraphObject * self,
PyObject *igraphmodule_Graph_write_graphml(igraphmodule_GraphObject * self,
PyObject * args, PyObject * kwds)
{
- PyObject *fname = NULL;
- static char *kwlist[] = { "f", NULL };
+ PyObject *fname = NULL, *prefixattr_o = Py_True;
+ static char *kwlist[] = { "f", "prefixattr", NULL };
igraphmodule_filehandle_t fobj;
- if (!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist, &fname))
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O", kwlist, &fname, &prefixattr_o))
return NULL;
if (igraphmodule_filehandle_init(&fobj, fname, "w"))
return NULL;
- if (igraph_write_graph_graphml(&self->g, igraphmodule_filehandle_get(&fobj),
- /*prefixattr=*/ 1)) {
+ if (igraph_write_graph_graphml(
+ &self->g, igraphmodule_filehandle_get(&fobj), PyObject_IsTrue(prefixattr_o)
+ )) {
igraphmodule_handle_igraph_error();
igraphmodule_filehandle_destroy(&fobj);
return NULL;
@@ -14553,10 +14554,10 @@ struct PyMethodDef igraphmodule_Graph_methods[] = {
// STRUCTURAL PROPERTIES OF GRAPHS //
/////////////////////////////////////
- // interface to igraph_are_connected
- {"are_connected", (PyCFunction) igraphmodule_Graph_are_connected,
+ // interface to igraph_are_adjacent
+ {"are_adjacent", (PyCFunction) igraphmodule_Graph_are_adjacent,
METH_VARARGS | METH_KEYWORDS,
- "are_connected(v1, v2)\n--\n\n"
+ "are_adjacent(v1, v2)\n--\n\n"
"Decides whether two given vertices are directly connected.\n\n"
"@param v1: the ID or name of the first vertex\n"
"@param v2: the ID or name of the second vertex\n"
@@ -15121,6 +15122,8 @@ struct PyMethodDef igraphmodule_Graph_methods[] = {
METH_VARARGS | METH_KEYWORDS,
"get_shortest_path(v, to, weights=None, mode=\"out\", output=\"vpath\", algorithm=\"auto\")\n--\n\n"
"Calculates the shortest path from a source vertex to a target vertex in a graph.\n\n"
+ "This function only returns a single shortest path. Consider using L{get_shortest_paths()}\n"
+ "to find all shortest paths between a source and one or more target vertices.\n\n"
"@param v: the source vertex of the path\n"
"@param to: the target vertex of the path\n"
"@param weights: edge weights in a list or the name of an edge attribute\n"
@@ -15138,7 +15141,8 @@ struct PyMethodDef igraphmodule_Graph_methods[] = {
" algorithm automatically based on whether the graph has negative weights\n"
" or not. C{\"dijkstra\"} uses Dijkstra's algorithm. C{\"bellman_ford\"}\n"
" uses the Bellman-Ford algorithm. Ignored for unweighted graphs.\n"
- "@return: see the documentation of the C{output} parameter.\n"},
+ "@return: see the documentation of the C{output} parameter.\n"
+ "@see: L{get_shortest_paths()}\n"},
/* interface to igraph_get_shortest_paths */
{"get_shortest_paths", (PyCFunction) igraphmodule_Graph_get_shortest_paths,
@@ -16930,9 +16934,13 @@ struct PyMethodDef igraphmodule_Graph_methods[] = {
/* interface to igraph_write_graph_edgelist */
{"write_graphml", (PyCFunction) igraphmodule_Graph_write_graphml,
METH_VARARGS | METH_KEYWORDS,
- "write_graphml(f)\n--\n\n"
+ "write_graphml(f, prefixattr=True)\n--\n\n"
"Writes the graph to a GraphML file.\n\n"
"@param f: the name of the file to be written or a Python file handle\n"
+ "@param prefixattr: whether attribute names in the written file should be\n"
+ " prefixed with C{g_}, C{v_} and C{e_} for graph, vertex and edge\n"
+ " attributes, respectively. This might be needed to ensure the uniqueness\n"
+ " of attribute identifiers in the written GraphML file.\n"
},
/* interface to igraph_write_graph_leda */
{"write_leda", (PyCFunction) igraphmodule_Graph_write_leda,
diff --git a/src/_igraph/igraphmodule_api.h b/src/_igraph/igraphmodule_api.h
index f7eeb932b..8ab364b50 100644
--- a/src/_igraph/igraphmodule_api.h
+++ b/src/_igraph/igraphmodule_api.h
@@ -56,25 +56,8 @@ extern "C" {
/* Return -1 and set exception on error, 0 on success */
static int import_igraph(void) {
- PyObject *c_api_object;
- PyObject *module;
-
- module = PyImport_ImportModule("igraph._igraph");
- if (module == 0)
- return -1;
-
- c_api_object = PyObject_GetAttrString(module, "_C_API");
- if (c_api_object == 0) {
- Py_DECREF(module);
- return -1;
- }
-
- if (PyCObject_Check(c_api_object))
- PyIGraph_API = (void**)PyCObject_AsVoidPtr(c_api_object);
-
- Py_DECREF(c_api_object);
- Py_DECREF(module);
- return 0;
+ PyIGraph_API = (void **)PyCapsule_Import("igraph._igraph._C_API", 0);
+ return (PyIGraph_API != NULL) ? 0 : -1;
}
#endif
diff --git a/src/igraph/__init__.py b/src/igraph/__init__.py
index c0997aa8c..7f0e328b7 100644
--- a/src/igraph/__init__.py
+++ b/src/igraph/__init__.py
@@ -948,6 +948,14 @@ def Incidence(cls, *args, **kwds):
deprecated("Graph.Incidence() is deprecated; use Graph.Biadjacency() instead")
return cls.Biadjacency(*args, **kwds)
+ def are_connected(self, *args, **kwds):
+ """Deprecated alias to L{Graph.are_adjacent()}."""
+ deprecated(
+ "Graph.are_connected() is deprecated; use Graph.are_adjacent() "
+ "instead"
+ )
+ return self.are_adjacent(*args, **kwds)
+
def get_incidence(self, *args, **kwds):
"""Deprecated alias to L{Graph.get_biadjacency()}."""
deprecated(
diff --git a/src/igraph/clustering.py b/src/igraph/clustering.py
index b45e6d9ce..b1fca9ab4 100644
--- a/src/igraph/clustering.py
+++ b/src/igraph/clustering.py
@@ -1459,6 +1459,9 @@ def _ensure_list(obj):
def compare_communities(comm1, comm2, method="vi", remove_none=False):
"""Compares two community structures using various distance measures.
+ For measures involving entropies (e.g., the variation of information metric),
+ igraph uses natural logarithms.
+
B{References}
- Meila M: Comparing clusterings by the variation of information. In:
diff --git a/src/igraph/io/adjacency.py b/src/igraph/io/adjacency.py
index ff83df5a6..7f09ed167 100644
--- a/src/igraph/io/adjacency.py
+++ b/src/igraph/io/adjacency.py
@@ -78,6 +78,8 @@ def _construct_graph_from_weighted_adjacency(
):
"""Generates a graph from its weighted adjacency matrix.
+ Only edges with a non-zero weight are created.
+
@param matrix: the adjacency matrix. Possible types are:
- a list of lists
- a numpy 2D array or matrix (will be converted to list of lists)
@@ -85,12 +87,12 @@ def _construct_graph_from_weighted_adjacency(
to a dense matrix)
@param mode: the mode to be used. Possible values are:
- C{"directed"} - the graph will be directed and a matrix element
- specifies the number of edges between two vertices.
+ specifies the weight of the corresponding edge.
- C{"undirected"} - the graph will be undirected and a matrix element
- specifies the number of edges between two vertices. The matrix must
+ specifies the weight of the corresponding edge. The matrix must
be symmetric.
- - C{"max"} - undirected graph will be created and the number of
- edges between vertex M{i} and M{j} is M{max(A(i,j), A(j,i))}
+ - C{"max"} - undirected graph will be created and the weight of the
+ edge between vertex M{i} and M{j} is M{max(A(i,j), A(j,i))}
- C{"min"} - like C{"max"}, but with M{min(A(i,j), A(j,i))}
- C{"plus"} - like C{"max"}, but with M{A(i,j) + A(j,i)}
- C{"upper"} - undirected graph with the upper right triangle of
diff --git a/src/igraph/io/libraries.py b/src/igraph/io/libraries.py
index 52e47b8af..f35cc9545 100644
--- a/src/igraph/io/libraries.py
+++ b/src/igraph/io/libraries.py
@@ -55,7 +55,7 @@ def _export_graph_to_networkx(
eattr["_igraph_index"] = i
if multigraph and "_nx_multiedge_key" in eattr:
- eattr["key"] = eattr.pop["_nx_multiedge_key"]
+ eattr["key"] = eattr.pop("_nx_multiedge_key")
if vertex_attr_hashable in graph.vertex_attributes():
hashable_source = graph.vs[vertex_attr_hashable][edge.source]
diff --git a/src/igraph/version.py b/src/igraph/version.py
index 4c4a3bf3a..92c6ba6fb 100644
--- a/src/igraph/version.py
+++ b/src/igraph/version.py
@@ -1,2 +1,2 @@
-__version_info__ = (0, 11, 4)
+__version_info__ = (0, 11, 5)
__version__ = ".".join("{0}".format(x) for x in __version_info__)
diff --git a/tests/test_attributes.py b/tests/test_attributes.py
index 4b6683d32..1299cada9 100644
--- a/tests/test_attributes.py
+++ b/tests/test_attributes.py
@@ -112,7 +112,7 @@ def testVertexNameIndexingBug196(self):
g.add_vertices([a, b])
g.add_edges([(a, b)])
self.assertEqual(g.ecount(), 1)
- self.assertTrue(g.are_connected(a, b))
+ self.assertTrue(g.are_adjacent(a, b))
def testInvalidAttributeNames(self):
g = Graph.Famous("bull")
diff --git a/tests/test_foreign.py b/tests/test_foreign.py
index 69657062e..72047dc1a 100644
--- a/tests/test_foreign.py
+++ b/tests/test_foreign.py
@@ -360,6 +360,7 @@ def testGraphML(self):
self.assertTrue("name" in g.vertex_attributes())
g.write_graphml(tmpfname)
+ g.write_graphml(tmpfname, prefixattr=False)
def testGraphMLz(self):
with temporary_file(
diff --git a/tests/test_structural.py b/tests/test_structural.py
index 5ad20648c..bfda0f2e5 100644
--- a/tests/test_structural.py
+++ b/tests/test_structural.py
@@ -994,7 +994,7 @@ def testGetAllSimplePaths(self):
self.assertEqual(15, path[-1])
curr = path[0]
for next in path[1:]:
- self.assertTrue(g.are_connected(curr, next))
+ self.assertTrue(g.are_adjacent(curr, next))
curr = next
def testPathLengthHist(self):
diff --git a/vendor/source/igraph b/vendor/source/igraph
index 2de2c37e3..9e7717014 160000
--- a/vendor/source/igraph
+++ b/vendor/source/igraph
@@ -1 +1 @@
-Subproject commit 2de2c37e353e15000d0608d2dd2f4b3c3595edb6
+Subproject commit 9e77170146f537ad44e81ca905548738a5a086a0