6262# ' `ergm_proposal_table()`, it also sets a call-back to remove all
6363# ' of its proposals from the table should the package be unloaded.
6464# '
65+ # ' @return If called without arguments, a table of the above. Note
66+ # ' that `Constraints` column is preprocessed into a [`list`] column
67+ # ' with character vector elements `$does` and `$can` and a logical
68+ # ' element `$can_any` indicating that it's a meta constraint.
69+ # '
6570# ' @keywords internal
6671# ' @export
6772ergm_proposal_table <- local({
@@ -156,13 +161,19 @@ prune.ergm_conlist <- function(conlist){
156161#
157162# #######################################################################################
158163
159- # If the constraints formula is two-sided, add a term .select(LHS) and remove LHS.
160- .embed_constraint_lhs <- function (formula ){
161- if (length(formula ) > 2 ){
162- lhs <- try(eval_lhs.formula(formula ), silent = TRUE )
163- if (is(lhs , " try-error" ) || ! is.character(lhs )) stop(" Constraint formula must be either one-sided or have a string expression as its LHS." )
164- nonsimp_update.formula(formula , substitute(~ . + .select(.. ), list (.. = lhs )))
165- }else formula
164+ # If the constraints formula is two-sided, add a term .select(LHS) and
165+ # remove LHS. Also, if the result is a character string, we construct
166+ # a one-sided formula with .select(x). term_lists get passed through.
167+ .embed_constraint_lhs <- function (x ) {
168+ if (is(x , " formula" )) {
169+ if (length(x ) > 2L ) {
170+ lhs <- try(eval_lhs.formula(x ), silent = TRUE )
171+ if (is(lhs , " try-error" ) || ! is.character(lhs )) stop(" Constraint formula must be either one-sided or have a string expression as its LHS." )
172+ nonsimp_update.formula(x , substitute(~ . + .select(.. ), list (.. = lhs )))
173+ } else x
174+ } else if (is.character(x )) as.formula(call(" ~" , call(" .select" , x )), baseenv())
175+ else if (is(x , " term_list" )) x
176+ else stop(" Constraint must be either a formula or a string." )
166177}
167178
168179.delete_term <- function (tl , terms ) discard(tl , ~ any(as.character(. )[1 ] %in% terms ))
@@ -283,6 +294,18 @@ ergm_proposal.character <- function(object, arguments, nw, ..., reference=ergm_r
283294# ' @export
284295ergm_conlist <- function (object , ... ) UseMethod(" ergm_conlist" )
285296
297+ # ' @describeIn ergm_conlist list of other eligible inputs: concatenates.
298+ # ' @export
299+ ergm_conlist.list <- function (object , ... ) {
300+ object <- compact(object )
301+ OK <- map_lgl(object , \(x ) is.character(x ) || is(x , " formula" ) || is(x , " term_list" ))
302+ if (any(! OK ))
303+ stop(" Invalid list element(s) " , deparse1(object [! OK ]), " passed to " ,
304+ sQuote(" ergm_conlist()" ), " 's " , sQuote(" list" ), " method." )
305+
306+ object | > ergm_flatten_conterm_list() | > ergm_conlist(... )
307+ }
308+
286309# ' @describeIn ergm_conlist identity method.
287310# ' @export
288311ergm_conlist.ergm_conlist <- function (object , ... ) object
@@ -299,8 +322,12 @@ ergm_conlist.NULL <- function(object, ...) NULL
299322# ' @template term_options
300323# '
301324# ' @export
302- ergm_conlist.formula <- function (object , nw , ... )
303- object | > .embed_constraint_lhs() | > list_rhs.formula() | > ergm_conlist(nw , ... )
325+ ergm_conlist.formula <- function (object , ... )
326+ object | > ergm_flatten_conterm_list() | > ergm_conlist(... )
327+
328+ # ' @describeIn ergm_conlist specify the proposal name directly.
329+ # ' @export
330+ ergm_conlist.character <- ergm_conlist.formula
304331
305332# ' @describeIn ergm_conlist initialize from [`term_list`].
306333# ' @export
@@ -495,13 +522,10 @@ call.ErgmReference <- function(term, env, nw, ..., term.options=list()){
495522# ' documentation for a similar argument for [ergm()] and see
496523# ' [`ergmConstraint`] for more information.
497524# ' @export
498- ergm_proposal.formula <- function (object , arguments , nw , hints = trim_env(~ sparse ), ... , term.options = list ()) {
499- NVL(hints ) <- trim_env(~ sparse )
500-
525+ ergm_proposal.formula <- function (object , arguments , nw , ... , term.options = list ()) {
501526 conlist <- if (" constraints" %in% names(arguments ))
502527 prune.ergm_conlist(arguments $ constraints )
503- else c(ergm_conlist(object , nw , term.options = term.options , ... ),
504- ergm_conlist(hints , nw , term.options = term.options , ... ))
528+ else ergm_conlist(object , nw , term.options = term.options , ... )
505529
506530 # # Hand it off to the class ergm_conlist method.
507531 ergm_proposal(conlist , arguments , nw , ... , term.options = term.options )
@@ -621,3 +645,37 @@ free_dyads <- function(con){
621645 else if (is.function(fd )) fd()
622646 else stop(" Unsupported free_dyad type; this is probably a programming error." )
623647}
648+
649+
650+ # ' Convert a list of constraint formulas or terms to a flat term list
651+ # '
652+ # ' This helper function processes the usual constraint specifications
653+ # ' ([`formula`]s, [`term_list`]s, [`character`] strings) or a list
654+ # ' thereof into a flat [`term_list`] of distinct values.
655+ # '
656+ # ' @param l a [`formula`], a [`term_list`], a [`character`], [`NULL`],
657+ # ' or a (potentially heterogeneous) list thereof.
658+ # '
659+ # ' @return a [`term_list`].
660+ # '
661+ # ' @examples
662+ # ' z <- 5
663+ # ' ergm_flatten_conterm_list(list(
664+ # ' NULL,
665+ # ' ~a(x) - b,
666+ # ' statnet.common::base_env(~c),
667+ # ' NULL,
668+ # ' term_list(call("f", z), env = baseenv(), sign = -1)
669+ # ' ))
670+ # '
671+ # ' @export
672+ ergm_flatten_conterm_list <- function (l ) {
673+ enlist(l ) | >
674+ compact() | >
675+ map(.embed_constraint_lhs ) | >
676+ map_if(\(tlf ) is(tlf , " formula" ),
677+ list_rhs.formula ) | >
678+ do.call(c , args = _) | >
679+ compact() | >
680+ unique()
681+ }
0 commit comments