diff --git a/DESCRIPTION b/DESCRIPTION index 3ce53cc..7b244a4 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,6 +1,6 @@ Package: keedwell Title: Latin Squares in R -Version: 0.1.1 +Version: 0.1.1.9000 Authors@R: person("Matthew", "Henderson", , "matthew.james.henderson@gmail.com", role = c("aut", "cre"), comment = c(ORCID = "0000-0001-7949-8208")) diff --git a/NAMESPACE b/NAMESPACE index 2ed5ff9..35344d5 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -1,7 +1,13 @@ # Generated by roxygen2: do not edit by hand +export(add_cols) export(add_rows) export(create_latin_square) export(edge_tbl) +export(next_col_matching) +export(next_col_random) export(next_row) +export(next_row_matching) +export(next_row_random) export(to_tidygraph) +export(to_tidygraph_2) diff --git a/R/add-cols.R b/R/add-cols.R new file mode 100644 index 0000000..bbcde27 --- /dev/null +++ b/R/add-cols.R @@ -0,0 +1,20 @@ +#' Add new columns to a latin rectangle +#' +#' @param R A latin rectangle +#' @param cols Indices of columns to add +#' @param l_order Dimension of latin square +#' @param strategy Strategy for filling columns +#' +#' @return A latin rectangle +#' @export +add_cols <- function(R, cols, l_order, strategy = next_col_matching) { + + for (i in cols) { + + R <- strategy(R, i, l_order) + + } + + return(R) + +} diff --git a/R/edge-tbl.R b/R/edge-tbl.R index 6e76887..fea2042 100644 --- a/R/edge-tbl.R +++ b/R/edge-tbl.R @@ -29,5 +29,31 @@ edge_tbl <- function(R, i, l_order = 3) { ) |> dplyr::mutate(from = paste0("c", i)) + return(edge_df) +} + +#' Symbols missing from rows data frame +#' +#' @param R latin rectangle +#' @param i row index +#' @param l_order size of latin square R is going to be embedded into +#' +#' @return The edge data frame. +edge_tbl_2 <- function(R, i, l_order = 3) { + + all_symbols <- 1:l_order + + # symbols used in row i + used <- R |> dplyr::filter(row == i) |> dplyr::pull(symbol) + + # symbols missing from row i + missing <- setdiff(all_symbols, used) + + # edge data frame for column i + edge_df <- tibble::tibble( + to = paste0("s", missing) + ) |> + dplyr::mutate(from = paste0("r", i)) + return(edge_df) } \ No newline at end of file diff --git a/R/next-col.R b/R/next-col.R new file mode 100644 index 0000000..cc337e3 --- /dev/null +++ b/R/next-col.R @@ -0,0 +1,69 @@ +#' Matching strategy for adding new columns +#' +#' @param R a latin rectangle +#' @param i column index +#' @param l_order dimension +#' +#' @return a latin rectangle with more columns +#' @export +next_col_matching <- function(R, i, l_order) { + + n_rows <- max(R$row) + n_cols <- max(R$column) + + bg <- to_tidygraph_2(R, l_order, n_rows, n_cols) + + m <- igraph::max_bipartite_match(bg) + + # names of edges in the matching + matching_names <- match(m$matching, names(m$matching)) + + # add a matching indicator to the edges + bg <- bg |> + tidygraph::activate(edges) |> + dplyr::mutate( + matching = to == matching_names[from] + ) |> + dplyr::filter(from <= n_rows) + + # just the matching itself, as a graph + mg <- bg |> + tidygraph::activate(edges) |> + dplyr::filter(matching) + + EE <- igraph::ends(mg, igraph::E(mg)) + + R |> + dplyr::bind_rows( + tibble::tibble( + column = rep(i, n_rows), + row = 1:n_rows, + symbol = as.numeric(gsub("s", "", EE[, 2])) + ) + ) + +} + +#' Random strategy for choosing a new columns +#' +#' @param R a latin rectangle +#' @param i column index +#' @param l_order dimension +#' +#' @return A latin rectangle with more columns +#' @export +next_col_random <- function(R, i, l_order) { + + n_rows <- max(R$row) + n_cols <- max(R$column) + + R |> + dplyr::bind_rows( + tibble::tibble( + column = rep(i, n_rows), + row = 1:n_rows, + symbol = sample(1:l_order, n_rows) + ) + ) + +} \ No newline at end of file diff --git a/R/next-row.R b/R/next-row.R index 09b989b..15bcba0 100644 --- a/R/next-row.R +++ b/R/next-row.R @@ -36,4 +36,74 @@ next_row <- function(R, l_order) { EE <- igraph::ends(mg, igraph::E(mg)) return(as.numeric(gsub("s", "", EE[,2]))) -} \ No newline at end of file +} + +#' Find a random new row for a latin rectangle +#' +#' @param R A latin rectangle +#' @param i Number of columns to add +#' @param l_order Order of R +#' +#' @return A latin square with more rows. +#' @export +next_row_random <- function(R, i, l_order) { + + R |> + dplyr::bind_rows( + tibble::tibble( + row = rep(i, l_order), + column = 1:l_order, + symbol = sample(1:l_order, l_order) + ) + ) + +} + +#' Find a compatible row for extending a latin rectangle +#' +#' Given an input latin rectangle this function will +#' generate a new row that can be added to the latin +#' rectangle. +#' +#' The method used is to create a bipartite graph +#' with vertex partitions for columns and symbols +#' missing from columns and then find a maximum +#' matching in that bipartite graph. +#' +#' @param R A latin rectangle +#' @param i Number of columns to add +#' @param l_order Order of R +#' +#' @return A latin rectangle with more rows. +#' @export +next_row_matching <- function(R, i, l_order) { + bg <- to_tidygraph(R, l_order) + + m <- igraph::max_bipartite_match(bg) + + # names of edges in the matching + matching_names <- match(m$matching, names(m$matching)) + + # add a matching indicator to the edges + bg <- bg |> + tidygraph::activate(edges) |> + dplyr::mutate( + matching = to == matching_names[from] + ) + + # just the matching itself, as a graph + mg <- bg |> + tidygraph::activate(edges) |> + dplyr::filter(matching) + + EE <- igraph::ends(mg, igraph::E(mg)) + + R |> + dplyr::bind_rows( + tibble::tibble( + row = rep(i, l_order), + column = 1:l_order, + symbol = as.numeric(gsub("s", "", EE[,2])) + ) + ) +} diff --git a/R/to-tidygraph.R b/R/to-tidygraph.R index 62bfbc3..543a49d 100644 --- a/R/to-tidygraph.R +++ b/R/to-tidygraph.R @@ -29,4 +29,44 @@ to_tidygraph <- function(R, l_order = 3) { tidygraph::tbl_graph(nodes = l_nodes, edges = l_edges) -} \ No newline at end of file +} + +#' Symbols missing from rows bipartite graph +#' + +#' @param R A latin rectangle. +#' @param l_order Order of R. +#' @param n_rows Number of rows. +#' @param n_cols Number of columns. +#' +#' @return A bipartite graph. +#' @export +to_tidygraph_2 <- function(R, l_order, n_rows, n_cols) { + + ## VERTEX DATA FRAME + row_vertices <- paste0("r", 1:n_rows) + symbol_vertices <- paste0("s", 1:l_order) + + n_dummy_nodes <- l_order - n_rows + dummy_vertices <- paste0("d", 1:n_dummy_nodes) + + l_nodes <- tibble::tibble( + name = c(row_vertices, dummy_vertices, symbol_vertices), + type = c(rep(TRUE, n_rows + n_dummy_nodes), rep(FALSE, l_order)) + ) + + ## EDGE DATA FRAME + f <- function(i) return(edge_tbl_2(R, i, l_order)) + + l_edges <- purrr::map_df(1:n_rows, f) + + G <- tidygraph::tbl_graph(nodes = l_nodes, edges = l_edges) + + d_edges <- tibble::tibble( + to = rep(symbol_vertices, as.numeric(l_order - n_cols - igraph::degree(G, symbol_vertices))), + from = rep(dummy_vertices, each = l_order - n_cols) + ) + + tidygraph::tbl_graph(nodes = l_nodes, edges = dplyr::bind_rows(l_edges, d_edges)) + +} diff --git a/man/add_cols.Rd b/man/add_cols.Rd new file mode 100644 index 0000000..d949a73 --- /dev/null +++ b/man/add_cols.Rd @@ -0,0 +1,23 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/add-cols.R +\name{add_cols} +\alias{add_cols} +\title{Add new columns to a latin rectangle} +\usage{ +add_cols(R, cols, l_order, strategy = next_col_matching) +} +\arguments{ +\item{R}{A latin rectangle} + +\item{cols}{Indices of columns to add} + +\item{l_order}{Dimension of latin square} + +\item{strategy}{Strategy for filling columns} +} +\value{ +A latin rectangle +} +\description{ +Add new columns to a latin rectangle +} diff --git a/man/edge_tbl_2.Rd b/man/edge_tbl_2.Rd new file mode 100644 index 0000000..e5082e5 --- /dev/null +++ b/man/edge_tbl_2.Rd @@ -0,0 +1,21 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/edge-tbl.R +\name{edge_tbl_2} +\alias{edge_tbl_2} +\title{Symbols missing from rows data frame} +\usage{ +edge_tbl_2(R, i, l_order = 3) +} +\arguments{ +\item{R}{latin rectangle} + +\item{i}{row index} + +\item{l_order}{size of latin square R is going to be embedded into} +} +\value{ +The edge data frame. +} +\description{ +Symbols missing from rows data frame +} diff --git a/man/next_col_matching.Rd b/man/next_col_matching.Rd new file mode 100644 index 0000000..b6f866c --- /dev/null +++ b/man/next_col_matching.Rd @@ -0,0 +1,21 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/next-col.R +\name{next_col_matching} +\alias{next_col_matching} +\title{Matching strategy for adding new columns} +\usage{ +next_col_matching(R, i, l_order) +} +\arguments{ +\item{R}{a latin rectangle} + +\item{i}{column index} + +\item{l_order}{dimension} +} +\value{ +a latin rectangle with more columns +} +\description{ +Matching strategy for adding new columns +} diff --git a/man/next_col_random.Rd b/man/next_col_random.Rd new file mode 100644 index 0000000..7ba64a1 --- /dev/null +++ b/man/next_col_random.Rd @@ -0,0 +1,21 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/next-col.R +\name{next_col_random} +\alias{next_col_random} +\title{Random strategy for choosing a new columns} +\usage{ +next_col_random(R, i, l_order) +} +\arguments{ +\item{R}{a latin rectangle} + +\item{i}{column index} + +\item{l_order}{dimension} +} +\value{ +A latin rectangle with more columns +} +\description{ +Random strategy for choosing a new columns +} diff --git a/man/next_row_matching.Rd b/man/next_row_matching.Rd new file mode 100644 index 0000000..78d211e --- /dev/null +++ b/man/next_row_matching.Rd @@ -0,0 +1,29 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/next-row.R +\name{next_row_matching} +\alias{next_row_matching} +\title{Find a compatible row for extending a latin rectangle} +\usage{ +next_row_matching(R, i, l_order) +} +\arguments{ +\item{R}{A latin rectangle} + +\item{i}{Number of columns to add} + +\item{l_order}{Order of R} +} +\value{ +A latin rectangle with more rows. +} +\description{ +Given an input latin rectangle this function will +generate a new row that can be added to the latin +rectangle. +} +\details{ +The method used is to create a bipartite graph +with vertex partitions for columns and symbols +missing from columns and then find a maximum +matching in that bipartite graph. +} diff --git a/man/next_row_random.Rd b/man/next_row_random.Rd new file mode 100644 index 0000000..f959c2c --- /dev/null +++ b/man/next_row_random.Rd @@ -0,0 +1,21 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/next-row.R +\name{next_row_random} +\alias{next_row_random} +\title{Find a random new row for a latin rectangle} +\usage{ +next_row_random(R, i, l_order) +} +\arguments{ +\item{R}{A latin rectangle} + +\item{i}{Number of columns to add} + +\item{l_order}{Order of R} +} +\value{ +A latin square with more rows. +} +\description{ +Find a random new row for a latin rectangle +} diff --git a/man/to_tidygraph_2.Rd b/man/to_tidygraph_2.Rd new file mode 100644 index 0000000..d0e4e35 --- /dev/null +++ b/man/to_tidygraph_2.Rd @@ -0,0 +1,23 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/to-tidygraph.R +\name{to_tidygraph_2} +\alias{to_tidygraph_2} +\title{Symbols missing from rows bipartite graph} +\usage{ +to_tidygraph_2(R, l_order, n_rows, n_cols) +} +\arguments{ +\item{R}{A latin rectangle.} + +\item{l_order}{Order of R.} + +\item{n_rows}{Number of rows.} + +\item{n_cols}{Number of columns.} +} +\value{ +A bipartite graph. +} +\description{ +Symbols missing from rows bipartite graph +}