Skip to content

parameter

parameter ¤

ParameterAddressBook ¤

Bases: AddressBook

The address book data structure for the parameter computational graphs. See TorchParameter. The address book stores a list of AddressBookEntry, where each entry stores the information needed to gather the inputs to each (possibly folded) node in the parameter computational graph.

Source code in cirkit/backend/torch/parameters/parameter.py
20
21
22
23
24
25
26
27
28
29
30
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
class ParameterAddressBook(AddressBook):
    """The address book data structure for the parameter computational graphs.
    See [TorchParameter][cirkit.backend.torch.parameters.parameter.TorchParameter].
    The address book stores a list of
    [AddressBookEntry][cirkit.backend.torch.graph.modules.AddressBookEntry],
    where each entry stores the information needed to gather the inputs to each (possibly folded)
    node in the parameter computational graph.
    """

    def lookup(
        self, module_outputs: list[Tensor], *, in_graph: Tensor | None = None
    ) -> Iterator[tuple[TorchParameterNode | None, tuple]]:
        # Loop through the entries and yield inputs
        for entry in self._entries:
            in_module_ids = entry.in_module_ids

            # Catch the case there are some inputs coming from other modules
            if in_module_ids:
                x = tuple(
                    ParameterAddressBook._select_index(module_outputs, mids, in_idx)
                    for mids, in_idx in zip(in_module_ids, entry.in_fold_idx)
                )
                yield entry.module, x
                continue

            # Catch the case there are no inputs coming from other modules
            yield entry.module, ()

    @staticmethod
    def _select_index(node_outputs: list[Tensor], mids: list[int], idx: Tensor | None) -> Tensor:
        # A useful function combining the modules outputs, and then possibly applying an index
        if len(mids) == 1:
            t = node_outputs[mids[0]]
        else:
            t = torch.cat([node_outputs[mid] for mid in mids], dim=0)
        return t if idx is None else t[idx]

    @classmethod
    def from_index_info(cls, fold_idx_info: FoldIndexInfo) -> "ParameterAddressBook":
        """Constructs the parameter nodes address book using fold index information.

        Args:
            fold_idx_info: The fold index information.

        Returns:
            A parameter nodes address book.
        """
        # The address book entries being built
        entries: list[AddressBookEntry] = []

        # A useful dictionary mapping module ids to their number of folds
        num_folds: dict[int, int] = {}

        # Build the bookkeeping data structure by following the topological ordering
        for mid, m in enumerate(fold_idx_info.ordering):
            # Retrieve the index information of the input modules
            in_modules_fold_idx = fold_idx_info.in_fold_idx[mid]

            # Catch the case of a folded module having the input of the network as input
            if in_modules_fold_idx:
                entry = build_address_book_entry(m, in_modules_fold_idx, num_folds=num_folds)
            # Catch the case of a folded module without inputs
            else:
                entry = AddressBookEntry(m, [], [])

            num_folds[mid] = m.num_folds
            entries.append(entry)

        # Append the last bookkeeping entry with the information to compute the output tensor
        entry = build_address_book_stacked_entry(
            None, [fold_idx_info.out_fold_idx], num_folds=num_folds, output=True
        )
        entries.append(entry)

        return ParameterAddressBook(entries)

_select_index(node_outputs, mids, idx) staticmethod ¤

Source code in cirkit/backend/torch/parameters/parameter.py
48
49
50
51
52
53
54
55
@staticmethod
def _select_index(node_outputs: list[Tensor], mids: list[int], idx: Tensor | None) -> Tensor:
    # A useful function combining the modules outputs, and then possibly applying an index
    if len(mids) == 1:
        t = node_outputs[mids[0]]
    else:
        t = torch.cat([node_outputs[mid] for mid in mids], dim=0)
    return t if idx is None else t[idx]

from_index_info(fold_idx_info) classmethod ¤

Constructs the parameter nodes address book using fold index information.

Parameters:

Name Type Description Default
fold_idx_info FoldIndexInfo

The fold index information.

required

Returns:

Type Description
ParameterAddressBook

A parameter nodes address book.

Source code in cirkit/backend/torch/parameters/parameter.py
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
@classmethod
def from_index_info(cls, fold_idx_info: FoldIndexInfo) -> "ParameterAddressBook":
    """Constructs the parameter nodes address book using fold index information.

    Args:
        fold_idx_info: The fold index information.

    Returns:
        A parameter nodes address book.
    """
    # The address book entries being built
    entries: list[AddressBookEntry] = []

    # A useful dictionary mapping module ids to their number of folds
    num_folds: dict[int, int] = {}

    # Build the bookkeeping data structure by following the topological ordering
    for mid, m in enumerate(fold_idx_info.ordering):
        # Retrieve the index information of the input modules
        in_modules_fold_idx = fold_idx_info.in_fold_idx[mid]

        # Catch the case of a folded module having the input of the network as input
        if in_modules_fold_idx:
            entry = build_address_book_entry(m, in_modules_fold_idx, num_folds=num_folds)
        # Catch the case of a folded module without inputs
        else:
            entry = AddressBookEntry(m, [], [])

        num_folds[mid] = m.num_folds
        entries.append(entry)

    # Append the last bookkeeping entry with the information to compute the output tensor
    entry = build_address_book_stacked_entry(
        None, [fold_idx_info.out_fold_idx], num_folds=num_folds, output=True
    )
    entries.append(entry)

    return ParameterAddressBook(entries)

