Skip to content

feat: Add GRAPH_PTR_LIST and MATRIX_LIST type support to enable autogeneration of *_many functions#2540

Merged
krlmlr merged 14 commits intomainfrom
copilot/autogenerate-non-callback-functions-again
Jan 24, 2026
Merged

feat: Add GRAPH_PTR_LIST and MATRIX_LIST type support to enable autogeneration of *_many functions#2540
krlmlr merged 14 commits intomainfrom
copilot/autogenerate-non-callback-functions-again

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Jan 24, 2026

Description

Implements complete two-layer type support (R validation + C conversion) for GRAPH_PTR_LIST and MATRIX_LIST to autogenerate functions operating on multiple graphs simultaneously. Previously ignored due to missing type support.

Changes

Type System - R Layer (types-RR.yaml)

  • Added GRAPH_PTR_LIST: Validates list of igraph objects with user-friendly error messages
  • Added MATRIX_LIST: Validates list of matrices, converts to numeric
  • All arguments are now checked in R before being passed to C

Type System - C Layer (types-RC.yaml)

  • Added GRAPH_PTR_LIST (wraps igraph_vector_ptr_t)
  • Implemented bidirectional conversions in rinterface_extra.c:
    • Rz_SEXP_to_graph_ptr_list(): R list → C vector_ptr using non-copying conversion to avoid memory leaks
    • Ry_igraph_graph_ptr_list_to_SEXP(): C vector_ptr → R list
  • Uses R_alloc() for graph storage and Rz_SEXP_to_igraph (non-copying) to match existing manual implementation patterns

Autogenerated Functions (4 new)

  • igraph_disjoint_union_many() - disjoint union of N graphs
  • igraph_union_many() - union with edge mapping
  • igraph_intersection_many() - intersection with edge mapping
  • igraph_layout_merge_dla() - merge layouts via DLA

Documentation

  • Added "Type Definitions" section to tools/README.md explaining:
    • Role of types-RR.yaml (R-level validation)
    • Role of types-RC.yaml (C-level conversion)
    • How to add new type support

Tests

  • Added coverage for all 4 functions in test-aaa-auto.R

Example

# Previously required manual implementation, now autogenerated:
graphs <- list(make_ring(3), make_star(4), make_full_graph(2))
result <- disjoint_union_many(graphs)  # Works directly with validation

# Layout merging with coordinate validation:
coords <- list(matrix(c(0,0,1,0,2,0), ncol=2, byrow=TRUE),
               matrix(c(0,1,1,1,2,1), ncol=2, byrow=TRUE))
layout <- layout_merge_dla(graphs[1:2], coords)

Note: Functions with callbacks (7) and SPARSEMAT types (5) remain correctly excluded per requirements. Three functions (igraph_get_eid, igraph_community_voronoi, igraph_subisomorphic_lad) require custom implementations due to API mismatches.

Memory Management

The implementation uses Rz_SEXP_to_igraph (non-copying) rather than Rz_SEXP_to_igraph_copy to avoid memory leaks. The non-copying version only sets pointers to existing R graph objects without allocating new memory, eliminating the need for cleanup of copied graph structures. This approach matches the pattern used in existing manual implementations like Rx_igraph_disjoint_union.


  • By submitting this pull request, I assign the copyright of my contribution to The igraph development team.
Original prompt

This section details on the original issue you should resolve

<issue_title>Autogenerate all non-callback functions except SPARSEMAT</issue_title>
<issue_description>Needs conversions for a few more types, both in RR and RC. Do not worry about sparse matrices.</issue_description>

Comments on the Issue (you are @copilot in this section)


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

