Skip to content

layers

layers ¤

BinomialLayer ¤

Bases: InputLayer

A symbolic Binomial layer, which is parameterized either by probabilities (yielding a normalized Binomial distribution) or by logits (yielding an unnormalized Binomial distribution).

Source code in cirkit/symbolic/layers.py
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
class BinomialLayer(InputLayer):
    """A symbolic Binomial layer, which is parameterized either by
    probabilities (yielding a normalized Binomial distribution) or by
    logits (yielding an unnormalized Binomial distribution)."""

    def __init__(
        self,
        scope: Scope,
        num_output_units: int,
        num_channels: int = 1,
        *,
        total_count: int = 2,
        logits: Parameter | None = None,
        probs: Parameter | None = None,
        logits_factory: ParameterFactory | None = None,
        probs_factory: ParameterFactory | None = None,
    ):
        r"""Initializes a Binomial layer.

        Args:
            scope: The variables scope the layer depends on.
            num_output_units: The number of Categorical units in the layer.
            num_channels: The number of channels per variable.
            total_count: The number of total counts for each variable and channel.
            logits: The logits parameter of shape $(K, C)$, where $K$ is the number of output
                units, $C$ is the number of channels. If it is None,
                then either the probabilities parameter is used (if it is not None) or a
                probabilities parameter parameterized by a
                [SigmoidParameter][cirkit.symbolic.parameters.SigmoidParameter].
            probs: The probabilities parameter of shape $(K, C)$ (see logits parameter
                description). If it is None, then the logits parameter must be specified.
            logits_factory: A factory used to construct the logits parameter, if neither logits nor
                probabilities are given.
            probs_factory: A factory used to construct the probabilities parameter, if neither
                logits nor probabilities nor the logits parameter factory are given.
        """
        if logits is not None and probs is not None:
            raise ValueError("At most one between 'logits' and 'probs' can be specified")
        if logits_factory is not None and probs_factory is not None:
            raise ValueError(
                "At most one between 'logits_factory' and 'probs_factory' can be specified"
            )
        if total_count < 0:
            raise ValueError("The number of trials should be non-negative")
        super().__init__(scope, num_output_units, num_channels)
        self.total_count = total_count
        if logits is None and probs is None:
            if logits_factory is not None:
                logits = logits_factory(self._probs_logits_shape)
            elif probs_factory is not None:
                probs = probs_factory(self._probs_logits_shape)
            else:  # Defaults to probs with sigmoid parameterization
                probs = Parameter.from_unary(
                    SigmoidParameter(self._probs_logits_shape),
                    TensorParameter(*self._probs_logits_shape, initializer=NormalInitializer()),
                )
        if logits is not None and logits.shape != self._probs_logits_shape:
            raise ValueError(
                f"Expected parameter shape {self._probs_logits_shape}, found {logits.shape}"
            )
        if probs is not None and probs.shape != self._probs_logits_shape:
            raise ValueError(
                f"Expected parameter shape {self._probs_logits_shape}, found {probs.shape}"
            )
        self.probs = probs
        self.logits = logits

    @property
    def _probs_logits_shape(self) -> tuple[int, ...]:
        return self.num_output_units, self.num_channels

    @property
    def config(self) -> dict:
        return {
            "scope": self.scope,
            "num_output_units": self.num_output_units,
            "num_channels": self.num_channels,
            "total_count": self.total_count,
        }

    @property
    def params(self) -> Mapping[str, Parameter]:
        if self.logits is None:
            return {"probs": self.probs}
        return {"logits": self.logits}

_probs_logits_shape property ¤

config property ¤

logits = logits instance-attribute ¤

params property ¤

probs = probs instance-attribute ¤

total_count = total_count instance-attribute ¤

__init__(scope, num_output_units, num_channels=1, *, total_count=2, logits=None, probs=None, logits_factory=None, probs_factory=None) ¤

Initializes a Binomial layer.

Parameters:

Name Type Description Default
scope Scope

The variables scope the layer depends on.

required
num_output_units int

The number of Categorical units in the layer.

required
num_channels int

The number of channels per variable.

1
total_count int

The number of total counts for each variable and channel.

2
logits Parameter | None

The logits parameter of shape \((K, C)\), where \(K\) is the number of output units, \(C\) is the number of channels. If it is None, then either the probabilities parameter is used (if it is not None) or a probabilities parameter parameterized by a SigmoidParameter.

None
probs Parameter | None

The probabilities parameter of shape \((K, C)\) (see logits parameter description). If it is None, then the logits parameter must be specified.

None
logits_factory ParameterFactory | None

A factory used to construct the logits parameter, if neither logits nor probabilities are given.

None
probs_factory ParameterFactory | None

A factory used to construct the probabilities parameter, if neither logits nor probabilities nor the logits parameter factory are given.

None
Source code in cirkit/symbolic/layers.py
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
def __init__(
    self,
    scope: Scope,
    num_output_units: int,
    num_channels: int = 1,
    *,
    total_count: int = 2,
    logits: Parameter | None = None,
    probs: Parameter | None = None,
    logits_factory: ParameterFactory | None = None,
    probs_factory: ParameterFactory | None = None,
):
    r"""Initializes a Binomial layer.

    Args:
        scope: The variables scope the layer depends on.
        num_output_units: The number of Categorical units in the layer.
        num_channels: The number of channels per variable.
        total_count: The number of total counts for each variable and channel.
        logits: The logits parameter of shape $(K, C)$, where $K$ is the number of output
            units, $C$ is the number of channels. If it is None,
            then either the probabilities parameter is used (if it is not None) or a
            probabilities parameter parameterized by a
            [SigmoidParameter][cirkit.symbolic.parameters.SigmoidParameter].
        probs: The probabilities parameter of shape $(K, C)$ (see logits parameter
            description). If it is None, then the logits parameter must be specified.
        logits_factory: A factory used to construct the logits parameter, if neither logits nor
            probabilities are given.
        probs_factory: A factory used to construct the probabilities parameter, if neither
            logits nor probabilities nor the logits parameter factory are given.
    """
    if logits is not None and probs is not None:
        raise ValueError("At most one between 'logits' and 'probs' can be specified")
    if logits_factory is not None and probs_factory is not None:
        raise ValueError(
            "At most one between 'logits_factory' and 'probs_factory' can be specified"
        )
    if total_count < 0:
        raise ValueError("The number of trials should be non-negative")
    super().__init__(scope, num_output_units, num_channels)
    self.total_count = total_count
    if logits is None and probs is None:
        if logits_factory is not None:
            logits = logits_factory(self._probs_logits_shape)
        elif probs_factory is not None:
            probs = probs_factory(self._probs_logits_shape)
        else:  # Defaults to probs with sigmoid parameterization
            probs = Parameter.from_unary(
                SigmoidParameter(self._probs_logits_shape),
                TensorParameter(*self._probs_logits_shape, initializer=NormalInitializer()),
            )
    if logits is not None and logits.shape != self._probs_logits_shape:
        raise ValueError(
            f"Expected parameter shape {self._probs_logits_shape}, found {logits.shape}"
        )
    if probs is not None and probs.shape != self._probs_logits_shape:
        raise ValueError(
            f"Expected parameter shape {self._probs_logits_shape}, found {probs.shape}"
        )
    self.probs = probs
    self.logits = logits

CategoricalLayer ¤

Bases: InputLayer

A symbolic Categorical layer, which is parameterized either by probabilities (yielding a normalized Categorical distribution) or by logits (yielding an unnormalized Categorical distribution).