lookup(module_outputs, *, in_graph=None) ¤

Source code in cirkit/backend/torch/parameters/parameter.py
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
def lookup(
    self, module_outputs: list[Tensor], *, in_graph: Tensor | None = None
) -> Iterator[tuple[TorchParameterNode | None, tuple]]:
    # Loop through the entries and yield inputs
    for entry in self._entries:
        in_module_ids = entry.in_module_ids

        # Catch the case there are some inputs coming from other modules
        if in_module_ids:
            x = tuple(
                ParameterAddressBook._select_index(module_outputs, mids, in_idx)
                for mids, in_idx in zip(in_module_ids, entry.in_fold_idx)
            )
            yield entry.module, x
            continue

        # Catch the case there are no inputs coming from other modules
        yield entry.module, ()

TorchParameter ¤

Bases: TorchDiAcyclicGraph[TorchParameterNode]

A torch parameter is a computational graph consisting of computational nodes, and computing a tensor parameter that is then used by a circuit layer. That is, given F the number of folds, and (K_1,\ldots,K_n) the shape of each parameter fold, a torch parameter computes a tensor of shape (F,K_1,\ldots,K_n). Note that a torch parameter does not take any tensor as input.

Source code in cirkit/backend/torch/parameters/parameter.py
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
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
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
class TorchParameter(TorchDiAcyclicGraph[TorchParameterNode]):
    r"""A torch parameter is a computational graph consisting of computational nodes,
    and computing a tensor parameter that is then used by a circuit layer. That is,
    given F the number of folds, and (K_1,\ldots,K_n) the shape of each parameter fold, a
    torch parameter computes a tensor of shape (F,K_1,\ldots,K_n).
    Note that a torch parameter does not take any tensor as input.
    """

    def __init__(
        self,
        modules: Sequence[TorchParameterNode],
        in_modules: dict[TorchParameterNode, Sequence[TorchParameterNode]],
        outputs: Sequence[TorchParameterNode],
        *,
        fold_idx_info: FoldIndexInfo | None = None,
    ):
        """Initialize a torch parameter computational graph.

        Args:
            modules: The parameter computational nodes.
            in_modules: A dictionary mapping nodes to their input nodes, if any.
            outputs: A list of nodes that are the output nodes in the computational graph.
            fold_idx_info: The folding index information.
                It can be None if the Torch graph is not folded.
        """
        super().__init__(modules, in_modules, outputs, fold_idx_info=fold_idx_info)

    @property
    def device(self) -> torch.device:
        """Retrieve the device of the parameter computational graph.
        Currently, it assumes all [torch.nn.parameter.Parameter][torch.nn.parameter.Parameter]
        it contains are stored in the same device.

        Returns:
            torch.device: The device.
        """
        # TODO: Obtaining the device in this way is only needed because
        #  the integrate() method in layers can output constant tensors that
        #  would be allocated on the CPU by default (e.g., log_partition_function()).
        #  Since having a device flag in nn.Module is malpractice (tensors are stored
        #  on devices but NOT modules), is there a better way to do this?
        return next(self.parameters()).device

    @property
    def num_folds(self) -> int:
        """The number of folds of the computed tensor parameter.

        Returns:
            The number of folds.
        """
        return self._address_book.num_outputs

    @property
    def shape(self) -> tuple[int, ...]:
        r"""The shape of the computed tensor parameter, without considering
        the number of folds. That is, if the number of folds
        (see [TorchParameter.num_folds][cirkit.backend.torch.parameters.parameter.TorchParameter.num_folds])
        is F and the shape is (K_1,\ldots,K_n), it means the torch parameter
        computes a tensor of shape (F, K_1,\ldots,K_n).

        Returns:
            The shape of the computed tensor parameter, without considering the number of folds.
        """
        return self.outputs[0].shape

    def reset_parameters(self) -> None:
        """Reset the parameters of the parameter computational graph."""
        for p in self.nodes:
            p.reset_parameters()

    def __call__(self) -> Tensor:
        # IGNORE: Idiom for nn.Module.__call__.
        return super().__call__()  # type: ignore[no-any-return,misc]

    def forward(self) -> Tensor:
        r"""Evaluate the parameter computational graph.

        Returns:
            Tensor: The output parameter tensor, having shape (F, K_1,\ldots K_n),
                where F is the number of folds, and (K_1,\ldots,K_n) is the shape
                of each parameter tensor slice.
        """
        return self.evaluate()

    def _build_unfold_index_info(self) -> FoldIndexInfo:
        return build_unfold_index_info(
            self.topological_ordering(), outputs=self.outputs, incomings_fn=self.node_inputs
        )

    def _build_address_book(self, fold_idx_info: FoldIndexInfo) -> AddressBook:
        return ParameterAddressBook.from_index_info(fold_idx_info)

    def extra_repr(self) -> str:
        return f"shape: {(self.num_folds, *self.shape)}"

