tests/testthat/test-vertex-spells-contract.R

v01_core_spells <- function() {
  data.frame(
    node = c("A", "B", "A", "C", "B", "E", "A", "B", "C", "B", "B"),
    start = c(0, 2, 2, 4, 1, 10, 5, 4, 0, 6, 2),
    end = c(3, 2, 5, 6, 4, 12, 7, 4, 2, 6, 2),
    onset_censored = c(TRUE, FALSE, FALSE, FALSE, FALSE, FALSE,
                       TRUE, FALSE, FALSE, FALSE, FALSE),
    terminus_censored = c(TRUE, FALSE, TRUE, FALSE, FALSE, TRUE,
                          FALSE, FALSE, FALSE, FALSE, FALSE),
    stringsAsFactors = FALSE
  )
}

v01_edges <- function() {
  data.frame(
    from = c("A", "D"), to = c("B", "C"),
    start = c(0, 0), end = c(7, 6), stringsAsFactors = FALSE
  )
}

v01_key <- function(x) {
  x[order(is.na(x$session), x$session, x$node, x$start, x$end),
    c("node", "start", "end", "duration", "instant", "session",
      "onset_censored", "terminus_censored")]
}

test_that("V01 canonicalizes declared vertex activity literally", {
  dn <- quiet_dynet(v01_edges(), vertex_spells = v01_core_spells())
  actual <- as.data.frame(dn, what = "vertex_spells")
  expected <- data.frame(
    vertex_spell = 1:7,
    node = c("A", "B", "B", "B", "C", "C", "E"),
    start = c(0, 1, 4, 6, 0, 4, 10),
    end = c(7, 4, 4, 6, 2, 6, 12),
    duration = c(7, 3, 0, 0, 2, 2, 2),
    instant = c(FALSE, FALSE, TRUE, TRUE, FALSE, FALSE, FALSE),
    session = rep(NA_character_, 7),
    onset_censored = c(TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE),
    terminus_censored = c(FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE),
    stringsAsFactors = FALSE
  )
  expect_equal(actual, expected, ignore_attr = TRUE)
  expect_identical(
    names(actual),
    c("vertex_spell", "node", "start", "end", "duration", "instant",
      "session", "onset_censored", "terminus_censored")
  )
  expect_identical(as.data.frame(dn, what = "nodes")$name,
                   c("A", "B", "C", "D", "E"))
  expect_identical(dn$meta$vertex_activity, "explicit")
  expect_identical(dn$meta$vertex_activity_default,
                   "always_for_unlisted_nodes")
  expect_identical(dn$meta$vertex_activity_interval,
                   "positive_half_open_instant_closed")
  expect_identical(dn$meta$vertex_activity_input_rows, 11L)
  expect_identical(dn$meta$vertex_activity_components, 7L)
  expect_identical(dn$meta$n_dynamic_vertices, 4L)
  expect_identical(dn$meta$n_implicit_static_vertices, 1L)
  expect_identical(dn$meta$n_vertex_onset_censored, 2L)
  expect_identical(dn$meta$n_vertex_terminus_censored, 3L)
  expect_identical(dn$meta$edge_vertex_activity_policy,
                   "snapshot_induced_v02_paths_endpoint_gated_v03")
})

test_that("V01 normalization helpers expose the same typed canonical contract", {
  direct <- Dynet:::.normalize_vertex_spells(
    v01_core_spells(), origin = 0, time_unit = "step"
  )
  public <- as.data.frame(quiet_dynet(
    v01_edges(), vertex_spells = v01_core_spells()
  ), what = "vertex_spells")
  expect_equal(direct$spells, public, ignore_attr = TRUE)
  expect_equal(Dynet:::.empty_vertex_spells(), public[FALSE, ],
               ignore_attr = TRUE)

  nodes <- data.frame(name = c("A", "B", "C", "D"),
                      role = c("x", "x", "y", "y"))
  with_isolate <- quiet_dynet(
    v01_edges(), nodes = nodes,
    vertex_spells = data.frame(node = "E", start = 0, end = 1)
  )
  node_table <- as.data.frame(with_isolate, what = "nodes")
  expect_true(is.na(node_table$role[node_table$name == "E"]))
})

test_that("V01 uses half-open point coverage and stable optional defaults", {
  spells <- data.frame(
    node = rep("A", 7),
    start = c(0, 1, 2, 4, 4, 5, 5),
    end = c(2, 4, 2, 4, 4, 5, 5)
  )
  dn <- quiet_dynet(data.frame(
    from = "A", to = "B", start = 0, end = 6
  ), vertex_spells = spells)
  actual <- as.data.frame(dn, what = "vertex_spells")
  expect_equal(actual$start, c(0, 4, 5))
  expect_equal(actual$end, c(4, 4, 5))
  expect_identical(actual$instant, c(FALSE, TRUE, TRUE))
  expect_identical(actual$session, rep(NA_character_, 3))
  expect_identical(actual$onset_censored, rep(FALSE, 3))
  expect_identical(actual$terminus_censored, rep(FALSE, 3))
})

test_that("V01 reduces only censor flags at union extrema with OR", {
  outer <- data.frame(
    node = "A", start = c(0, 0, 1, 1), end = c(4, 3, 4, 3),
    onset_censored = c(FALSE, TRUE, FALSE, TRUE),
    terminus_censored = c(FALSE, FALSE, TRUE, TRUE)
  )
  out <- as.data.frame(quiet_dynet(data.frame(
    from = "A", to = "B", start = 0, end = 4
  ), vertex_spells = outer), what = "vertex_spells")
  expect_equal(out[c("start", "end")], data.frame(start = 0, end = 4))
  expect_true(out$onset_censored)
  expect_true(out$terminus_censored)

  interior <- data.frame(
    node = "A", start = c(0, 1), end = c(4, 3),
    onset_censored = c(FALSE, TRUE),
    terminus_censored = c(FALSE, TRUE)
  )
  out <- as.data.frame(quiet_dynet(data.frame(
    from = "A", to = "B", start = 0, end = 4
  ), vertex_spells = interior), what = "vertex_spells")
  expect_false(out$onset_censored)
  expect_false(out$terminus_censored)
})

test_that("V01 keeps sessions separate without changing the edge session universe", {
  edges <- data.frame(
    from = c("A", "A"), to = c("B", "B"), start = c(0, 0), end = c(5, 5),
    wave = c("s1", "s2")
  )
  spells <- data.frame(
    node = c("A", "A", "A"), start = c(0, 2, 0), end = c(2, 4, 4),
    session = c("s1", "s1", "s2")
  )
  dn <- quiet_dynet(edges, session = "wave", vertex_spells = spells)
  actual <- as.data.frame(dn, what = "vertex_spells")
  expect_equal(actual$node, c("A", "A"))
  expect_equal(actual$start, c(0, 0))
  expect_equal(actual$end, c(4, 4))
  expect_identical(actual$session, c("s1", "s2"))
  expect_identical(dn$meta$sessions, c("s1", "s2"))
  expect_identical(dn$meta$vertex_sessions, c("s1", "s2"))

  global <- quiet_dynet(
    edges, session = "wave",
    vertex_spells = data.frame(node = "A", start = 0, end = 4)
  )
  expect_true(is.na(as.data.frame(global, what = "vertex_spells")$session))
  expect_identical(global$meta$sessions, c("s1", "s2"))
  expect_null(global$meta$vertex_sessions)
})

test_that("V01 session components retain points, gaps, and outer censor state", {
  edges <- data.frame(
    from = c("A", "A"), to = c("B", "B"), start = 0, end = 5,
    wave = c("s1", "s2")
  )
  spells <- data.frame(
    node = "A", start = c(0, 2, 4, 0, 1, 3),
    end = c(2, 4, 4, 1, 1, 4),
    session = c("s1", "s1", "s1", "s2", "s2", "s2"),
    onset_censored = c(TRUE, FALSE, FALSE, FALSE, FALSE, FALSE),
    terminus_censored = c(FALSE, TRUE, FALSE, FALSE, FALSE, TRUE)
  )
  actual <- as.data.frame(quiet_dynet(
    edges, session = "wave", vertex_spells = spells
  ), what = "vertex_spells")
  expected <- data.frame(
    vertex_spell = 1:5, node = rep("A", 5),
    start = c(0, 4, 0, 1, 3), end = c(4, 4, 1, 1, 4),
    duration = c(4, 0, 1, 0, 1),
    instant = c(FALSE, TRUE, FALSE, TRUE, FALSE),
    session = c("s1", "s1", "s2", "s2", "s2"),
    onset_censored = c(TRUE, FALSE, FALSE, FALSE, FALSE),
    terminus_censored = c(TRUE, FALSE, FALSE, FALSE, TRUE)
  )
  expect_equal(actual, expected, ignore_attr = TRUE)
})