Source code in cirkit/symbolic/layers.py
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
class CategoricalLayer(InputLayer):
    """A symbolic Categorical layer, which is parameterized either by
    probabilities (yielding a normalized Categorical distribution) or by
    logits (yielding an unnormalized Categorical distribution)."""

    def __init__(
        self,
        scope: Scope,
        num_output_units: int,
        num_channels: int = 1,
        *,
        num_categories: int,
        logits: Parameter | None = None,
        probs: Parameter | None = None,
        logits_factory: ParameterFactory | None = None,
        probs_factory: ParameterFactory | None = None,
    ):
        r"""Initializes a Categorical layer.

        Args:
            scope: The variables scope the layer depends on.
            num_output_units: The number of Categorical units in the layer.
            num_channels: The number of channels per variable.
            num_categories: The number of categories for each variable and channel.
            logits: The logits parameter of shape $(K, C, N)$, where $K$ is the number of output
                units, $C$ is the number of channels, and $N$ is the number of categories. If it is
                None, then either the probabilities parameter is used (if it is not None) or a
                probabilities parameter parameterized by a
                [SoftmaxParameter][cirkit.symbolic.parameters.SoftmaxParameter].
            probs: The probabilities parameter of shape $(K, C, N)$ (see logits parameter
                description). If it is None, then the logits parameter must be specified.
            logits_factory: A factory used to construct the logits parameter, if neither logits nor
                probabilities are given.
            probs_factory: A factory used to construct the probabilities parameter, if neither
                logits nor probabilities nor the logits parameter factory are given.
        """
        if len(scope) != 1:
            raise ValueError("The Categorical layer encodes a univariate distribution")
        if logits is not None and probs is not None:
            raise ValueError("At most one between 'logits' and 'probs' can be specified")
        if logits_factory is not None and probs_factory is not None:
            raise ValueError(
                "At most one between 'logits_factory' and 'probs_factory' can be specified"
            )
        if num_categories < 2:
            raise ValueError("At least two categories must be specified")
        super().__init__(scope, num_output_units, num_channels)
        self.num_categories = num_categories
        if logits is None and probs is None:
            if logits_factory is not None:
                logits = logits_factory(self._probs_logits_shape)
            elif probs_factory is not None:
                probs = probs_factory(self._probs_logits_shape)
            else:  # Defaults to probs with softmax parameterization
                probs = Parameter.from_unary(
                    SoftmaxParameter(self._probs_logits_shape),
                    TensorParameter(*self._probs_logits_shape, initializer=NormalInitializer()),
                )
        if logits is not None and logits.shape != self._probs_logits_shape:
            raise ValueError(
                f"Expected parameter shape {self._probs_logits_shape}, found {logits.shape}"
            )
        if probs is not None and probs.shape != self._probs_logits_shape:
            raise ValueError(
                f"Expected parameter shape {self._probs_logits_shape}, found {probs.shape}"
            )
        self.probs = probs
        self.logits = logits

    @property
    def _probs_logits_shape(self) -> tuple[int, ...]:
        return self.num_output_units, self.num_channels, self.num_categories

    @property
    def config(self) -> Mapping[str, Any]:
        return {
            "scope": self.scope,
            "num_output_units": self.num_output_units,
            "num_channels": self.num_channels,
            "num_categories": self.num_categories,
        }

    @property
    def params(self) -> Mapping[str, Parameter]:
        if self.logits is None:
            return {"probs": self.probs}
        return {"logits": self.logits}

_probs_logits_shape property ¤

config property ¤

logits = logits instance-attribute ¤

num_categories = num_categories instance-attribute ¤

params property ¤

probs = probs instance-attribute ¤

__init__(scope, num_output_units, num_channels=1, *, num_categories, logits=None, probs=None, logits_factory=None, probs_factory=None) ¤

Initializes a Categorical layer.

Parameters:

Name Type Description Default
scope Scope

The variables scope the layer depends on.

required
num_output_units int

The number of Categorical units in the layer.

required
num_channels int

The number of channels per variable.

1
num_categories int

The number of categories for each variable and channel.

required
logits Parameter | None

The logits parameter of shape \((K, C, N)\), where \(K\) is the number of output units, \(C\) is the number of channels, and \(N\) is the number of categories. If it is None, then either the probabilities parameter is used (if it is not None) or a probabilities parameter parameterized by a SoftmaxParameter.

None
probs Parameter | None

The probabilities parameter of shape \((K, C, N)\) (see logits parameter description). If it is None, then the logits parameter must be specified.

None
logits_factory ParameterFactory | None

A factory used to construct the logits parameter, if neither logits nor probabilities are given.

None
probs_factory ParameterFactory | None

A factory used to construct the probabilities parameter, if neither logits nor probabilities nor the logits parameter factory are given.

None
Source code in cirkit/symbolic/layers.py
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
def __init__(
    self,
    scope: Scope,
    num_output_units: int,
    num_channels: int = 1,
    *,
    num_categories: int,
    logits: Parameter | None = None,
    probs: Parameter | None = None,
    logits_factory: ParameterFactory | None = None,
    probs_factory: ParameterFactory | None = None,
):
    r"""Initializes a Categorical layer.

    Args:
        scope: The variables scope the layer depends on.
        num_output_units: The number of Categorical units in the layer.
        num_channels: The number of channels per variable.
        num_categories: The number of categories for each variable and channel.
        logits: The logits parameter of shape $(K, C, N)$, where $K$ is the number of output
            units, $C$ is the number of channels, and $N$ is the number of categories. If it is
            None, then either the probabilities parameter is used (if it is not None) or a
            probabilities parameter parameterized by a
            [SoftmaxParameter][cirkit.symbolic.parameters.SoftmaxParameter].
        probs: The probabilities parameter of shape $(K, C, N)$ (see logits parameter
            description). If it is None, then the logits parameter must be specified.
        logits_factory: A factory used to construct the logits parameter, if neither logits nor
            probabilities are given.
        probs_factory: A factory used to construct the probabilities parameter, if neither
            logits nor probabilities nor the logits parameter factory are given.
    """
    if len(scope) != 1:
        raise ValueError("The Categorical layer encodes a univariate distribution")
    if logits is not None and probs is not None:
        raise ValueError("At most one between 'logits' and 'probs' can be specified")
    if logits_factory is not None and probs_factory is not None:
        raise ValueError(
            "At most one between 'logits_factory' and 'probs_factory' can be specified"
        )
    if num_categories < 2:
        raise ValueError("At least two categories must be specified")
    super().__init__(scope, num_output_units, num_channels)
    self.num_categories = num_categories
    if logits is None and probs is None:
        if logits_factory is not None:
            logits = logits_factory(self._probs_logits_shape)
        elif probs_factory is not None:
            probs = probs_factory(self._probs_logits_shape)
        else:  # Defaults to probs with softmax parameterization
            probs = Parameter.from_unary(
                SoftmaxParameter(self._probs_logits_shape),
                TensorParameter(*self._probs_logits_shape, initializer=NormalInitializer()),
            )
    if logits is not None and logits.shape != self._probs_logits_shape:
        raise ValueError(
            f"Expected parameter shape {self._probs_logits_shape}, found {logits.shape}"
        )
    if probs is not None and probs.shape != self._probs_logits_shape:
        raise ValueError(
            f"Expected parameter shape {self._probs_logits_shape}, found {probs.shape}"
        )
    self.probs = probs
    self.logits = logits

ConstantLayer ¤

Bases: InputLayer, ABC

The symbolic layer computing a constant vector, i.e., it does not depend on any variable.

Source code in cirkit/symbolic/layers.py
169
170
171
172
173
174
175
176
177
178
class ConstantLayer(InputLayer, ABC):
    """The symbolic layer computing a constant vector, i.e., it does not depend on any variable."""

    def __init__(self, num_output_units: int):
        """Initializes a symbolic constant layer.

        Args:
            num_output_units: The number of input units in the layer.
        """
        super().__init__(Scope([]), num_output_units)

__init__(num_output_units) ¤

Initializes a symbolic constant layer.

Parameters:

Name Type Description Default
num_output_units int

The number of input units in the layer.

