From 0359bdeef1e5f7d769daa2ce4c8c0fe79d3b4d5a Mon Sep 17 00:00:00 2001
From: "Pavel N. Krivitsky"
Date: Sun, 24 Nov 2024 22:56:01 +1100
Subject: [PATCH] Improved the precision of GW decay calculations in dgw*sp()
and in R by doing more of them on log scale.
---
DESCRIPTION | 4 ++--
R/InitErgmTerm.R | 8 ++++----
R/ergm.utility.R | 3 +++
src/changestats_dgw_sp.c | 22 ++++++++++------------
4 files changed, 19 insertions(+), 18 deletions(-)
diff --git a/DESCRIPTION b/DESCRIPTION
index c3fbdaa81..05b6f8e23 100644
--- a/DESCRIPTION
+++ b/DESCRIPTION
@@ -1,6 +1,6 @@
Package: ergm
-Version: 4.7.5-7458
-Date: 2024-11-04
+Version: 4.7.5-7485
+Date: 2024-11-24
Title: Fit, Simulate and Diagnose Exponential-Family Models for Networks
Authors@R: c(
person(c("Mark", "S."), "Handcock", role=c("aut"), email="handcock@stat.ucla.edu"),
diff --git a/R/InitErgmTerm.R b/R/InitErgmTerm.R
index 32ca87dea..3e1542b21 100644
--- a/R/InitErgmTerm.R
+++ b/R/InitErgmTerm.R
@@ -72,13 +72,13 @@
ergm_GWDECAY <- list(
map = function(x,n,...) {
i <- 1:n
- x[1] * ifelse(i==1, 1, (exp(x[2])*(1-(1-exp(-x[2]))^i)))
+ x[1] * exp(x[2] + log1mexp(-log1mexp(x[2])*i))
},
gradient = function(x,n,...) {
i <- 1:n
- e2 <- exp(x[2])
- a <- 1-exp(-x[2])
- rbind((1-a^i)*e2, ifelse(i==1, 0, x[1] * ( (1-a^i)*e2 - i*a^(i-1) ) ) )
+ a <- log1mexp(x[2])
+ w <- exp(x[2] + log1mexp(-a*i))
+ rbind(w, x[1] * (w - i*exp(a*(i-1))))
},
minpar = c(-Inf, 0)
)
diff --git a/R/ergm.utility.R b/R/ergm.utility.R
index cbf16b115..c7a3e4d06 100644
--- a/R/ergm.utility.R
+++ b/R/ergm.utility.R
@@ -275,3 +275,6 @@ is.const.sample <- function(x){
if(is.mcmc.list(x)) x <- as.matrix(x)
isTRUE(all.equal(apply(x,2,stats::sd), rep(0,ncol(x)), check.names=FALSE))
}
+
+## Implementation of the C macro in R.
+log1mexp <- function(x) ifelse(-x > -log(2), log(-expm1(-x)), log1p(-exp(-x)))
diff --git a/src/changestats_dgw_sp.c b/src/changestats_dgw_sp.c
index fd9862d8d..e7cb78d07 100644
--- a/src/changestats_dgw_sp.c
+++ b/src/changestats_dgw_sp.c
@@ -88,29 +88,27 @@
}
-#define gwsp_args tail,head,mtp,nwp,edgestate,spcache,alpha,oneexpa
+#define gwsp_args tail,head,mtp,nwp,edgestate,spcache,alpha,loneexpa
#define gw_calc(term) \
- static inline double term ## _gw_calc(Vertex tail, Vertex head, ModelTerm *mtp, Network *nwp, Rboolean edgestate, StoreStrictDyadMapUInt *spcache, double alpha, double oneexpa) { \
+ static inline double term ## _gw_calc(Vertex tail, Vertex head, ModelTerm *mtp, Network *nwp, Rboolean edgestate, StoreStrictDyadMapUInt *spcache, double alpha, double loneexpa) { \
double cumchange = 0; \
term ## _change({ \
- cumchange += pow(oneexpa, L2-edgestate); \
+ cumchange += exp(loneexpa*(L2-edgestate)); \
},{ \
- if(alpha < 100.0) cumchange += exp(alpha)*(1-pow(oneexpa, L2)); \
- else cumchange += L2; \
+ cumchange += exp(alpha + log1mexp(-loneexpa*L2)); \
}); \
return cumchange; \
}
#define gw_calc2(term) \
- static inline double term ## _gw_calc(Vertex tail, Vertex head, ModelTerm *mtp, Network *nwp, Rboolean edgestate, StoreStrictDyadMapUInt *spcache, double alpha, double oneexpa) { \
+ static inline double term ## _gw_calc(Vertex tail, Vertex head, ModelTerm *mtp, Network *nwp, Rboolean edgestate, StoreStrictDyadMapUInt *spcache, double alpha, double loneexpa) { \
double cumchange = 0; \
term ## _change({ \
- cumchange += pow(oneexpa, L2-edgestate)*2; \
+ cumchange += exp(loneexpa*(L2-edgestate))*2; \
},{ \
- if(alpha < 100.0) cumchange += exp(alpha)*(1-pow(oneexpa, L2)); \
- else cumchange += L2; \
+ cumchange += exp(alpha + log1mexp(-loneexpa*L2)); \
}); \
return cumchange; \
}
@@ -198,7 +196,7 @@ C_CHANGESTAT_FN(c_dgwdsp) {
/*Set things up*/
StoreStrictDyadMapUInt *spcache = N_AUX ? AUX_STORAGE : NULL;
double alpha = INPUT_PARAM[0]; /*Get alpha*/
- double oneexpa = 1.0-exp(-alpha); /*Precompute (1-exp(-alpha))*/
+ double loneexpa = log1mexp(alpha); /*Precompute log(1-exp(-alpha))*/
int type = IINPUT_PARAM[0]; /*Get the ESP type code to be used*/
double cumchange = 0;
@@ -299,7 +297,7 @@ C_CHANGESTAT_FN(c_dgwesp) {
/*Set things up*/
StoreStrictDyadMapUInt *spcache = N_AUX ? AUX_STORAGE : NULL;
double alpha = INPUT_PARAM[0]; /*Get alpha*/
- double oneexpa = 1.0-exp(-alpha); /*Precompute (1-exp(-alpha))*/
+ double loneexpa = log1mexp(alpha); /*Precompute (1-exp(-alpha))*/
int type = IINPUT_PARAM[0]; /*Get the ESP type code to be used*/
double cumchange = 0;
@@ -441,7 +439,7 @@ C_CHANGESTAT_FN(c_dgwnsp) {
/*Set things up*/
StoreStrictDyadMapUInt *spcache = N_AUX ? AUX_STORAGE : NULL;
double alpha = INPUT_PARAM[0]; /*Get alpha*/
- double oneexpa = 1.0-exp(-alpha); /*Precompute (1-exp(-alpha))*/
+ double loneexpa = log1mexp(alpha); /*Precompute (1-exp(-alpha))*/
int type = IINPUT_PARAM[0]; /*Get the ESP type code to be used*/
double cumchange = 0;