test_that("V01 session and node identifiers cannot collide through serialization", {
  edges <- data.frame(
    from = c("b\rc", "c"), to = c("X", "Y"), start = 0, end = 2,
    wave = c("a", "a\rb")
  )
  spells <- data.frame(
    node = c("b\rc", "c"), start = c(0, 1), end = c(1, 2),
    session = c("a", "a\rb")
  )
  actual <- as.data.frame(quiet_dynet(
    edges, session = "wave", vertex_spells = spells
  ), what = "vertex_spells")
  expect_equal(nrow(actual), 2L)
  expect_setequal(paste(actual$session, actual$node),
                  paste(spells$session, spells$node))
})

test_that("V01 accepts numeric, Date, and POSIXct vertex clocks", {
  numeric_dn <- quiet_dynet(data.frame(
    from = "A", to = "B", start = 10, end = 20
  ), vertex_spells = data.frame(node = "A", start = 12, end = 17))
  expect_equal(as.data.frame(numeric_dn, what = "vertex_spells")$start, 12)
  expect_equal(as.data.frame(numeric_dn, what = "vertex_spells")$end, 17)

  day <- as.Date("2026-08-01")
  date_dn <- quiet_dynet(data.frame(
    from = "A", to = "B", start = day, end = day + 5
  ), time_unit = "days",
  vertex_spells = data.frame(node = "A", start = day + 1, end = day + 3))
  expect_equal(as.data.frame(date_dn, what = "vertex_spells")$start, 1)
  expect_equal(as.data.frame(date_dn, what = "vertex_spells")$end, 3)

  hour <- as.POSIXct("2026-08-01 00:00:00", tz = "UTC")
  posix_dn <- quiet_dynet(data.frame(
    from = "A", to = "B", start = hour, end = hour + 6 * 3600
  ), time_unit = "hours", vertex_spells = data.frame(
    node = "A", start = hour + 3600, end = hour + 3 * 3600
  ))
  expect_equal(as.data.frame(posix_dn, what = "vertex_spells")$start, 1)
  expect_equal(as.data.frame(posix_dn, what = "vertex_spells")$end, 3)
})