Copilot AI and others added 5 commits January 24, 2026 12:59
Co-authored-by: krlmlr <1741643+krlmlr@users.noreply.github.com>
Co-authored-by: krlmlr <1741643+krlmlr@users.noreply.github.com>
Co-authored-by: krlmlr <1741643+krlmlr@users.noreply.github.com>
Co-authored-by: krlmlr <1741643+krlmlr@users.noreply.github.com>
Co-authored-by: krlmlr <1741643+krlmlr@users.noreply.github.com>
Copilot AI changed the title [WIP] Autogenerate all non-callback functions except SPARSEMAT feat: Add GRAPH_PTR_LIST type support to enable autogeneration of *_many functions Jan 24, 2026
Copilot AI requested a review from krlmlr January 24, 2026 13:11
Copy link
Copy Markdown
Contributor

@krlmlr krlmlr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot: All arguments need a check/conversion in R, need to add corresponding definitions to types-RR.yaml -- also the coords argument. Document this in an appropriate place.

Comment thread tools/stimulus/functions-R.yaml Outdated
Comment on lines +622 to +623
# Needs custom handling - GRAPH_PTR_LIST type not properly supported
IGNORE: RR, RC
# IGNORE: RR, RC
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove the comments, no longer needed.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed obsolete comments from functions-R.yaml for all *_many functions and layout_merge_dla. (commit bfb9caf)

- Added GRAPH_PTR_LIST and MATRIX_LIST to types-RR.yaml with proper argument validation
- Ensures all list arguments contain correct object types before passing to C
- Removed obsolete comments from functions-R.yaml
- Documented type definition system in tools/README.md

Co-authored-by: krlmlr <1741643+krlmlr@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Jan 24, 2026

@copilot: All arguments need a check/conversion in R, need to add corresponding definitions to types-RR.yaml -- also the coords argument. Document this in an appropriate place.

Added GRAPH_PTR_LIST and MATRIX_LIST type definitions to types-RR.yaml with proper argument validation. All list arguments now have R-level type checks before being passed to C. Documented the type definition system in tools/README.md. (commit bfb9caf)

Copilot AI changed the title feat: Add GRAPH_PTR_LIST type support to enable autogeneration of *_many functions feat: Add GRAPH_PTR_LIST and MATRIX_LIST type support to enable autogeneration of *_many functions Jan 24, 2026
Copilot AI requested a review from krlmlr January 24, 2026 17:28
@krlmlr krlmlr marked this pull request as ready for review January 24, 2026 17:47
Copilot AI review requested due to automatic review settings January 24, 2026 17:47
@krlmlr krlmlr enabled auto-merge (squash) January 24, 2026 17:47
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds support for GRAPH_PTR_LIST and MATRIX_LIST types to the Stimulus code generation framework, enabling autogeneration of 4 functions that operate on multiple graphs: disjoint_union_many, union_many, intersection_many, and layout_merge_dla.

Changes:

  • Added two-layer type validation system: R-level validation in types-RR.yaml and C-level conversion in types-RC.yaml
  • Implemented C conversion functions in rinterface_extra.c for bidirectional GRAPH_PTR_LIST transformations
  • Removed IGNORE directives from functions-R.yaml to enable autogeneration of 4 previously manual functions
  • Added comprehensive tests for all 4 newly autogenerated functions

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
tools/stimulus/types-RR.yaml Added R-level validation for GRAPH_PTR_LIST and MATRIX_LIST with user-friendly error messages
tools/stimulus/types-RC.yaml Added C-level type definitions for GRAPH_PTR_LIST with conversion infrastructure
src/rinterface_extra.c Implemented bidirectional conversion functions between R lists and igraph_vector_ptr_t
src/rinterface.h Added function declarations for new conversion functions
src/rinterface.c Autogenerated C wrapper functions for 4 *_many operations
R/aaa-auto.R Autogenerated R wrapper functions with argument validation
tools/stimulus/functions-R.yaml Removed IGNORE directives to enable autogeneration
tools/README.md Added documentation explaining the two-layer type system
tests/testthat/test-aaa-auto.R Added basic tests for all 4 newly autogenerated functions