required
Source code in cirkit/symbolic/layers.py
172
173
174
175
176
177
178
def __init__(self, num_output_units: int):
    """Initializes a symbolic constant layer.

    Args:
        num_output_units: The number of input units in the layer.
    """
    super().__init__(Scope([]), num_output_units)

ConstantValueLayer ¤

Bases: ConstantLayer

A symbolic layer computing a constant function encoded by a parameter.

Source code in cirkit/symbolic/layers.py
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
class ConstantValueLayer(ConstantLayer):
    """A symbolic layer computing a constant function encoded by a parameter."""

    def __init__(self, num_output_units: int, *, log_space: bool = False, value: Parameter):
        """Initializes a constant value layer.

        Args:
            num_output_units: The number of output log partition functions.
            log_space: Whether the given value is in the log-space, i.e., this constant
                layer should encode ```exp(value)``` rather than ```value```.
            value: The symbolic parameter representing the encoded value.
                This symbolic paramater should have shape (K,), where K is the number of
                output units.
        """
        super().__init__(num_output_units)
        if value.shape != self._value_shape:
            raise ValueError(f"Expected parameter shape {self._value_shape}, found {value.shape}")
        self.value = value
        self.log_space = log_space

    @property
    def _value_shape(self) -> tuple[int, ...]:
        return (self.num_output_units,)

    @property
    def config(self) -> Mapping[str, Any]:
        return {"num_output_units": self.num_output_units, "log_space": self.log_space}

    @property
    def params(self) -> Mapping[str, Parameter]:
        return {"value": self.value}

_value_shape property ¤

config property ¤

log_space = log_space instance-attribute ¤

params property ¤

value = value instance-attribute ¤

__init__(num_output_units, *, log_space=False, value) ¤

Initializes a constant value layer.

Parameters:

Name Type Description Default
num_output_units int

The number of output log partition functions.

required
log_space bool

Whether the given value is in the log-space, i.e., this constant layer should encode exp(value) rather than value.

False
value Parameter

The symbolic parameter representing the encoded value. This symbolic paramater should have shape (K,), where K is the number of output units.

required
Source code in cirkit/symbolic/layers.py
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
def __init__(self, num_output_units: int, *, log_space: bool = False, value: Parameter):
    """Initializes a constant value layer.

    Args:
        num_output_units: The number of output log partition functions.
        log_space: Whether the given value is in the log-space, i.e., this constant
            layer should encode ```exp(value)``` rather than ```value```.
        value: The symbolic parameter representing the encoded value.
            This symbolic paramater should have shape (K,), where K is the number of
            output units.
    """
    super().__init__(num_output_units)
    if value.shape != self._value_shape:
        raise ValueError(f"Expected parameter shape {self._value_shape}, found {value.shape}")
    self.value = value
    self.log_space = log_space

EmbeddingLayer ¤

Bases: InputLayer

A symbolic Embedding layer, which is parameterized by as many embedding matrices as the number of variables. Each embedding matrix has size \(M\times N\), where \(M\) is the number of output units of the layer, and \(N\) is the number of states each variable can assume.

Source code in cirkit/symbolic/layers.py
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
class EmbeddingLayer(InputLayer):
    r"""A symbolic Embedding layer, which is parameterized by as many embedding matrices as
    the number of variables. Each embedding matrix has size $M\times N$, where $M$ is the number
    of output units of the layer, and $N$ is the number of states each variable can assume.
    """

    def __init__(
        self,
        scope: Scope,
        num_output_units: int,
        num_channels: int,
        *,
        num_states: int = 2,
        weight: Parameter | None = None,
        weight_factory: ParameterFactory | None = None,
    ):
        r"""Initializes an Embedding layer.

        Args:
            scope: The variables scope the layer depends on.
            num_output_units: The number of Categorical units in the layer.
            num_channels: The number of channels per variable.
            num_states: The number of categories for each variable and channel.
            weight: The weight parameter of shape $(K, C, N)$, where $K$ is the number of output
                units, $C$ is the number of channels, and $N$ is the number of states. If it is
                None, then either the weight factory is used (if it is not None) or a
                weight parameter is initialized.
            weight_factory: A factory used to construct the weight parameter,
                if it is not given
        """
        if len(scope) != 1:
            raise ValueError("The Embedding layer encodes univariate functions")
        if num_states <= 1:
            raise ValueError("The number of states must be at least 2")
        super().__init__(scope, num_output_units, num_channels)
        self.num_states = num_states
        if weight is None:
            if weight_factory is None:
                weight = Parameter.from_input(
                    TensorParameter(*self._weight_shape, initializer=NormalInitializer())
                )
            else:
                weight = weight_factory(self._weight_shape)
        if weight.shape != self._weight_shape:
            raise ValueError(f"Expected parameter shape {self._weight_shape}, found {weight.shape}")
        self.weight = weight

    @property
    def _weight_shape(self) -> tuple[int, ...]:
        return (
            self.num_output_units,
            self.num_channels,
            self.num_states,
        )

    @property
    def config(self) -> Mapping[str, Any]:
        return {
            "scope": self.scope,
            "num_output_units": self.num_output_units,
            "num_channels": self.num_channels,
            "num_states": self.num_states,
        }

    @property
    def params(self) -> Mapping[str, Parameter]:
        return {"weight": self.weight}

_weight_shape property ¤

config property ¤

num_states = num_states instance-attribute ¤

params property ¤

weight = weight instance-attribute ¤

__init__(scope, num_output_units, num_channels, *, num_states=2, weight=None, weight_factory=None) ¤

Initializes an Embedding layer.

Parameters:

Name Type Description Default
scope Scope

The variables scope the layer depends on.

required
num_output_units int

The number of Categorical units in the layer.

required
num_channels int

The number of channels per variable.

required
num_states int

The number of categories for each variable and channel.

2
weight Parameter | None

The weight parameter of shape \((K, C, N)\), where \(K\) is the number of output units, \(C\) is the number of channels, and \(N\) is the number of states. If it is None, then either the weight factory is used (if it is not None) or a weight parameter is initialized.

None
weight_factory ParameterFactory | None

A factory used to construct the weight parameter, if it is not given

None
Source code in cirkit/symbolic/layers.py
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
def __init__(
    self,
    scope: Scope,
    num_output_units: int,
    num_channels: int,
    *,
    num_states: int = 2,
    weight: Parameter | None = None,
    weight_factory: ParameterFactory | None = None,
):
    r"""Initializes an Embedding layer.

    Args:
        scope: The variables scope the layer depends on.
        num_output_units: The number of Categorical units in the layer.
        num_channels: The number of channels per variable.
        num_states: The number of categories for each variable and channel.
        weight: The weight parameter of shape $(K, C, N)$, where $K$ is the number of output
            units, $C$ is the number of channels, and $N$ is the number of states. If it is
            None, then either the weight factory is used (if it is not None) or a
            weight parameter is initialized.
        weight_factory: A factory used to construct the weight parameter,
            if it is not given
    """
    if len(scope) != 1:
        raise ValueError("The Embedding layer encodes univariate functions")
    if num_states <= 1:
        raise ValueError("The number of states must be at least 2")
    super().__init__(scope, num_output_units, num_channels)
    self.num_states = num_states
    if weight is None:
        if weight_factory is None:
            weight = Parameter.from_input(
                TensorParameter(*self._weight_shape, initializer=NormalInitializer())
            )
        else:
            weight = weight_factory(self._weight_shape)
    if weight.shape != self._weight_shape:
        raise ValueError(f"Expected parameter shape {self._weight_shape}, found {weight.shape}")
    self.weight = weight

EvidenceLayer ¤

Bases: ConstantLayer

The symbolic layer computing the output of an input layer given by a complete observation. The only parameter of an evidence layer is a complete observation of the variables.

Source code in cirkit/symbolic/layers.py
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
class EvidenceLayer(ConstantLayer):
    """The symbolic layer computing the output of an input layer given by a complete observation.
    The only parameter of an evidence layer is a complete observation of the variables."""

    def __init__(self, layer: InputLayer, *, observation: Parameter):
        r"""Initializes a symbolic evidence layer.

        Args:
            layer: The symbolic input layer to condition, i.e., to evaluate on the observation.
            observation: The observation stored as a parameter that outputs a constant (i.e.,
                non-learnable) tensor of shape $(C, D)$, where $D$ is the number of variable the
                symbolic input layer is defined on, and $C$ is the number of channels per variable.

        Raises:
            ValueError: If the observation parameter shape has not two dimensions, or if the
                number of its channels (resp. variables) does not match the number of channels
                (resp. variables) of the symbolic input layer.
        """
        if len(observation.shape) != 2:
            raise ValueError(
                f"Expected observation of shape (num_channels, num_variables), "
                f"but found {observation.shape}"
            )
        num_channels, num_variables = observation.shape
        if num_channels != layer.num_channels:
            raise ValueError(
                f"Expected an observation with number of channels {layer.num_channels}, "
                f"but found {num_channels}"
            )
        if num_variables != layer.num_variables:
            raise ValueError(
                f"Expected an observation with number of variables {layer.num_variables}, "
                f"but found {num_variables}"
            )
        super().__init__(layer.num_output_units)
        self.layer = layer
        self.observation = observation

    @property
    def config(self) -> Mapping[str, Any]:
        return {"layer": self.layer}

    @property
    def params(self) -> Mapping[str, Parameter]:
        return {"observation": self.observation}

config property ¤

layer = layer instance-attribute ¤

observation = observation instance-attribute ¤

params property ¤

__init__(layer, *, observation) ¤

Initializes a symbolic evidence layer.

Parameters:

Name Type Description Default
layer InputLayer

The symbolic input layer to condition, i.e., to evaluate on the observation.

required
observation Parameter

The observation stored as a parameter that outputs a constant (i.e., non-learnable) tensor of shape \((C, D)\), where \(D\) is the number of variable the symbolic input layer is defined on, and \(C\) is the number of channels per variable.

required

Raises:

Type Description
ValueError

If the observation parameter shape has not two dimensions, or if the number of its channels (resp. variables) does not match the number of channels (resp. variables) of the symbolic input layer.

Source code in cirkit/symbolic/layers.py
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
def __init__(self, layer: InputLayer, *, observation: Parameter):
    r"""Initializes a symbolic evidence layer.

    Args:
        layer: The symbolic input layer to condition, i.e., to evaluate on the observation.
        observation: The observation stored as a parameter that outputs a constant (i.e.,
            non-learnable) tensor of shape $(C, D)$, where $D$ is the number of variable the
            symbolic input layer is defined on, and $C$ is the number of channels per variable.

    Raises:
        ValueError: If the observation parameter shape has not two dimensions, or if the
            number of its channels (resp. variables) does not match the number of channels
            (resp. variables) of the symbolic input layer.
    """
    if len(observation.shape) != 2:
        raise ValueError(
            f"Expected observation of shape (num_channels, num_variables), "
            f"but found {observation.shape}"
        )
    num_channels, num_variables = observation.shape
    if num_channels != layer.num_channels:
        raise ValueError(
            f"Expected an observation with number of channels {layer.num_channels}, "
            f"but found {num_channels}"
        )
    if num_variables != layer.num_variables:
        raise ValueError(
            f"Expected an observation with number of variables {layer.num_variables}, "
            f"but found {num_variables}"
        )
    super().__init__(layer.num_output_units)
    self.layer = layer
    self.observation = observation

GaussianLayer ¤

Bases: InputLayer

A symbolic Gaussian layer, which is parameterized by mean and standard deviations. Optionally, it can represent an unnormalized Gaussian layer by specifying the log partition function.

Source code in cirkit/symbolic/layers.py
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
class GaussianLayer(InputLayer):
    """A symbolic Gaussian layer, which is parameterized by mean and standard deviations.
    Optionally, it can represent an unnormalized Gaussian layer by specifying the log partition
    function."""

    def __init__(
        self,
        scope: Scope,
        num_output_units: int,
        num_channels: int,
        *,
        mean: Parameter | None = None,
        stddev: Parameter | None = None,
        log_partition: Parameter | None = None,
        mean_factory: ParameterFactory | None = None,
        stddev_factory: ParameterFactory | None = None,
    ):
        r"""Initializes a Gaussian layer.

        Args:
            scope: The variables scope the layer depends on.
            num_output_units: The number of Gaussian units in the layer.
            num_channels: The number of channels per variable.
            mean: The mean parameter of shape $(K, C)$, where $K$ is the number of output units, and
                $C$ is the number of channels. If it is None, then a default symbolic parameter will
                be instantiated with a
                [NormalInitializer][cirkit.symbolic.initializers.NormalInitializer] as
                symbolic initializer.
            stddev: The standard deviation parameter of shape $(K, C)$, where $K$ is the number of
                output units, and $C$ is the number of channels. If it is None, then a default
                symbolic parameter will be instantiated with a
                [NormalInitializer][cirkit.symbolic.initializers.NormalInitializer] as
                symbolic initializer, which is then re-parameterized to be positve using a
                [ScaledSigmoidParameter][cirkit.symbolic.parameters.ScaledSigmoidParameter].
            mean: A factory used to construct the mean parameter, if it is not specified.
            stddev: A factory used to construct the standard deviation parameter, if it is not
                specified.
        """
        if len(scope) != 1:
            raise ValueError("The Gaussian layer encodes a univariate distribution")
        super().__init__(scope, num_output_units, num_channels)
        if mean is None:
            if mean_factory is None:
                mean = Parameter.from_input(
                    TensorParameter(*self._mean_stddev_shape, initializer=NormalInitializer())
                )
            else:
                mean = mean_factory(self._mean_stddev_shape)
        if stddev is None:
            if stddev_factory is None:
                stddev = Parameter.from_unary(
                    ScaledSigmoidParameter(self._mean_stddev_shape, vmin=1e-5, vmax=1.0),
                    TensorParameter(*self._mean_stddev_shape, initializer=NormalInitializer()),
                )
            else:
                stddev = stddev_factory(self._mean_stddev_shape)
        if mean.shape != self._mean_stddev_shape:
            raise ValueError(
                f"Expected parameter shape {self._mean_stddev_shape}, found {mean.shape}"
            )
        if stddev.shape != self._mean_stddev_shape:
            raise ValueError(
                f"Expected parameter shape {self._mean_stddev_shape}, found {stddev.shape}"
            )
        if log_partition is not None and log_partition.shape != self._log_partition_shape:
            raise ValueError(
                f"Expected parameter shape {self._log_partition_shape}, found {log_partition.shape}"
            )
        self.mean = mean
        self.stddev = stddev
        self.log_partition = log_partition

    @property
    def _mean_stddev_shape(self) -> tuple[int, ...]:
        return self.num_output_units, self.num_channels

    @property
    def _log_partition_shape(self) -> tuple[int, ...]:
        return self.num_output_units, self.num_channels

    @property
    def config(self) -> Mapping[str, Any]:
        return {
            "scope": self.scope,
            "num_output_units": self.num_output_units,
            "num_channels": self.num_channels,
        }

    @property
    def params(self) -> Mapping[str, Parameter]:
        params = {"mean": self.mean, "stddev": self.stddev}
        if self.log_partition is not None:
            params.update(log_partition=self.log_partition)
        return params

_log_partition_shape property ¤

_mean_stddev_shape property ¤

config property ¤

log_partition = log_partition instance-attribute ¤

mean = mean instance-attribute ¤

params property ¤

stddev = stddev instance-attribute ¤

__init__(scope, num_output_units, num_channels, *, mean=None, stddev=None, log_partition=None, mean_factory=None, stddev_factory=None) ¤

Initializes a Gaussian layer.

Parameters:

Name Type Description Default
scope Scope

The variables scope the layer depends on.

required
num_output_units int

