Add graph references
This commit is contained in:
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
igraph library.
|
||||
Copyright (C) 2021 The igraph development team <igraph@igraph.org>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef IGRAPH_GRAPH_ATTRIBUTES_H
|
||||
#define IGRAPH_GRAPH_ATTRIBUTES_H
|
||||
|
||||
#include "igraph_attributes.h"
|
||||
#include "igraph_decls.h"
|
||||
#include "igraph_strvector.h"
|
||||
#include "igraph_types.h"
|
||||
|
||||
IGRAPH_BEGIN_C_DECLS
|
||||
|
||||
igraph_error_t igraph_i_attribute_init(
|
||||
igraph_t *graph, const igraph_attribute_record_list_t *attr
|
||||
);
|
||||
void igraph_i_attribute_destroy(igraph_t *graph);
|
||||
igraph_error_t igraph_i_attribute_copy(
|
||||
igraph_t *to, const igraph_t *from,
|
||||
igraph_bool_t ga, igraph_bool_t va, igraph_bool_t ea
|
||||
);
|
||||
igraph_error_t igraph_i_attribute_add_vertices(
|
||||
igraph_t *graph, igraph_int_t nv,
|
||||
const igraph_attribute_record_list_t *attr
|
||||
);
|
||||
igraph_error_t igraph_i_attribute_permute_vertices(const igraph_t *graph,
|
||||
igraph_t *newgraph,
|
||||
const igraph_vector_int_t *idx);
|
||||
igraph_error_t igraph_i_attribute_combine_vertices(const igraph_t *graph,
|
||||
igraph_t *newgraph,
|
||||
const igraph_vector_int_list_t *merges,
|
||||
const igraph_attribute_combination_t *comb);
|
||||
igraph_error_t igraph_i_attribute_add_edges(
|
||||
igraph_t *graph, const igraph_vector_int_t *edges,
|
||||
const igraph_attribute_record_list_t *attr
|
||||
);
|
||||
igraph_error_t igraph_i_attribute_permute_edges(const igraph_t *graph,
|
||||
igraph_t *newgraph,
|
||||
const igraph_vector_int_t *idx);
|
||||
igraph_error_t igraph_i_attribute_combine_edges(const igraph_t *graph,
|
||||
igraph_t *newgraph,
|
||||
const igraph_vector_int_list_t *merges,
|
||||
const igraph_attribute_combination_t *comb);
|
||||
|
||||
igraph_error_t igraph_i_attribute_get_info(const igraph_t *graph,
|
||||
igraph_strvector_t *gnames,
|
||||
igraph_vector_int_t *gtypes,
|
||||
igraph_strvector_t *vnames,
|
||||
igraph_vector_int_t *vtypes,
|
||||
igraph_strvector_t *enames,
|
||||
igraph_vector_int_t *etypes);
|
||||
igraph_bool_t igraph_i_attribute_has_attr(const igraph_t *graph,
|
||||
igraph_attribute_elemtype_t type,
|
||||
const char *name);
|
||||
igraph_error_t igraph_i_attribute_get_type(const igraph_t *graph,
|
||||
igraph_attribute_type_t *type,
|
||||
igraph_attribute_elemtype_t elemtype,
|
||||
const char *name);
|
||||
|
||||
igraph_error_t igraph_i_attribute_get_numeric_graph_attr(const igraph_t *graph,
|
||||
const char *name,
|
||||
igraph_vector_t *value);
|
||||
igraph_error_t igraph_i_attribute_get_numeric_vertex_attr(const igraph_t *graph,
|
||||
const char *name,
|
||||
igraph_vs_t vs,
|
||||
igraph_vector_t *value);
|
||||
igraph_error_t igraph_i_attribute_get_numeric_edge_attr(const igraph_t *graph,
|
||||
const char *name,
|
||||
igraph_es_t es,
|
||||
igraph_vector_t *value);
|
||||
igraph_error_t igraph_i_attribute_get_string_graph_attr(const igraph_t *graph,
|
||||
const char *name,
|
||||
igraph_strvector_t *value);
|
||||
igraph_error_t igraph_i_attribute_get_string_vertex_attr(const igraph_t *graph,
|
||||
const char *name,
|
||||
igraph_vs_t vs,
|
||||
igraph_strvector_t *value);
|
||||
igraph_error_t igraph_i_attribute_get_string_edge_attr(const igraph_t *graph,
|
||||
const char *name,
|
||||
igraph_es_t es,
|
||||
igraph_strvector_t *value);
|
||||
igraph_error_t igraph_i_attribute_get_bool_graph_attr(const igraph_t *graph,
|
||||
const char *name,
|
||||
igraph_vector_bool_t *value);
|
||||
igraph_error_t igraph_i_attribute_get_bool_vertex_attr(const igraph_t *graph,
|
||||
const char *name,
|
||||
igraph_vs_t vs,
|
||||
igraph_vector_bool_t *value);
|
||||
igraph_error_t igraph_i_attribute_get_bool_edge_attr(const igraph_t *graph,
|
||||
const char *name,
|
||||
igraph_es_t es,
|
||||
igraph_vector_bool_t *value);
|
||||
|
||||
IGRAPH_END_C_DECLS
|
||||
|
||||
#endif /* IGRAPH_GRAPH_ATTRIBUTES_H */
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
igraph library.
|
||||
Copyright (C) 2005-2012 Gabor Csardi <csardi.gabor@gmail.com>
|
||||
334 Harvard street, Cambridge, MA 02139 USA
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
#include "igraph_datatype.h"
|
||||
#include "igraph_types.h"
|
||||
#include "igraph_interface.h"
|
||||
#include "igraph_structural.h"
|
||||
|
||||
/**
|
||||
* \ingroup structural
|
||||
* \function igraph_are_adjacent
|
||||
* \brief Decides whether two vertices are adjacent.
|
||||
*
|
||||
* Decides whether there are any edges that have \p v1 and \p v2
|
||||
* as endpoints. This function is of course symmetric for undirected
|
||||
* graphs.
|
||||
*
|
||||
* \param graph The graph object.
|
||||
* \param v1 The first vertex.
|
||||
* \param v2 The second vertex.
|
||||
* \param res Boolean, \c true if there is an edge from
|
||||
* \p v1 to \p v2, \c false otherwise.
|
||||
* \return The error code \c IGRAPH_EINVVID is returned if an invalid
|
||||
* vertex ID is given.
|
||||
*
|
||||
* Time complexity: O( min(log(d1), log(d2)) ),
|
||||
* d1 is the (out-)degree of \p v1 and d2 is the (in-)degree of \p v2.
|
||||
*/
|
||||
igraph_error_t igraph_are_adjacent(const igraph_t *graph,
|
||||
igraph_int_t v1, igraph_int_t v2,
|
||||
igraph_bool_t *res) {
|
||||
|
||||
igraph_int_t nov = igraph_vcount(graph);
|
||||
igraph_int_t eid = -1;
|
||||
|
||||
if (v1 < 0 || v2 < 0 || v1 > nov - 1 || v2 > nov - 1) {
|
||||
IGRAPH_ERROR("Invalid vertex ID when checking if two vertices are connected.", IGRAPH_EINVVID);
|
||||
}
|
||||
|
||||
igraph_get_eid(graph, &eid, v1, v2, IGRAPH_DIRECTED, /*error=*/ false);
|
||||
*res = (eid >= 0);
|
||||
|
||||
return IGRAPH_SUCCESS;
|
||||
}
|
||||
@@ -0,0 +1,214 @@
|
||||
/*
|
||||
igraph library.
|
||||
Copyright (C) 2022 The igraph development team <igraph@igraph.org>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "igraph_interface.h"
|
||||
|
||||
#include "graph/caching.h"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
/****** Strictly internal functions ******/
|
||||
|
||||
/**
|
||||
* \brief Initializes a property cache, ensuring that all values are unknown.
|
||||
*/
|
||||
igraph_error_t igraph_i_property_cache_init(igraph_i_property_cache_t *cache) {
|
||||
IGRAPH_STATIC_ASSERT(IGRAPH_PROP_I_SIZE <= 32);
|
||||
|
||||
memset(cache->value, 0, sizeof(cache->value));
|
||||
cache->known = 0;
|
||||
return IGRAPH_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Copies a property cache.
|
||||
*/
|
||||
igraph_error_t igraph_i_property_cache_copy(
|
||||
igraph_i_property_cache_t *cache,
|
||||
const igraph_i_property_cache_t *other_cache) {
|
||||
*cache = *other_cache;
|
||||
return IGRAPH_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Destroys a property cache.
|
||||
*/
|
||||
void igraph_i_property_cache_destroy(igraph_i_property_cache_t *cache) {
|
||||
IGRAPH_UNUSED(cache);
|
||||
/* Nothing to do */
|
||||
}
|
||||
|
||||
/***** Developer functions, exposed *****/
|
||||
|
||||
/**
|
||||
* \brief Returns the value of a cached boolean property.
|
||||
*
|
||||
* This function provides valid results only when the property is already
|
||||
* cached. Use \ref igraph_i_property_cache_has() to retrieve whether the
|
||||
* property is cached.
|
||||
*
|
||||
* \param graph the graph whose cache is to be checked
|
||||
* \param prop the property to retrieve from the cache
|
||||
* \return the cached value of the property if the value is in the cache, or
|
||||
* an undefined value otherwise
|
||||
*/
|
||||
igraph_bool_t igraph_i_property_cache_get_bool(const igraph_t *graph, igraph_cached_property_t prop) {
|
||||
IGRAPH_ASSERT(prop >= 0 && prop < IGRAPH_PROP_I_SIZE);
|
||||
assert(graph->cache != NULL);
|
||||
return graph->cache->value[prop];
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Returns whether the cache contains a value for the given cached property.
|
||||
*
|
||||
* \param graph the graph whose cache is to be checked
|
||||
* \param prop the property to check in the cache
|
||||
*/
|
||||
igraph_bool_t igraph_i_property_cache_has(const igraph_t *graph, igraph_cached_property_t prop) {
|
||||
IGRAPH_ASSERT(prop >= 0 && prop < IGRAPH_PROP_I_SIZE);
|
||||
assert(graph->cache != NULL);
|
||||
return graph->cache->known & (1 << prop);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Stores a property value in the cache.
|
||||
*
|
||||
* \param graph the graph whose cache is to be modified
|
||||
* \param prop the property to update in the cache
|
||||
* \param value the value of the property to add to the cache
|
||||
*/
|
||||
void igraph_i_property_cache_set_bool(const igraph_t *graph, igraph_cached_property_t prop, igraph_bool_t value) {
|
||||
IGRAPH_ASSERT(prop >= 0 && prop < IGRAPH_PROP_I_SIZE);
|
||||
assert(graph->cache != NULL);
|
||||
/* Even though graph is const, updating the cache is not considered modification.
|
||||
* Functions that merely compute graph properties, and thus leave the graph structure
|
||||
* intact, will often update the cache. */
|
||||
graph->cache->value[prop] = value;
|
||||
graph->cache->known |= (1 << prop);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Stores a property value in the cache.
|
||||
*
|
||||
* This function asserts that if the value of \p prop was already known,
|
||||
* then \p value is consistent with the previously stored value.
|
||||
* If this is not the case, a fatal error is triggered, with the reasoning
|
||||
* that the cache must have become invalid/inconsistent due to a bug.
|
||||
*
|
||||
* Therefore, this function cannot be used to change an already stored
|
||||
* property to a different value. If this is your intention, invalidate
|
||||
* the cache explicitly first.
|
||||
*
|
||||
* \param graph the graph whose cache is to be modified
|
||||
* \param prop the property to update in the cache
|
||||
* \param value the value of the property to add to the cache
|
||||
*/
|
||||
void igraph_i_property_cache_set_bool_checked(const igraph_t *graph, igraph_cached_property_t prop, igraph_bool_t value) {
|
||||
IGRAPH_ASSERT(prop >= 0 && prop < IGRAPH_PROP_I_SIZE);
|
||||
assert(graph->cache != NULL);
|
||||
/* Even though graph is const, updating the cache is not considered modification.
|
||||
* Functions that merely compute graph properties, and thus leave the graph structure
|
||||
* intact, will often update the cache. */
|
||||
if (graph->cache->known & (1 << prop)) {
|
||||
IGRAPH_ASSERT(graph->cache->value[prop] == value);
|
||||
} else {
|
||||
igraph_i_property_cache_set_bool(graph, prop, value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Invalidates the cached value of a property in a graph.
|
||||
*
|
||||
* \param graph the graph whose cache is to be modified
|
||||
* \param prop the property to invalidate in the cache
|
||||
*/
|
||||
void igraph_i_property_cache_invalidate(const igraph_t *graph, igraph_cached_property_t prop) {
|
||||
IGRAPH_ASSERT(prop >= 0 && prop < IGRAPH_PROP_I_SIZE);
|
||||
assert(graph->cache != NULL);
|
||||
graph->cache->known &= ~(1 << prop);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Invalidates all cached properties of the graph.
|
||||
*
|
||||
* This function is typically called after the graph is modified.
|
||||
*
|
||||
* \param graph the graph whose cache is to be invalidated
|
||||
*/
|
||||
void igraph_i_property_cache_invalidate_all(const igraph_t *graph) {
|
||||
assert(graph->cache != NULL);
|
||||
graph->cache->known = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Invalidates all but a few cached properties of the graph, subject to specific conditions.
|
||||
*
|
||||
* This function is typically called after the graph is modified if we know that
|
||||
* the modification does not affect certain cached properties in certain cases.
|
||||
* For instance, adding more vertices does not make a connected graph disconnected,
|
||||
* so we can keep the cached properties related to graph connectivity if they
|
||||
* were already cached as true, but we need to invalidate them if they were
|
||||
* cached as false.
|
||||
*
|
||||
* </para><para>
|
||||
* Use <code>1 << IGRAPH_PROP_SOMETHING</code> to encode an individual property
|
||||
* in the bits of the bitmask used in the arguments of this function.
|
||||
*
|
||||
* \param graph the graph whose cache is to be invalidated
|
||||
* \param keep_always bitmask where the i-th bit corresponds to cached property \em i
|
||||
* and it should be set to 1 if the property should be \em kept ,
|
||||
* irrespectively of its current cached value.
|
||||
*/
|
||||
void igraph_i_property_cache_invalidate_conditionally(
|
||||
const igraph_t *graph, uint32_t keep_always, uint32_t keep_when_false,
|
||||
uint32_t keep_when_true
|
||||
) {
|
||||
uint32_t invalidate = ~keep_always;
|
||||
uint32_t mask;
|
||||
uint32_t maybe_keep;
|
||||
igraph_bool_t cached_value;
|
||||
|
||||
assert(graph->cache != NULL);
|
||||
|
||||
/* The bits of maybe_keep are set to 1 for those properties that are:
|
||||
*
|
||||
* - currently cached
|
||||
* - should _probably_ be invalidated
|
||||
* - _but_ the current cached value of the property may change the decision
|
||||
*/
|
||||
maybe_keep = graph->cache->known & invalidate & (keep_when_false | keep_when_true);
|
||||
|
||||
if (maybe_keep) {
|
||||
for (igraph_cached_property_t prop = (igraph_cached_property_t ) 0; prop < IGRAPH_PROP_I_SIZE; ++prop) {
|
||||
mask = 1 << prop;
|
||||
if (maybe_keep & mask) {
|
||||
/* if we get here, we know that the property is cached; we have
|
||||
* masked maybe_keep with graph->cache->known */
|
||||
cached_value = igraph_i_property_cache_get_bool(graph, prop);
|
||||
if (
|
||||
((keep_when_false & mask) && !cached_value) ||
|
||||
((keep_when_true & mask) && cached_value)
|
||||
) {
|
||||
invalidate &= ~mask;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
graph->cache->known &= ~invalidate;
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
igraph library.
|
||||
Copyright (C) 2022 The igraph development team <igraph@igraph.org>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef IGRAPH_CACHING_H
|
||||
#define IGRAPH_CACHING_H
|
||||
|
||||
#include "igraph_datatype.h"
|
||||
#include "igraph_decls.h"
|
||||
#include "igraph_error.h"
|
||||
#include "igraph_types.h"
|
||||
|
||||
#include "internal/hacks.h"
|
||||
|
||||
#include <string.h> /* memset */
|
||||
|
||||
IGRAPH_BEGIN_C_DECLS
|
||||
|
||||
struct igraph_i_property_cache_t {
|
||||
igraph_bool_t value[IGRAPH_PROP_I_SIZE];
|
||||
|
||||
/** Bit field that stores which of the properties are cached at the moment */
|
||||
uint32_t known;
|
||||
};
|
||||
|
||||
igraph_error_t igraph_i_property_cache_init(igraph_i_property_cache_t *cache);
|
||||
igraph_error_t igraph_i_property_cache_copy(
|
||||
igraph_i_property_cache_t *cache,
|
||||
const igraph_i_property_cache_t *other_cache);
|
||||
void igraph_i_property_cache_destroy(igraph_i_property_cache_t *cache);
|
||||
|
||||
void igraph_i_property_cache_invalidate_conditionally(
|
||||
const igraph_t *graph, uint32_t keep_always, uint32_t keep_when_false, uint32_t keep_when_true
|
||||
);
|
||||
|
||||
IGRAPH_END_C_DECLS
|
||||
|
||||
#endif /* IGRAPH_CACHING_H */
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
igraph library.
|
||||
Copyright (C) 2022 The igraph development team
|
||||
334 Harvard street, Cambridge, MA 02139 USA
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
#include "igraph_graph_list.h"
|
||||
|
||||
#include "igraph_error.h"
|
||||
#include "igraph_interface.h"
|
||||
#include "igraph_types.h"
|
||||
|
||||
#define GRAPH_LIST
|
||||
#define BASE_GRAPH
|
||||
#define CUSTOM_INIT_DESTROY
|
||||
#include "igraph_pmt.h"
|
||||
#include "core/typed_list.pmt"
|
||||
#include "igraph_pmt_off.h"
|
||||
#undef CUSTOM_INIT_DESTROY
|
||||
#undef BASE_GRAPH
|
||||
#undef GRAPH_LIST
|
||||
|
||||
void igraph_graph_list_set_directed(
|
||||
igraph_graph_list_t* list, igraph_bool_t directed
|
||||
) {
|
||||
IGRAPH_ASSERT(list != 0);
|
||||
list->directed = directed;
|
||||
}
|
||||
|
||||
static igraph_error_t igraph_i_graph_list_init_item(
|
||||
const igraph_graph_list_t* list, igraph_t* item
|
||||
) {
|
||||
return igraph_empty(item, 0, list->directed);
|
||||
}
|
||||
|
||||
static igraph_error_t igraph_i_graph_list_copy_item(
|
||||
igraph_t* dest, const igraph_t* source
|
||||
) {
|
||||
return igraph_copy(dest, source);
|
||||
}
|
||||
|
||||
static void igraph_i_graph_list_destroy_item(igraph_t* item) {
|
||||
igraph_destroy(item);
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
igraph library.
|
||||
Copyright (C) 2021 The igraph development team <igraph@igraph.org>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef IGRAPH_GRAPH_INTERNAL_H
|
||||
#define IGRAPH_GRAPH_INTERNAL_H
|
||||
|
||||
#include "igraph_datatype.h"
|
||||
#include "igraph_decls.h"
|
||||
#include "igraph_error.h"
|
||||
|
||||
IGRAPH_BEGIN_C_DECLS
|
||||
|
||||
igraph_error_t igraph_i_reverse(igraph_t *graph);
|
||||
|
||||
IGRAPH_END_C_DECLS
|
||||
|
||||
#endif /* IGRAPH_GRAPH_INTERNAL_H */
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,243 @@
|
||||
/*
|
||||
igraph library.
|
||||
Copyright (C) 2005-2021 The igraph development team
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
#include "igraph_datatype.h"
|
||||
#include "igraph_interface.h"
|
||||
|
||||
/* Internal functions */
|
||||
|
||||
/* The functions in this file are sensible "default" implementations for some
|
||||
* of the core API functions that simply call other core API functions. If
|
||||
* you are implementing your own data type, chances are that you can use these
|
||||
* as is. */
|
||||
|
||||
/**
|
||||
* \ingroup interface
|
||||
* \function igraph_empty
|
||||
* \brief Creates an empty graph with some vertices and no edges.
|
||||
*
|
||||
* </para><para>
|
||||
* The most basic constructor, all the other constructors should call
|
||||
* this to create a minimal graph object. Our use of the term "empty graph"
|
||||
* in the above description should be distinguished from the mathematical
|
||||
* definition of the empty or null graph. Strictly speaking, the empty or null
|
||||
* graph in graph theory is the graph with no vertices and no edges. However
|
||||
* by "empty graph" as used in \a igraph we mean a graph having zero or more
|
||||
* vertices, but no edges.
|
||||
* \param graph Pointer to a not-yet initialized graph object.
|
||||
* \param n The number of vertices in the graph, a non-negative
|
||||
* integer number is expected.
|
||||
* \param directed Boolean; whether the graph is directed or not. Supported
|
||||
* values are:
|
||||
* \clist
|
||||
* \cli IGRAPH_DIRECTED
|
||||
* The graph will be \em directed.
|
||||
* \cli IGRAPH_UNDIRECTED
|
||||
* The graph will be \em undirected.
|
||||
* \endclist
|
||||
* \return Error code:
|
||||
* \c IGRAPH_EINVAL: invalid number of vertices.
|
||||
*
|
||||
* Time complexity: O(|V|) for a graph with
|
||||
* |V| vertices (and no edges).
|
||||
*
|
||||
* \example examples/simple/creation.c
|
||||
*/
|
||||
igraph_error_t igraph_empty(igraph_t *graph, igraph_int_t n, igraph_bool_t directed) {
|
||||
return igraph_empty_attrs(graph, n, directed, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* \ingroup interface
|
||||
* \function igraph_delete_vertices
|
||||
* \brief Removes some vertices (with all their edges) from the graph.
|
||||
*
|
||||
* </para><para>
|
||||
* This function changes the IDs of the vertices (except in some very
|
||||
* special cases, but these should not be relied on anyway).
|
||||
*
|
||||
* </para><para>
|
||||
* This function invalidates all iterators.
|
||||
*
|
||||
* \param graph The graph to work on.
|
||||
* \param vertices The IDs of the vertices to remove, in a vector. The vector
|
||||
* may contain the same ID more than once.
|
||||
* \return Error code:
|
||||
* \c IGRAPH_EINVVID: invalid vertex ID.
|
||||
*
|
||||
* Time complexity: O(|V|+|E|), |V| and |E| are the number of vertices and
|
||||
* edges in the original graph.
|
||||
*
|
||||
* \example examples/simple/igraph_delete_vertices.c
|
||||
*/
|
||||
igraph_error_t igraph_delete_vertices(igraph_t *graph, const igraph_vs_t vertices) {
|
||||
return igraph_delete_vertices_map(graph, vertices, /* idx= */ 0, /* invidx= */ 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* \function igraph_delete_vertices_idx
|
||||
* \brief Removes some vertices (with all their edges) from the graph (deprecated alias).
|
||||
*
|
||||
* \deprecated-by igraph_delete_vertices_map 0.11.0
|
||||
*/
|
||||
igraph_error_t igraph_delete_vertices_idx(
|
||||
igraph_t *graph, const igraph_vs_t vertices, igraph_vector_int_t *idx,
|
||||
igraph_vector_int_t *invidx
|
||||
) {
|
||||
return igraph_delete_vertices_map(graph, vertices, idx, invidx);
|
||||
}
|
||||
|
||||
/**
|
||||
* \function igraph_edge
|
||||
* \brief Returns the head and tail vertices of an edge.
|
||||
*
|
||||
* \param graph The graph object.
|
||||
* \param eid The edge ID.
|
||||
* \param from Pointer to an \type igraph_int_t. The tail (source) of
|
||||
* the edge will be placed here.
|
||||
* \param to Pointer to an \type igraph_int_t. The head (target) of the
|
||||
* edge will be placed here.
|
||||
* \return Error code.
|
||||
*
|
||||
* \sa \ref igraph_get_eid() for the opposite operation;
|
||||
* \ref igraph_edges() to get the endpoints of several edges;
|
||||
* \ref IGRAPH_TO(), \ref IGRAPH_FROM() and \ref IGRAPH_OTHER() for
|
||||
* a faster but non-error-checked version.
|
||||
*
|
||||
* Added in version 0.2.</para><para>
|
||||
*
|
||||
* Time complexity: O(1).
|
||||
*/
|
||||
igraph_error_t igraph_edge(
|
||||
const igraph_t *graph, igraph_int_t eid,
|
||||
igraph_int_t *from, igraph_int_t *to
|
||||
) {
|
||||
|
||||
if (eid < 0 || eid >= igraph_ecount(graph)) {
|
||||
IGRAPH_ERROR("Cannot retrieve edge endpoints.", IGRAPH_EINVEID);
|
||||
}
|
||||
|
||||
if (igraph_is_directed(graph)) {
|
||||
*from = IGRAPH_FROM(graph, eid);
|
||||
*to = IGRAPH_TO(graph, eid);
|
||||
} else {
|
||||
*from = IGRAPH_TO(graph, eid);
|
||||
*to = IGRAPH_FROM(graph, eid);
|
||||
}
|
||||
|
||||
return IGRAPH_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* \function igraph_edges
|
||||
* \brief Gives the head and tail vertices of a series of edges.
|
||||
*
|
||||
* \param graph The graph object.
|
||||
* \param eids Edge selector, the series of edges.
|
||||
* \param edges Pointer to an initialized vector. The start and endpoints of
|
||||
* each edge will be placed here.
|
||||
* \param bycol Boolean constant. If true, the edges will be returned
|
||||
* columnwise, e.g. the first edge is
|
||||
* <code>res[0]->res[|E|]</code>, the second is
|
||||
* <code>res[1]->res[|E|+1]</code>, etc. Supply false to get
|
||||
* the edge list in a format compatible with \ref igraph_add_edges().
|
||||
* \return Error code.
|
||||
* \sa \ref igraph_get_eids() for the opposite operation;
|
||||
* \ref igraph_edge() for getting the endpoints of a single edge;
|
||||
* \ref IGRAPH_TO(), \ref IGRAPH_FROM() and \ref IGRAPH_OTHER() for
|
||||
* a faster but non-error-checked method.
|
||||
*
|
||||
* Time complexity: O(k) where k is the number of edges in the selector.
|
||||
*/
|
||||
igraph_error_t igraph_edges(
|
||||
const igraph_t *graph, igraph_es_t eids, igraph_vector_int_t *edges,
|
||||
igraph_bool_t bycol
|
||||
) {
|
||||
igraph_eit_t eit;
|
||||
igraph_int_t n, ptr = 0, ptr2;
|
||||
|
||||
IGRAPH_CHECK(igraph_eit_create(graph, eids, &eit));
|
||||
IGRAPH_FINALLY(igraph_eit_destroy, &eit);
|
||||
n = IGRAPH_EIT_SIZE(eit);
|
||||
IGRAPH_CHECK(igraph_vector_int_resize(edges, n * 2));
|
||||
|
||||
if (bycol) {
|
||||
ptr2 = n;
|
||||
if (igraph_is_directed(graph)) {
|
||||
for (; !IGRAPH_EIT_END(eit); IGRAPH_EIT_NEXT(eit)) {
|
||||
igraph_int_t e = IGRAPH_EIT_GET(eit);
|
||||
VECTOR(*edges)[ptr++] = IGRAPH_FROM(graph, e);
|
||||
VECTOR(*edges)[ptr2++] = IGRAPH_TO(graph, e);
|
||||
}
|
||||
} else {
|
||||
for (; !IGRAPH_EIT_END(eit); IGRAPH_EIT_NEXT(eit)) {
|
||||
igraph_int_t e = IGRAPH_EIT_GET(eit);
|
||||
VECTOR(*edges)[ptr++] = IGRAPH_TO(graph, e);
|
||||
VECTOR(*edges)[ptr2++] = IGRAPH_FROM(graph, e);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (igraph_is_directed(graph)) {
|
||||
for (; !IGRAPH_EIT_END(eit); IGRAPH_EIT_NEXT(eit)) {
|
||||
igraph_int_t e = IGRAPH_EIT_GET(eit);
|
||||
VECTOR(*edges)[ptr++] = IGRAPH_FROM(graph, e);
|
||||
VECTOR(*edges)[ptr++] = IGRAPH_TO(graph, e);
|
||||
}
|
||||
} else {
|
||||
for (; !IGRAPH_EIT_END(eit); IGRAPH_EIT_NEXT(eit)) {
|
||||
igraph_int_t e = IGRAPH_EIT_GET(eit);
|
||||
VECTOR(*edges)[ptr++] = IGRAPH_TO(graph, e);
|
||||
VECTOR(*edges)[ptr++] = IGRAPH_FROM(graph, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
igraph_eit_destroy(&eit);
|
||||
IGRAPH_FINALLY_CLEAN(1);
|
||||
|
||||
return IGRAPH_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* \function igraph_invalidate_cache
|
||||
* \brief Invalidates the internal cache of an igraph graph.
|
||||
*
|
||||
* </para><para>
|
||||
* igraph graphs cache some basic properties about themselves in an internal
|
||||
* data structure. This function invalidates the contents of the cache and
|
||||
* forces a recalculation of the cached properties the next time they are
|
||||
* needed.
|
||||
*
|
||||
* </para><para>
|
||||
* You should not need to call this function during normal usage; however, we
|
||||
* might ask you to call this function explicitly if we suspect that you are
|
||||
* running into a bug in igraph's cache handling. A tell-tale sign of an invalid
|
||||
* cache entry is that the result of a cached igraph function (such as
|
||||
* \ref igraph_is_dag() or \ref igraph_is_simple()) is different before and
|
||||
* after a cache invalidation.
|
||||
*
|
||||
* \param graph The graph whose cache is to be invalidated.
|
||||
*
|
||||
* Time complexity: O(1).
|
||||
*/
|
||||
void igraph_invalidate_cache(const igraph_t* graph) {
|
||||
igraph_i_property_cache_invalidate_all(graph);
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,661 @@
|
||||
/*
|
||||
igraph library.
|
||||
Copyright (C) 2006-2023 The igraph development team <igraph@igraph.org>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "igraph_visitor.h"
|
||||
|
||||
#include "igraph_adjlist.h"
|
||||
#include "igraph_bitset.h"
|
||||
#include "igraph_interface.h"
|
||||
#include "igraph_dqueue.h"
|
||||
#include "igraph_stack.h"
|
||||
|
||||
/**
|
||||
* \function igraph_bfs
|
||||
* \brief Breadth-first search.
|
||||
*
|
||||
* A simple breadth-first search, with a lot of different results and
|
||||
* the possibility to call a callback whenever a vertex is visited.
|
||||
* It is allowed to supply null pointers as the output arguments the
|
||||
* user is not interested in, in this case they will be ignored.
|
||||
*
|
||||
* </para><para>
|
||||
* If not all vertices can be reached from the supplied root vertex,
|
||||
* then additional root vertices will be used, in the order of their
|
||||
* vertex IDs.
|
||||
*
|
||||
* </para><para>
|
||||
* Consider using \ref igraph_bfs_simple instead if you set most of the output
|
||||
* arguments provided by this function to a null pointer.
|
||||
*
|
||||
* \param graph The input graph.
|
||||
* \param root The id of the root vertex. It is ignored if the \c
|
||||
* roots argument is not a null pointer.
|
||||
* \param roots Pointer to an initialized vector, or a null
|
||||
* pointer. If not a null pointer, then it is a vector
|
||||
* containing root vertices to start the BFS from. The vertices
|
||||
* are considered in the order they appear. If a root vertex
|
||||
* was already found while searching from another one, then no
|
||||
* search is conducted from it.
|
||||
* \param mode For directed graphs, it defines which edges to follow.
|
||||
* \c IGRAPH_OUT means following the direction of the edges,
|
||||
* \c IGRAPH_IN means the opposite, and
|
||||
* \c IGRAPH_ALL ignores the direction of the edges.
|
||||
* This parameter is ignored for undirected graphs.
|
||||
* \param unreachable Boolean, whether the search should visit
|
||||
* the vertices that are unreachable from the given root
|
||||
* node(s). If true, then additional searches are performed
|
||||
* until all vertices are visited.
|
||||
* \param restricted If not a null pointer, then it must be a pointer
|
||||
* to a vector containing vertex IDs. The BFS is carried out
|
||||
* only on these vertices.
|
||||
* \param order If not null pointer, then the vertex IDs of the graph are
|
||||
* stored here, in the same order as they were visited.
|
||||
* \param rank If not a null pointer, then the rank of each vertex is
|
||||
* stored here.
|
||||
* \param parents If not a null pointer, then the id of the parent of
|
||||
* each vertex is stored here. When a vertex was not visited
|
||||
* during the traversal, -2 will be stored as the ID of its parent.
|
||||
* When a vertex was visited during the traversal and it was one of
|
||||
* the roots of the search trees, -1 will be stored as the ID of
|
||||
* its parent.
|
||||
* \param pred If not a null pointer, then the id of vertex that was
|
||||
* visited before the current one is stored here. If there is
|
||||
* no such vertex (the current vertex is the root of a search
|
||||
* tree), then -1 is stored as the predecessor of the vertex.
|
||||
* If the vertex was not visited at all, then -2 is stored for
|
||||
* the predecessor of the vertex.
|
||||
* \param succ If not a null pointer, then the id of the vertex that
|
||||
* was visited after the current one is stored here. If there
|
||||
* is no such vertex (the current one is the last in a search
|
||||
* tree), then -1 is stored as the successor of the vertex.
|
||||
* If the vertex was not visited at all, then -2 is stored for
|
||||
* the successor of the vertex.
|
||||
* \param dist If not a null pointer, then the distance from the root of
|
||||
* the current search tree is stored here for each vertex. If a
|
||||
* vertex was not reached during the traversal, its distance will
|
||||
* be -1 in this vector.
|
||||
* \param callback If not null, then it should be a pointer to a
|
||||
* function of type \ref igraph_bfshandler_t. This function
|
||||
* will be called, whenever a new vertex is visited.
|
||||
* \param extra Extra argument to pass to the callback function.
|
||||
* \return Error code.
|
||||
*
|
||||
* Time complexity: O(|V|+|E|), linear in the number of vertices and
|
||||
* edges.
|
||||
*
|
||||
* \example examples/simple/igraph_bfs.c
|
||||
* \example examples/simple/igraph_bfs_callback.c
|
||||
*/
|
||||
igraph_error_t igraph_bfs(const igraph_t *graph,
|
||||
igraph_int_t root, const igraph_vector_int_t *roots,
|
||||
igraph_neimode_t mode, igraph_bool_t unreachable,
|
||||
const igraph_vector_int_t *restricted,
|
||||
igraph_vector_int_t *order, igraph_vector_int_t *rank,
|
||||
igraph_vector_int_t *parents,
|
||||
igraph_vector_int_t *pred, igraph_vector_int_t *succ,
|
||||
igraph_vector_int_t *dist, igraph_bfshandler_t *callback,
|
||||
void *extra) {
|
||||
|
||||
const igraph_int_t no_of_nodes = igraph_vcount(graph);
|
||||
|
||||
igraph_error_t ret;
|
||||
|
||||
igraph_dqueue_int_t Q;
|
||||
igraph_int_t actroot = 0;
|
||||
igraph_bitset_t added;
|
||||
|
||||
igraph_lazy_adjlist_t adjlist;
|
||||
|
||||
igraph_int_t act_rank = 0;
|
||||
igraph_int_t pred_vec = -1;
|
||||
|
||||
igraph_int_t rootpos = 0;
|
||||
igraph_int_t noroots = roots ? igraph_vector_int_size(roots) : 1;
|
||||
|
||||
if (!roots && (root < 0 || root >= no_of_nodes)) {
|
||||
IGRAPH_ERROR("Invalid root vertex in BFS.", IGRAPH_EINVVID);
|
||||
}
|
||||
|
||||
if (roots && !igraph_vector_int_isininterval(roots, 0, no_of_nodes-1)) {
|
||||
IGRAPH_ERROR("Invalid root vertex in BFS.", IGRAPH_EINVVID);
|
||||
}
|
||||
|
||||
if (restricted && !igraph_vector_int_isininterval(restricted, 0, no_of_nodes-1)) {
|
||||
IGRAPH_ERROR("Invalid vertex ID in restricted set.", IGRAPH_EINVVID);
|
||||
}
|
||||
|
||||
if (mode != IGRAPH_OUT && mode != IGRAPH_IN &&
|
||||
mode != IGRAPH_ALL) {
|
||||
IGRAPH_ERROR("Invalid mode argument.", IGRAPH_EINVMODE);
|
||||
}
|
||||
|
||||
if (!igraph_is_directed(graph)) {
|
||||
mode = IGRAPH_ALL;
|
||||
}
|
||||
|
||||
IGRAPH_BITSET_INIT_FINALLY(&added, no_of_nodes);
|
||||
IGRAPH_DQUEUE_INT_INIT_FINALLY(&Q, 100);
|
||||
|
||||
IGRAPH_CHECK(igraph_lazy_adjlist_init(graph, &adjlist, mode, IGRAPH_LOOPS, IGRAPH_MULTIPLE));
|
||||
IGRAPH_FINALLY(igraph_lazy_adjlist_destroy, &adjlist);
|
||||
|
||||
/* Mark the vertices that are not in the restricted set, as already
|
||||
found. Special care must be taken for vertices that are not in
|
||||
the restricted set, but are to be used as 'root' vertices. */
|
||||
if (restricted) {
|
||||
igraph_int_t i, n = igraph_vector_int_size(restricted);
|
||||
igraph_bitset_fill(&added, true);
|
||||
for (i = 0; i < n; i++) {
|
||||
igraph_int_t v = VECTOR(*restricted)[i];
|
||||
IGRAPH_BIT_CLEAR(added, v);
|
||||
}
|
||||
}
|
||||
|
||||
/* Resize result vectors, and fill them with the initial value. */
|
||||
|
||||
# define VINIT(v, initial) \
|
||||
if (v) { \
|
||||
IGRAPH_CHECK(igraph_vector_int_resize((v), no_of_nodes)); \
|
||||
igraph_vector_int_fill((v), initial); \
|
||||
}
|
||||
|
||||
VINIT(order, -1);
|
||||
VINIT(rank, -1);
|
||||
VINIT(parents, -2);
|
||||
VINIT(pred, -2);
|
||||
VINIT(succ, -2);
|
||||
VINIT(dist, -1);
|
||||
# undef VINIT
|
||||
|
||||
while (1) {
|
||||
|
||||
/* Get the next root vertex, if any */
|
||||
|
||||
if (roots && rootpos < noroots) {
|
||||
/* We are still going through the 'roots' vector */
|
||||
actroot = VECTOR(*roots)[rootpos++];
|
||||
} else if (!roots && rootpos == 0) {
|
||||
/* We have a single root vertex given, and start now */
|
||||
actroot = root;
|
||||
rootpos++;
|
||||
} else if (rootpos == noroots && unreachable) {
|
||||
/* We finished the given root(s), but other vertices are also
|
||||
tried as root */
|
||||
actroot = 0;
|
||||
rootpos++;
|
||||
} else if (unreachable && actroot + 1 < no_of_nodes) {
|
||||
/* We are already doing the other vertices, take the next one */
|
||||
actroot++;
|
||||
} else {
|
||||
/* No more root nodes to do */
|
||||
break;
|
||||
}
|
||||
|
||||
/* OK, we have a new root, start BFS */
|
||||
if (IGRAPH_BIT_TEST(added, actroot)) {
|
||||
continue;
|
||||
}
|
||||
IGRAPH_CHECK(igraph_dqueue_int_push(&Q, actroot));
|
||||
IGRAPH_CHECK(igraph_dqueue_int_push(&Q, 0));
|
||||
IGRAPH_BIT_SET(added, actroot);
|
||||
if (parents) {
|
||||
VECTOR(*parents)[actroot] = -1;
|
||||
}
|
||||
|
||||
pred_vec = -1;
|
||||
|
||||
while (!igraph_dqueue_int_empty(&Q)) {
|
||||
igraph_int_t actvect = igraph_dqueue_int_pop(&Q);
|
||||
igraph_int_t actdist = igraph_dqueue_int_pop(&Q);
|
||||
igraph_int_t succ_vec;
|
||||
igraph_vector_int_t *neis = igraph_lazy_adjlist_get(&adjlist, actvect);
|
||||
|
||||
IGRAPH_CHECK_OOM(neis, "Failed to query neighbors.");
|
||||
const igraph_int_t n = igraph_vector_int_size(neis);
|
||||
|
||||
if (pred) {
|
||||
VECTOR(*pred)[actvect] = pred_vec;
|
||||
}
|
||||
if (rank) {
|
||||
VECTOR(*rank)[actvect] = act_rank;
|
||||
}
|
||||
if (order) {
|
||||
VECTOR(*order)[act_rank++] = actvect;
|
||||
}
|
||||
if (dist) {
|
||||
VECTOR(*dist)[actvect] = actdist;
|
||||
}
|
||||
|
||||
for (igraph_int_t i = 0; i < n; i++) {
|
||||
igraph_int_t nei = VECTOR(*neis)[i];
|
||||
if (! IGRAPH_BIT_TEST(added, nei)) {
|
||||
IGRAPH_BIT_SET(added, nei);
|
||||
IGRAPH_CHECK(igraph_dqueue_int_push(&Q, nei));
|
||||
IGRAPH_CHECK(igraph_dqueue_int_push(&Q, actdist + 1));
|
||||
if (parents) {
|
||||
VECTOR(*parents)[nei] = actvect;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
succ_vec = igraph_dqueue_int_empty(&Q)
|
||||
? -1
|
||||
: igraph_dqueue_int_head(&Q);
|
||||
if (callback) {
|
||||
IGRAPH_CHECK_CALLBACK(
|
||||
callback(graph, actvect, pred_vec, succ_vec, act_rank - 1, actdist, extra),
|
||||
&ret
|
||||
);
|
||||
|
||||
if (ret == IGRAPH_STOP) {
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
if (succ) {
|
||||
VECTOR(*succ)[actvect] = succ_vec;
|
||||
}
|
||||
pred_vec = actvect;
|
||||
|
||||
} /* while Q !empty */
|
||||
|
||||
} /* for actroot < no_of_nodes */
|
||||
|
||||
cleanup:
|
||||
|
||||
igraph_lazy_adjlist_destroy(&adjlist);
|
||||
igraph_dqueue_int_destroy(&Q);
|
||||
igraph_bitset_destroy(&added);
|
||||
IGRAPH_FINALLY_CLEAN(3);
|
||||
|
||||
return IGRAPH_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* \function igraph_bfs_simple
|
||||
* Breadth-first search, single-source version
|
||||
*
|
||||
* An alternative breadth-first search implementation to cater for the
|
||||
* simpler use-cases when only a single breadth-first search has to be conducted
|
||||
* from a source node and most of the output arguments from \ref igraph_bfs
|
||||
* are not needed. It is allowed to supply null pointers as
|
||||
* the output arguments the user is not interested in, in this case they will
|
||||
* be ignored.
|
||||
*
|
||||
* \param graph The input graph.
|
||||
* \param root The id of the root vertex.
|
||||
* \param mode For directed graphs, it defines which edges to follow.
|
||||
* \c IGRAPH_OUT means following the direction of the edges,
|
||||
* \c IGRAPH_IN means the opposite, and
|
||||
* \c IGRAPH_ALL ignores the direction of the edges.
|
||||
* This parameter is ignored for undirected graphs.
|
||||
* \param order If not a null pointer, then an initialized vector must be passed
|
||||
* here. The IDs of the vertices visited during the traversal will be
|
||||
* stored here, in the same order as they were visited.
|
||||
* \param layers If not a null pointer, then an initialized vector must be
|
||||
* passed here. The i-th element of the vector will contain the index
|
||||
* into \c order where the vertices that are at distance i from the root
|
||||
* are stored. In other words, if you are interested in the vertices that
|
||||
* are at distance i from the root, you need to look in the \c order
|
||||
* vector from \c layers[i] to \c layers[i+1].
|
||||
* \param parents If not a null pointer, then an initialized vector must be
|
||||
* passed here. The vector will be resized so its length is equal to the
|
||||
* number of nodes, and it will contain the index of the parent node for
|
||||
* each \em visited node. The values in the vector are set to -2 for
|
||||
* vertices that were \em not visited, and -1 for the root vertex.
|
||||
* \return Error code.
|
||||
*
|
||||
* Time complexity: O(|V|+|E|), linear in the number of vertices and
|
||||
* edges.
|
||||
*
|
||||
* \example examples/simple/igraph_bfs_simple.c
|
||||
*/
|
||||
igraph_error_t igraph_bfs_simple(
|
||||
const igraph_t *graph, igraph_int_t root, igraph_neimode_t mode,
|
||||
igraph_vector_int_t *order, igraph_vector_int_t *layers,
|
||||
igraph_vector_int_t *parents
|
||||
) {
|
||||
|
||||
const igraph_int_t no_of_nodes = igraph_vcount(graph);
|
||||
igraph_dqueue_int_t q;
|
||||
igraph_int_t num_visited = 0;
|
||||
igraph_vector_int_t neis;
|
||||
igraph_bitset_t added;
|
||||
igraph_int_t lastlayer = -1;
|
||||
|
||||
if (!igraph_is_directed(graph)) {
|
||||
mode = IGRAPH_ALL;
|
||||
}
|
||||
|
||||
if (mode != IGRAPH_OUT && mode != IGRAPH_IN &&
|
||||
mode != IGRAPH_ALL) {
|
||||
IGRAPH_ERROR("Invalid mode argument.", IGRAPH_EINVMODE);
|
||||
}
|
||||
|
||||
/* temporary storage */
|
||||
|
||||
IGRAPH_BITSET_INIT_FINALLY(&added, no_of_nodes);
|
||||
IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0);
|
||||
IGRAPH_CHECK(igraph_dqueue_int_init(&q, 100));
|
||||
IGRAPH_FINALLY(igraph_dqueue_int_destroy, &q);
|
||||
|
||||
/* results */
|
||||
if (order) {
|
||||
igraph_vector_int_clear(order);
|
||||
}
|
||||
if (layers) {
|
||||
igraph_vector_int_clear(layers);
|
||||
}
|
||||
if (parents) {
|
||||
IGRAPH_CHECK(igraph_vector_int_resize(parents, no_of_nodes));
|
||||
igraph_vector_int_fill(parents, -2);
|
||||
}
|
||||
|
||||
/* ok start with root */
|
||||
IGRAPH_CHECK(igraph_dqueue_int_push(&q, root));
|
||||
IGRAPH_CHECK(igraph_dqueue_int_push(&q, 0));
|
||||
if (layers) {
|
||||
IGRAPH_CHECK(igraph_vector_int_push_back(layers, num_visited));
|
||||
}
|
||||
if (order) {
|
||||
IGRAPH_CHECK(igraph_vector_int_push_back(order, root));
|
||||
}
|
||||
if (parents) {
|
||||
VECTOR(*parents)[root] = -1;
|
||||
}
|
||||
num_visited++;
|
||||
IGRAPH_BIT_SET(added, root);
|
||||
|
||||
while (!igraph_dqueue_int_empty(&q)) {
|
||||
igraph_int_t actvect = igraph_dqueue_int_pop(&q);
|
||||
igraph_int_t actdist = igraph_dqueue_int_pop(&q);
|
||||
IGRAPH_CHECK(igraph_neighbors(
|
||||
graph, &neis, actvect, mode, IGRAPH_LOOPS, IGRAPH_MULTIPLE
|
||||
));
|
||||
igraph_int_t nei_count = igraph_vector_int_size(&neis);
|
||||
for (igraph_int_t i = 0; i < nei_count; i++) {
|
||||
const igraph_int_t neighbor = VECTOR(neis)[i];
|
||||
if (! IGRAPH_BIT_TEST(added, neighbor)) {
|
||||
IGRAPH_BIT_SET(added, neighbor);
|
||||
if (parents) {
|
||||
VECTOR(*parents)[neighbor] = actvect;
|
||||
}
|
||||
IGRAPH_CHECK(igraph_dqueue_int_push(&q, neighbor));
|
||||
IGRAPH_CHECK(igraph_dqueue_int_push(&q, actdist + 1));
|
||||
if (layers && lastlayer != actdist + 1) {
|
||||
IGRAPH_CHECK(igraph_vector_int_push_back(layers, num_visited));
|
||||
}
|
||||
if (order) {
|
||||
IGRAPH_CHECK(igraph_vector_int_push_back(order, neighbor));
|
||||
}
|
||||
num_visited++;
|
||||
lastlayer = actdist + 1;
|
||||
}
|
||||
} /* for i in neis */
|
||||
} /* while ! dqueue_int_empty */
|
||||
|
||||
if (layers) {
|
||||
IGRAPH_CHECK(igraph_vector_int_push_back(layers, num_visited));
|
||||
}
|
||||
|
||||
igraph_vector_int_destroy(&neis);
|
||||
igraph_dqueue_int_destroy(&q);
|
||||
igraph_bitset_destroy(&added);
|
||||
IGRAPH_FINALLY_CLEAN(3);
|
||||
|
||||
return IGRAPH_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* \function igraph_dfs
|
||||
* \brief Depth-first search.
|
||||
*
|
||||
* A simple depth-first search, with
|
||||
* the possibility to call a callback whenever a vertex is discovered
|
||||
* and/or whenever a subtree is finished.
|
||||
* It is allowed to supply null pointers as the output arguments the
|
||||
* user is not interested in, in this case they will be ignored.
|
||||
*
|
||||
* </para><para>
|
||||
* If not all vertices can be reached from the supplied root vertex,
|
||||
* then additional root vertices will be used, in the order of their
|
||||
* vertex IDs.
|
||||
*
|
||||
* \param graph The input graph.
|
||||
* \param root The id of the root vertex.
|
||||
* \param mode For directed graphs, it defines which edges to follow.
|
||||
* \c IGRAPH_OUT means following the direction of the edges,
|
||||
* \c IGRAPH_IN means the opposite, and
|
||||
* \c IGRAPH_ALL ignores the direction of the edges.
|
||||
* This parameter is ignored for undirected graphs.
|
||||
* \param unreachable Boolean, whether the search should visit
|
||||
* the vertices that are unreachable from the given root
|
||||
* node(s). If true, then additional searches are performed
|
||||
* until all vertices are visited.
|
||||
* \param order If not null pointer, then the vertex IDs of the graph are
|
||||
* stored here, in the same order as they were discovered. The tail of
|
||||
* the vector will be padded with -1 to ensure that the length of the
|
||||
* vector is the same as the number of vertices, even if some vertices
|
||||
* were not visited during the traversal.
|
||||
* \param order_out If not a null pointer, then the vertex IDs of the
|
||||
* graphs are stored here, in the order of the completion of
|
||||
* their subtree. The tail of the vector will be padded with -1 to ensure
|
||||
* that the length of the vector is the same as the number of vertices,
|
||||
* even if some vertices were not visited during the traversal.
|
||||
* \param parents If not a null pointer, then the id of the parent of
|
||||
* each vertex is stored here. -1 will be stored for the root of the
|
||||
* search tree; -2 will be stored for vertices that were not visited.
|
||||
* \param dist If not a null pointer, then the distance from the root of
|
||||
* the current search tree is stored here. -1 will be stored for vertices
|
||||
* that were not visited.
|
||||
* \param in_callback If not null, then it should be a pointer to a
|
||||
* function of type \ref igraph_dfshandler_t. This function
|
||||
* will be called, whenever a new vertex is discovered.
|
||||
* \param out_callback If not null, then it should be a pointer to a
|
||||
* function of type \ref igraph_dfshandler_t. This function
|
||||
* will be called, whenever the subtree of a vertex is completed.
|
||||
* \param extra Extra argument to pass to the callback function(s).
|
||||
* \return Error code.
|
||||
*
|
||||
* Time complexity: O(|V|+|E|), linear in the number of vertices and
|
||||
* edges.
|
||||
*/
|
||||
|
||||
igraph_error_t igraph_dfs(const igraph_t *graph, igraph_int_t root,
|
||||
igraph_neimode_t mode, igraph_bool_t unreachable,
|
||||
igraph_vector_int_t *order,
|
||||
igraph_vector_int_t *order_out, igraph_vector_int_t *parents,
|
||||
igraph_vector_int_t *dist, igraph_dfshandler_t *in_callback,
|
||||
igraph_dfshandler_t *out_callback,
|
||||
void *extra) {
|
||||
|
||||
const igraph_int_t no_of_nodes = igraph_vcount(graph);
|
||||
igraph_lazy_adjlist_t adjlist;
|
||||
igraph_stack_int_t stack;
|
||||
igraph_bitset_t added;
|
||||
igraph_vector_int_t nptr;
|
||||
igraph_error_t ret;
|
||||
igraph_int_t act_rank = 0;
|
||||
igraph_int_t rank_out = 0;
|
||||
igraph_int_t act_dist = 0;
|
||||
|
||||
if (root < 0 || root >= no_of_nodes) {
|
||||
IGRAPH_ERROR("Invalid root vertex for DFS.", IGRAPH_EINVAL);
|
||||
}
|
||||
|
||||
if (mode != IGRAPH_OUT && mode != IGRAPH_IN &&
|
||||
mode != IGRAPH_ALL) {
|
||||
IGRAPH_ERROR("Invalid mode argument.", IGRAPH_EINVMODE);
|
||||
}
|
||||
|
||||
if (!igraph_is_directed(graph)) {
|
||||
mode = IGRAPH_ALL;
|
||||
}
|
||||
|
||||
IGRAPH_BITSET_INIT_FINALLY(&added, no_of_nodes);
|
||||
IGRAPH_STACK_INT_INIT_FINALLY(&stack, 100);
|
||||
|
||||
IGRAPH_CHECK(igraph_lazy_adjlist_init(graph, &adjlist, mode, IGRAPH_LOOPS, IGRAPH_MULTIPLE));
|
||||
IGRAPH_FINALLY(igraph_lazy_adjlist_destroy, &adjlist);
|
||||
|
||||
IGRAPH_VECTOR_INT_INIT_FINALLY(&nptr, no_of_nodes);
|
||||
|
||||
# define FREE_ALL() do { \
|
||||
igraph_vector_int_destroy(&nptr); \
|
||||
igraph_lazy_adjlist_destroy(&adjlist); \
|
||||
igraph_stack_int_destroy(&stack); \
|
||||
igraph_bitset_destroy(&added); \
|
||||
IGRAPH_FINALLY_CLEAN(4); } while (0)
|
||||
|
||||
/* Resize result vectors and fill them with the initial value */
|
||||
|
||||
# define VINIT(v, initial) if (v) { \
|
||||
IGRAPH_CHECK(igraph_vector_int_resize(v, no_of_nodes)); \
|
||||
igraph_vector_int_fill(v, initial); }
|
||||
|
||||
VINIT(order, -1);
|
||||
VINIT(order_out, -1);
|
||||
VINIT(parents, -2);
|
||||
VINIT(dist, -1);
|
||||
|
||||
# undef VINIT
|
||||
|
||||
IGRAPH_CHECK(igraph_stack_int_push(&stack, root));
|
||||
IGRAPH_BIT_SET(added, root);
|
||||
if (parents) {
|
||||
VECTOR(*parents)[root] = -1;
|
||||
}
|
||||
if (order) {
|
||||
VECTOR(*order)[act_rank++] = root;
|
||||
}
|
||||
if (dist) {
|
||||
VECTOR(*dist)[root] = 0;
|
||||
}
|
||||
if (in_callback) {
|
||||
IGRAPH_CHECK_CALLBACK(in_callback(graph, root, 0, extra), &ret);
|
||||
if (ret == IGRAPH_STOP) {
|
||||
FREE_ALL();
|
||||
return IGRAPH_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
for (igraph_int_t actroot = 0; actroot < no_of_nodes; ) {
|
||||
|
||||
/* 'root' first, then all other vertices */
|
||||
if (igraph_stack_int_empty(&stack)) {
|
||||
if (!unreachable) {
|
||||
break;
|
||||
}
|
||||
if (IGRAPH_BIT_TEST(added, actroot)) {
|
||||
actroot++;
|
||||
continue;
|
||||
}
|
||||
IGRAPH_CHECK(igraph_stack_int_push(&stack, actroot));
|
||||
IGRAPH_BIT_SET(added, actroot);
|
||||
if (parents) {
|
||||
VECTOR(*parents)[actroot] = -1;
|
||||
}
|
||||
if (order) {
|
||||
VECTOR(*order)[act_rank++] = actroot;
|
||||
}
|
||||
if (dist) {
|
||||
VECTOR(*dist)[actroot] = 0;
|
||||
}
|
||||
|
||||
if (in_callback) {
|
||||
IGRAPH_CHECK_CALLBACK(in_callback(graph, actroot, 0, extra), &ret);
|
||||
if (ret == IGRAPH_STOP) {
|
||||
FREE_ALL();
|
||||
return IGRAPH_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
actroot++;
|
||||
}
|
||||
|
||||
while (!igraph_stack_int_empty(&stack)) {
|
||||
igraph_int_t actvect = igraph_stack_int_top(&stack);
|
||||
igraph_int_t *ptr = igraph_vector_int_get_ptr(&nptr, actvect);
|
||||
|
||||
igraph_vector_int_t *neis = igraph_lazy_adjlist_get(&adjlist, actvect);
|
||||
IGRAPH_CHECK_OOM(neis, "Failed to query neighbors.");
|
||||
|
||||
const igraph_int_t n = igraph_vector_int_size(neis);
|
||||
|
||||
/* Search for a neighbor that was not yet visited */
|
||||
igraph_bool_t any = false;
|
||||
igraph_int_t nei = 0;
|
||||
while (!any && (*ptr) < n) {
|
||||
nei = VECTOR(*neis)[(*ptr)];
|
||||
any = !IGRAPH_BIT_TEST(added, nei);
|
||||
(*ptr) ++;
|
||||
}
|
||||
if (any) {
|
||||
/* There is such a neighbor, add it */
|
||||
IGRAPH_CHECK(igraph_stack_int_push(&stack, nei));
|
||||
IGRAPH_BIT_SET(added, nei);
|
||||
if (parents) {
|
||||
VECTOR(*parents)[ nei ] = actvect;
|
||||
}
|
||||
if (order) {
|
||||
VECTOR(*order)[act_rank++] = nei;
|
||||
}
|
||||
act_dist++;
|
||||
if (dist) {
|
||||
VECTOR(*dist)[nei] = act_dist;
|
||||
}
|
||||
|
||||
if (in_callback) {
|
||||
IGRAPH_CHECK_CALLBACK(
|
||||
in_callback(graph, nei, act_dist, extra),
|
||||
&ret
|
||||
);
|
||||
if (ret == IGRAPH_STOP) {
|
||||
FREE_ALL();
|
||||
return IGRAPH_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
/* There is no such neighbor, finished with the subtree */
|
||||
igraph_stack_int_pop(&stack);
|
||||
if (order_out) {
|
||||
VECTOR(*order_out)[rank_out++] = actvect;
|
||||
}
|
||||
act_dist--;
|
||||
|
||||
if (out_callback) {
|
||||
IGRAPH_CHECK_CALLBACK(
|
||||
out_callback(graph, actvect, act_dist, extra),
|
||||
&ret
|
||||
);
|
||||
|
||||
if (ret == IGRAPH_STOP) {
|
||||
FREE_ALL();
|
||||
return IGRAPH_SUCCESS;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FREE_ALL();
|
||||
# undef FREE_ALL
|
||||
|
||||
return IGRAPH_SUCCESS;
|
||||
}
|
||||
Reference in New Issue
Block a user