fxtensor-salmon 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ Copyright 2025 Tadachika Oki
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
+
5
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+
7
+ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,63 @@
1
+ Metadata-Version: 2.4
2
+ Name: fxtensor-salmon
3
+ Version: 0.1.0
4
+ Summary: Markov Category Module
5
+ Author-email: あなたの名前 <sakesnare@gmail.com>
6
+ License: Copyright 2025 Tadachika Oki
7
+
8
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
11
+
12
+ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
13
+
14
+ Project-URL: Homepage, https://github.com/yourusername/fxtensor-salmon
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: License :: OSI Approved :: MIT License
17
+ Classifier: Operating System :: OS Independent
18
+ Classifier: Topic :: Scientific/Engineering :: Mathematics
19
+ Requires-Python: >=3.8
20
+ Description-Content-Type: text/markdown
21
+ License-File: LICENSE
22
+ Requires-Dist: numpy>=1.20.0
23
+ Dynamic: license-file
24
+
25
+ # FXTensor
26
+
27
+ マルコフ・テンソルを計算するモジュールです。
28
+
29
+ ## インストール
30
+
31
+ ```bash
32
+ pip install fxtensor-salmon
33
+ ```
34
+
35
+ ## 使い方
36
+
37
+ JSON定義から`FXTensor`オブジェクトを作成する方法は以下の通りです。
38
+
39
+ ```python
40
+ import numpy as np
41
+ from fxtensor import FXTensor
42
+
43
+ json_data = {
44
+ "profile": [[2], [2]],
45
+ "strands": [
46
+ {"from": [0], "to": [0], "weight": 0.3},
47
+ {"from": [0], "to": [1], "weight": 0.7},
48
+ {"from": [1], "to": [0], "weight": 0.5},
49
+ {"from": [1], "to": [1], "weight": 0.5}
50
+ ]
51
+ }
52
+
53
+ # JSONデータからテンソルを作成
54
+ stochastic_map = FXTensor.from_json(json_data)
55
+
56
+ print(stochastic_map)
57
+ # 出力予測: FXTensor(profile=[[2], [2]], shape=(2, 2))
58
+
59
+ print(stochastic_map.data)
60
+ # 出力予測:
61
+ # [[0.3 0.7]
62
+ # [0.5 0.5]]
63
+ ```
@@ -0,0 +1,39 @@
1
+ # FXTensor
2
+
3
+ マルコフ・テンソルを計算するモジュールです。
4
+
5
+ ## インストール
6
+
7
+ ```bash
8
+ pip install fxtensor-salmon
9
+ ```
10
+
11
+ ## 使い方
12
+
13
+ JSON定義から`FXTensor`オブジェクトを作成する方法は以下の通りです。
14
+
15
+ ```python
16
+ import numpy as np
17
+ from fxtensor import FXTensor
18
+
19
+ json_data = {
20
+ "profile": [[2], [2]],
21
+ "strands": [
22
+ {"from": [0], "to": [0], "weight": 0.3},
23
+ {"from": [0], "to": [1], "weight": 0.7},
24
+ {"from": [1], "to": [0], "weight": 0.5},
25
+ {"from": [1], "to": [1], "weight": 0.5}
26
+ ]
27
+ }
28
+
29
+ # JSONデータからテンソルを作成
30
+ stochastic_map = FXTensor.from_json(json_data)
31
+
32
+ print(stochastic_map)
33
+ # 出力予測: FXTensor(profile=[[2], [2]], shape=(2, 2))
34
+
35
+ print(stochastic_map.data)
36
+ # 出力予測:
37
+ # [[0.3 0.7]
38
+ # [0.5 0.5]]
39
+ ```
@@ -0,0 +1,28 @@
1
+ # pyproject.toml
2
+
3
+ [build-system]
4
+ requires = ["setuptools>=61.0"]
5
+ build-backend = "setuptools.build_meta"
6
+
7
+ [project]
8
+ name = "fxtensor-salmon"
9
+ version = "0.1.0"
10
+ authors = [
11
+ { name="あなたの名前", email="sakesnare@gmail.com" },
12
+ ]
13
+ description = "Markov Category Module"
14
+ readme = "README.md"
15
+ requires-python = ">=3.8"
16
+ license = { file="LICENSE" }
17
+ classifiers = [
18
+ "Programming Language :: Python :: 3",
19
+ "License :: OSI Approved :: MIT License",
20
+ "Operating System :: OS Independent",
21
+ "Topic :: Scientific/Engineering :: Mathematics",
22
+ ]
23
+ dependencies = [
24
+ "numpy>=1.20.0",
25
+ ]
26
+
27
+ [project.urls]
28
+ Homepage = "https://github.com/yourusername/fxtensor-salmon"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,280 @@
1
+ import numpy as np
2
+ import json
3
+ import ast
4
+
5
+ class FXTensor:
6
+ def __init__(self, profile, data=None):
7
+ self.profile = profile
8
+ domain_profile = profile[0]
9
+ codomain_profile = profile[1]
10
+ self.shape = tuple(domain_profile) + tuple(codomain_profile)
11
+ if data is None:
12
+ self.data = np.zeros(self.shape)
13
+ else:
14
+ # Ensure the provided data shape matches the profile
15
+ assert data.shape == self.shape, "Data shape must match profile shape"
16
+ self.data = data
17
+
18
+ @staticmethod
19
+ def from_json(json_data):
20
+ """Creates an FXTensor instance from a JSON object."""
21
+ profile = json_data["profile"]
22
+ domain_profile = profile[0]
23
+ codomain_profile = profile[1]
24
+ shape = tuple(domain_profile) + tuple(codomain_profile)
25
+ data = np.zeros(shape)
26
+ for strand in json_data["strands"]:
27
+ from_idx = tuple(strand["from"])
28
+ to_idx = tuple(strand["to"])
29
+ full_idx = from_idx + to_idx
30
+ data[full_idx] = strand["weight"]
31
+ return FXTensor(profile, data=data)
32
+
33
+ def to_json(self):
34
+ """Converts the FXTensor instance to a JSON serializable dictionary."""
35
+ profile = self.profile
36
+ strands = []
37
+ k = len(self.profile[0]) # Number of dimensions in the domain
38
+ for idx in np.ndindex(self.data.shape):
39
+ if self.data[idx] != 0:
40
+ from_idx = list(idx[:k])
41
+ to_idx = list(idx[k:])
42
+ weight = float(self.data[idx])
43
+ strands.append({"from": from_idx, "to": to_idx, "weight": weight})
44
+ return {"profile": profile, "strands": strands}
45
+
46
+ def save_to_file(self, filename):
47
+ """Saves the tensor to a JSON file."""
48
+ with open(filename, 'w') as f:
49
+ json.dump(self.to_json(), f, indent=2)
50
+
51
+ @staticmethod
52
+ def load_from_file(filename):
53
+ """Loads a tensor from a JSON file."""
54
+ with open(filename, 'r') as f:
55
+ json_data = json.load(f)
56
+ return FXTensor.from_json(json_data)
57
+
58
+ @staticmethod
59
+ def from_strands(profile, strands):
60
+ """Creates a tensor from a dictionary of strands (string representation)."""
61
+ tensor = FXTensor(profile)
62
+ for strand_str, weight in strands.items():
63
+ # Safely evaluate the string representation of the list
64
+ strand = ast.literal_eval(strand_str)
65
+ # Adjust for 1-based indexing if necessary
66
+ domain_idx = tuple(x - 1 for x in strand[0])
67
+ codomain_idx = tuple(x - 1 for x in strand[1])
68
+ idx = domain_idx + codomain_idx
69
+ tensor.data[idx] = weight
70
+ return tensor
71
+
72
+ def to_original_dict(self):
73
+ """Converts the tensor back to the original dictionary format with 1-based indexing."""
74
+ profile = self.profile
75
+ strands = {}
76
+ total_domain_dims = len(self.profile[0])
77
+ for idx in np.ndindex(self.data.shape):
78
+ weight = self.data[idx]
79
+ if weight != 0:
80
+ domain_idx = [d + 1 for d in idx[:total_domain_dims]]
81
+ codomain_idx = [c + 1 for c in idx[total_domain_dims:]]
82
+ strand_key = str([domain_idx, codomain_idx])
83
+ strands[strand_key] = weight
84
+ return {"profile": profile, "strands": strands}
85
+
86
+ def composition(self, other):
87
+ """Performs tensor composition (matrix multiplication for tensors)."""
88
+ if self.profile[1] != other.profile[0]:
89
+ raise ValueError("Tensors are not composable: codomain of self must match domain of other.")
90
+
91
+ # Axes to contract over
92
+ self_codomain_axes = list(range(len(self.profile[0]), len(self.shape)))
93
+ other_domain_axes = list(range(len(other.profile[0])))
94
+
95
+ result_data = np.tensordot(self.data, other.data, axes=(self_codomain_axes, other_domain_axes))
96
+ result_profile = [self.profile[0], other.profile[1]]
97
+ return FXTensor(result_profile, data=result_data)
98
+
99
+ def tensor_product(self, other):
100
+ """Performs the tensor product of two FXTensors."""
101
+ dx, cx = self.profile
102
+ dy, cy = other.profile
103
+
104
+ # Reshape self and other to be broadcastable
105
+ self_reshaped_shape = tuple(dx) + (1,) * len(dy) + tuple(cx) + (1,) * len(cy)
106
+ other_reshaped_shape = (1,) * len(dx) + tuple(dy) + (1,) * len(cx) + tuple(cy)
107
+
108
+ self_reshaped = self.data.reshape(self_reshaped_shape)
109
+ other_reshaped = other.data.reshape(other_reshaped_shape)
110
+
111
+ # Broadcasting multiplication
112
+ result_data = self_reshaped * other_reshaped
113
+
114
+ # New profile for the resulting tensor
115
+ result_profile = [dx + dy, cx + cy]
116
+ return FXTensor(result_profile, data=result_data)
117
+
118
+ @staticmethod
119
+ def unit_tensor(list_x):
120
+ """Creates a unit tensor (identity)."""
121
+ profile = [list_x, list_x]
122
+ shape = tuple(list_x) + tuple(list_x)
123
+ data = np.identity(np.prod(list_x)).reshape(shape)
124
+ return FXTensor(profile, data=data)
125
+
126
+ @staticmethod
127
+ def delta(list_x):
128
+ """Creates a delta tensor (copying)."""
129
+ domain = list_x
130
+ codomain = list_x + list_x
131
+ profile = [domain, codomain]
132
+ shape = tuple(domain) + tuple(codomain)
133
+ data = np.zeros(shape)
134
+
135
+ # Create an identity matrix of size prod(list_x) x prod(list_x)
136
+ identity_matrix = np.identity(np.prod(list_x))
137
+ # Reshape it to the desired tensor shape
138
+ data = identity_matrix.reshape(tuple(list_x) + tuple(list_x))
139
+
140
+ # This is a more efficient way to create the delta tensor
141
+ # The previous loop was complex; this creates the same result
142
+ temp_data = np.zeros(tuple(list_x) + tuple(list_x))
143
+ for idx in np.ndindex(*tuple(list_x)):
144
+ temp_data[idx + idx] = 1
145
+
146
+ final_data = temp_data.reshape(shape)
147
+
148
+ return FXTensor(profile, data=final_data)
149
+
150
+
151
+ @staticmethod
152
+ def exclamation(list_x):
153
+ """Creates an exclamation tensor (discarding)."""
154
+ profile = [list_x, []]
155
+ shape = tuple(list_x)
156
+ data = np.ones(shape)
157
+ return FXTensor(profile, data=data)
158
+
159
+ def is_markov(self):
160
+ """Checks if the tensor represents a Markov kernel."""
161
+ codomain_axes = tuple(range(len(self.profile[0]), len(self.shape)))
162
+ if not codomain_axes: # Handle case where codomain is empty
163
+ return np.all(self.data == 1.0)
164
+ row_sums = np.sum(self.data, axis=codomain_axes)
165
+ return np.allclose(row_sums, 1)
166
+
167
+ def partial_composition(self, other, concat_start_index):
168
+ """Performs partial composition."""
169
+ codomain_profile = self.profile[1]
170
+ c_part = codomain_profile[concat_start_index - 1:]
171
+ unit_c = FXTensor.unit_tensor(c_part)
172
+ tp = other.tensor_product(unit_c)
173
+ return self.composition(tp)
174
+
175
+ def jointification(self, other):
176
+ """Creates a joint state from two tensors."""
177
+ if self.profile[0] != [] or other.profile[0] != self.profile[1]:
178
+ raise ValueError("Tensors are not suitable for jointification")
179
+ a = self.profile[1]
180
+ b = other.profile[1]
181
+
182
+ # Reshape self.data to be broadcastable with other.data
183
+ self_expanded = self.data.reshape(tuple(a) + (1,) * len(b))
184
+
185
+ result_data = self_expanded * other.data
186
+ result_profile = [[], a + b]
187
+ return FXTensor(result_profile, data=result_data)
188
+
189
+ def conditionalization(self, concat_start_index):
190
+ """Creates a conditional probability distribution."""
191
+ if self.profile[0] != []:
192
+ raise ValueError("Tensor must have empty domain for conditionalization")
193
+ codomain = self.profile[1]
194
+ start_B = concat_start_index - 1
195
+ A_sizes = codomain[0:start_B]
196
+ B_sizes = codomain[start_B:]
197
+
198
+ # Sum over the dimensions of B to get the marginal distribution of A
199
+ sum_axes = tuple(range(len(A_sizes), len(A_sizes) + len(B_sizes)))
200
+ marginal_A = np.sum(self.data, axis=sum_axes)
201
+
202
+ # Expand marginal_A to be broadcastable with the original data
203
+ marginal_A_expanded_shape = tuple(A_sizes) + (1,) * len(B_sizes)
204
+ marginal_A_expanded = marginal_A.reshape(marginal_A_expanded_shape)
205
+
206
+ # Avoid division by zero
207
+ # Where marginal_A is zero, the conditional probability is also zero.
208
+ result_data = np.divide(self.data, marginal_A_expanded, out=np.zeros_like(self.data), where=marginal_A_expanded!=0)
209
+
210
+ result_profile = [A_sizes, B_sizes]
211
+ return FXTensor(result_profile, data=result_data)
212
+
213
+ def first_marginalization(self, concat_start_index):
214
+ """Marginalizes out the second part of the codomain."""
215
+ codomain = self.profile[1]
216
+ start_B = concat_start_index - 1
217
+ A_sizes = codomain[0:start_B]
218
+ B_sizes = codomain[start_B:]
219
+
220
+ unit_A = FXTensor.unit_tensor(A_sizes)
221
+ excl_B = FXTensor.exclamation(B_sizes)
222
+
223
+ tp = unit_A.tensor_product(excl_B)
224
+ result = self.composition(tp)
225
+ return result
226
+
227
+ def second_marginalization(self, concat_start_index):
228
+ """Marginalizes out the first part of the codomain."""
229
+ codomain = self.profile[1]
230
+ start_B = concat_start_index - 1
231
+ A_sizes = codomain[0:start_B]
232
+ B_sizes = codomain[start_B:]
233
+
234
+ excl_A = FXTensor.exclamation(A_sizes)
235
+ unit_B = FXTensor.unit_tensor(B_sizes)
236
+
237
+ tp = excl_A.tensor_product(unit_B)
238
+ result = self.composition(tp)
239
+ return result
240
+
241
+ @staticmethod
242
+ def swap(list_a, list_b):
243
+ """Creates a swap tensor."""
244
+ domain = list_a + list_b
245
+ codomain = list_b + list_a
246
+ profile = [domain, codomain]
247
+
248
+ # Create a permutation of axes
249
+ len_a = len(list_a)
250
+ len_b = len(list_b)
251
+
252
+ # The permutation will move the block of axes for 'a' to the end
253
+ # and the block for 'b' to the beginning.
254
+ # Original axes: 0, 1, ..., len_a-1, len_a, ..., len_a+len_b-1
255
+ # New axes: len_a, ..., len_a+len_b-1, 0, 1, ..., len_a-1
256
+ permutation = list(range(len_a, len_a + len_b)) + list(range(len_a))
257
+
258
+ # Create an identity tensor and transpose its domain axes
259
+ identity_data = np.identity(np.prod(domain)).reshape(tuple(domain) * 2)
260
+ result_data = np.transpose(identity_data, permutation + [i + len(domain) for i in range(len(domain))])
261
+
262
+ # This is a more direct way to create the swap tensor
263
+ shape = tuple(domain) + tuple(codomain)
264
+ data = np.zeros(shape)
265
+
266
+ # Iterate over the domain indices
267
+ from_indices = np.ndindex(*tuple(domain))
268
+
269
+ for from_idx in from_indices:
270
+ idx_a = from_idx[:len_a]
271
+ idx_b = from_idx[len_a:]
272
+ to_idx = idx_b + idx_a
273
+ full_idx = from_idx + to_idx
274
+ data[full_idx] = 1
275
+
276
+ return FXTensor(profile, data=data)
277
+
278
+ def __repr__(self):
279
+ """Provides a string representation of the FXTensor instance."""
280
+ return f"FXTensor(profile={self.profile}, shape={self.data.shape})"
@@ -0,0 +1,63 @@
1
+ Metadata-Version: 2.4
2
+ Name: fxtensor-salmon
3
+ Version: 0.1.0
4
+ Summary: Markov Category Module
5
+ Author-email: あなたの名前 <sakesnare@gmail.com>
6
+ License: Copyright 2025 Tadachika Oki
7
+
8
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
11
+
12
+ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
13
+
14
+ Project-URL: Homepage, https://github.com/yourusername/fxtensor-salmon
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: License :: OSI Approved :: MIT License
17
+ Classifier: Operating System :: OS Independent
18
+ Classifier: Topic :: Scientific/Engineering :: Mathematics
19
+ Requires-Python: >=3.8
20
+ Description-Content-Type: text/markdown
21
+ License-File: LICENSE
22
+ Requires-Dist: numpy>=1.20.0
23
+ Dynamic: license-file
24
+
25
+ # FXTensor
26
+
27
+ マルコフ・テンソルを計算するモジュールです。
28
+
29
+ ## インストール
30
+
31
+ ```bash
32
+ pip install fxtensor-salmon
33
+ ```
34
+
35
+ ## 使い方
36
+
37
+ JSON定義から`FXTensor`オブジェクトを作成する方法は以下の通りです。
38
+
39
+ ```python
40
+ import numpy as np
41
+ from fxtensor import FXTensor
42
+
43
+ json_data = {
44
+ "profile": [[2], [2]],
45
+ "strands": [
46
+ {"from": [0], "to": [0], "weight": 0.3},
47
+ {"from": [0], "to": [1], "weight": 0.7},
48
+ {"from": [1], "to": [0], "weight": 0.5},
49
+ {"from": [1], "to": [1], "weight": 0.5}
50
+ ]
51
+ }
52
+
53
+ # JSONデータからテンソルを作成
54
+ stochastic_map = FXTensor.from_json(json_data)
55
+
56
+ print(stochastic_map)
57
+ # 出力予測: FXTensor(profile=[[2], [2]], shape=(2, 2))
58
+
59
+ print(stochastic_map.data)
60
+ # 出力予測:
61
+ # [[0.3 0.7]
62
+ # [0.5 0.5]]
63
+ ```
@@ -0,0 +1,9 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ src/fxtensor-salmon/__init__.py
5
+ src/fxtensor_salmon.egg-info/PKG-INFO
6
+ src/fxtensor_salmon.egg-info/SOURCES.txt
7
+ src/fxtensor_salmon.egg-info/dependency_links.txt
8
+ src/fxtensor_salmon.egg-info/requires.txt
9
+ src/fxtensor_salmon.egg-info/top_level.txt
@@ -0,0 +1 @@
1
+ numpy>=1.20.0
@@ -0,0 +1 @@
1
+ fxtensor-salmon