Skip to content

Distance term for ergm (Minkowski and spherical distances) - #660

Open
CarterButts wants to merge 4 commits into
statnet:masterfrom
CarterButts:master
Open

Distance term for ergm (Minkowski and spherical distances)#660
CarterButts wants to merge 4 commits into
statnet:masterfrom
CarterButts:master

Conversation

@CarterButts

Copy link
Copy Markdown

This update adds a term called distance that can be used for binary ERGMs (directed or undirected). The term includes support for

  • Minkowski metrics with arbitrary exponents in arbitrary numbers of dimensions
  • Spherical (great circle) distances on spheres of arbitrary size (defaulting to the IUGG geosphere, for lat/lon coordinates)
  • Logarithmic distance scaling (necessary for most social network applications)
  • Offsets and "safety" thresholding for log distances
  • Coordinates passed either directly in matrix form, or via an embedded network attribute

This functionality was previously in an ergm userterms package, but given recent demand from Steve, Sam, and Martina, I dusted the package off, upgraded it, and then integrated the functionality into the ergm package.

The function is fully documented (with examples), and includes some basic tests. I have confirmed that the package w/the term passes R CMD check, the tests pass, and the examples work correctly. I think all is copacetic, but let me know if there are things that require fixin'.

… check,

the term passes its checks, and the examples run correctly.  A NEWS item
has been added, and the term is also documented (and a test has been added).
@krivit

krivit commented Jul 28, 2026

Copy link
Copy Markdown
Member

Thanks, Carter! Definitely worth including. My thoughts so far:

  1. This could probably use a c_ function rather than d_.
  2. Positions are vertex attributes, so semantically it would make more sense to store them there. This would also work better with ergm.multi and tergm.
  3. ergm_get_vattr() API can fetch multiple vertex attributes. For example, nodecov(c("Lat", "Long")) or nodecov(~cbind(Lat, Long)) will internally fetch a 2-column matrix.
    1. At this point, list-form vertex attributes aren't supported, I think, but that's easily fixed.

@CarterButts

Copy link
Copy Markdown
Author

Hi, Pavel. To your points:

  1. Although we have obviously discussed it in the past, I forget the specs for c_* functions versus d_* functions. Is there much of an advantage for independence terms? Also, where are they currently documented? They postdate the ergmuserterms package, and the ergm 4 paper doesn't discuss them. I can port the d_* term if you think it is needed, but I need the relevant specs. (I always use the d_* form, being Olde Skoole.)
  2. I don't store the coordinates on the vertices because this is poses various annoying issues. We need to support an arbitrary number of dimensions (one cannot assume that the data is two-dimensional), so storing the coordinates in a vertex attribute would require using vector-valued attributes. network certainly supports that, but it's much more work for the user to have to decompose coordinates into individual vectors and then write them into place using the set.vertex.attribute command than to just pass a matrix (or stuff it in a network attribute). It's also more of a pain for us to retrieve, and creates checks that have to be done for consistency, etc. I actually started to implement that, but then realized that it was inferior to just using a network attribute, so stopped. One could name every dimension, as you suggest, but then the user has to (1) store every dimension under its own name, and then (2) pass the whole vector of names to the function. This seems to me unlikely to be easier than just putting the data in a network attribute. If we really wanted to support this, it could be implemented, but then there's the question of how to signal whether an attribute name (if only one is passed) refers to a vertex or a network attribute. I suppose one way to go would be to first check to see if there is e.g. a vertex attribute, and then go look for a network attribute if that fails. Seems inelegant, and could lead to unexpected behavior if the user doesn't read the docs, but better than having to add yet another argument to the function to tell it what kind of attribute we want....
  3. I wasn't familiar with that, but can use it if you think it is important to have the functionality in (2). Let me know if you have a better alternative to the failover idea (i.e., that if a single character string is passed, first we look for a vertex attribute with that name - if none exists, we then look for a network attribute with that name and use it instead).

@krivit

krivit commented Jul 28, 2026

Copy link
Copy Markdown
Member

I can only answer briefly at the moment:

The c_ API is documented in the Terms API vignette, and most of the terms have been ported over. It reduces boilerplate since it handles 1 toggle at a time, and it receives the current edge state as a part of the call rather than having to look it up, so it's somewhat faster.

There might be a simpler way to do it, but apply(M, 1, identity, simplify = FALSE) creates a list of rows suitable for assignment to a vertex attribute.

If you really want to access the network attribute via a nodal attribute API, you can: for example, nodecov(~.%n%"M")) will grab the network attribute matrix.

@CarterButts

Copy link
Copy Markdown
Author

OK, making those changes (and adding a few more features). Will update....

functionality (including vertex based attributes, scaling, and the ability
to combine spherical and Minkowski metrics), updated docs and tests, and
tested everything.  Seems to be working fine.
@CarterButts

Copy link
Copy Markdown
Author

@krivit OK, I have made the suggested changes, and added some new functionality. Among other things, I switched from d_* to c_* form for the changescore, and one can now either use vertex or network attributes (in addition to matrices) to supply coordinates. It is now possible to mix spherical and Minkowski effects, and I added a scaling option. I updated the tests and docs accordingly. Everything seems to be passing checks, and my own examples/tests are working fine. So it seems to be copacetic, as far as I can tell.

krivit added a commit that referenced this pull request Jul 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants