openarchx 0.1.0__py3-none-any.whl
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.
- openarchx/__init__.py +11 -0
- openarchx/core/tensor.py +179 -0
- openarchx/cuda/__init__.py +27 -0
- openarchx/cuda/cuda_ops.py +296 -0
- openarchx/layers/activations.py +63 -0
- openarchx/layers/base.py +40 -0
- openarchx/layers/cnn.py +145 -0
- openarchx/layers/transformer.py +131 -0
- openarchx/nn/__init__.py +26 -0
- openarchx/nn/activations.py +127 -0
- openarchx/nn/containers.py +174 -0
- openarchx/nn/dropout.py +121 -0
- openarchx/nn/layers.py +338 -0
- openarchx/nn/losses.py +156 -0
- openarchx/nn/module.py +18 -0
- openarchx/nn/padding.py +120 -0
- openarchx/nn/pooling.py +318 -0
- openarchx/nn/rnn.py +226 -0
- openarchx/nn/transformers.py +187 -0
- openarchx/optimizers/adam.py +49 -0
- openarchx/optimizers/adaptive.py +63 -0
- openarchx/optimizers/base.py +24 -0
- openarchx/optimizers/modern.py +98 -0
- openarchx/optimizers/optx.py +91 -0
- openarchx/optimizers/sgd.py +63 -0
- openarchx/quantum/circuit.py +92 -0
- openarchx/quantum/gates.py +126 -0
- openarchx/utils/__init__.py +50 -0
- openarchx/utils/data.py +229 -0
- openarchx/utils/huggingface.py +288 -0
- openarchx/utils/losses.py +21 -0
- openarchx/utils/model_io.py +553 -0
- openarchx/utils/pytorch.py +420 -0
- openarchx/utils/tensorflow.py +467 -0
- openarchx/utils/transforms.py +259 -0
- openarchx-0.1.0.dist-info/METADATA +180 -0
- openarchx-0.1.0.dist-info/RECORD +43 -0
- openarchx-0.1.0.dist-info/WHEEL +5 -0
- openarchx-0.1.0.dist-info/licenses/LICENSE +21 -0
- openarchx-0.1.0.dist-info/top_level.txt +2 -0
- tests/__init__.py +1 -0
- tests/test_cuda_ops.py +205 -0
- tests/test_integrations.py +236 -0
@@ -0,0 +1,420 @@
|
|
1
|
+
"""
|
2
|
+
PyTorch Integration Utilities for OpenArchX.
|
3
|
+
|
4
|
+
This module provides conversion and adapter utilities for using PyTorch models
|
5
|
+
and datasets with OpenArchX. These utilities are completely optional and do not
|
6
|
+
affect OpenArchX's core functionality, which remains independent from external libraries.
|
7
|
+
"""
|
8
|
+
|
9
|
+
import numpy as np
|
10
|
+
import importlib.util
|
11
|
+
from ..core.tensor import Tensor
|
12
|
+
|
13
|
+
|
14
|
+
class PyTorchModelAdapter:
|
15
|
+
"""Adapter for using PyTorch models with OpenArchX."""
|
16
|
+
|
17
|
+
def __init__(self, torch_model, device=None):
|
18
|
+
"""
|
19
|
+
Initialize a PyTorch model adapter.
|
20
|
+
|
21
|
+
Args:
|
22
|
+
torch_model: A PyTorch nn.Module model.
|
23
|
+
device: The device to run the model on ('cpu', 'cuda', etc.).
|
24
|
+
"""
|
25
|
+
# Check if torch is installed
|
26
|
+
if importlib.util.find_spec("torch") is None:
|
27
|
+
raise ImportError("PyTorch is required. Install with 'pip install torch'")
|
28
|
+
|
29
|
+
import torch
|
30
|
+
|
31
|
+
self.model = torch_model
|
32
|
+
|
33
|
+
# Handle device placement
|
34
|
+
if device is None:
|
35
|
+
device = "cuda" if torch.cuda.is_available() else "cpu"
|
36
|
+
self.device = torch.device(device)
|
37
|
+
self.model.to(self.device)
|
38
|
+
self.model.eval() # Set to evaluation mode
|
39
|
+
|
40
|
+
def __call__(self, inputs, **kwargs):
|
41
|
+
"""
|
42
|
+
Process inputs through the PyTorch model.
|
43
|
+
|
44
|
+
Args:
|
45
|
+
inputs: Input data, can be numpy arrays, lists, or OpenArchX Tensors.
|
46
|
+
**kwargs: Additional arguments to pass to the model.
|
47
|
+
|
48
|
+
Returns:
|
49
|
+
OpenArchX Tensor containing the model output.
|
50
|
+
"""
|
51
|
+
import torch
|
52
|
+
|
53
|
+
# Convert inputs to torch tensors
|
54
|
+
if isinstance(inputs, Tensor):
|
55
|
+
inputs = inputs.data
|
56
|
+
|
57
|
+
if isinstance(inputs, np.ndarray):
|
58
|
+
inputs = torch.tensor(inputs, dtype=torch.float32, device=self.device)
|
59
|
+
elif isinstance(inputs, list):
|
60
|
+
inputs = torch.tensor(np.array(inputs), dtype=torch.float32, device=self.device)
|
61
|
+
elif not isinstance(inputs, torch.Tensor):
|
62
|
+
raise TypeError(f"Unsupported input type: {type(inputs)}")
|
63
|
+
|
64
|
+
# If inputs is already a torch tensor, ensure it's on the right device
|
65
|
+
if isinstance(inputs, torch.Tensor) and inputs.device != self.device:
|
66
|
+
inputs = inputs.to(self.device)
|
67
|
+
|
68
|
+
# Forward pass with gradient tracking disabled
|
69
|
+
with torch.no_grad():
|
70
|
+
outputs = self.model(inputs, **kwargs)
|
71
|
+
|
72
|
+
# Convert output to numpy and then to Tensor
|
73
|
+
if isinstance(outputs, torch.Tensor):
|
74
|
+
return Tensor(outputs.detach().cpu().numpy())
|
75
|
+
elif isinstance(outputs, (tuple, list)):
|
76
|
+
return tuple(Tensor(output.detach().cpu().numpy()) for output in outputs
|
77
|
+
if isinstance(output, torch.Tensor))
|
78
|
+
elif isinstance(outputs, dict):
|
79
|
+
return {k: Tensor(v.detach().cpu().numpy()) if isinstance(v, torch.Tensor) else v
|
80
|
+
for k, v in outputs.items()}
|
81
|
+
else:
|
82
|
+
return outputs
|
83
|
+
|
84
|
+
|
85
|
+
class PyTorchDatasetConverter:
|
86
|
+
"""Utility for converting between PyTorch and OpenArchX datasets."""
|
87
|
+
|
88
|
+
@staticmethod
|
89
|
+
def to_openarchx_dataset(torch_dataset, transform=None):
|
90
|
+
"""
|
91
|
+
Convert a PyTorch Dataset to an OpenArchX Dataset.
|
92
|
+
|
93
|
+
Args:
|
94
|
+
torch_dataset: A PyTorch Dataset instance.
|
95
|
+
transform: Optional transform to apply to the data.
|
96
|
+
|
97
|
+
Returns:
|
98
|
+
An OpenArchX Dataset.
|
99
|
+
"""
|
100
|
+
from .data import Dataset
|
101
|
+
|
102
|
+
# Check if torch is installed
|
103
|
+
if importlib.util.find_spec("torch") is None:
|
104
|
+
raise ImportError("PyTorch is required. Install with 'pip install torch'")
|
105
|
+
|
106
|
+
class OpenArchXDatasetFromPyTorch(Dataset):
|
107
|
+
def __init__(self, torch_dataset, transform=None):
|
108
|
+
self.torch_dataset = torch_dataset
|
109
|
+
self.transform = transform
|
110
|
+
|
111
|
+
def __len__(self):
|
112
|
+
return len(self.torch_dataset)
|
113
|
+
|
114
|
+
def __getitem__(self, idx):
|
115
|
+
data = self.torch_dataset[idx]
|
116
|
+
|
117
|
+
# Handle different return types from PyTorch dataset
|
118
|
+
if isinstance(data, tuple) and len(data) == 2:
|
119
|
+
# Standard (input, target) format
|
120
|
+
features, target = data
|
121
|
+
|
122
|
+
# Convert PyTorch tensors to numpy arrays
|
123
|
+
if hasattr(features, 'numpy'):
|
124
|
+
features = features.numpy()
|
125
|
+
if hasattr(target, 'numpy'):
|
126
|
+
target = target.numpy()
|
127
|
+
|
128
|
+
# Apply transform if provided
|
129
|
+
if self.transform:
|
130
|
+
features = self.transform(features)
|
131
|
+
|
132
|
+
return features, target
|
133
|
+
else:
|
134
|
+
# Generic handling for other formats
|
135
|
+
if hasattr(data, 'numpy'):
|
136
|
+
data = data.numpy()
|
137
|
+
return data
|
138
|
+
|
139
|
+
return OpenArchXDatasetFromPyTorch(torch_dataset, transform)
|
140
|
+
|
141
|
+
@staticmethod
|
142
|
+
def from_openarchx_dataset(ox_dataset, tensor_dtype=None):
|
143
|
+
"""
|
144
|
+
Convert an OpenArchX Dataset to a PyTorch Dataset.
|
145
|
+
|
146
|
+
Args:
|
147
|
+
ox_dataset: An OpenArchX Dataset instance.
|
148
|
+
tensor_dtype: Optional dtype for the PyTorch tensors.
|
149
|
+
|
150
|
+
Returns:
|
151
|
+
A PyTorch Dataset.
|
152
|
+
"""
|
153
|
+
# Check if torch is installed
|
154
|
+
if importlib.util.find_spec("torch") is None:
|
155
|
+
raise ImportError("PyTorch is required. Install with 'pip install torch'")
|
156
|
+
|
157
|
+
import torch
|
158
|
+
from torch.utils.data import Dataset as TorchDataset
|
159
|
+
|
160
|
+
class PyTorchDatasetFromOpenArchX(TorchDataset):
|
161
|
+
def __init__(self, ox_dataset, tensor_dtype=None):
|
162
|
+
self.ox_dataset = ox_dataset
|
163
|
+
self.tensor_dtype = tensor_dtype or torch.float32
|
164
|
+
|
165
|
+
def __len__(self):
|
166
|
+
return len(self.ox_dataset)
|
167
|
+
|
168
|
+
def __getitem__(self, idx):
|
169
|
+
data = self.ox_dataset[idx]
|
170
|
+
|
171
|
+
# Handle different return types from OpenArchX dataset
|
172
|
+
if isinstance(data, tuple) and len(data) == 2:
|
173
|
+
# Standard (input, target) format
|
174
|
+
features, target = data
|
175
|
+
|
176
|
+
# Convert to PyTorch tensors
|
177
|
+
if isinstance(features, Tensor):
|
178
|
+
features = torch.tensor(features.data, dtype=self.tensor_dtype)
|
179
|
+
elif isinstance(features, np.ndarray):
|
180
|
+
features = torch.tensor(features, dtype=self.tensor_dtype)
|
181
|
+
|
182
|
+
if isinstance(target, Tensor):
|
183
|
+
target = torch.tensor(target.data, dtype=self.tensor_dtype)
|
184
|
+
elif isinstance(target, np.ndarray):
|
185
|
+
target = torch.tensor(target, dtype=self.tensor_dtype)
|
186
|
+
|
187
|
+
return features, target
|
188
|
+
else:
|
189
|
+
# Generic handling for other formats
|
190
|
+
if isinstance(data, Tensor):
|
191
|
+
return torch.tensor(data.data, dtype=self.tensor_dtype)
|
192
|
+
elif isinstance(data, np.ndarray):
|
193
|
+
return torch.tensor(data, dtype=self.tensor_dtype)
|
194
|
+
return data
|
195
|
+
|
196
|
+
return PyTorchDatasetFromOpenArchX(ox_dataset, tensor_dtype)
|
197
|
+
|
198
|
+
|
199
|
+
class PyTorchModelConverter:
|
200
|
+
"""Utility for converting PyTorch models to OpenArchX architecture."""
|
201
|
+
|
202
|
+
@staticmethod
|
203
|
+
def convert_model(torch_model, input_shape=None, framework_dependence=False):
|
204
|
+
"""
|
205
|
+
Convert a PyTorch model to an OpenArchX model.
|
206
|
+
|
207
|
+
Args:
|
208
|
+
torch_model: A PyTorch nn.Module model.
|
209
|
+
input_shape: The shape of the input tensor (excluding batch dimension).
|
210
|
+
framework_dependence: If True, the resulting model will still rely on PyTorch
|
211
|
+
for forward passes. If False, it will be converted to
|
212
|
+
pure OpenArchX layers.
|
213
|
+
|
214
|
+
Returns:
|
215
|
+
An OpenArchX model.
|
216
|
+
"""
|
217
|
+
# Check if torch is installed
|
218
|
+
if importlib.util.find_spec("torch") is None:
|
219
|
+
raise ImportError("PyTorch is required. Install with 'pip install torch'")
|
220
|
+
|
221
|
+
import torch
|
222
|
+
import torch.nn as nn
|
223
|
+
from ..nn.base import Layer, Model
|
224
|
+
from ..nn.layers import Dense, Conv2D, MaxPool2D, Flatten, Dropout
|
225
|
+
|
226
|
+
if framework_dependence:
|
227
|
+
# Create a wrapper around the PyTorch model
|
228
|
+
class PyTorchWrappedModel(Model):
|
229
|
+
def __init__(self, torch_model):
|
230
|
+
super().__init__()
|
231
|
+
self.torch_model = torch_model
|
232
|
+
self.torch_model.eval() # Set to evaluation mode
|
233
|
+
|
234
|
+
def forward(self, x):
|
235
|
+
# Convert OpenArchX Tensor to PyTorch tensor
|
236
|
+
if isinstance(x, Tensor):
|
237
|
+
x_torch = torch.tensor(x.data, dtype=torch.float32)
|
238
|
+
else:
|
239
|
+
x_torch = torch.tensor(x, dtype=torch.float32)
|
240
|
+
|
241
|
+
# Forward pass with gradient tracking disabled
|
242
|
+
with torch.no_grad():
|
243
|
+
output = self.torch_model(x_torch)
|
244
|
+
|
245
|
+
# Convert back to OpenArchX Tensor
|
246
|
+
if isinstance(output, torch.Tensor):
|
247
|
+
return Tensor(output.numpy())
|
248
|
+
else:
|
249
|
+
return output
|
250
|
+
|
251
|
+
return PyTorchWrappedModel(torch_model)
|
252
|
+
|
253
|
+
else:
|
254
|
+
# Convert to pure OpenArchX model by translating each layer
|
255
|
+
class OpenArchXModelFromPyTorch(Model):
|
256
|
+
def __init__(self, torch_model, input_shape):
|
257
|
+
super().__init__()
|
258
|
+
self.layers = []
|
259
|
+
|
260
|
+
# We need sample input to trace through the PyTorch model
|
261
|
+
if input_shape is None:
|
262
|
+
raise ValueError("input_shape must be provided for full conversion")
|
263
|
+
|
264
|
+
# Create a sample input tensor to trace the model
|
265
|
+
sample_input = torch.zeros((1,) + input_shape)
|
266
|
+
|
267
|
+
# Collect layer information
|
268
|
+
layer_info = []
|
269
|
+
|
270
|
+
def hook_fn(module, input, output):
|
271
|
+
layer_info.append({
|
272
|
+
'module': module,
|
273
|
+
'input_shape': [tuple(t.shape) for t in input if isinstance(t, torch.Tensor)],
|
274
|
+
'output_shape': output.shape if isinstance(output, torch.Tensor)
|
275
|
+
else [t.shape for t in output if isinstance(t, torch.Tensor)]
|
276
|
+
})
|
277
|
+
|
278
|
+
# Register hooks
|
279
|
+
hooks = []
|
280
|
+
for name, module in torch_model.named_modules():
|
281
|
+
if isinstance(module, (nn.Linear, nn.Conv2d, nn.MaxPool2d, nn.Flatten, nn.Dropout)):
|
282
|
+
hooks.append(module.register_forward_hook(hook_fn))
|
283
|
+
|
284
|
+
# Forward pass to activate hooks
|
285
|
+
with torch.no_grad():
|
286
|
+
torch_model(sample_input)
|
287
|
+
|
288
|
+
# Remove hooks
|
289
|
+
for hook in hooks:
|
290
|
+
hook.remove()
|
291
|
+
|
292
|
+
# Convert each layer
|
293
|
+
for info in layer_info:
|
294
|
+
module = info['module']
|
295
|
+
if isinstance(module, nn.Linear):
|
296
|
+
layer = Dense(module.out_features)
|
297
|
+
# Set weights and biases
|
298
|
+
layer.weights = Tensor(module.weight.detach().numpy().T)
|
299
|
+
if module.bias is not None:
|
300
|
+
layer.bias = Tensor(module.bias.detach().numpy())
|
301
|
+
self.layers.append(layer)
|
302
|
+
|
303
|
+
elif isinstance(module, nn.Conv2d):
|
304
|
+
layer = Conv2D(
|
305
|
+
filters=module.out_channels,
|
306
|
+
kernel_size=module.kernel_size,
|
307
|
+
strides=module.stride,
|
308
|
+
padding=module.padding
|
309
|
+
)
|
310
|
+
# Set weights and biases
|
311
|
+
layer.kernels = Tensor(module.weight.detach().numpy())
|
312
|
+
if module.bias is not None:
|
313
|
+
layer.bias = Tensor(module.bias.detach().numpy())
|
314
|
+
self.layers.append(layer)
|
315
|
+
|
316
|
+
elif isinstance(module, nn.MaxPool2d):
|
317
|
+
layer = MaxPool2D(
|
318
|
+
pool_size=module.kernel_size,
|
319
|
+
strides=module.stride,
|
320
|
+
padding=module.padding
|
321
|
+
)
|
322
|
+
self.layers.append(layer)
|
323
|
+
|
324
|
+
elif isinstance(module, nn.Flatten):
|
325
|
+
layer = Flatten()
|
326
|
+
self.layers.append(layer)
|
327
|
+
|
328
|
+
elif isinstance(module, nn.Dropout):
|
329
|
+
layer = Dropout(rate=module.p)
|
330
|
+
self.layers.append(layer)
|
331
|
+
|
332
|
+
def forward(self, x):
|
333
|
+
for layer in self.layers:
|
334
|
+
x = layer(x)
|
335
|
+
return x
|
336
|
+
|
337
|
+
return OpenArchXModelFromPyTorch(torch_model, input_shape)
|
338
|
+
|
339
|
+
|
340
|
+
# Convenience functions
|
341
|
+
|
342
|
+
def get_pytorch_model_adapter(torch_model, device=None):
|
343
|
+
"""
|
344
|
+
Helper function to get a PyTorch model adapter.
|
345
|
+
|
346
|
+
Args:
|
347
|
+
torch_model: A PyTorch nn.Module model.
|
348
|
+
device: The device to run the model on.
|
349
|
+
|
350
|
+
Returns:
|
351
|
+
A PyTorchModelAdapter instance.
|
352
|
+
"""
|
353
|
+
return PyTorchModelAdapter(torch_model, device)
|
354
|
+
|
355
|
+
|
356
|
+
def convert_to_pytorch_dataset(ox_dataset, tensor_dtype=None):
|
357
|
+
"""
|
358
|
+
Convert an OpenArchX Dataset to a PyTorch Dataset.
|
359
|
+
|
360
|
+
Args:
|
361
|
+
ox_dataset: An OpenArchX Dataset instance.
|
362
|
+
tensor_dtype: Optional dtype for the PyTorch tensors.
|
363
|
+
|
364
|
+
Returns:
|
365
|
+
A PyTorch Dataset.
|
366
|
+
"""
|
367
|
+
return PyTorchDatasetConverter.from_openarchx_dataset(ox_dataset, tensor_dtype)
|
368
|
+
|
369
|
+
|
370
|
+
def convert_from_pytorch_dataset(torch_dataset, transform=None):
|
371
|
+
"""
|
372
|
+
Convert a PyTorch Dataset to an OpenArchX Dataset.
|
373
|
+
|
374
|
+
Args:
|
375
|
+
torch_dataset: A PyTorch Dataset instance.
|
376
|
+
transform: Optional transform to apply to the data.
|
377
|
+
|
378
|
+
Returns:
|
379
|
+
An OpenArchX Dataset.
|
380
|
+
"""
|
381
|
+
return PyTorchDatasetConverter.to_openarchx_dataset(torch_dataset, transform)
|
382
|
+
|
383
|
+
|
384
|
+
def convert_pytorch_model(torch_model, input_shape=None, framework_dependence=False):
|
385
|
+
"""
|
386
|
+
Convert a PyTorch model to an OpenArchX model.
|
387
|
+
|
388
|
+
Args:
|
389
|
+
torch_model: A PyTorch nn.Module model.
|
390
|
+
input_shape: The shape of the input tensor (excluding batch dimension).
|
391
|
+
framework_dependence: If True, the resulting model will still rely on PyTorch.
|
392
|
+
If False, it will be converted to pure OpenArchX layers.
|
393
|
+
|
394
|
+
Returns:
|
395
|
+
An OpenArchX model.
|
396
|
+
"""
|
397
|
+
return PyTorchModelConverter.convert_model(torch_model, input_shape, framework_dependence)
|
398
|
+
|
399
|
+
|
400
|
+
def extract_pytorch_weights(torch_model):
|
401
|
+
"""
|
402
|
+
Extract weights from a PyTorch model.
|
403
|
+
|
404
|
+
Args:
|
405
|
+
torch_model: A PyTorch nn.Module model.
|
406
|
+
|
407
|
+
Returns:
|
408
|
+
Dictionary mapping parameter names to OpenArchX Tensors.
|
409
|
+
"""
|
410
|
+
# Check if torch is installed
|
411
|
+
if importlib.util.find_spec("torch") is None:
|
412
|
+
raise ImportError("PyTorch is required. Install with 'pip install torch'")
|
413
|
+
|
414
|
+
weights_dict = {}
|
415
|
+
|
416
|
+
# Iterate through named parameters
|
417
|
+
for name, param in torch_model.named_parameters():
|
418
|
+
weights_dict[name] = Tensor(param.detach().cpu().numpy())
|
419
|
+
|
420
|
+
return weights_dict
|