test_that("V01 is invariant to source order, relabeling, translation, and scale", {
  raw <- v01_core_spells()
  base <- quiet_dynet(v01_edges(), vertex_spells = raw)
  permuted <- quiet_dynet(v01_edges(), vertex_spells = raw[c(11:1), ])
  expect_equal(as.data.frame(permuted, what = "vertex_spells"),
               as.data.frame(base, what = "vertex_spells"),
               ignore_attr = TRUE)

  map <- c(A = "z", B = "x", C = "y", D = "v", E = "w")
  renamed <- raw
  renamed$node <- unname(map[renamed$node])
  renamed_edges <- v01_edges()
  renamed_edges$from <- unname(map[renamed_edges$from])
  renamed_edges$to <- unname(map[renamed_edges$to])
  relabeled <- quiet_dynet(renamed_edges, vertex_spells = renamed)
  expect_identical(as.data.frame(relabeled, what = "nodes")$name,
                   sort(unname(map)))
  relabeled_table <- as.data.frame(relabeled, what = "vertex_spells")
  relabeled_table$node <- names(map)[match(relabeled_table$node, map)]
  expect_equal(v01_key(relabeled_table),
               v01_key(as.data.frame(base, what = "vertex_spells")),
               ignore_attr = TRUE)

  shifted <- transform(raw, start = start + 17, end = end + 17)
  shifted_edges <- transform(v01_edges(), start = start + 17, end = end + 17)
  shifted_table <- as.data.frame(
    quiet_dynet(shifted_edges, vertex_spells = shifted),
    what = "vertex_spells"
  )
  shifted_table$start <- shifted_table$start - 17
  shifted_table$end <- shifted_table$end - 17
  expect_equal(v01_key(shifted_table),
               v01_key(as.data.frame(base, what = "vertex_spells")),
               ignore_attr = TRUE)

  scaled <- transform(raw, start = start * 3, end = end * 3)
  scaled_edges <- transform(v01_edges(), start = start * 3, end = end * 3)
  scaled_table <- as.data.frame(
    quiet_dynet(scaled_edges, vertex_spells = scaled),
    what = "vertex_spells"
  )
  expect_equal(scaled_table$duration,
               3 * as.data.frame(base, what = "vertex_spells")$duration)
  scaled_table$start <- scaled_table$start / 3
  scaled_table$end <- scaled_table$end / 3
  scaled_table$duration <- scaled_table$duration / 3
  expect_equal(v01_key(scaled_table),
               v01_key(as.data.frame(base, what = "vertex_spells")),
               ignore_attr = TRUE)

  reversal_source <- raw[raw$start < raw$end, , drop = FALSE]
  reversal_base <- quiet_dynet(v01_edges(), vertex_spells = reversal_source)
  reversed <- transform(
    reversal_source, start = 20 - end, end = 20 - start,
    onset_censored = terminus_censored,
    terminus_censored = onset_censored
  )
  reversed_edges <- transform(
    v01_edges(), start = 20 - end, end = 20 - start
  )
  reversed_table <- as.data.frame(quiet_dynet(
    reversed_edges, vertex_spells = reversed
  ), what = "vertex_spells")
  expected_reversed <- as.data.frame(reversal_base, what = "vertex_spells")
  old_start <- expected_reversed$start
  expected_reversed$start <- 20 - expected_reversed$end
  expected_reversed$end <- 20 - old_start
  old_onset <- expected_reversed$onset_censored
  expected_reversed$onset_censored <- expected_reversed$terminus_censored
  expected_reversed$terminus_censored <- old_onset
  expected_reversed <- expected_reversed[order(
    expected_reversed$node, expected_reversed$start, expected_reversed$end
  ), , drop = FALSE]
  rownames(expected_reversed) <- NULL
  expected_reversed$vertex_spell <- seq_len(nrow(expected_reversed))
  expect_equal(reversed_table, expected_reversed, ignore_attr = TRUE)
})

test_that("V01 session relabeling changes labels but not components", {
  edges <- data.frame(
    from = c("A", "A"), to = c("B", "B"), start = 0, end = 5,
    wave = c("s1", "s2")
  )
  spells <- data.frame(
    node = c("A", "A", "A"), start = c(0, 2, 1), end = c(2, 4, 3),
    session = c("s1", "s1", "s2")
  )
  base <- as.data.frame(quiet_dynet(
    edges, session = "wave", vertex_spells = spells
  ), what = "vertex_spells")
  session_map <- c(s1 = "beta", s2 = "alpha")
  edges$wave <- unname(session_map[edges$wave])
  spells$session <- unname(session_map[spells$session])
  renamed <- as.data.frame(quiet_dynet(
    edges, session = "wave", vertex_spells = spells
  ), what = "vertex_spells")
  renamed$session <- names(session_map)[match(renamed$session, session_map)]
  expect_equal(v01_key(renamed), v01_key(base), ignore_attr = TRUE)
})

