From 76500b01c2482126edf9dfcc19aee790448db0e8 Mon Sep 17 00:00:00 2001
From: "Pavel N. Krivitsky"
Date: Sun, 22 Dec 2024 22:25:54 +1100
Subject: [PATCH 1/7] Modernized the Sum() operator, removing code left over
from before the move to .Call() and using the iinputs API.
---
DESCRIPTION | 2 +-
R/InitErgmTerm.operator.R | 4 +--
src/changestats_operator.c | 34 +++++--------------
.../testthat/{test-Sum.R => test-term-Sum.R} | 0
4 files changed, 10 insertions(+), 30 deletions(-)
rename tests/testthat/{test-Sum.R => test-term-Sum.R} (100%)
diff --git a/DESCRIPTION b/DESCRIPTION
index 3767c9e7a..aea9d0ce3 100644
--- a/DESCRIPTION
+++ b/DESCRIPTION
@@ -1,5 +1,5 @@
Package: ergm
-Version: 4.8.0-7022
+Version: 4.8.0-7504
Date: 2024-12-22
Title: Fit, Simulate and Diagnose Exponential-Family Models for Networks
Authors@R: c(
diff --git a/R/InitErgmTerm.operator.R b/R/InitErgmTerm.operator.R
index aab9d9498..126668234 100644
--- a/R/InitErgmTerm.operator.R
+++ b/R/InitErgmTerm.operator.R
@@ -715,8 +715,6 @@ InitErgmTerm.Sum <- function(nw, arglist,...){
nparam <- nparams[1]
inputs <- unlist(wl%>%map(t))
- inputs <- c(nf, length(inputs), inputs)
-
if(is.function(a$label)){
cns <- lapply(ms, param_names, canonical=TRUE)
@@ -770,7 +768,7 @@ InitErgmTerm.Sum <- function(nw, arglist,...){
}
}else offset <- FALSE
- c(list(name="Sum", coef.names = coef.names, inputs=inputs, submodels=ms, emptynwstats=gs,
+ c(list(name="Sum", coef.names = coef.names, inputs=inputs, iinputs=nf, submodels=ms, emptynwstats=gs,
dependence=dependence, offset=offset,
ext.encode = if(ms %>% map("terms") %>% unlist(FALSE) %>% map("ext.encode") %>% compact %>% length)
function(el, nw0)
diff --git a/src/changestats_operator.c b/src/changestats_operator.c
index 36503f4da..bb02a7a4e 100644
--- a/src/changestats_operator.c
+++ b/src/changestats_operator.c
@@ -177,18 +177,7 @@ F_CHANGESTAT_FN(f__submodel_and_summary_term){
// Sum: Take a weighted sum of the models' statistics.
I_CHANGESTAT_FN(i_Sum){
- /*
- inputs expected:
- 1: number of models (nms)
- 1: total length of all weight matrices (tml)
- tml: a list of mapping matrices in row-major order
- */
-
- double *inputs = INPUT_PARAM;
- unsigned int nms = *(inputs++);
- unsigned int tml = *(inputs++);
- inputs+=tml; // Jump to start of model specifications.
-
+ unsigned int nms = *IINPUT_PARAM;
ALLOC_STORAGE(nms, Model*, ms);
SEXP submodels = getListElement(mtp->R, "submodels");
@@ -200,11 +189,9 @@ I_CHANGESTAT_FN(i_Sum){
}
C_CHANGESTAT_FN(c_Sum){
- double *inputs = INPUT_PARAM;
GET_STORAGE(Model*, ms);
- unsigned int nms = *(inputs++);
- inputs++; // Skip total length of weight matrices.
- double *wts = inputs;
+ unsigned int nms = *IINPUT_PARAM;
+ double *wts = INPUT_PARAM;
for(unsigned int i=0; i
Date: Sun, 22 Dec 2024 22:55:56 +1100
Subject: [PATCH 2/7] The MH proposal C API apparently did not make available
the lengths of the input vectors as the change statistics API did.
---
inst/include/ergm_MHproposal.h | 9 +++++++--
inst/include/ergm_wtMHproposal.h | 9 +++++++--
src/MHproposal.c | 6 ++++--
src/wtMHproposal.c | 6 ++++--
4 files changed, 22 insertions(+), 8 deletions(-)
diff --git a/inst/include/ergm_MHproposal.h b/inst/include/ergm_MHproposal.h
index 20bdba633..db3bd54db 100644
--- a/inst/include/ergm_MHproposal.h
+++ b/inst/include/ergm_MHproposal.h
@@ -65,8 +65,10 @@ typedef struct MHProposalstruct {
Vertex *togglehead;
double logratio;
int status;
- double *inputs; /* may be used if needed, ignored if not. */
- int *iinputs; /* may be used if needed, ignored if not. */
+ int ninputs;
+ double *inputs;
+ int niinputs;
+ int *iinputs;
void *storage;
void **aux_storage;
unsigned int n_aux;
@@ -79,8 +81,11 @@ MHProposal *MHProposalInitialize(SEXP pR, Network *nwp, void **aux_storage);
void MHProposalDestroy(MHProposal *MHp, Network *nwp);
/* Helper macros */
+#define MH_N_DINPUTS MHp->ninputs
#define MH_DINPUTS MHp->inputs
+#define MH_N_INPUTS MH_N_DINPUTS
#define MH_INPUTS MH_DINPUTS
+#define MH_N_IINPUTS MHp->niinputs
#define MH_IINPUTS MHp->iinputs
#define Mtail (MHp->toggletail)
diff --git a/inst/include/ergm_wtMHproposal.h b/inst/include/ergm_wtMHproposal.h
index 604e83809..d5f4e750a 100644
--- a/inst/include/ergm_wtMHproposal.h
+++ b/inst/include/ergm_wtMHproposal.h
@@ -66,8 +66,10 @@ typedef struct WtMHProposalstruct {
double *toggleweight;
double logratio;
int status;
- double *inputs; /* may be used if needed, ignored if not. */
- int *iinputs; /* may be used if needed, ignored if not. */
+ int ninputs;
+ double *inputs;
+ int niinputs;
+ int *iinputs;
void *storage;
void **aux_storage;
unsigned int n_aux;
@@ -79,8 +81,11 @@ WtMHProposal *WtMHProposalInitialize(SEXP pR, WtNetwork *nwp, void **aux_storage
void WtMHProposalDestroy(WtMHProposal *MH, WtNetwork *nwp);
/* Helper macros */
+#define MH_N_DINPUTS MHp->ninputs
#define MH_DINPUTS MHp->inputs
+#define MH_N_INPUTS MH_N_DINPUTS
#define MH_INPUTS MH_DINPUTS
+#define MH_N_IINPUTS MHp->niinputs
#define MH_IINPUTS MHp->iinputs
#define Mtail (MHp->toggletail)
diff --git a/src/MHproposal.c b/src/MHproposal.c
index a81506d64..e576638d6 100644
--- a/src/MHproposal.c
+++ b/src/MHproposal.c
@@ -62,9 +62,11 @@ MHProposal *MHProposalInitialize(SEXP pR, Network *nwp, void **aux_storage){
MHp->x_func=(void (*)(unsigned int, void *, MHProposal*, Network*)) R_FindSymbol(fn,sn,NULL);
SEXP tmp = getListElement(pR, "inputs");
- MHp->inputs=length(tmp) ? REAL(tmp) : NULL;
+ MHp->ninputs = length(tmp);
+ MHp->inputs = MHp->ninputs ? REAL(tmp) : NULL;
tmp = getListElement(pR, "iinputs");
- MHp->iinputs=length(tmp) ? INTEGER(tmp) : NULL;
+ MHp->niinputs = length(tmp);
+ MHp->iinputs = MHp->niinputs ? INTEGER(tmp) : NULL;
/*Clean up by freeing sn and fn*/
R_Free(fn);
diff --git a/src/wtMHproposal.c b/src/wtMHproposal.c
index b2d95f8d3..9dcbe5aac 100644
--- a/src/wtMHproposal.c
+++ b/src/wtMHproposal.c
@@ -61,9 +61,11 @@ WtMHProposal *WtMHProposalInitialize(SEXP pR, WtNetwork *nwp, void **aux_storage
MHp->x_func=(void (*)(unsigned int, void *, WtMHProposal*, WtNetwork*)) R_FindSymbol(fn,sn,NULL);
SEXP tmp = getListElement(pR, "inputs");
- MHp->inputs=length(tmp) ? REAL(tmp) : NULL;
+ MHp->ninputs = length(tmp);
+ MHp->inputs = MHp->ninputs ? REAL(tmp) : NULL;
tmp = getListElement(pR, "iinputs");
- MHp->iinputs=length(tmp) ? INTEGER(tmp) : NULL;
+ MHp->niinputs = length(tmp);
+ MHp->iinputs = MHp->niinputs ? INTEGER(tmp) : NULL;
/*Clean up by freeing sn and fn*/
R_Free(fn);
From e71e4fc88972d080372457e50778ed8cbc641077 Mon Sep 17 00:00:00 2001
From: "Pavel N. Krivitsky"
Date: Sun, 22 Dec 2024 23:27:50 +1100
Subject: [PATCH 3/7] fixallbut() constraint now also accepts an rlebdm as
input.
---
DESCRIPTION | 2 +-
R/InitErgmConstraint.R | 25 ++++++++++++------------
man/fixallbut-ergmConstraint-ea96b2e0.Rd | 2 +-
tests/testthat/test-constraints.R | 25 +++++++++++++++---------
4 files changed, 31 insertions(+), 23 deletions(-)
diff --git a/DESCRIPTION b/DESCRIPTION
index aea9d0ce3..b0ce889d4 100644
--- a/DESCRIPTION
+++ b/DESCRIPTION
@@ -1,5 +1,5 @@
Package: ergm
-Version: 4.8.0-7504
+Version: 4.8.0-7505
Date: 2024-12-22
Title: Fit, Simulate and Diagnose Exponential-Family Models for Networks
Authors@R: c(
diff --git a/R/InitErgmConstraint.R b/R/InitErgmConstraint.R
index d081be948..a93ef63e3 100644
--- a/R/InitErgmConstraint.R
+++ b/R/InitErgmConstraint.R
@@ -415,7 +415,7 @@ InitErgmConstraint.observed <- function(nw, arglist, ...){
}
warn_netsize <- function(.n, ...){
- mismatch <- vapply(list(...), function(x) is.network(x) && network.size(x) != .n, logical(1))
+ mismatch <- map_lgl(list(...), function(x) (is.network(x) && network.size(x) != .n) || (is(x, "rlebdm") && nrow(x) != .n))
if(any(mismatch))
ergm_Init_warning("Network size of argument(s) ", paste.and(sQuote(...names()[mismatch])), " differs from that of the response network.")
}
@@ -477,7 +477,7 @@ InitErgmConstraint.fixedas<-function(nw, arglist,...){
#'
#' @usage
#' # fixallbut(free.dyads)
-#' @param free.dyads edgelist or network. Networks will be converted to the corresponding edgelist.
+#' @param free.dyads a two-column edge list, a [`network`], or an [`rlebdm`]. Networks will be converted to the corresponding edgelist.
#'
#' @template ergmConstraint-general
#'
@@ -487,7 +487,7 @@ InitErgmConstraint.fixedas<-function(nw, arglist,...){
InitErgmConstraint.fixallbut<-function(nw, arglist,...){
a <- check.ErgmTerm(nw, arglist,
varnames = c("free.dyads"),
- vartypes = c("network,matrix"),
+ vartypes = c("network,matrix,rlebdm"),
defaultvalues = list(NULL),
required = c(TRUE))
free.dyads <- a$free.dyads
@@ -495,15 +495,16 @@ InitErgmConstraint.fixallbut<-function(nw, arglist,...){
warn_netsize(network.size(nw), free.dyads = free.dyads)
list(
- free_dyads = function(){
- if(is.network(free.dyads)) free.dyads <- as.edgelist(free.dyads)
- else free.dyads <- as.edgelist(free.dyads,
- n=nw%n%"n",
- directed=nw%n%"directed",
- bipartite=nw%n%"bipartite",
- loops=nw%n%"loops")
- as.rlebdm(free.dyads)
- },
+ free_dyads =
+ if(is(free.dyads, "rlebdm")) free.dyads
+ else function()
+ as.rlebdm(if(is.network(free.dyads)) as.edgelist(free.dyads)
+ else as.edgelist(free.dyads,
+ n=nw%n%"n",
+ directed=nw%n%"directed",
+ bipartite=nw%n%"bipartite",
+ loops=nw%n%"loops")
+ ),
dependence = FALSE)
}
diff --git a/man/fixallbut-ergmConstraint-ea96b2e0.Rd b/man/fixallbut-ergmConstraint-ea96b2e0.Rd
index 860ee2b86..cb0782a38 100644
--- a/man/fixallbut-ergmConstraint-ea96b2e0.Rd
+++ b/man/fixallbut-ergmConstraint-ea96b2e0.Rd
@@ -8,7 +8,7 @@
# fixallbut(free.dyads)
}
\arguments{
-\item{free.dyads}{edgelist or network. Networks will be converted to the corresponding edgelist.}
+\item{free.dyads}{a two-column edge list, a \code{\link[network:network]{network}}, or an \code{\link{rlebdm}}. Networks will be converted to the corresponding edgelist.}
}
\description{
Preserve the dyad status in all but \code{free.dyads}.
diff --git a/tests/testthat/test-constraints.R b/tests/testthat/test-constraints.R
index 42b199e61..aad2348b0 100644
--- a/tests/testthat/test-constraints.R
+++ b/tests/testthat/test-constraints.R
@@ -74,18 +74,25 @@ test_that("fixedas with network input", {
expect_true(all(!sapply(s1,function(x)as.data.frame(t(as.edgelist(absent))) %in% as.data.frame(t(as.edgelist(x))))))
})
-test_that("fixallbut with network input", {
- net1 <- network(10,directed=FALSE,density=0.5)
- free.dyads <- matrix(sample(2:9,8,replace=FALSE),4,2)
+net1 <- network(10,directed=FALSE,density=0.5)
+fdel <- matrix(sample(2:9,8,replace=FALSE),4,2)
- t1 <- ergm(net1~edges, constraint = ~fixallbut(free.dyads = free.dyads))
- s1 <- simulate(t1, 100)
+for(free.dyads in list(
+ fdel,
+ fdnw <- as.network(structure(fdel, n = 10), directed = FALSE),
+ fd <- as.rlebdm(fdnw)
+ )){
+ test_that(sprintf("fixallbut with %s input", class(free.dyads)[1]), {
+ t1 <- ergm(net1~edges, constraint = ~fixallbut(free.dyads = free.dyads))
+ s1 <- simulate(t1, 100)
- fixed.dyads <- as.edgelist(!update(net1,free.dyads,matrix.type="edgelist"))
- fixed.dyads.state <- net1[fixed.dyads]
+ fixed.dyads <- as.edgelist(!update(net1,fdel,matrix.type="edgelist"))
+ fixed.dyads.state <- net1[fixed.dyads]
+
+ expect_true(all(sapply(s1,function(x) all.equal(x[fixed.dyads],fixed.dyads.state))))
+ })
+}
- expect_true(all(sapply(s1,function(x) all.equal(x[fixed.dyads],fixed.dyads.state))))
-})
test_that("constraint conflict is detected", {
data(florentine)
From e04e0563bd3bc951c3223079068eaea8c52c75ce Mon Sep 17 00:00:00 2001
From: "Pavel N. Krivitsky"
Date: Mon, 23 Dec 2024 00:08:11 +1100
Subject: [PATCH 4/7] Fixed a typo exposed by a fix in
statnet.common::trim_env() that affected initialization of constrained MH
proposal when it requested auxiliaries.
---
R/ergm.R | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/R/ergm.R b/R/ergm.R
index 148a9782d..76b9bb0af 100644
--- a/R/ergm.R
+++ b/R/ergm.R
@@ -491,7 +491,7 @@ ergm <- function(formula, response=NULL,
model.obs <- NULL
if(identical(proposal$auxiliaries, proposal.obs$auxiliaries)){
## Reuse auxiliaries from the unconstrained proposal if identical.
- proposal.obs$slots.extra.aux <- model$slots.extra.aux$proposal
+ proposal.obs$aux.slots <- model$slots.extra.aux$proposal
}else if(!is.null(proposal.obs$auxiliaries)){
if (verbose) message("Constrained proposal requires different auxiliaries: reinitializing model...")
model.obs <- ergm_model(formula, nw, extra.aux = NVL3(proposal.obs$auxiliaries,list(proposal=.)), term.options=control$term.options)
From 6f91e7f91bc73fe46722fc88579b067512ef4a04 Mon Sep 17 00:00:00 2001
From: "Pavel N. Krivitsky"
Date: Mon, 23 Dec 2024 01:20:28 +1100
Subject: [PATCH 5/7] Bumped the C API version number in ergm_constants.h .
---
inst/include/ergm_constants.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/inst/include/ergm_constants.h b/inst/include/ergm_constants.h
index ff2fcace0..7bc4e9618 100644
--- a/inst/include/ergm_constants.h
+++ b/inst/include/ergm_constants.h
@@ -12,7 +12,7 @@
// Macros indicating the version of the C API.
#define ERGM_API_MAJOR 4
-#define ERGM_API_MINOR 7
+#define ERGM_API_MINOR 8
typedef enum MCMCStatus_enum {
MCMC_OK = 0,
From 8bd0d94c30c1f9b0cd6d580fea072f75a26f4742 Mon Sep 17 00:00:00 2001
From: "Pavel N. Krivitsky"
Date: Mon, 23 Dec 2024 10:59:09 +1100
Subject: [PATCH 6/7] Updated NEWS.
---
inst/NEWS.Rd | 110 +++++++++++++++++++++++++++++++++++++++++++++------
1 file changed, 98 insertions(+), 12 deletions(-)
diff --git a/inst/NEWS.Rd b/inst/NEWS.Rd
index cec235c79..e43bb64fe 100644
--- a/inst/NEWS.Rd
+++ b/inst/NEWS.Rd
@@ -11,6 +11,15 @@
\title{NEWS file for the \code{\link[=ergm-package]{ergm}} package}
\encoding{UTF-8}
+
+%% Note: This needs to be kept in sync with man/macros/ergmTerm.Rd
+\newcommand{\ergmTerm}{\code{\link[#1:#2-ergmTerm]{#2#3}}}
+\newcommand{\ergmConstraint}{\code{\link[#1:#2-ergmConstraint]{#2#3}}}
+\newcommand{\ergmReference}{\code{\link[#1:#2-ergmReference]{#2#3}}}
+\newcommand{\ergmHint}{\code{\link[#1:#2-ergmHint]{#2#3}}}
+\newcommand{\ergmProposal}{\code{\link[#1:#2-ergmProposal]{#2#3}}}
+
+
% When a release is forked, copy the sections to a new release and
% delete unused sections.
@@ -65,6 +74,83 @@
%% }
+\section{Changes in version 4.8.0}{
+
+ \subsection{NEW FEATURES}{
+ \itemize{
+ \item{
+ A new bipartite term operator \ergmTerm{ergm}{Project}{()}, with aliases \ergmTerm{ergm}{Proj1}{()} and \ergmTerm{ergm}{Proj2}{()}, evaluating the statistics on an undirected valued network of counts constructed by projecting the bipartite network onto one of its modes.
+ }
+ \item{
+ A new family of terms, \ergmTerm{ergm}{nodecovrange}{()}, \ergmTerm{ergm}{nodeocovrange}{()}, \ergmTerm{ergm}{nodeicovrange}{()}, \ergmTerm{ergm}{b1covrange}{()}, and \ergmTerm{ergm}{b2covrange}{()}, to evaluate the sum over the nodes of the range of the specified quantitative attribute value among the node's neighbors. (Thanks to Marion Hoffman for the idea!)
+ }
+ \item{
+ A new family of terms, \ergmTerm{ergm}{nodefactordistinct}{()}, \ergmTerm{ergm}{nodeofactordistinct}{()}, \ergmTerm{ergm}{nodeifactordistinct}{()}, \ergmTerm{ergm}{b1factordistinct}{()}, and \ergmTerm{ergm}{b2factordistinct}{()}, to evaluate the sum over the nodes of the number of distinct values of a categorical factor among the node's neighbors. (Thanks to Marion Hoffman for the idea!)
+ }
+ \item{
+ The \ergmConstraint{ergm}{fixallbut}{()} now supports an \code{\link{rlebdm}} (in addition to an edge list matrix and a \code{\link[network]{network}}) for its argument.
+ }
+ }
+ }
+
+
+ \subsection{BUG FIXES}{
+ \itemize{
+ \item{
+ Fix in stochastic approximation code for compatibility with most recent \CRANpkg{coda}.
+ }
+ \item{
+ In \code{\link[ergm]{ergm()}}, a bug in initialization of MH proposals for constrained sampling requesting auxiliaries has been fixed.
+ }
+ }
+ }
+
+
+ \subsection{OTHER USER-VISIBLE CHANGES}{
+ \itemize{
+ \item{
+ Precision of geometrically weighted statistics (degrees and shared partners) has been improved, particularly for large decay parameters.
+ }
+ \item{
+ Dynamic term documentation is now more robust to extension developer error.
+ }
+ \item{
+ A new \code{\link[=as.rle.rlebdm]{as.rle()}} method for \code{\link{rlebdm}} objects to cast it back to an \code{\link{rle}}.
+ }
+ }
+ }
+
+ \subsection{C-LEVEL FACILITIES}{
+ \itemize{
+ \item{
+ Degree-conditioned proposals are now exported in \file{inst/include/}.
+ }
+ \item{
+ The M-H proposal API now makes lengths of input vectors available to the proposals, paralleling the change statistics API.
+ }
+ }
+ }
+
+ %% \subsection{UTILITIES}{
+ %% \itemize{
+ %% \item Likewise, "UTILITIES" is probably unnecessary.
+ %% }
+ %% }
+
+ %% \subsection{INSTALLATION}{
+ %% \itemize{
+ %% \item ... as is "INSTALLATION".
+ %% }
+ %% }
+
+ %% \subsection{PACKAGE INSTALLATION}{
+ %% \itemize{
+ %% \item ditto.
+ %% }
+ %% }
+
+}
+
\section{Changes in version 4.7.5}{
@@ -105,7 +191,7 @@
\code{\link[=blockdiag-ergmConstraint]{blockdiag}} constraint initialization now detects when blocks are non-contiguous and stops with an error. (Previously, it would behave in undefined ways.)
}
\item{
- \code{\link[=Sum-ergmTerm]{Sum}} operator now handles \code{\link{I}()} label specifications correctly; this also fixes an error in the \code{\link[=Prod-ergmTerm]{Prod}} term.
+ \ergmTerm{ergm}{Sum}{} operator now handles \code{\link{I}()} label specifications correctly; this also fixes an error in the \ergmTerm{ergm}{Prod}{} term.
}
\item{
Detection of dyadic dependence now ignores dyadic dependence of hints (since they do not affect the sample space) and auxiliaries (since they do not, in and of themselves, add dyad-dependent statistics).
@@ -123,7 +209,7 @@
In proposal help, listing of proposal table entries works again.
}
\item{
- \code{\link[=nodemix-ergmTerm]{nodemix}()} now uses correct parameter names.
+ \ergmTerm{ergm}{nodemix}{()} now uses correct parameter names.
}
\item{
Godambe information for the MPLE now works if the model has an offset. (Thanks, Cornelius Fritz for reporting and MichaĆ Bojanowski for fixing.)
@@ -132,7 +218,7 @@
When constructing the starting networks for missing data MCMC, imputation of dyads is skipped if the constraints are dyad-dependent, since inserting edges may break the constraint.
}
\item{
- \code{\link[=edgecov-ergmTerm]{edgecov}()} and \code{\link[=dyadcov-ergmTerm]{dyadcov}()} now handle \code{\link[network]{network}}-format input correctly.
+ \ergmTerm{ergm}{edgecov}{()} and \ergmTerm{ergm}{dyadcov}{()} now handle \code{\link[network]{network}}-format input correctly.
}
\item{
Missing data MLE code can now handle the scenarios in which the statistics in the constrained sample are constant and/or the statistics in the unconstrained sample are highly correlated.
@@ -144,10 +230,10 @@
Likelihood calculation is now robust to dropped parameters and parameters fixed at infinity.
}
\item{
- \code{\link[Label-ergmTerm]{Label}()} operator now documents its behavior when the model is curved more clearly.
+ \ergmTerm{ergm}{Label}{()} operator now documents its behavior when the model is curved more clearly.
}
\item{
- Invalid \code{levels2} specification for \code{\link[=mm-ergmTerm]{mm()}} no longer causes memory errors.
+ Invalid \code{levels2} specification for \ergmTerm{ergm}{mm}{()} no longer causes memory errors.
}
\item{
Valued proposal updater function was not being passed the current edge state.
@@ -167,7 +253,7 @@
\code{\link{LARGEST}}, \code{\link{SMALLEST}}, and \code{\link{COLLAPSE_SMALLEST}} now break ties lexicographically with a warning.
}
\item{
- \code{\link[=absdiffcat-ergmTerm]{absdiffcat}} is now more memory-efficient during initialization.
+ \ergmTerm{ergm}{absdiffcat}{} is now more memory-efficient during initialization.
}
\item{
MCMLE estimation code for missing data MLE with high missingness fraction is more robust.
@@ -191,7 +277,7 @@
\code{\link{ergm_Init_stop}()}, \code{\link{ergm_Init_warning}()}, and \code{\link{ergm_Init_message}()} that behave more like their \pkg{base} counterparts have been added to the API. \code{\link{ergm_Init_abort}()}, \code{\link{ergm_Init_warn}()}, and \code{\link{ergm_Init_info}()} will eventually change to use their \CRANpkg{rlang} semantics.
}
\item{
- \code{\link[=edgecov-ergmTerm]{edgecov}()} and \code{\link[=dyadcov-ergmTerm]{dyadcov}()} now check that their covariate matrix has the correct dimension.
+ \ergmTerm{ergm}{edgecov}{()} and \ergmTerm{ergm}{dyadcov}{()} now check that their covariate matrix has the correct dimension.
}
\item{
Constraints \code{\link[fixedas-ergmConstraint]{fixedas}()} and \code{\link[fixedas-ergmConstraint]{fixallbut}()} now warn when given a network whose size does not match the LHS network's.
@@ -493,7 +579,7 @@
New functions \code{\link{search.ergmHints}()} and \code{\link{search.ergmReferences}()} can now be used to search available \code{\link{ergmHints}} and \code{\link{ergmReferences}}, respectively.
}
\item{
- A new \code{\link[=For-ergmTerm]{For}()} term operator to construct a list of terms with some varying parameter.
+ A new \ergmTerm{ergm}{For}{()} term operator to construct a list of terms with some varying parameter.
}
\item{
Term API vignette is now more complete, and includes all API elements, not just the post-4.0 ones.
@@ -594,7 +680,7 @@
\subsection{NEW FEATURES}{
\itemize{
\item{
- In terms \code{\link[=edgecov-ergmTerm]{edgecov}()} and \code{\link[=dyadcov-ergmTerm]{dyadcov}()}, the argument can now be a network.
+ In terms \ergmTerm{ergm}{edgecov}{()} and \ergmTerm{ergm}{dyadcov}{()}, the argument can now be a network.
}
}
}
@@ -704,7 +790,7 @@
}
\item{
- Term \code{\link[=nodemix-ergmTerm]{nodemix}} can now be passed a \code{levels2} argument that is a \code{\link{factor}} or \code{\link{character}} and optionally a matrix, allowing multiple cells to be mapped to the same statistic. (Joyce Cheng)
+ Term \ergmTerm{ergm}{nodemix}{} can now be passed a \code{levels2} argument that is a \code{\link{factor}} or \code{\link{character}} and optionally a matrix, allowing multiple cells to be mapped to the same statistic. (Joyce Cheng)
}
\item{
@@ -733,7 +819,7 @@
}
\item{
- \code{\link[=edgecov-ergmTerm]{edgecov}} and \code{\link[=dyadcov-ergmTerm]{dyadcov}} now detect and stop with an error when the specified network attribute is not found.
+ \ergmTerm{ergm}{edgecov}{} and \ergmTerm{ergm}{dyadcov}{} now detect and stop with an error when the specified network attribute is not found.
}
\item{
@@ -813,7 +899,7 @@
}
\item{
- \code{\link{check.ErgmTerm}()} helper function can now also capture the expressions resulting in term arguments. This incidentally fixes a regression in statistic naming of \code{\link{edgecov-ergmTerm}} and \code{\link{dyadcov-ergmTerm}} effects.
+ \code{\link{check.ErgmTerm}()} helper function can now also capture the expressions resulting in term arguments. This incidentally fixes a regression in statistic naming of \ergmTerm{ergm}{edgecov}{()} and \ergmTerm{ergm}{dyadcov}{()} effects.
}
\item{
From d0c5e5c22f954d22e1a664a2ac50e355e7eb0d03 Mon Sep 17 00:00:00 2001
From: "Pavel N. Krivitsky"
Date: Tue, 24 Dec 2024 09:50:37 +1100
Subject: [PATCH 7/7] In the C implementation of the ergm.eta() family, fixed
the memory error when the dimension of theta is 0.
---
src/etamap.c | 27 +++++++++++++++------------
tests/testthat/test-C-curved.R | 6 +++---
2 files changed, 18 insertions(+), 15 deletions(-)
diff --git a/src/etamap.c b/src/etamap.c
index 953e0b5b2..d61c6c3f1 100644
--- a/src/etamap.c
+++ b/src/etamap.c
@@ -10,21 +10,24 @@
#include "ergm_etamap.h"
#include
-#define SETUP_CALL(fun) \
- SEXP cm = VECTOR_ELT(curved, i); \
- SEXP toR = getListElement(cm, "to"); \
- unsigned int to = INTEGER(toR)[0]; \
- unsigned int nto = length(toR); \
- SEXP fromR = getListElement(cm, "from"); \
- unsigned int from = INTEGER(fromR)[0]; \
- unsigned int nfrom = length(fromR); \
- SEXP cov = getListElement(cm, "cov"); \
- SEXP fun = getListElement(cm, #fun); \
- \
+/* UINT_MAX's are there to segfault as soon as possible if empty from
+ and to vectors are actually used rather than return misleading
+ results. */
+#define SETUP_CALL(fun) \
+ SEXP cm = VECTOR_ELT(curved, i); \
+ SEXP toR = getListElement(cm, "to"); \
+ unsigned int nto = length(toR); \
+ unsigned int to = nto ? INTEGER(toR)[0] : UINT_MAX; \
+ SEXP fromR = getListElement(cm, "from"); \
+ unsigned int nfrom = length(fromR); \
+ unsigned int from = nfrom ? INTEGER(fromR)[0] : UINT_MAX; \
+ SEXP cov = getListElement(cm, "cov"); \
+ SEXP fun = getListElement(cm, #fun); \
+ \
SEXP pos = call, arg; \
SETCAR(pos, fun); pos = CDR(pos); \
SETCAR(pos, (arg = allocVector(REALSXP, nfrom))); pos = CDR(pos); /* Don't need to PROTECT the vector this way. */ \
- memcpy(REAL(arg), theta1+from, nfrom*sizeof(double)); \
+ if(nfrom) memcpy(REAL(arg), theta1+from, nfrom*sizeof(double)); \
SETCAR(pos, ScalarInteger(nto)); pos = CDR(pos); \
SETCAR(pos, cov);
diff --git a/tests/testthat/test-C-curved.R b/tests/testthat/test-C-curved.R
index 72dc83bfc..8682a58c6 100644
--- a/tests/testthat/test-C-curved.R
+++ b/tests/testthat/test-C-curved.R
@@ -52,9 +52,9 @@ ergm.etagradmult.R <- function(theta, v, etamap) {
}
data(faux.mesa.high)
-flom <- ergm_model(~edges+gwesp()+gwdegree()+absdiffcat("Grade")+Offset(~nodefactor("Grade"),c(+1,-1), c(2,3))+gwesp()+NodematchFilter(~gwesp()+nodefactor("Grade"), "Grade"), faux.mesa.high)
-(neta <- nparam(flom, canonical=TRUE))
-(ntheta <- nparam(flom, canonical=FALSE))
+flom <- ergm_model(~edges+gwesp()+gwdegree()+absdiffcat("Grade")+Offset(~edges+edges,c(+1,-1))+Offset(~nodefactor("Grade"),c(+1,-1), c(2,3))+gwesp()+NodematchFilter(~gwesp()+nodefactor("Grade"), "Grade"), faux.mesa.high)
+neta <- nparam(flom, canonical=TRUE)
+ntheta <- nparam(flom, canonical=FALSE)
test_that("C implementation of ergm.eta gives the same answer as R implementation.", {
expect_equal(ergm.eta(1:ntheta, flom$etamap), ergm.eta.R(1:ntheta, flom$etamap))