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
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
384
385
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
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,
        *,
        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.
            total_count: The number of total counts for each variable.
            logits: The logits parameter of shape $(K,)$, where $K$ is the number of output
                units. 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,)$ (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)
        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,)

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

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

config property ¤

logits = logits instance-attribute ¤

params property ¤

probs = probs instance-attribute ¤

total_count = total_count instance-attribute ¤

__init__(scope, num_output_units, *, 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
total_count int

The number of total counts for each variable.

2
logits Parameter | None

The logits parameter of shape \((K,)\), where \(K\) is the number of output units. 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,)\) (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
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
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
def __init__(
    self,
    scope: Scope,
    num_output_units: int,
    *,
    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.
        total_count: The number of total counts for each variable.
        logits: The logits parameter of shape $(K,)$, where $K$ is the number of output
            units. 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,)$ (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)
    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
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
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
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_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_categories: The number of categories for each variable.
            logits: The logits parameter of shape $(K, N)$, where $K$ is the number of output
                units, 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, 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)
        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_categories

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

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

config property ¤

logits = logits instance-attribute ¤

num_categories = num_categories instance-attribute ¤

params property ¤

probs = probs instance-attribute ¤

__init__(scope, num_output_units, *, 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_categories int

The number of categories for each variable.

required
logits Parameter | None

The logits parameter of shape \((K, N)\), where \(K\) is the number of output units, 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, 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
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
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
def __init__(
    self,
    scope: Scope,
    num_output_units: int,
    *,
    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_categories: The number of categories for each variable.
        logits: The logits parameter of shape $(K, N)$, where $K$ is the number of output
            units, 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, 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)
    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
158
159
160
161
162
163
164
165
166
167
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
161
162
163
164
165
166
167
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
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
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}

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
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
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
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
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
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_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_states: The number of categories for each variable.
            weight: The weight parameter of shape $(K, N)$, where $K$ is the number of output
                units, 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)
        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_states,
        )

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

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

config property ¤

num_states = num_states instance-attribute ¤

params property ¤

weight = weight instance-attribute ¤

__init__(scope, num_output_units, *, 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_states int

The number of categories for each variable.

2
weight Parameter | None

The weight parameter of shape \((K, N)\), where \(K\) is the number of output units, 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
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
def __init__(
    self,
    scope: Scope,
    num_output_units: 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_states: The number of categories for each variable.
        weight: The weight parameter of shape $(K, N)$, where $K$ is the number of output
            units, 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)
    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
170
171
172
173
174
175
176
177
178
179
180
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
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 $(D,)$, where $D$ is the number of variable the
                symbolic input layer is defined on.

        Raises:
            ValueError: If the observation parameter shape has not two dimensions.
        """
        if len(observation.shape) != 1:
            raise ValueError(
                f"Expected observation of shape (num_variables,), " f"but found {observation.shape}"
            )
        if observation.shape[0] != layer.num_variables:
            raise ValueError(
                f"Expected an observation with number of variables {layer.num_variables}, "
                f"but found {observation.shape[0]}"
            )
        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 \((D,)\), where \(D\) is the number of variable the symbolic input layer is defined on.

required

Raises:

Type Description
ValueError

If the observation parameter shape has not two dimensions.

Source code in cirkit/symbolic/layers.py
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
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 $(D,)$, where $D$ is the number of variable the
            symbolic input layer is defined on.

    Raises:
        ValueError: If the observation parameter shape has not two dimensions.
    """
    if len(observation.shape) != 1:
        raise ValueError(
            f"Expected observation of shape (num_variables,), " f"but found {observation.shape}"
        )
    if observation.shape[0] != layer.num_variables:
        raise ValueError(
            f"Expected an observation with number of variables {layer.num_variables}, "
            f"but found {observation.shape[0]}"
        )
    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
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
471
472
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
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,
        *,
        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.
            mean: The mean parameter of shape $(K)$, where $K$ is the number of output units.
                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)$, where $K$ is the number of
                output units. 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].
            log_partition: The log-partition parameter of the Gaussian, of shape $(K,)$.
                If the Gaussian is a normalized Gaussian, then this should be None.
            mean_factory: A factory used to construct the mean parameter, if it is not specified.
            stddev_factory: 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)
        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,)

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

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

    @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

