Skip to content

Weight Criteria

weight_criteria

Weight optimisation criteria for weighted generative network models.

This subpackage provides various criteria for optimising the weights in weighted generative network models. Each criterion defines an objective function that guides how weights evolve during network generation.

The module includes:

  • Base abstract class for defining custom optimisation criteria
  • Communicability-based criteria that optimise based on network communication properties
  • Distance-based criteria that incorporate spatial constraints
  • Simple weight-based criteria that focus on the weight distribution itself

These criteria can be used to create networks with different properties by guiding the optimisation process toward different objective functions.

gnm.weight_criteria.OptimisationCriterion

Bases: ABC

Abstract base class for optimisation criteria used in weighted generative networks.

This class provides a framework for defining various optimisation objectives that guide the evolution of weights in weighted generative network models. Each criterion implements an objective function L(W) that the network attempts to optimise during generation.

Subclasses must implement a __call__ method to compute the objective function value for a given weight matrix

Examples:

>>> # Define a custom optimisation criterion
>>> class CustomCriterion(OptimisationCriterion):
...     def __call__(self, weight_matrix):
...         # Calculate some objective function
...         return torch.sum(weight_matrix)
See Also

__call__(weight_matrix) abstractmethod

Compute the final criterion \(L(W)\) for optimisation of the network weights.

Source code in src/gnm/weight_criteria/optimisation_criteria.py
34
35
36
37
38
39
40
@abstractmethod
@jaxtyped(typechecker=typechecked)
def __call__(
    self, weight_matrix: Float[torch.Tensor, "num_simulations num_nodes num_nodes"]
) -> Float[torch.Tensor, "num_simulations"]:
    """Compute the final criterion $L(W)$ for optimisation of the network weights."""
    pass

gnm.weight_criteria.ScaledCriterion(weight_criterion, coefficient)

Bases: OptimisationCriterion

Scaled optimisation criterion.

This class scales an existing optimisation criterion by a given coefficient.

Examples:

>>> import torch
>>> from gnm.weight_criteria import Communicability, ScaledCriterion
>>> # Define the scaled criterion directly
>>> scaled_communicability_direct = ScaledCriterion(weight_criterion=Communicability(), coefficient=2.4)
>>> # Define the scaled criterion indirectly
>>> scaled_communicability_indirect = 2.4 * Communicability()
>>> # Check that the two definitions are equivalent
>>> scaled_communicability_direct == scaled_communicability_indirect
True
>>> # Verify that scaling works as intended
>>> communiability = Communicability()
>>> from gnm.defaults import get_weighted_network
>>> weight_matrix = get_weighted_network()
>>> ( scaled_communicability_direct(weight_matrix) == 2.4 * communicability(weight_matrix) ).item()
True
See Also

Parameters:

Name Type Description Default
weight_criterion OptimisationCriterion

The optimisation criterion to scale.

required
coefficient float

The coefficient by which to scale the criterion.

required
Source code in src/gnm/weight_criteria/optimisation_criteria.py
 97
 98
 99
100
101
102
103
104
105
106
107
108
@jaxtyped(typechecker=typechecked)
def __init__(self, weight_criterion: OptimisationCriterion, coefficient: float):
    r"""
    Args:
        weight_criterion:
            The optimisation criterion to scale.
        coefficient:
            The coefficient by which to scale the criterion.
    """

    self.criterion = weight_criterion
    self.coefficient = coefficient

gnm.weight_criteria.LinearCombinationCriterion(weight_criteria, coefficients)

Bases: OptimisationCriterion

Linear combination optimisation criterion.

This class allows for the combination of multiple optimisation criteria into a single criterion via a weighted sum.

Examples:

>>> from gnm.weight_criteria import Communicability, Weight, LinearCombinationCriterion
>>> # Define a linear combination criterion directly
>>> lc_direct = LinearCombinationCriterion(weight_criteria=[Communicability(), Weight()], coefficients=[1.0,-0.5])
>>> # Define a linear combination cirterion indirectly
>>> lc_indirect = Communicability() - 0.5 * Weight()
>>> # Check that these are equivalent
>>> lc_direct == lc_indirect
True
>>> str(lc_direct)
'LinearCombinationCriterion(Communicability, Weight (coefficient=-0.5))'
See Also