device property ¤

Retrieve the device of the parameter computational graph. Currently, it assumes all torch.nn.parameter.Parameter it contains are stored in the same device.

Returns:

Type Description
device

torch.device: The device.

num_folds property ¤

The number of folds of the computed tensor parameter.

Returns:

Type Description
int

The number of folds.

shape property ¤

The shape of the computed tensor parameter, without considering the number of folds. That is, if the number of folds (see TorchParameter.num_folds) is F and the shape is (K_1,\ldots,K_n), it means the torch parameter computes a tensor of shape (F, K_1,\ldots,K_n).

Returns:

Type Description
tuple[int, ...]

The shape of the computed tensor parameter, without considering the number of folds.

__call__() ¤

Source code in cirkit/backend/torch/parameters/parameter.py
167
168
169
def __call__(self) -> Tensor:
    # IGNORE: Idiom for nn.Module.__call__.
    return super().__call__()  # type: ignore[no-any-return,misc]

__init__(modules, in_modules, outputs, *, fold_idx_info=None) ¤

Initialize a torch parameter computational graph.

Parameters:

Name Type Description Default
modules Sequence[TorchParameterNode]

The parameter computational nodes.

required
in_modules dict[TorchParameterNode, Sequence[TorchParameterNode]]

A dictionary mapping nodes to their input nodes, if any.

required
outputs Sequence[TorchParameterNode]

A list of nodes that are the output nodes in the computational graph.

required
fold_idx_info FoldIndexInfo | None

The folding index information. It can be None if the Torch graph is not folded.

None
Source code in cirkit/backend/torch/parameters/parameter.py
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
def __init__(
    self,
    modules: Sequence[TorchParameterNode],
    in_modules: dict[TorchParameterNode, Sequence[TorchParameterNode]],
    outputs: Sequence[TorchParameterNode],
    *,
    fold_idx_info: FoldIndexInfo | None = None,
):
    """Initialize a torch parameter computational graph.

    Args:
        modules: The parameter computational nodes.
        in_modules: A dictionary mapping nodes to their input nodes, if any.
        outputs: A list of nodes that are the output nodes in the computational graph.
        fold_idx_info: The folding index information.
            It can be None if the Torch graph is not folded.
    """
    super().__init__(modules, in_modules, outputs, fold_idx_info=fold_idx_info)

_build_address_book(fold_idx_info) ¤

Source code in cirkit/backend/torch/parameters/parameter.py
186
187
def _build_address_book(self, fold_idx_info: FoldIndexInfo) -> AddressBook:
    return ParameterAddressBook.from_index_info(fold_idx_info)

_build_unfold_index_info() ¤

Source code in cirkit/backend/torch/parameters/parameter.py
181
182
183
184
def _build_unfold_index_info(self) -> FoldIndexInfo:
    return build_unfold_index_info(
        self.topological_ordering(), outputs=self.outputs, incomings_fn=self.node_inputs
    )

extra_repr() ¤

Source code in cirkit/backend/torch/parameters/parameter.py
189
190
def extra_repr(self) -> str:
    return f"shape: {(self.num_folds, *self.shape)}"

forward() ¤

Evaluate the parameter computational graph.

Returns:

Name Type Description
Tensor Tensor

The output parameter tensor, having shape (F, K_1,\ldots K_n), where F is the number of folds, and (K_1,\ldots,K_n) is the shape of each parameter tensor slice.

Source code in cirkit/backend/torch/parameters/parameter.py
171
172
173
174
175
176
177
178
179
def forward(self) -> Tensor:
    r"""Evaluate the parameter computational graph.

    Returns:
        Tensor: The output parameter tensor, having shape (F, K_1,\ldots K_n),
            where F is the number of folds, and (K_1,\ldots,K_n) is the shape
            of each parameter tensor slice.
    """
    return self.evaluate()

reset_parameters() ¤

Reset the parameters of the parameter computational graph.

Source code in cirkit/backend/torch/parameters/parameter.py
162
163
164
165
def reset_parameters(self) -> None:
    """Reset the parameters of the parameter computational graph."""
    for p in self.nodes:
        p.reset_parameters()