The number of Gaussian units in the layer.

required
num_channels int

The number of channels per variable.

required
mean Parameter | None

The mean parameter of shape \((K, C)\), where \(K\) is the number of output units, and \(C\) is the number of channels. If it is None, then a default symbolic parameter will be instantiated with a NormalInitializer as symbolic initializer.

None
stddev Parameter | None

The standard deviation parameter of shape \((K, C)\), where \(K\) is the number of output units, and \(C\) is the number of channels. If it is None, then a default symbolic parameter will be instantiated with a NormalInitializer as symbolic initializer, which is then re-parameterized to be positve using a ScaledSigmoidParameter.

None
mean Parameter | None

A factory used to construct the mean parameter, if it is not specified.

None
stddev Parameter | None

A factory used to construct the standard deviation parameter, if it is not specified.

None
Source code in cirkit/symbolic/layers.py
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
def __init__(
    self,
    scope: Scope,
    num_output_units: int,
    num_channels: int,
    *,
    mean: Parameter | None = None,
    stddev: Parameter | None = None,
    log_partition: Parameter | None = None,
    mean_factory: ParameterFactory | None = None,
    stddev_factory: ParameterFactory | None = None,
):
    r"""Initializes a Gaussian layer.

    Args:
        scope: The variables scope the layer depends on.
        num_output_units: The number of Gaussian units in the layer.
        num_channels: The number of channels per variable.
        mean: The mean parameter of shape $(K, C)$, where $K$ is the number of output units, and
            $C$ is the number of channels. If it is None, then a default symbolic parameter will
            be instantiated with a
            [NormalInitializer][cirkit.symbolic.initializers.NormalInitializer] as
            symbolic initializer.
        stddev: The standard deviation parameter of shape $(K, C)$, where $K$ is the number of
            output units, and $C$ is the number of channels. If it is None, then a default
            symbolic parameter will be instantiated with a
            [NormalInitializer][cirkit.symbolic.initializers.NormalInitializer] as
            symbolic initializer, which is then re-parameterized to be positve using a
            [ScaledSigmoidParameter][cirkit.symbolic.parameters.ScaledSigmoidParameter].
        mean: A factory used to construct the mean parameter, if it is not specified.
        stddev: A factory used to construct the standard deviation parameter, if it is not
            specified.
    """
    if len(scope) != 1:
        raise ValueError("The Gaussian layer encodes a univariate distribution")
    super().__init__(scope, num_output_units, num_channels)
    if mean is None:
        if mean_factory is None:
            mean = Parameter.from_input(
                TensorParameter(*self._mean_stddev_shape, initializer=NormalInitializer())
            )
        else:
            mean = mean_factory(self._mean_stddev_shape)
    if stddev is None:
        if stddev_factory is None:
            stddev = Parameter.from_unary(
                ScaledSigmoidParameter(self._mean_stddev_shape, vmin=1e-5, vmax=1.0),
                TensorParameter(*self._mean_stddev_shape, initializer=NormalInitializer()),
            )
        else:
            stddev = stddev_factory(self._mean_stddev_shape)
    if mean.shape != self._mean_stddev_shape:
        raise ValueError(
            f"Expected parameter shape {self._mean_stddev_shape}, found {mean.shape}"
        )
    if stddev.shape != self._mean_stddev_shape:
        raise ValueError(
            f"Expected parameter shape {self._mean_stddev_shape}, found {stddev.shape}"
        )
    if log_partition is not None and log_partition.shape != self._log_partition_shape:
        raise ValueError(
            f"Expected parameter shape {self._log_partition_shape}, found {log_partition.shape}"
        )
    self.mean = mean
    self.stddev = stddev
    self.log_partition = log_partition

HadamardLayer ¤

Bases: ProductLayer

The symbolic element-wise product (or Hadamard) layer. This layer computes the element-wise product of the vectors given in output by some input layers. Therefore, the number of product units in the layer is equal to the number of units in each input layer.

Source code in cirkit/symbolic/layers.py
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
class HadamardLayer(ProductLayer):
    """The symbolic element-wise product (or Hadamard) layer. This layer computes the element-wise
    product of the vectors given in output by some input layers. Therefore, the number of product
    units in the layer is equal to the number of units in each input layer."""

    def __init__(self, num_input_units: int, arity: int = 2):
        """Initializes a Hadamard product layer.

        Args:
            num_input_units: The number of units in each input layer.
            arity: The arity of the layer, i.e., the number of input layers to the product layer.

        Raises:
            ValueError: If the arity is less than two.
        """
        super().__init__(num_input_units, num_input_units, arity=arity)

    @property
    def config(self) -> Mapping[str, Any]:
        return {"num_input_units": self.num_input_units, "arity": self.arity}

config property ¤

__init__(num_input_units, arity=2) ¤

Initializes a Hadamard product layer.

Parameters:

Name Type Description Default
num_input_units int

The number of units in each input layer.

required
arity int

The arity of the layer, i.e., the number of input layers to the product layer.

2

Raises:

Type Description
ValueError

If the arity is less than two.

Source code in cirkit/symbolic/layers.py
687
688
689
690
691
692
693
694
695
696
697
def __init__(self, num_input_units: int, arity: int = 2):
    """Initializes a Hadamard product layer.

    Args:
        num_input_units: The number of units in each input layer.
        arity: The arity of the layer, i.e., the number of input layers to the product layer.

    Raises:
        ValueError: If the arity is less than two.
    """
    super().__init__(num_input_units, num_input_units, arity=arity)

InputLayer ¤

Bases: Layer, ABC

The symbolic input layer class.

Source code in cirkit/symbolic/layers.py
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
class InputLayer(Layer, ABC):
    """The symbolic input layer class."""

    def __init__(self, scope: Scope, num_output_units: int, num_channels: int = 1):
        """Initializes a symbolic input layer.

        Args:
            scope: The variables scope of the layer.
            num_output_units: The number of input units in the layer.
            num_channels: The number of channels for each variable in the scope.

        Raises:
            ValueError: If the number of outputs or the number of channels are not positive.
        """
        if num_output_units <= 0:
            raise ValueError("The number of output units should be positive")
        if num_channels <= 0:
            raise ValueError("The number of channels should be positive")
        super().__init__(len(scope), num_output_units, num_channels)
        self.scope = scope

    @property
    def num_variables(self) -> int:
        """The number of variables modelled by the input layer.

        Returns:
            int: The number of variables in the scope.
        """
        return self.num_input_units

    @property
    def num_channels(self) -> int:
        """The number of channels per variable modelled by the input layer.

        Returns:
            int: The number of channels per variable.
        """
        return self.arity

    def __repr__(self) -> str:
        config_repr = ", ".join(f"{k}={v}" for k, v in self.config.items())
        params_repr = ", ".join(f"{k}={v}" for k, v in self.params.items())
        return (
            f"{self.__class__.__name__}("
            f"scope={self.scope}, "
            f"num_channels={self.arity}, "
            f"num_output_units={self.num_output_units}, "
            f"config=({config_repr})"
            f"params=({params_repr})"
            ")"
        )

num_channels property ¤

The number of channels per variable modelled by the input layer.

Returns:

Name Type Description
int int

The number of channels per variable.

num_variables property ¤

The number of variables modelled by the input layer.

Returns:

Name Type Description
int int

The number of variables in the scope.

scope = scope instance-attribute ¤

__init__(scope, num_output_units, num_channels=1) ¤

Initializes a symbolic input layer.

Parameters:

Name Type Description Default
scope Scope

The variables scope of the layer.

required
num_output_units int

The number of input units in the layer.

required
num_channels int

The number of channels for each variable in the scope.

1

Raises:

Type Description
ValueError

If the number of outputs or the number of channels are not positive.