Parameters:

Name Type Description Default
weight_criteria List[OptimisationCriterion]

List of optimisation criteria to combine.

required
coefficients List[float]

List of coefficients to apply to each criterion.

required
Source code in src/gnm/weight_criteria/optimisation_criteria.py
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
@jaxtyped(typechecker=typechecked)
def __init__(
    self, weight_criteria: List[OptimisationCriterion], coefficients: List[float]
):
    r"""
    Args:
        weight_criteria:
            List of optimisation criteria to combine.
        coefficients:
            List of coefficients to apply to each criterion.
    """
    assert len(weight_criteria) == len(
        coefficients
    ), f"List of weight criteria and coefficients must be the same length. Got {len(weight_criteria)} and {len(coefficients)} respectively."

    self.weight_criteria = []
    self.coefficients = []

    for ii in range(len(weight_criteria)):
        if isinstance(weight_criteria[ii], LinearCombinationCriterion):
            self.weight_criteria.extend(weight_criteria[ii].weight_criteria)
            self.coefficients.extend(
                [
                    coefficients[ii] * coeff
                    for coeff in weight_criteria[ii].coefficients
                ]
            )
        else:
            self.weight_criteria.append(weight_criteria[ii])
            self.coefficients.append(coefficients[ii])

    for ii in range(len(self.weight_criteria)):
        if isinstance(self.weight_criteria[ii], ScaledCriterion):
            self.coefficients[ii] = (
                self.coefficients[ii] * self.weight_criteria[ii].coefficient
            )
            self.weight_criteria[ii] = self.weight_criteria[ii].criterion

Basic Weight Criteria

gnm.weight_criteria.Weight(omega=1.0)

Bases: OptimisationCriterion

Weight optimisation criterion.

This criterion simply optimises based on the sum of all weights raised to a power. It provides a basic measure of the overall magnitude of weights in the network.

To compute the optimisation criterion, we sum over the elements of the weight matrix raised to the power of \(\omega\).

The loss is then given by: $$ L(W) = \sum_{ij} W_{ij}^\omega $$

Examples:

>>> from gnm.weight_criteria import Weight
>>> from gnm.defaults import get_weighted_network
>>> # Create a weight criterion with default parameters
>>> criterion = Weight(omega=1.0)
>>> # Apply to a network
>>> weight_matrix = get_weighted_network()
>>> loss = criterion(weight_matrix)
>>> loss.shape
torch.Size([1])
See Also

Parameters:

Name Type Description Default
omega float

The power to which to raise each element of the weight matrix before performing the sum. Defaults to 1.0.

1.0
Source code in src/gnm/weight_criteria/optimisation_criteria.py
676
677
678
679
680
681
682
def __init__(self, omega: float = 1.0):
    r"""
    Args:
        omega:
            The power to which to raise each element of the weight matrix before performing the sum. Defaults to 1.0.
    """
    self.omega = omega

gnm.weight_criteria.NormalisedWeight(omega=1.0)

Bases: OptimisationCriterion

Normalised weight optimisation criterion.

This criterion optimises based on the sum of all normalised weights. The normalisation makes this criterion invariant to the absolute scale of weights by dividing by the maximum weight.

To compute the optimisation criterion, we normalise the weight matrix by dividing by the maximum element, and then sum over the elements of the normalised weight matrix raised to the power of \(\omega\).

The loss is then given by: $$ L(W) = \frac{ \sum_{ij} W^\omega_{ij} }{ \max_{ij} W^\omega_{ij} } $$

Examples:

>>> from gnm.weight_criteria import NormalisedWeight
>>> from gnm.defaults import get_weighted_network
>>> # Create a normalised weight criterion with default parameters
>>> criterion = NormalisedWeight(omega=1.0)
>>> # Apply to a network
>>> weight_matrix = get_weighted_network()
>>> loss = criterion(weight_matrix)
>>> loss.shape
torch.Size([1])
See Also