test_that("V01 raw storage stays non-destructive after V03 derived views", {
  edges <- data.frame(
    from = c("A", "B"), to = c("B", "C"),
    start = c(0, 2), end = c(7, 6)
  )
  spells <- data.frame(
    node = c("A", "B", "C"), start = c(1, 4, 0), end = c(3, 5, 1)
  )
  legacy <- quiet_dynet(edges)
  expect_no_warning(explicit <- quiet_dynet(edges, vertex_spells = spells))
  expect_equal(as.data.frame(explicit), as.data.frame(legacy),
               ignore_attr = TRUE)
  expect_equal(as.data.frame(explicit, what = "bins"),
               as.data.frame(legacy, what = "bins"), ignore_attr = TRUE)
  expect_false(identical(
    as.data.frame(metrics(explicit, measure = "density"))$value,
    as.data.frame(metrics(legacy, measure = "density"))$value
  ))
  expect_equal(nrow(snapshots(explicit, at = 3, window = 0)), 0)
  expect_gt(nrow(snapshots(legacy, at = 3, window = 0)), 0)
  explicit_paths <- as.data.frame(paths(explicit, from = "A"))
  legacy_paths <- as.data.frame(paths(legacy, from = "A"))
  # A is anchored at its own entry (t = 1); B is never present while A is,
  # so nothing beyond the source itself is reachable.
  others <- explicit_paths$node != "A"
  expect_false(any(explicit_paths$reachable[others]))
  expect_true(all(explicit_paths$n_paths[others] == 0))
  expect_true(all(legacy_paths$reachable))
  expect_equal(as.data.frame(events(
    explicit, measure = c("formation", "dissolution", "active", "new_pairs")
  )), as.data.frame(events(
    legacy, measure = c("formation", "dissolution", "active", "new_pairs")
  )), ignore_attr = TRUE)
  explicit_duration <- as.data.frame(durations(
    explicit, measure = c("events", "total")
  ))
  legacy_duration <- as.data.frame(durations(
    legacy, measure = c("events", "total")
  ))
  expect_equal(nrow(explicit_duration), 0L)
  expect_equal(nrow(legacy_duration), 4L)
  expect_equal(explicit$meta$time_range, legacy$meta$time_range)

  bounded <- quiet_dynet(
    edges, vertex_spells = spells,
    observation_start = 1, observation_end = 5
  )
  gapped <- quiet_dynet(
    edges, vertex_spells = spells,
    observation_spells = data.frame(start = c(1, 4), end = c(2, 5))
  )
  expect_equal(as.data.frame(bounded, what = "vertex_spells"),
               as.data.frame(explicit, what = "vertex_spells"),
               ignore_attr = TRUE)
  expect_equal(as.data.frame(gapped, what = "vertex_spells"),
               as.data.frame(explicit, what = "vertex_spells"),
               ignore_attr = TRUE)
})

test_that("V01 all-static inputs have a typed empty accessor", {
  legacy <- quiet_dynet(v01_edges())
  empty <- data.frame(node = character(), start = numeric(), end = numeric())
  explicit_empty <- quiet_dynet(v01_edges(), vertex_spells = empty)
  expected <- data.frame(
    vertex_spell = integer(), node = character(), start = numeric(),
    end = numeric(), duration = numeric(), instant = logical(),
    session = character(), onset_censored = logical(),
    terminus_censored = logical(), stringsAsFactors = FALSE
  )
  expect_equal(as.data.frame(legacy, what = "vertex_spells"), expected,
               ignore_attr = TRUE)
  expect_equal(as.data.frame(explicit_empty, what = "vertex_spells"), expected,
               ignore_attr = TRUE)
  expect_identical(legacy$meta$vertex_activity, "static")
  expect_identical(explicit_empty$meta$vertex_activity, "static")
  expect_equal(as.data.frame(explicit_empty), as.data.frame(legacy),
               ignore_attr = TRUE)

  empty_session <- data.frame(
    node = character(), start = numeric(), end = numeric(),
    session = character()
  )
  session_typed <- quiet_dynet(v01_edges(), vertex_spells = empty_session)
  expect_equal(as.data.frame(session_typed, what = "vertex_spells"), expected,
               ignore_attr = TRUE)
})

test_that("V01 loop cleaning and internal netobject slices preserve activity", {
  edges <- data.frame(
    from = c("A", "A"), to = c("A", "B"), start = c(0, 0), end = c(2, 2)
  )
  spells <- data.frame(node = c("A", "B"), start = c(0, 1), end = c(2, 2))
  dropped <- quiet_dynet(edges, loops = FALSE, vertex_spells = spells)
  kept <- quiet_dynet(edges, loops = TRUE, vertex_spells = spells)
  expect_equal(as.data.frame(dropped, what = "vertex_spells"),
               as.data.frame(kept, what = "vertex_spells"),
               ignore_attr = TRUE)
  expect_identical(as.data.frame(dropped, what = "nodes")$name, c("A", "B"))
  expect_identical(as.data.frame(kept, what = "nodes")$name, c("A", "B"))

  expected <- as.data.frame(kept, what = "vertex_spells")
  ranged <- Dynet:::.range_netobject(kept, 0, 2)
  binned <- Dynet:::.bin_netobject(kept, 1)
  expect_equal(as.data.frame(ranged, what = "vertex_spells"), expected,
               ignore_attr = TRUE)
  expect_equal(as.data.frame(binned, what = "vertex_spells"), expected,
               ignore_attr = TRUE)
})