Source code in cirkit/symbolic/layers.py
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
def __init__(self, scope: Scope, num_output_units: int, num_channels: int = 1):
    """Initializes a symbolic input layer.

    Args:
        scope: The variables scope of the layer.
        num_output_units: The number of input units in the layer.
        num_channels: The number of channels for each variable in the scope.

    Raises:
        ValueError: If the number of outputs or the number of channels are not positive.
    """
    if num_output_units <= 0:
        raise ValueError("The number of output units should be positive")
    if num_channels <= 0:
        raise ValueError("The number of channels should be positive")
    super().__init__(len(scope), num_output_units, num_channels)
    self.scope = scope

__repr__() ¤

Source code in cirkit/symbolic/layers.py
155
156
157
158
159
160
161
162
163
164
165
166
def __repr__(self) -> str:
    config_repr = ", ".join(f"{k}={v}" for k, v in self.config.items())
    params_repr = ", ".join(f"{k}={v}" for k, v in self.params.items())
    return (
        f"{self.__class__.__name__}("
        f"scope={self.scope}, "
        f"num_channels={self.arity}, "
        f"num_output_units={self.num_output_units}, "
        f"config=({config_repr})"
        f"params=({params_repr})"
        ")"
    )

KroneckerLayer ¤

Bases: ProductLayer

The symbolic outer product (or Kronecker) layer. This layer computes the outer product of the vectors given in output by some input layers. Therefore, the number of product units in the layer is equal to the product of the number of units in each input layer. Note that the output of a Kronecker layer is a vector.

Source code in cirkit/symbolic/layers.py
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
class KroneckerLayer(ProductLayer):
    """The symbolic outer product (or Kronecker) layer. This layer computes the outer
    product of the vectors given in output by some input layers. Therefore, the number of product
    units in the layer is equal to the product of the number of units in each input layer.
    Note that the output of a Kronecker layer is a vector."""

    def __init__(self, num_input_units: int, arity: int = 2):
        """Initializes a Kronecker product layer.

        Args:
            num_input_units: The number of units in each input layer.
            arity: The arity of the layer, i.e., the number of input layers to the product layer.

        Raises:
            ValueError: If the arity is less than two.
        """
        if arity < 2:
            raise ValueError("The arity should be at least 2")
        super().__init__(
            num_input_units,
            cast(int, num_input_units**arity),
            arity=arity,
        )

    @property
    def config(self) -> Mapping[str, Any]:
        return {"num_input_units": self.num_input_units, "arity": self.arity}

config property ¤

__init__(num_input_units, arity=2) ¤

Initializes a Kronecker product layer.

Parameters:

Name Type Description Default
num_input_units int

The number of units in each input layer.

required
arity int

The arity of the layer, i.e., the number of input layers to the product layer.

2

Raises:

Type Description
ValueError

If the arity is less than two.

Source code in cirkit/symbolic/layers.py
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
def __init__(self, num_input_units: int, arity: int = 2):
    """Initializes a Kronecker product layer.

    Args:
        num_input_units: The number of units in each input layer.
        arity: The arity of the layer, i.e., the number of input layers to the product layer.

    Raises:
        ValueError: If the arity is less than two.
    """
    if arity < 2:
        raise ValueError("The arity should be at least 2")
    super().__init__(
        num_input_units,
        cast(int, num_input_units**arity),
        arity=arity,
    )

Layer ¤

Bases: ABC

The symbolic layer class. A symbolic layer consists of useful metadata of input, product and sum layers. A layer that specializes this class must specify two property methods: 1. config(self) -> Mapping[str, Any]: A dictionary mapping the non-parameter arguments to the __init__ method to the corresponding values, e.g., the arity. 2. params(self) -> Mapping[str, Parameter]: A dictionary mapping the parameter arguments the __init__ method to the corresponding symbolic parameter, e.g., the mean and standard deviations symbolic parameters in a GaussianLayer.

Source code in cirkit/symbolic/layers.py
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
class Layer(ABC):
    """The symbolic layer class. A symbolic layer consists of useful metadata of input, product
    and sum layers. A layer that specializes this class must specify two property methods:
        1. config(self) -> Mapping[str, Any]: A dictionary mapping the non-parameter arguments to
            the ```__init__``` method to the corresponding values, e.g., the arity.
        2. params(self) -> Mapping[str, Parameter]: A dictionary mapping the parameter arguments
            the ```__init__``` method to the corresponding symbolic parameter, e.g., the mean and
            standard deviations symbolic parameters in a
            [GaussianLayer][cirkit.symbolic.layers.GaussianLayer].
    """

    def __init__(
        self,
        num_input_units: int,
        num_output_units: int,
        arity: int = 1,
    ):
        """Initializes a symbolic layer.

        Args:
            num_input_units: The number of units in each input layer.
            num_output_units: The number of output units, i.e., the number of computational units
                in this layer.
            arity: The arity of the layer, i.e., the number of input layers to this layer.

        Raises:
            ValueError: If the number of input units, output units or the arity are not positvie.
        """
        if num_input_units < 0:
            raise ValueError("The number of input units should be non-negative")
        if num_output_units <= 0:
            raise ValueError("The number of output units should be positive")
        if arity <= 0:
            raise ValueError("The arity should be positive")
        self.num_input_units = num_input_units
        self.num_output_units = num_output_units
        self.arity = arity

    @property
    @abstractmethod
    def config(self) -> Mapping[str, Any]:
        """Retrieves the configuration of the layer, i.e., a dictionary mapping hyperparameters
        of the layer to their values. The hyperparameter names must match the argument names in
        the ```__init__``` method.

        Returns:
            Mapping[str, Any]: A dictionary from hyperparameter names to their value.
        """

    @property
    def params(self) -> Mapping[str, Parameter]:
        """Retrieve the symbolic parameters of the layer, i.e., a dictionary mapping the names of
        the symbolic parameters to the actual symbolic parameter instance. The parameter names must
        match the argument names in the```__init__``` method.

        Returns:
            Mapping[str, Parameter]: A dictionary from parameter names to the corresponding symbolic
                parameter instance.
        """
        return {}

    def copyref(self) -> "Layer":
        """Creates a _shallow_ copy of the layer, i.e., a copy where the symbolic parameters
        are copied by reference, thus effectively creating a symbolic parameter sharing between
        the new layer and the layer being copied.

        Returns:
            A shallow copy of the layer, with reference to the parameters.
        """
        ref_params = {pname: pgraph.ref() for pname, pgraph in self.params.items()}
        return type(self)(**self.config, **ref_params)

    def __repr__(self) -> str:
        config_repr = ", ".join(f"{k}={v}" for k, v in self.config.items())
        params_repr = ", ".join(f"{k}={v}" for k, v in self.params.items())
        return (
            f"{self.__class__.__name__}("
            f"num_input_units={self.num_input_units}, "
            f"num_output_units={self.num_output_units}, "
            f"arity={self.arity}, "
            f"config=({config_repr}), "
            f"params=({params_repr})"
        )

arity = arity instance-attribute ¤

config abstractmethod property ¤

Retrieves the configuration of the layer, i.e., a dictionary mapping hyperparameters of the layer to their values. The hyperparameter names must match the argument names in the __init__ method.

Returns:

Type Description
Mapping[str, Any]

Mapping[str, Any]: A dictionary from hyperparameter names to their value.

num_input_units = num_input_units instance-attribute ¤

num_output_units = num_output_units instance-attribute ¤

params property ¤

Retrieve the symbolic parameters of the layer, i.e., a dictionary mapping the names of the symbolic parameters to the actual symbolic parameter instance. The parameter names must match the argument names in the__init__ method.

Returns:

Type Description
Mapping[str, Parameter]

Mapping[str, Parameter]: A dictionary from parameter names to the corresponding symbolic parameter instance.

__init__(num_input_units, num_output_units, arity=1) ¤

Initializes a symbolic layer.

Parameters:

Name Type Description Default
num_input_units int

The number of units in each input layer.

required
num_output_units int

The number of output units, i.e., the number of computational units in this layer.