Parameters:

Name Type Description Default
omega float

The power to which to raise each element of the weight matrix before performing the sum and normalising. Defaults to 1.0.

1.0
Source code in src/gnm/weight_criteria/optimisation_criteria.py
725
726
727
728
729
730
731
def __init__(self, omega: float = 1.0):
    r"""
    Args:
        omega:
            The power to which to raise each element of the weight matrix before performing the sum and normalising. Defaults to 1.0.
    """
    self.omega = omega

Distance-Based Criteria

gnm.weight_criteria.WeightedDistance(distance_matrix, omega=1.0)

Bases: OptimisationCriterion

Weighted distance optimisation criterion.

This criterion optimises network weights based on the product of weights and distances. It penalises strong connections between distant nodes, encouraging a more spatially efficient network structure.

To compute the optimisation criterion, we go through the following steps:

  1. Take the element-wise product of the distance matrix and the weight matrix, \(D \odot W\).
  2. Raise each element of this product to the power of \(\omega\), \((D \odot W)_{ij}^\omega\).
  3. Sum over the elements of the weighted distance matrix rasied to the power of \(\omega\) to get the loss.

The loss is then given by: $$ L(W) = \sum_{ij} \left( D_{ij} W_{ij} \right)^\omega $$

Examples:

>>> from gnm.weight_criteria import WeightedDistance
>>> from gnm.defaults import get_weighted_network, get_distance_matrix
>>> # Create a weighted distance criterion with default parameters
>>> distance_matrix = get_distance_matrix()
>>> criterion = WeightedDistance(distance_matrix, omega=1.0)
>>> # Apply to a network
>>> weight_matrix = get_weighted_network()
>>> loss = criterion(weight_matrix)
>>> loss.shape
torch.Size([1])
See Also

Parameters:

Name Type Description Default
distance_matrix Union[Float[Tensor, 'num_simulations num_nodes num_nodes'], Float[Tensor, 'num_nodes num_nodes']]

The distance matrix of the network.

required
omega float

The power to which to raise each element of the weighted distance before performing the sum. Defaults to 1.0.

1.0
Source code in src/gnm/weight_criteria/optimisation_criteria.py
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
def __init__(
    self,
    distance_matrix: Union[
        Float[torch.Tensor, "num_simulations num_nodes num_nodes"],
        Float[torch.Tensor, "num_nodes num_nodes"],
    ],
    omega: float = 1.0,
):
    r"""
    Args:
        distance_matrix:
            The distance matrix of the network.
        omega:
            The power to which to raise each element of the weighted distance before performing
            the sum. Defaults to 1.0.
    """

    if len(distance_matrix.shape) == 2:
        self.distance_matrix = distance_matrix.unsqueeze(0)
    else:
        self.distance_matrix = distance_matrix

    weighted_checks(self.distance_matrix)
    self.omega = omega

gnm.weight_criteria.NormalisedWeightedDistance(distance_matrix, omega=1.0)

Bases: OptimisationCriterion

Normalised weighted distance optimisation criterion.

This criterion optimises network weights based on the normalised product of weights and distances. The normalisation makes this criterion less sensitive to absolute magnitudes by dividing by the maximum value.

To compute the optimisation criterion, we go through the following steps:

  1. Take the element-wise product of the distance matrix and the weight matrix, \(D \odot W\).
  2. Raise each element of this product to the power of \(\omega\), \((D \odot W)_{ij}^\omega\).
  3. Normalise by dividing by the maximum element.
  4. Sum over the elements of the weighted distance matrix rasied to the power of \(\omega\) to get the loss.

The loss is then given by: $$ L(W) = \frac{ \sum_{ij} \left( D_{ij} W_{ij} \right)^\omega }{ \max_{ij} \left( D_{ij} W_{ij} \right)^\omega } $$

Examples:

>>> from gnm.weight_criteria import NormalisedWeightedDistance
>>> from gnm.defaults import get_weighted_network, get_distance_matrix
>>> # Create a normalised weighted distance criterion with default parameters
>>> distance_matrix = get_distance_matrix()
>>> criterion = NormalisedWeightedDistance(distance_matrix, omega=1.0)
>>> # Apply to a network
>>> weight_matrix = get_weighted_network()
>>> loss = criterion(weight_matrix)
>>> loss.shape
torch.Size([1])
See Also

Parameters:

Name Type Description Default
distance_matrix Union[Float[Tensor, 'num_simulations num_nodes num_nodes'], Float[Tensor, 'num_nodes num_nodes']]

The distance matrix of the network.

required
omega float

The power to which to raise each element of the weighted distance before performing the sum and normalising. Defaults to 1.0.

1.0
Source code in src/gnm/weight_criteria/optimisation_criteria.py
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
def __init__(
    self,
    distance_matrix: Union[
        Float[torch.Tensor, "num_simulations num_nodes num_nodes"],
        Float[torch.Tensor, "num_nodes num_nodes"],
    ],
    omega: float = 1.0,
):
    r"""
    Args:
        distance_matrix:
            The distance matrix of the network.
        omega:
            The power to which to raise each element of the weighted distance before performing
            the sum and normalising. Defaults to 1.0.
    """
    if len(distance_matrix.shape) == 2:
        self.distance_matrix = distance_matrix.unsqueeze(0)
    else:
        self.distance_matrix = distance_matrix

    weighted_checks(self.distance_matrix)
    self.omega = omega

Communicability-Based Criteria

gnm.weight_criteria.Communicability(omega=1.0)

Bases: OptimisationCriterion

Communicability optimisation criterion.

This criterion optimises network weights based on the total communicability of the network. Communicability measures the ease with which information can flow between nodes through all possible paths.

To compute this optimisation criterion, we follow these steps:

  1. Compute the diagonal node strength matrix, \(S_{ii} = \sum_j W_{ij}\) (plus a small constant to prevent division by zero)
  2. Compute the normalised weight matrix, \(S^{-1/2} W S^{-1/2}\)
  3. Compute the communicability matrix by taking the matrix exponential, \(\exp(S^{-1/2} W S^{-1/2})\)
  4. Raise each element of this product to the power of \(\omega\), \(\exp(S^{-1/2} W S^{-1/2})_{ij}^\omega\)
  5. Sum over the elements of the communicability matrix raised to the power of \(\omega\) to get the loss

The loss \(L(W)\) is given by: $$ L(W) = \sum_{ij} \left( \exp( S^{-1/2} W S^{-1/2} )_{ij} \right)^\omega $$

Examples:

>>> from gnm.weight_criteria import Communicability
>>> from gnm.defaults import get_weighted_network
>>> # Create a communicability criterion with default parameters
>>> criterion = Communicability(omega=1.0)
>>> # Apply to a network
>>> weight_matrix = get_weighted_network()
>>> loss = criterion(weight_matrix)
>>> loss.shape
torch.Size([1])
See Also

Parameters:

Name Type Description Default
omega float

The power to which to raise each element of the communicability matrix before performing the sum. Defaults to 1.0.

1.0
Source code in src/gnm/weight_criteria/optimisation_criteria.py
247
248
249
250
251
252
253
def __init__(self, omega: float = 1.0):
    r"""
    Args:
        omega:
            The power to which to raise each element of the communicability matrix before performing the sum. Defaults to 1.0.
    """
    self.omega = omega

gnm.weight_criteria.NormalisedCommunicability(omega=1.0)

Bases: OptimisationCriterion

Normalised communicability optimisation criterion.