config property ¤

log_partition = log_partition instance-attribute ¤

mean = mean instance-attribute ¤

params property ¤

stddev = stddev instance-attribute ¤

__init__(scope, num_output_units, *, 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
mean Parameter | None

The mean parameter of shape \((K)\), where \(K\) is the number of output units. 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)\), where \(K\) is the number of output units. 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
log_partition Parameter | None

The log-partition parameter of the Gaussian, of shape \((K,)\). If the Gaussian is a normalized Gaussian, then this should be None.

None
mean_factory ParameterFactory | None

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

None
stddev_factory ParameterFactory | None

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

None
Source code in cirkit/symbolic/layers.py
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
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
def __init__(
    self,
    scope: Scope,
    num_output_units: 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.
        mean: The mean parameter of shape $(K)$, where $K$ is the number of output units.
            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)$, where $K$ is the number of
            output units. 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].
        log_partition: The log-partition parameter of the Gaussian, of shape $(K,)$.
            If the Gaussian is a normalized Gaussian, then this should be None.
        mean_factory: A factory used to construct the mean parameter, if it is not specified.
        stddev_factory: 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)
    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
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
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
647
648
649
650
651
652
653
654
655
656
657
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
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
class InputLayer(Layer, ABC):
    """The symbolic input layer class."""

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

        Args:
            scope: The variables scope of the layer.
            num_output_units: The number of input units in the layer.

        Raises:
            ValueError: If the number of outputs is not positive.
        """
        if num_output_units <= 0:
            raise ValueError("The number of output units should be positive")
        super().__init__(len(scope), num_output_units)
        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

    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_output_units={self.num_output_units}, "
            f"config=({config_repr}), "
            f"params=({params_repr})"
            ")"
        )

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) ¤

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

Raises:

Type Description
ValueError

If the number of outputs is not positive.

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

    Args:
        scope: The variables scope of the layer.
        num_output_units: The number of input units in the layer.

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

__repr__() ¤

Source code in cirkit/symbolic/layers.py
145
146
147
148
149
150
151
152
153
154
155
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_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
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
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
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
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
 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
114
115
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) -> Self:
        """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.
        """
        kwargs: dict[str, Any] = {pname: pgraph.ref() for pname, pgraph in self.params.items()}
        kwargs.update(self.config)
        return type(self)(**kwargs)

    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
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
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
105
106
107
108
109
110
111
112
113
114
115
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
Self

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

Source code in cirkit/symbolic/layers.py
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
def copyref(self) -> Self:
    """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.
    """
    kwargs: dict[str, Any] = {pname: pgraph.ref() for pname, pgraph in self.params.items()}
    kwargs.update(self.config)
    return type(self)(**kwargs)

LayerOperator ¤

Bases: IntEnum

The available symbolic operators defined over layers.

Source code in cirkit/symbolic/layers.py
19
20
21
22
23
24
25
26
27
28
29
class LayerOperator(IntEnum):
    """The available 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
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
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
class PolynomialLayer(InputLayer):
    """A symbolic layer that evaluates polynomials."""

    def __init__(
        self,
        scope: Scope,
        num_output_units: 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.
            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)
        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,
            "degree": self.degree,
        }

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

coeff = coeff instance-attribute ¤

config property ¤

degree = degree instance-attribute ¤

params property ¤

__init__(scope, num_output_units, *, 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
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
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
567
568
569
570
571
def __init__(
    self,
    scope: Scope,
    num_output_units: 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.
        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)
    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
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
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
626
627
628
629
630
631
632
633
634
635
636
637
638
639
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
693
694
695
696
697
698
699
700
701
702
703
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
731
732
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
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}

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
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
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