required
arity int

The arity of the layer, i.e., the number of input layers to this layer.

1

Raises:

Type Description
ValueError

If the number of input units, output units or the arity are not positvie.

Source code in cirkit/symbolic/layers.py
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
def __init__(
    self,
    num_input_units: int,
    num_output_units: int,
    arity: int = 1,
):
    """Initializes a symbolic layer.

    Args:
        num_input_units: The number of units in each input layer.
        num_output_units: The number of output units, i.e., the number of computational units
            in this layer.
        arity: The arity of the layer, i.e., the number of input layers to this layer.

    Raises:
        ValueError: If the number of input units, output units or the arity are not positvie.
    """
    if num_input_units < 0:
        raise ValueError("The number of input units should be non-negative")
    if num_output_units <= 0:
        raise ValueError("The number of output units should be positive")
    if arity <= 0:
        raise ValueError("The arity should be positive")
    self.num_input_units = num_input_units
    self.num_output_units = num_output_units
    self.arity = arity

__repr__() ¤

Source code in cirkit/symbolic/layers.py
103
104
105
106
107
108
109
110
111
112
113
def __repr__(self) -> str:
    config_repr = ", ".join(f"{k}={v}" for k, v in self.config.items())
    params_repr = ", ".join(f"{k}={v}" for k, v in self.params.items())
    return (
        f"{self.__class__.__name__}("
        f"num_input_units={self.num_input_units}, "
        f"num_output_units={self.num_output_units}, "
        f"arity={self.arity}, "
        f"config=({config_repr}), "
        f"params=({params_repr})"
    )

copyref() ¤

Creates a shallow copy of the layer, i.e., a copy where the symbolic parameters are copied by reference, thus effectively creating a symbolic parameter sharing between the new layer and the layer being copied.

Returns:

Type Description
Layer

A shallow copy of the layer, with reference to the parameters.

Source code in cirkit/symbolic/layers.py
 92
 93
 94
 95
 96
 97
 98
 99
100
101
def copyref(self) -> "Layer":
    """Creates a _shallow_ copy of the layer, i.e., a copy where the symbolic parameters
    are copied by reference, thus effectively creating a symbolic parameter sharing between
    the new layer and the layer being copied.

    Returns:
        A shallow copy of the layer, with reference to the parameters.
    """
    ref_params = {pname: pgraph.ref() for pname, pgraph in self.params.items()}
    return type(self)(**self.config, **ref_params)

LayerOperator ¤

Bases: IntEnum

The avaliable symbolic operators defined over layers.

Source code in cirkit/symbolic/layers.py
18
19
20
21
22
23
24
25
26
27
28
class LayerOperator(IntEnum):
    """The avaliable symbolic operators defined over layers."""

    INTEGRATION = auto()
    """The integration operator defined over input layers."""
    DIFFERENTIATION = auto()
    """The differentiation operator defined over layers."""
    MULTIPLICATION = auto()
    """The multiplication (Kronecker product) operator defined over layers."""
    CONJUGATION = auto()
    """The conjugation opereator defined over sum and input layers."""

CONJUGATION = auto() class-attribute instance-attribute ¤

The conjugation opereator defined over sum and input layers.

DIFFERENTIATION = auto() class-attribute instance-attribute ¤

The differentiation operator defined over layers.

INTEGRATION = auto() class-attribute instance-attribute ¤

The integration operator defined over input layers.

MULTIPLICATION = auto() class-attribute instance-attribute ¤

The multiplication (Kronecker product) operator defined over layers.

PolynomialLayer ¤

Bases: InputLayer

A symbolic layer that evaluates polynomials.

Source code in cirkit/symbolic/layers.py
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
class PolynomialLayer(InputLayer):
    """A symbolic layer that evaluates polynomials."""

    def __init__(
        self,
        scope: Scope,
        num_output_units: int,
        num_channels: int,
        *,
        degree: int,
        coeff: Parameter | None = None,
        coeff_factory: ParameterFactory | None = None,
    ):
        r"""Initializes a polynomial layer,

        Args:
            scope: The variables scope the layer depends on.
            num_output_units: The number of units each encoding a polynomial in the layer.
            num_channels: The number of channels per variable.
            degree: The degree of the polynomials.
            coeff: The coefficient parameter of shape $(K, \mathsf{degree} + 1)$, where $K$ is the
                number of output units. If it is None, then either the coefficient factory
                is used (if it not None), or a default
                symbolic parameter will be instantiated with a
                [NormalInitializer][cirkit.symbolic.initializers.NormalInitializer] as
                symbolic initializer.
            coeff_factory: A factory used to construct the coeff parameter, if it is not specified.
        """
        if len(scope) != 1:
            raise ValueError("The Polynomial layer encodes univariate functions")
        super().__init__(scope, num_output_units, num_channels)
        self.degree = degree
        if coeff is None:
            if coeff_factory is None:
                coeff = Parameter.from_input(
                    TensorParameter(*self._coeff_shape, initializer=NormalInitializer())
                )
            else:
                coeff = coeff_factory(self._coeff_shape)
        if coeff.shape != self._coeff_shape:
            raise ValueError(f"Expected parameter shape {self._coeff_shape}, found {coeff.shape}")
        self.coeff = coeff

    @property
    def _coeff_shape(self) -> tuple[int, ...]:
        return self.num_output_units, self.degree + 1

    @property
    def config(self) -> Mapping[str, Any]:
        return {
            "scope": self.scope,
            "num_output_units": self.num_output_units,
            "num_channels": self.num_channels,
            "degree": self.degree,
        }

    @property
    def params(self) -> Mapping[str, Parameter]:
        return {"coeff": self.coeff}

_coeff_shape property ¤

coeff = coeff instance-attribute ¤

config property ¤

degree = degree instance-attribute ¤

params property ¤

__init__(scope, num_output_units, num_channels, *, degree, coeff=None, coeff_factory=None) ¤

Initializes a polynomial layer,

Parameters:

Name Type Description Default
scope Scope

The variables scope the layer depends on.

required
num_output_units int

The number of units each encoding a polynomial in the layer.

required
num_channels int

The number of channels per variable.

required
degree int

The degree of the polynomials.

required
coeff Parameter | None

The coefficient parameter of shape \((K, \mathsf{degree} + 1)\), where \(K\) is the number of output units. If it is None, then either the coefficient factory is used (if it not None), or a default symbolic parameter will be instantiated with a NormalInitializer as symbolic initializer.

None
coeff_factory ParameterFactory | None

A factory used to construct the coeff parameter, if it is not specified.

None
Source code in cirkit/symbolic/layers.py
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
def __init__(
    self,
    scope: Scope,
    num_output_units: int,
    num_channels: int,
    *,
    degree: int,
    coeff: Parameter | None = None,
    coeff_factory: ParameterFactory | None = None,
):
    r"""Initializes a polynomial layer,

    Args:
        scope: The variables scope the layer depends on.
        num_output_units: The number of units each encoding a polynomial in the layer.
        num_channels: The number of channels per variable.
        degree: The degree of the polynomials.
        coeff: The coefficient parameter of shape $(K, \mathsf{degree} + 1)$, where $K$ is the
            number of output units. If it is None, then either the coefficient factory
            is used (if it not None), or a default
            symbolic parameter will be instantiated with a
            [NormalInitializer][cirkit.symbolic.initializers.NormalInitializer] as
            symbolic initializer.
        coeff_factory: A factory used to construct the coeff parameter, if it is not specified.
    """
    if len(scope) != 1:
        raise ValueError("The Polynomial layer encodes univariate functions")
    super().__init__(scope, num_output_units, num_channels)
    self.degree = degree
    if coeff is None:
        if coeff_factory is None:
            coeff = Parameter.from_input(
                TensorParameter(*self._coeff_shape, initializer=NormalInitializer())
            )
        else:
            coeff = coeff_factory(self._coeff_shape)
    if coeff.shape != self._coeff_shape:
        raise ValueError(f"Expected parameter shape {self._coeff_shape}, found {coeff.shape}")
    self.coeff = coeff