This criterion optimises network weights based on the normalised total communicability of the network. The normalisation makes this criterion less sensitive to absolute weight magnitudes by dividing by the maximum communicability value.

  1. Compute the diagonal node strength matrix, \(S_{ii} = \sum_j W_{ij}\) (plus a small constant to prevent division by zero).
  2. Compute the normalised weight matrix, \(S^{-1/2} W S^{-1/2}\).
  3. Compute the communicability matrix by taking the matrix exponential, \(\exp( S^{-1/2} W S^{-1/2} )\).
  4. Raise each element of this product to the power of \(\omega\), \(\exp( S^{-1/2} W S^{-1/2} )_{ij}^\omega\).
  5. Normalise by dividing by the maximum element.
  6. Sum over the elements of the normalised communicability matrix rasied to the power of \(\omega\) to get the loss.

The loss is then given by: $$ L(W) = \frac{ \sum_{ij} \exp( S^{-1/2} W S^{-1/2} ){ij}^\omega }{ \max{ij} \exp( S^{-1/2} W S^{-1/2} )_{ij}^\omega } $$

Examples:

>>> from gnm.weight_criteria import NormalisedCommunicability
>>> from gnm.defaults import get_weighted_network
>>> # Create a normalied communicability criterion with default parameters
>>> criterion = NormalisedCommunicability()
>>> # Apply to a network
>>> weight_matrix = get_weighted_network()
>>> loss = criterion(weight_matrix)
>>> loss.shape
torch.Size([1])
See Also

Parameters:

Name Type Description Default
omega float

The power to which to raise each element of the communicability matrix before performing the sum and normalising. Defaults to 1.0.

1.0
Source code in src/gnm/weight_criteria/optimisation_criteria.py
302
303
304
305
306
307
308
def __init__(self, omega: float = 1.0):
    r"""
    Args:
        omega:
            The power to which to raise each element of the communicability matrix before performing the sum and normalising. Defaults to 1.0.
    """
    self.omega = omega

Distance-Weighted Communicability Criteria

gnm.weight_criteria.DistanceWeightedCommunicability(distance_matrix, omega=1.0)

Bases: OptimisationCriterion

Distance-weighted communicability optimisation criterion.

This criterion optimises network weights based on the communicability weighted by the distances between nodes. This adds a spatial constraint to the optimisation, where communicability between distant nodes contributes more to the objective function.

To compute this optimisation criterion, we go through the following steps:

  1. Compute the diagonal node strength matrix, \(S_{ii} = \sum_j W_{ij}\) (plus a small constant to prevent division by zero).
  2. Compute the normalised weight matrix, \(S^{-1/2} W S^{-1/2}\).
  3. Compute the communicability matrix by taking the matrix exponential, \(\exp( S^{-1/2} W S^{-1/2} )\).
  4. Take the element-wise product of the communicability matrix and the distance matrix, \(\exp( S^{-1/2} W S^{-1/2} ) \odot D\)
  5. Raise each element of this product to the power of \(\omega\), \((\exp( S^{-1/2} W S^{-1/2} ) \odot D)_{ij}^\omega\).
  6. Sum over the elements of the distance-weighted communicability matrix rasied to the power of \(\omega\) to get the loss.

The loss is then given by: $$ L(W) = \sum_{ij} \left( \exp( S^{-1/2} W S^{-1/2} ){ij} D{ij} \right)^\omega $$

Examples:

>>> from gnm.weight_criteria import DistanceWeightedCommunicability
>>> from gnm.defaults import get_weighted_network, get_distance_matrix
>>> # Create a distance-weighted communicability criterion with default parameters
>>> distance_matrix = get_distance_matrix()
>>> criterion = DistanceWeightedCommunicability(distance_matrix, omega=1.0)
>>> # Apply to a network
>>> weight_matrix = get_weighted_network()
>>> loss = criterion(weight_matrix)
>>> loss.shape
torch.Size([1])
See Also

Parameters:

Name Type Description Default
distance_matrix Union[Float[Tensor, 'num_simulations num_nodes num_nodes'], Float[Tensor, 'num_nodes num_nodes']]

The distance matrix of the network.

required
omega float

The power to which to raise each element of the distance weighted communicability before performing the sum. Defaults to 1.0.

