diff --git a/handbook/architecture/stimulus/README.md b/handbook/architecture/stimulus/README.md index 215a326fa92..ebabc602cb2 100644 --- a/handbook/architecture/stimulus/README.md +++ b/handbook/architecture/stimulus/README.md @@ -7,6 +7,9 @@ The code generator that writes the R/C interface: `src/rinterface.c` and `R/aaa- the C library's own `src/vendor/cigraph/interfaces/functions.yaml` and `types.yaml`, together with this package's `tools/stimulus/functions-R.yaml`, `types-RC.yaml` and `types-RR.yaml`. Stimulus writes one `.build/aaa-auto.R`, which `tools/split-aaa-auto.R` splits into the per-category `R/aaa-*.R` files that `tools/aaa-categories.yaml` names. +A generated function without an entry there stops the split, so a function a newer C core adds needs its category first. +Entries for functions the vendored core does not generate yet are ignored, so they can go to `main` ahead of the core that adds them. +`tools/rebuild-cats.R` holds the placements and rewrites the YAML. A change to the interface goes to those YAML files, or to a `patch/` entry when the C library's own definition is wrong ([`operations/vendoring/adaptation/`](/handbook/operations/vendoring/adaptation/README.md)). diff --git a/tools/aaa-categories.yaml b/tools/aaa-categories.yaml index 59056b7588d..38b6f31af45 100644 --- a/tools/aaa-categories.yaml +++ b/tools/aaa-categories.yaml @@ -7,6 +7,7 @@ basic-igraph: - igraph_delete_edges - igraph_delete_vertices - igraph_delete_vertices_idx + - igraph_delete_vertices_map basic-query-operations: - igraph_degree - igraph_ecount @@ -32,10 +33,12 @@ bipartite: bipartite-adjacency-matrices: - igraph_biadjacency - igraph_get_biadjacency + - igraph_weighted_biadjacency create-two-mode-networks: - igraph_bipartite_game - igraph_bipartite_game_gnm - igraph_bipartite_game_gnp + - igraph_bipartite_iea_game - igraph_create_bipartite - igraph_full_bipartite other-operations-on-bipartite-graphs: @@ -97,6 +100,7 @@ community: community-structure-based-on-the-optimization-of-modularity: - igraph_community_fastgreedy - igraph_community_leiden + - igraph_community_leiden_simple - igraph_community_multilevel edge-betweenness-based-community-detection: - igraph_community_eb_get_merges @@ -218,6 +222,7 @@ games: - igraph_erdos_renyi_game_gnp - igraph_hsbm_game - igraph_hsbm_list_game + - igraph_iea_game - igraph_preference_game - igraph_sbm_game growing-random-games: @@ -239,6 +244,9 @@ games: - igraph_recent_degree_aging_game - igraph_recent_degree_game random-vectors: + - igraph_rng_sample_dirichlet + - igraph_rng_sample_sphere_surface + - igraph_rng_sample_sphere_volume - igraph_sample_dirichlet - igraph_sample_sphere_surface - igraph_sample_sphere_volume @@ -273,7 +281,9 @@ generators: - igraph_circulant - igraph_cycle_graph - igraph_extended_chordal_ring + - igraph_hamming - igraph_hypercube + - igraph_lcf - igraph_lcf_vector - igraph_path_graph - igraph_ring @@ -321,8 +331,11 @@ isomorphism: - igraph_isoclass_subgraph isomorphism-simple-interface: - igraph_automorphism_group + - igraph_automorphism_group_bliss - igraph_canonical_permutation + - igraph_canonical_permutation_bliss - igraph_count_automorphisms + - igraph_count_automorphisms_bliss - igraph_isomorphic - igraph_subisomorphic isomorphism-utility-functions: @@ -379,6 +392,7 @@ motifs: finding-triangles: - igraph_count_adjacent_triangles - igraph_count_triangles + - igraph_is_triangle_free - igraph_list_triangles graph-motifs: - igraph_motifs_randesu @@ -463,6 +477,16 @@ separators: spatial: nongraph-spatial: - igraph_convex_hull_2d + proximity-graphs: + - igraph_beta_weighted_gabriel_graph + - igraph_circle_beta_skeleton + - igraph_delaunay_graph + - igraph_gabriel_graph + - igraph_lune_beta_skeleton + - igraph_nearest_neighbor_graph + - igraph_relative_neighborhood_graph + edge-lengths: + - igraph_spatial_edge_lengths structural: basic-properties: diff --git a/tools/rebuild-cats.R b/tools/rebuild-cats.R index c17a86ae6ce..c82690c76e2 100644 --- a/tools/rebuild-cats.R +++ b/tools/rebuild-cats.R @@ -187,7 +187,38 @@ placements <- rbind( c("structural", "summary-statistics", "igraph_density"), c("structural", "summary-statistics", "igraph_diversity"), - c("structural", "summary-statistics", "igraph_mean_degree") + c("structural", "summary-statistics", "igraph_mean_degree"), + + # --- Ahead of the vendored C core: functions only igraph 1.0 generates. --- + # tools/split-aaa-auto.R ignores entries for functions that are not + # generated, so placing them now keeps a rebuild on a newer core from + # producing R/aaa-uncategorized.R. + + # renamed or added beside a function placed above + c("basic-igraph", "adding-and-deleting-vertices-and-edges", "igraph_delete_vertices_map"), + c("generators", "regular-structure-generators", "igraph_lcf"), + c("games", "random-vectors", "igraph_rng_sample_dirichlet"), + c("games", "random-vectors", "igraph_rng_sample_sphere_surface"), + c("games", "random-vectors", "igraph_rng_sample_sphere_volume"), + c("isomorphism", "isomorphism-simple-interface", "igraph_automorphism_group_bliss"), + c("isomorphism", "isomorphism-simple-interface", "igraph_canonical_permutation_bliss"), + c("isomorphism", "isomorphism-simple-interface", "igraph_count_automorphisms_bliss"), + + # new + c("bipartite", "bipartite-adjacency-matrices", "igraph_weighted_biadjacency"), + c("bipartite", "create-two-mode-networks", "igraph_bipartite_iea_game"), + c("community", "community-structure-based-on-the-optimization-of-modularity", "igraph_community_leiden_simple"), + c("games", "erdos-renyi-games", "igraph_iea_game"), + c("generators", "regular-structure-generators", "igraph_hamming"), + c("motifs", "finding-triangles", "igraph_is_triangle_free"), + c("spatial", "proximity-graphs", "igraph_beta_weighted_gabriel_graph"), + c("spatial", "proximity-graphs", "igraph_circle_beta_skeleton"), + c("spatial", "proximity-graphs", "igraph_delaunay_graph"), + c("spatial", "proximity-graphs", "igraph_gabriel_graph"), + c("spatial", "proximity-graphs", "igraph_lune_beta_skeleton"), + c("spatial", "proximity-graphs", "igraph_nearest_neighbor_graph"), + c("spatial", "proximity-graphs", "igraph_relative_neighborhood_graph"), + c("spatial", "edge-lengths", "igraph_spatial_edge_lengths") ) colnames(placements) <- c("category", "subcategory", "fn") placements <- as.data.frame(placements, stringsAsFactors = FALSE) @@ -302,26 +333,9 @@ subcategory_order_overrides <- list( # ---- 3. Truly stale entries to remove -------------------------------------- -stale_to_remove <- c( - "igraph_automorphism_group_bliss", - "igraph_canonical_permutation_bliss", - "igraph_count_automorphisms_bliss", - "igraph_bipartite_iea_game", - "igraph_iea_game", - "igraph_community_leiden_simple", - "igraph_delete_vertices_map", - "igraph_hamming", - "igraph_lcf", - "igraph_weighted_biadjacency", - "igraph_beta_weighted_gabriel_graph", - "igraph_circle_beta_skeleton", - "igraph_delaunay_graph", - "igraph_gabriel_graph", - "igraph_lune_beta_skeleton", - "igraph_nearest_neighbor_graph", - "igraph_relative_neighborhood_graph", - "igraph_spatial_edge_lengths" -) +# Functions that are generated by no vendored C core, old or new. Functions +# only a newer core generates are not stale: they keep their placement. +stale_to_remove <- character() # ---- 4. Load current cats.yml and build the working structure -------------- @@ -387,10 +401,11 @@ tbl <- rbind(tbl, placements) # ---- 6. Validate ------------------------------------------------------------ # Missing fns (in the generated wrappers but not in cats.yml) are appended to a -# top-level `uncategorized:` flat list and warned about — this keeps the YAML in -# sync with upstream Stimulus additions without erroring. Extra fns (in cats.yml -# but not in the wrappers) are warned about too, but left in place so a human -# can decide whether to move them into `stale_to_remove`. +# top-level `uncategorized:` flat list and warned about. tools/split-aaa-auto.R +# stops on a function without a category and takes `uncategorized:` as the +# explicit opt-out, so move them to a real placement before committing. Extra +# fns (in cats.yml but not in the wrappers) are reported and left in place: +# a newer vendored C core generates them. have <- sort(unique(tbl$fn)) missing <- setdiff(canonical, have) @@ -414,11 +429,11 @@ if (length(missing)) { ) } if (length(extra)) { - warning( - "Extra entries in cats.yml with no matching wrapper:\n ", + message( + "Entries in cats.yml with no matching wrapper, kept:\n ", paste(extra, collapse = "\n "), - "\nRemove them from tools/aaa-categories.yaml or add to `stale_to_remove`.", - call. = FALSE + "\nExpected for functions only a newer vendored C core generates;", + " add any that no core generates to `stale_to_remove`." ) } diff --git a/tools/split-aaa-auto.R b/tools/split-aaa-auto.R index 7d2fec6dcc6..aacc991c253 100644 --- a/tools/split-aaa-auto.R +++ b/tools/split-aaa-auto.R @@ -163,9 +163,10 @@ message("parsed ", length(impls), " _impl wrappers from source") # ---- map each impl to (category, subcategory); validate no gaps --------- -# Functions not listed in aaa-categories.yaml are emitted into -# R/aaa-uncategorized.R with a warning, so a new wrapper landing upstream -# never breaks the build. The warning lists exactly what needs a home. +# Every impl needs a category in aaa-categories.yaml: a function without one +# stops the build rather than landing in R/aaa-uncategorized.R unnoticed. The +# error lists exactly what needs a home. Listing a function under a top-level +# `uncategorized:` key is the explicit opt-out, and writes it to that file. unassigned <- character() for (i in seq_along(impls)) { lookup <- cat_lookup[[impls[[i]]$c_function]] @@ -174,18 +175,16 @@ for (i in seq_along(impls)) { unassigned, paste0(impls[[i]]$impl_name, " -> ", impls[[i]]$c_function) ) - impls[[i]]$category <- "uncategorized" - impls[[i]]$subcategory <- NA_character_ } else { impls[[i]]$category <- lookup$category impls[[i]]$subcategory <- lookup$subcategory } } if (length(unassigned)) { - warning( + stop( "impls without a category in ", CATS, - " — written to R/aaa-uncategorized.R:\n ", + ":\n ", paste(unassigned, collapse = "\n "), "\nAdd placements in tools/rebuild-cats.R and rerun it to clear this.", call. = FALSE