ProductLayer ¤

Bases: Layer, ABC

The abstract base class for symbolic product layers.

Source code in cirkit/symbolic/layers.py
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
class ProductLayer(Layer, ABC):
    """The abstract base class for symbolic product layers."""

    def __init__(self, num_input_units: int, num_output_units: int, arity: int = 2):
        """Initializes a product layer.

        Args:
            num_input_units: The number of units in each input layer.
            num_output_units: The number of product units in the product layer.
            arity: The arity of the layer, i.e., the number of input layers to the product layer.

        Raises:
            ValueError: If the arity is less than two.
        """
        if arity < 2:
            raise ValueError("The arity should be at least 2")
        super().__init__(num_input_units, num_output_units, arity)

__init__(num_input_units, num_output_units, arity=2) ¤

Initializes a product layer.

Parameters:

Name Type Description Default
num_input_units int

The number of units in each input layer.

required
num_output_units int

The number of product units in the product layer.

required
arity int

The arity of the layer, i.e., the number of input layers to the product layer.

2

Raises:

Type Description
ValueError

If the arity is less than two.

Source code in cirkit/symbolic/layers.py
666
667
668
669
670
671
672
673
674
675
676
677
678
679
def __init__(self, num_input_units: int, num_output_units: int, arity: int = 2):
    """Initializes a product layer.

    Args:
        num_input_units: The number of units in each input layer.
        num_output_units: The number of product units in the product layer.
        arity: The arity of the layer, i.e., the number of input layers to the product layer.

    Raises:
        ValueError: If the arity is less than two.
    """
    if arity < 2:
        raise ValueError("The arity should be at least 2")
    super().__init__(num_input_units, num_output_units, arity)

SumLayer ¤

Bases: Layer

The symbolic sum layer. A sum layer computes a matrix-by-vector product \(\mathbf{W} \mathbf{x}\), where \(\mathbf{W}\in\mathbb{R}^{K_1\times HK_2}\), where \(K_1\) is the number of output units, \(K_2\) is the number of input units, and \(H\) is the arity, i.e., the number of layers that are input to the sum layer. In the product \(\mathbf{W} \mathbf{x}\) above, \(\mathbf{x}\) is the vector obtained by concatenating the outputs of all layers that are input to the sum layer. Note that if the arity is exactly 1, then this layer computes a simple linear transformation of an input vector.

Depending on the parameterization of the parameter matrix \(\mathbf{W}\), a different semantics can be set for the sum layer. For instance, if the parameter weight factory is chosen to be the mixing weight factory, then the sum layer computes a weighted linear combination of the input vectors. See the mixing weight factory for more details.

Source code in cirkit/symbolic/layers.py
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
class SumLayer(Layer):
    r"""The symbolic sum layer. A sum layer computes a matrix-by-vector product
    $\mathbf{W} \mathbf{x}$, where $\mathbf{W}\in\mathbb{R}^{K_1\times HK_2}$, where $K_1$ is the number
    of output units, $K_2$ is the number of input units, and $H$ is the arity, i.e., the number of
    layers that are input to the sum layer.
    In the product $\mathbf{W} \mathbf{x}$ above, $\mathbf{x}$ is the vector obtained by
    concatenating the outputs of all layers that are input to the sum layer. Note that if the arity
    is exactly 1, then this layer computes a simple linear transformation of an input vector.

    Depending on the parameterization of the parameter matrix $\mathbf{W}$, a different semantics
    can be set for the sum layer. For instance, if the parameter weight factory is chosen to be the
    [mixing weight factory][cirkit.symbolic.parameters.mixing_weight_factory], then the sum layer
    computes a weighted linear combination of the input vectors. See the
    [mixing weight factory][cirkit.symbolic.parameters.mixing_weight_factory] for more details.
    """

    def __init__(
        self,
        num_input_units: int,
        num_output_units: int,
        arity: int = 1,
        weight: Parameter | None = None,
        weight_factory: ParameterFactory | None = None,
    ):
        r"""Initializes a dense layer.

        Args:
            num_input_units: The number of units of the input layers.
            num_output_units: The number of sum units in the sum layer.
            arity: The arity of the layer, i.e., the number of input layers to the sum layer.
            weight: The symbolic weight matrix parameter, having shape
                $(K_o, K_i \times H)$, where $K_o$ is the number of output units, $K_i$ is the
                number of input units, and $H$ is the arity. It can be None.
            weight_factory: A factory that constructs the symbolic weight matrix parameter,
                if the given weight is None. If this factory is also None, then a weight
                parameter with [NormalInitializer][cirkit.symbolic.initializers.NormalInitializer]
                as initializer will be instantiated.
        """
        super().__init__(num_input_units, num_output_units, arity=arity)
        if weight is None:
            if weight_factory is None:
                weight = Parameter.from_input(
                    TensorParameter(*self._weight_shape, initializer=NormalInitializer())
                )
            else:
                weight = weight_factory(self._weight_shape)
        if weight.shape != self._weight_shape:
            raise ValueError(f"Expected parameter shape {self._weight_shape}, found {weight.shape}")
        self.weight = weight

    @property
    def _weight_shape(self) -> tuple[int, ...]:
        return self.num_output_units, self.arity * self.num_input_units

    @property
    def config(self) -> Mapping[str, Any]:
        return {
            "num_input_units": self.num_input_units,
            "num_output_units": self.num_output_units,
            "arity": self.arity,
        }

    @property
    def params(self) -> Mapping[str, Parameter]:
        return {"weight": self.weight}

_weight_shape property ¤

config property ¤

params property ¤

weight = weight instance-attribute ¤

__init__(num_input_units, num_output_units, arity=1, weight=None, weight_factory=None) ¤

Initializes a dense layer.

Parameters:

Name Type Description Default
num_input_units int

The number of units of the input layers.

required
num_output_units int

The number of sum units in the sum layer.

required
arity int

The arity of the layer, i.e., the number of input layers to the sum layer.

1
weight Parameter | None

The symbolic weight matrix parameter, having shape \((K_o, K_i \times H)\), where \(K_o\) is the number of output units, \(K_i\) is the number of input units, and \(H\) is the arity. It can be None.

None
weight_factory ParameterFactory | None

A factory that constructs the symbolic weight matrix parameter, if the given weight is None. If this factory is also None, then a weight parameter with NormalInitializer as initializer will be instantiated.

None
Source code in cirkit/symbolic/layers.py
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
def __init__(
    self,
    num_input_units: int,
    num_output_units: int,
    arity: int = 1,
    weight: Parameter | None = None,
    weight_factory: ParameterFactory | None = None,
):
    r"""Initializes a dense layer.

    Args:
        num_input_units: The number of units of the input layers.
        num_output_units: The number of sum units in the sum layer.
        arity: The arity of the layer, i.e., the number of input layers to the sum layer.
        weight: The symbolic weight matrix parameter, having shape
            $(K_o, K_i \times H)$, where $K_o$ is the number of output units, $K_i$ is the
            number of input units, and $H$ is the arity. It can be None.
        weight_factory: A factory that constructs the symbolic weight matrix parameter,
            if the given weight is None. If this factory is also None, then a weight
            parameter with [NormalInitializer][cirkit.symbolic.initializers.NormalInitializer]
            as initializer will be instantiated.
    """
    super().__init__(num_input_units, num_output_units, arity=arity)
    if weight is None:
        if weight_factory is None:
            weight = Parameter.from_input(
                TensorParameter(*self._weight_shape, initializer=NormalInitializer())
            )
        else:
            weight = weight_factory(self._weight_shape)
    if weight.shape != self._weight_shape:
        raise ValueError(f"Expected parameter shape {self._weight_shape}, found {weight.shape}")
    self.weight = weight