test_that("V01 validates its fixed schema, values, flags, and sessions", {
  edges <- v01_edges()
  good <- data.frame(node = "A", start = 0, end = 1)
  expect_error(dynet(edges, vertex_spells = 1),
               class = "dynet_bad_vertex_spells")
  expect_error(dynet(edges, vertex_spells = data.frame(node = "A", start = 0)),
               class = "dynet_missing_column")
  # Other columns are attributes of a node table and are ignored (0.4.5).
  with_attribute <- dynet(edges, vertex_spells = transform(good, attribute = 1))
  expect_identical(as.data.frame(with_attribute, what = "vertex_spells"),
                   as.data.frame(dynet(edges, vertex_spells = good),
                                 what = "vertex_spells"))
  aliased <- data.frame(vertex.id = "A", onset = 0, terminus = 1, region = "x")
  expect_identical(as.data.frame(dynet(edges, vertex_spells = aliased),
                                 what = "vertex_spells"),
                   as.data.frame(dynet(edges, vertex_spells = good),
                                 what = "vertex_spells"))
  duplicate_names <- good
  names(duplicate_names)[3] <- "start"
  expect_error(dynet(edges, vertex_spells = duplicate_names),
               class = "dynet_bad_vertex_spells")
  expect_error(dynet(edges, vertex_spells = transform(good, node = NA_character_)),
               class = "dynet_bad_vertex_spells")
  expect_error(dynet(edges, vertex_spells = transform(good, node = "")),
               class = "dynet_bad_vertex_spells")
  expect_error(dynet(edges, vertex_spells = transform(good, start = Inf)),
               class = "dynet_bad_vertex_spells")
  expect_error(dynet(edges, vertex_spells = transform(good, start = 2, end = 1)),
               class = "dynet_bad_vertex_spells")
  expect_error(dynet(edges, vertex_spells = transform(good,
    onset_censored = 1)), class = "dynet_bad_vertex_censor")
  expect_error(dynet(edges, vertex_spells = transform(good,
    onset_censored = NA)), class = "dynet_bad_vertex_censor")
  expect_error(dynet(edges, vertex_spells = data.frame(
    node = "A", start = 1, end = 1, onset_censored = TRUE
  )), class = "dynet_bad_vertex_censor")
  expect_error(dynet(edges, vertex_spells = transform(good, session = "s1")),
               class = "dynet_incompatible_vertex_spells")

  session_edges <- transform(edges, wave = c("s1", "s2"))
  expect_error(dynet(session_edges, session = "wave",
                     vertex_spells = transform(good, session = "s3")),
               class = "dynet_unknown_session")
  mixed <- rbind(transform(good, session = "s1"),
                 transform(good, session = NA_character_))
  expect_error(dynet(session_edges, session = "wave", vertex_spells = mixed),
               class = "dynet_bad_vertex_spells")
})

test_that("set_vertex_spells(\"ties\") spans each vertex from first tie to last", {
  dn <- quiet_dynet(school_contacts)
  spanned <- set_vertex_spells(dn, "ties")
  declared <- as.data.frame(spanned, what = "vertex_spells")
  ties <- as.data.frame(dn)

  # One spell per vertex that has a tie, from its earliest start to its
  # latest end, checked vertex by vertex against the tie table.
  expect_identical(sort(unique(declared$node)),
                   sort(unique(c(ties$from, ties$to))))
  expect_false(anyDuplicated(declared$node) > 0)
  hand <- vapply(declared$node, function(v) {
    own <- ties[ties$from == v | ties$to == v, , drop = FALSE]
    c(min(own$start), max(own$end))
  }, numeric(2L))
  expect_equal(declared$start, unname(hand[1L, ]))
  expect_equal(declared$end, unname(hand[2L, ]))

  # The invariant: declaring spans can only remove presence, never add it,
  # and the ties themselves are untouched.
  before <- summary(snapshots(dn, step = 4, window = 4))
  after <- summary(snapshots(spanned, step = 4, window = 4))
  expect_true(all(after$nodes <= before$nodes))
  expect_identical(after$ties, before$ties)
  expect_identical(as.data.frame(spanned), as.data.frame(dn))

  expect_error(set_vertex_spells(dn, "everything"), class = "dynet_bad_input")
  # Every vertex of a dynet has a tie, so every vertex gets a span.
  expect_identical(nrow(declared), nrow(dn$nodes))
})

Try the Dynet package in your browser

Any scripts or data that you put into this service are public.

Dynet documentation built on Oct. 7, 2026, 5:08 p.m.