Comment on lines +40 to +50
IGRAPH_FINALLY(igraph_vector_ptr_destroy, &%C%);
OUT: |-
IGRAPH_R_CHECK(igraph_vector_ptr_init(&%C%, 0));
IGRAPH_FINALLY(igraph_vector_ptr_destroy, &%C%);
OUTCONV:
IN: |-
igraph_vector_ptr_destroy(&%C%);
IGRAPH_FINALLY_CLEAN(1);
OUT: |-
PROTECT(%I%=Ry_igraph_graph_ptr_list_to_SEXP(&%C%));
igraph_vector_ptr_destroy(&%C%);
Copy link

Copilot AI Jan 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing cleanup registration for copied graphs. The GRAPH_PTR_LIST type definition only registers cleanup for the vector_ptr container itself, but not for the individual graph copies created by Rz_SEXP_to_igraph_copy. This will cause memory leaks when errors occur after graph conversion but before normal cleanup in the calling function.

The type definition should either:

  1. Use Rz_SEXP_to_igraph (non-copying) instead of Rz_SEXP_to_igraph_copy, matching the pattern in existing manual implementations like Rx_igraph_disjoint_union in rinterface_extra.c:4981, or
  2. Add proper cleanup handlers that destroy all copied graphs when errors occur.
Suggested change
IGRAPH_FINALLY(igraph_vector_ptr_destroy, &%C%);
OUT: |-
IGRAPH_R_CHECK(igraph_vector_ptr_init(&%C%, 0));
IGRAPH_FINALLY(igraph_vector_ptr_destroy, &%C%);
OUTCONV:
IN: |-
igraph_vector_ptr_destroy(&%C%);
IGRAPH_FINALLY_CLEAN(1);
OUT: |-
PROTECT(%I%=Ry_igraph_graph_ptr_list_to_SEXP(&%C%));
igraph_vector_ptr_destroy(&%C%);
IGRAPH_FINALLY(igraph_vector_ptr_destroy_all, &%C%);
OUT: |-
IGRAPH_R_CHECK(igraph_vector_ptr_init(&%C%, 0));
IGRAPH_FINALLY(igraph_vector_ptr_destroy_all, &%C%);
OUTCONV:
IN: |-
igraph_vector_ptr_destroy_all(&%C%);
IGRAPH_FINALLY_CLEAN(1);
OUT: |-
PROTECT(%I%=Ry_igraph_graph_ptr_list_to_SEXP(&%C%));
igraph_vector_ptr_destroy_all(&%C%);

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed by changing to use Rz_SEXP_to_igraph (non-copying) instead of Rz_SEXP_to_igraph_copy. The non-copying version doesn't allocate new graph structures, eliminating the memory leak. This matches the pattern in existing manual implementations like Rx_igraph_disjoint_union. (commit 089ef94)

Comment thread src/rinterface_extra.c
Comment on lines +3058 to +3077
/* Convert SEXP list of graphs to igraph_vector_ptr_t */
igraph_error_t Rz_SEXP_to_graph_ptr_list(SEXP graphlist, igraph_vector_ptr_t *ptr,
igraph_t **storage) {
igraph_integer_t n = Rf_xlength(graphlist);

/* Allocate storage for the graphs */
*storage = (igraph_t*) R_alloc(n, sizeof(igraph_t));

/* Initialize the vector_ptr */
IGRAPH_R_CHECK(igraph_vector_ptr_init(ptr, n));

/* Convert each graph */
for (igraph_integer_t i = 0; i < n; i++) {
SEXP item = VECTOR_ELT(graphlist, i);
IGRAPH_R_CHECK(Rz_SEXP_to_igraph_copy(item, &(*storage)[i]));
VECTOR(*ptr)[i] = &(*storage)[i];
}

return IGRAPH_SUCCESS;
}
Copy link