1.0
Source code in src/gnm/weight_criteria/optimisation_criteria.py
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
def __init__(
    self,
    distance_matrix: Union[
        Float[torch.Tensor, "num_simulations num_nodes num_nodes"],
        Float[torch.Tensor, "num_nodes num_nodes"],
    ],
    omega: float = 1.0,
):
    r"""
    Args:
        distance_matrix:
            The distance matrix of the network.
        omega:
            The power to which to raise each element of the distance weighted communicability before performing
            the sum. Defaults to 1.0."""
    if len(distance_matrix.shape) == 2:
        self.distance_matrix = distance_matrix.unsqueeze(0)
    else:
        self.distance_matrix = distance_matrix

    weighted_checks(self.distance_matrix)
    self.omega = omega

gnm.weight_criteria.NormalisedDistanceWeightedCommunicability(distance_matrix, omega=1.0)

Bases: OptimisationCriterion

Normalised distance-weighted communicability optimisation criterion.

This criterion optimises network weights based on the normalised communicability weighted by the distances between nodes. The normalisation makes this criterion less sensitive to absolute magnitudes by dividing by the maximum value.

To compute this optimisation criterion, we go through the following steps:

  1. Compute the diagonal node strength matrix, \(S_{ii} = \sum_j W_{ij}\) (plus a small constant to prevent division by zero).
  2. Compute the normalised weight matrix, \(S^{-1/2} W S^{-1/2}\).
  3. Compute the communicability matrix by taking the matrix exponential, \(\exp( S^{-1/2} W S^{-1/2} )\).
  4. Take the element-wise product of the communicability matrix and the distance matrix, \(\exp( S^{-1/2} W S^{-1/2} ) \odot D\)
  5. Raise each element of this product to the power of \(\omega\), \((\exp( S^{-1/2} W S^{-1/2} ) \odot D)_{ij}^\omega\).
  6. Normalise by dividing by the maximum element.
  7. Sum over the elements of the distance-weighted communicability matrix rasied to the power of \(\omega\) to get the loss.

The loss is then given by: $$ L(W) = \frac{ \sum_{ij} \left( \exp( S^{-1/2} W S^{-1/2} ){ij} D{ij} \right)^\omega }{ \max_{ij} \left( \exp( S^{-1/2} W S^{-1/2} ){ij} D{ij} \right)^\omega } $$

Examples:

>>> from gnm.weight_criteria import NormalisedDistanceWeightedCommunicability
>>> from gnm.defaults import get_weighted_network, get_distance_matrix
>>> # Create a normalised distance-weighted communicability criterion with default parameters
>>> distance_matrix = get_distance_matrix()
>>> criterion = NormalisedDistanceWeightedCommunicability(distance_matrix, omega=1.0)
>>> # Apply to a network
>>> weight_matrix = get_weighted_network()
>>> loss = criterion(weight_matrix)
>>> loss.shape
torch.Size([1])
See Also

Parameters:

Name Type Description Default
distance_matrix Union[Float[Tensor, 'num_simulations num_nodes num_nodes'], Float[Tensor, 'num_nodes num_nodes']]

The distance matrix of the network.

required
omega float

The power to which to raise each element of the distance weighted communicability before performing the sum and normalising. Defaults to 1.0.

1.0
Source code in src/gnm/weight_criteria/optimisation_criteria.py
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
def __init__(
    self,
    distance_matrix: Union[
        Float[torch.Tensor, "num_simulations num_nodes num_nodes"],
        Float[torch.Tensor, "num_nodes num_nodes"],
    ],
    omega: float = 1.0,
):
    r"""
    Args:
        distance_matrix:
            The distance matrix of the network.
        omega:
            The power to which to raise each element of the distance weighted communicability before performing
            the sum and normalising. Defaults to 1.0."""
    if len(distance_matrix.shape) == 2:
        self.distance_matrix = distance_matrix.unsqueeze(0)
    else:
        self.distance_matrix = distance_matrix

    weighted_checks(self.distance_matrix)
    self.omega = omega