Copilot AI Jan 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Memory leak: The graphs copied by Rz_SEXP_to_igraph_copy at line 3072 are never destroyed. When igraph_copy is called inside Rz_SEXP_to_igraph_copy, it allocates internal graph structures that must be explicitly freed with igraph_destroy. However, the current implementation only registers cleanup for igraph_vector_ptr_destroy (which doesn't free the graph contents), and the c_graphs_storage array is allocated with R_alloc (which only frees the array itself, not the graph structures it contains).

The existing manual implementations (e.g., Rx_igraph_disjoint_union at line 4981) correctly use Rz_SEXP_to_igraph without copy, which only references existing graph structures and requires no cleanup. Consider changing this function to use Rz_SEXP_to_igraph instead of Rz_SEXP_to_igraph_copy, or add a loop to call igraph_destroy on each graph in the storage array when errors occur or after successful completion.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed by using Rz_SEXP_to_igraph (non-copying) instead of Rz_SEXP_to_igraph_copy. This avoids the memory leak since no new graph structures are allocated that would need cleanup. (commit 089ef94)

Copy link
Copy Markdown
Contributor

@krlmlr krlmlr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot: The review remarks seem to be consistent with the test failures. Review and fix.

══ Failed tests ════════════════════════════════════════════════════════════════
── Error ('test-aaa-auto.R:11139:3'): disjoint_union_many_impl basic ───────────
Error in `disjoint_union_many_impl(graphs = list(g1, g2, g3))`: object 'R_igraph_disjoint_union_many' not found
Backtrace:
    ▆
 1. └─igraph:::disjoint_union_many_impl(graphs = list(g1, g2, g3))
── Error ('test-aaa-auto.R:11158:3'): union_many_impl basic ────────────────────
Error in `union_many_impl(graphs = list(g1, g2, g3))`: object 'R_igraph_union_many' not found
Backtrace:
    ▆
 1. └─igraph:::union_many_impl(graphs = list(g1, g2, g3))
── Error ('test-aaa-auto.R:11177:3'): intersection_many_impl basic ─────────────
Error in `intersection_many_impl(graphs = list(g1, g2, g3))`: object 'R_igraph_intersection_many' not found
Backtrace:
    ▆
 1. └─igraph:::intersection_many_impl(graphs = list(g1, g2, g3))
── Error ('test-aaa-auto.R:11197:3'): layout_merge_dla_impl basic ──────────────
Error in `layout_merge_dla_impl(graphs = list(g1, g2), coords = list(coords1, coords2))`: object 'R_igraph_layout_merge_dla' not found
Backtrace:
    ▆
 1. └─igraph:::layout_merge_dla_impl(...)

[ FAIL 4 | WARN 0 | SKIP 4 | PASS 7512 ]

Changed Rz_SEXP_to_graph_ptr_list to use Rz_SEXP_to_igraph (non-copying)
instead of Rz_SEXP_to_igraph_copy. The non-copying version just sets
pointers to existing R graph objects, avoiding memory leaks that would
occur with copied graphs. This matches the pattern used in existing manual
implementations like Rx_igraph_disjoint_union.

Co-authored-by: krlmlr <1741643+krlmlr@users.noreply.github.com>
auto-merge was automatically disabled January 24, 2026 18:14

Head branch was pushed to by a user without write access

Copy link
Copy Markdown
Contributor Author

Copilot AI commented Jan 24, 2026

@copilot: The review remarks seem to be consistent with the test failures. Review and fix.

...

Fixed the memory leak by changing to non-copying graph conversion (Rz_SEXP_to_igraph). The test failures were due to missing regeneration of src/rinterface.c after the last commit - this has been resolved and the package now builds and tests successfully. (commit 089ef94)

Copilot AI requested a review from krlmlr January 24, 2026 18:15
@krlmlr krlmlr enabled auto-merge (squash) January 24, 2026 18:20
@krlmlr krlmlr merged commit afca211 into main Jan 24, 2026
1 check passed
@krlmlr krlmlr deleted the copilot/autogenerate-non-callback-functions-again branch January 24, 2026 19:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Autogenerate all non-callback functions except SPARSEMAT

3 participants