Added more PyTorch wrapper nodes.

This commit is contained in:
HowToSD
2025-02-27 23:52:21 -08:00
parent b0782fb422
commit b22193dc2b
58 changed files with 2753 additions and 26 deletions
+17 -23
View File
@@ -15,35 +15,29 @@ I hope you find it useful and engaging for data exploration.
<hr>
![Baseball tutorial](docs/images/baseball_final.png)
**ComfyUI-Data-Analysis** is an extension for ComfyUI that provides custom nodes for data analysis using Pandas, Seaborn, and Matplotlib within ComfyUI.
ComfyUI-Data-Analysis is an extension for ComfyUI consisting of many custom nodes for analyzing data using Pandas, Seaborn and Matplotlib within ComfyUI.
Originally designed for structural data analysis, this extension is for you if you:
- Want access to PyTorch's low-level methods (e.g., `add`, `div`, `max`, `where`, `scatter`, `gather`) to manipulate PyTorch tensors or extract information.
- Are comfortable with ComfyUI and want to learn foundational PyTorch methods **without writing a single line of code**.
- Want to analyze structural data (tabular data) using ComfyUI's node system.
ComfyUI-Data-Analysis supports a wide range of custom nodes in the categories below. For a complete list, see [Node Reference](docs/reference/node_reference.md):
* Aggregation
* Arithmetic method
* Cumulative calculations
* Data cleansing
* Data subset selection
* Data type conversion
* DataFrame information
* Date and time processing
* Display data
* IO
* Math
* Plot
* Summary statistics
* Data transformation
## Features
You can connect these nodes to conduct complex analysis in an intuitive graphical way.
You can also use other Comfy custom nodes to fit your needs (e.g., combining images).
**ComfyUI-Data-Analysis** includes numerous custom nodes that wrap Pandas and PyTorch.
For a complete list, see the [Node Reference](docs/reference/node_reference.md).
These custom nodes enhance efficiency and usability in data exploration and analysis.
These nodes allow you to conduct complex analyses in an intuitive, graphical way. You can also combine them with other ComfyUI custom nodes to fit your workflow needs (e.g., integrating images).
Creating sample data and plotting it is incredibly simple, as shown below:
![SNS Line Plot](docs/images/sns_line.png)
An example plot
## Visualization & Analysis
These custom nodes enhance efficiency and usability in data exploration and visualization. Creating sample data and plotting it is incredibly simple:
![SNS Line Plot](docs/images/sns_line.png)
*Example plot*
Additionally, you can analyze images generated in ComfyUI’s workflows. Below is an example of a histogram showing the red channel pixel distribution:
You can also analyze images generated in ComfyUI's image generation workflows. Below screenshot shows a histogram of red channel pixels.
![Red channel pixel distribution](docs/images/red_pixel_distribution.png)
## Installation
+51
View File
@@ -121,6 +121,8 @@ from .modules.pandas_wrapper.pandas_xs import PandasXs
from .modules.pytorch_wrapper.pt_abs import PtAbs
from .modules.pytorch_wrapper.pt_acos import PtAcos
from .modules.pytorch_wrapper.pt_add import PtAdd
from .modules.pytorch_wrapper.pt_argmax import PtArgmax
from .modules.pytorch_wrapper.pt_argmin import PtArgmin
from .modules.pytorch_wrapper.pt_asin import PtAsin
from .modules.pytorch_wrapper.pt_atan import PtAtan
from .modules.pytorch_wrapper.pt_bitwise_and import PtBitwiseAnd
@@ -144,8 +146,10 @@ from .modules.pytorch_wrapper.pt_from_image import PtFromImage
from .modules.pytorch_wrapper.pt_from_latent import PtFromLatent
from .modules.pytorch_wrapper.pt_from_numpy import PtFromNumpy
from .modules.pytorch_wrapper.pt_full import PtFull
from .modules.pytorch_wrapper.pt_gather import PtGather
from .modules.pytorch_wrapper.pt_ge import PtGe
from .modules.pytorch_wrapper.pt_gt import PtGt
from .modules.pytorch_wrapper.pt_index_select import PtIndexSelect
from .modules.pytorch_wrapper.pt_int_create import PtIntCreate
from .modules.pytorch_wrapper.pt_le import PtLe
from .modules.pytorch_wrapper.pt_log import PtLog
@@ -154,7 +158,12 @@ from .modules.pytorch_wrapper.pt_logical_not import PtLogicalNot
from .modules.pytorch_wrapper.pt_logical_or import PtLogicalOr
from .modules.pytorch_wrapper.pt_logical_xor import PtLogicalXor
from .modules.pytorch_wrapper.pt_lt import PtLt
from .modules.pytorch_wrapper.pt_masked_select import PtMaskedSelect
from .modules.pytorch_wrapper.pt_matmul import PtMatMul
from .modules.pytorch_wrapper.pt_max import PtMax
from .modules.pytorch_wrapper.pt_mean import PtMean
from .modules.pytorch_wrapper.pt_median import PtMedian
from .modules.pytorch_wrapper.pt_min import PtMin
from .modules.pytorch_wrapper.pt_mm import PtMm
from .modules.pytorch_wrapper.pt_mul import PtMul
from .modules.pytorch_wrapper.pt_ne import PtNe
@@ -162,10 +171,13 @@ from .modules.pytorch_wrapper.pt_neg import PtNeg
from .modules.pytorch_wrapper.pt_ones import PtOnes
from .modules.pytorch_wrapper.pt_permute import PtPermute
from .modules.pytorch_wrapper.pt_pow import PtPow
from .modules.pytorch_wrapper.pt_prod import PtProd
from .modules.pytorch_wrapper.pt_rand import PtRand
from .modules.pytorch_wrapper.pt_rand_int import PtRandInt
from .modules.pytorch_wrapper.pt_randn import PtRandn
from .modules.pytorch_wrapper.pt_remainder import PtRemainder
from .modules.pytorch_wrapper.pt_reshape import PtReshape
from .modules.pytorch_wrapper.pt_scatter import PtScatter
from .modules.pytorch_wrapper.pt_show_text import PtShowText
from .modules.pytorch_wrapper.pt_sin import PtSin
from .modules.pytorch_wrapper.pt_sinh import PtSinh
@@ -173,8 +185,11 @@ from .modules.pytorch_wrapper.pt_size import PtSize
from .modules.pytorch_wrapper.pt_size_create import PtSizeCreate
from .modules.pytorch_wrapper.pt_size_to_numpy import PtSizeToNumpy
from .modules.pytorch_wrapper.pt_size_to_string import PtSizeToString
from .modules.pytorch_wrapper.pt_sqrt import PtSqrt
from .modules.pytorch_wrapper.pt_squeeze import PtSqueeze
from .modules.pytorch_wrapper.pt_std import PtStd
from .modules.pytorch_wrapper.pt_sub import PtSub
from .modules.pytorch_wrapper.pt_sum import PtSum
from .modules.pytorch_wrapper.pt_tan import PtTan
from .modules.pytorch_wrapper.pt_tanh import PtTanh
from .modules.pytorch_wrapper.pt_to_image import PtToImage
@@ -182,7 +197,9 @@ from .modules.pytorch_wrapper.pt_to_latent import PtToLatent
from .modules.pytorch_wrapper.pt_to_numpy import PtToNumpy
from .modules.pytorch_wrapper.pt_to_rgb_tensors import PtToRgbTensors
from .modules.pytorch_wrapper.pt_unsqueeze import PtUnsqueeze
from .modules.pytorch_wrapper.pt_var import PtVar
from .modules.pytorch_wrapper.pt_view import PtView
from .modules.pytorch_wrapper.pt_where import PtWhere
from .modules.pytorch_wrapper.pt_zeros import PtZeros
from .modules.seaborn_wrapper.sns_bar import SNSBar
from .modules.seaborn_wrapper.sns_boxplot import SNSBoxplot
@@ -316,6 +333,8 @@ NODE_CLASS_MAPPINGS: Dict[str, Type[T]] = {
"PtAbs": PtAbs,
"PtAcos": PtAcos,
"PtAdd": PtAdd,
"PtArgmax": PtArgmax,
"PtArgmin": PtArgmin,
"PtAsin": PtAsin,
"PtAtan": PtAtan,
"PtBitwiseAnd": PtBitwiseAnd,
@@ -339,8 +358,10 @@ NODE_CLASS_MAPPINGS: Dict[str, Type[T]] = {
"PtFromLatent": PtFromLatent,
"PtFromNumpy": PtFromNumpy,
"PtFull": PtFull,
"PtGather": PtGather,
"PtGe": PtGe,
"PtGt": PtGt,
"PtIndexSelect": PtIndexSelect,
"PtIntCreate": PtIntCreate,
"PtLe": PtLe,
"PtLog": PtLog,
@@ -349,7 +370,12 @@ NODE_CLASS_MAPPINGS: Dict[str, Type[T]] = {
"PtLogicalOr": PtLogicalOr,
"PtLogicalXor": PtLogicalXor,
"PtLt": PtLt,
"PtMaskedSelect": PtMaskedSelect,
"PtMatMul": PtMatMul,
"PtMax": PtMax,
"PtMean": PtMean,
"PtMedian": PtMedian,
"PtMin": PtMin,
"PtMm": PtMm,
"PtMul": PtMul,
"PtNe": PtNe,
@@ -357,10 +383,13 @@ NODE_CLASS_MAPPINGS: Dict[str, Type[T]] = {
"PtOnes": PtOnes,
"PtPermute": PtPermute,
"PtPow": PtPow,
"PtProd": PtProd,
"PtRand": PtRand,
"PtRandInt": PtRandInt,
"PtRandn": PtRandn,
"PtRemainder": PtRemainder,
"PtReshape": PtReshape,
"PtScatter": PtScatter,
"PtShowText": PtShowText,
"PtSin": PtSin,
"PtSinh": PtSinh,
@@ -368,8 +397,11 @@ NODE_CLASS_MAPPINGS: Dict[str, Type[T]] = {
"PtSizeCreate": PtSizeCreate,
"PtSizeToNumpy": PtSizeToNumpy,
"PtSizeToString": PtSizeToString,
"PtSqrt": PtSqrt,
"PtSqueeze": PtSqueeze,
"PtStd": PtStd,
"PtSub": PtSub,
"PtSum": PtSum,
"PtTan": PtTan,
"PtTanh": PtTanh,
"PtToImage": PtToImage,
@@ -377,7 +409,9 @@ NODE_CLASS_MAPPINGS: Dict[str, Type[T]] = {
"PtToNumpy": PtToNumpy,
"PtToRgbTensors": PtToRgbTensors,
"PtUnsqueeze": PtUnsqueeze,
"PtVar": PtVar,
"PtView": PtView,
"PtWhere": PtWhere,
"PtZeros": PtZeros,
"PyKvFloatCreate": PyKvFloatCreate,
"PyKvIntCreate": PyKvIntCreate,
@@ -514,6 +548,8 @@ NODE_DISPLAY_NAME_MAPPINGS: Dict[str, str] = {
"PtAbs": "Pt Abs",
"PtAcos": "Pt Acos",
"PtAdd": "Pt Add",
"PtArgmax": "Pt Argmax",
"PtArgmin": "Pt Argmin",
"PtAsin": "Pt Asin",
"PtAtan": "Pt Atan",
"PtBitwiseAnd": "Pt Bitwise And",
@@ -537,8 +573,10 @@ NODE_DISPLAY_NAME_MAPPINGS: Dict[str, str] = {
"PtFromLatent": "Pt From Latent",
"PtFromNumpy": "Pt From Numpy",
"PtFull": "Pt Full",
"PtGather": "Pt Gather",
"PtGe": "Pt Ge",
"PtGt": "Pt Gt",
"PtIndexSelect": "Pt Index Select",
"PtIntCreate": "Pt Int Create",
"PtLe": "Pt Le",
"PtLog": "Pt Log",
@@ -547,7 +585,12 @@ NODE_DISPLAY_NAME_MAPPINGS: Dict[str, str] = {
"PtLogicalOr": "Pt Logical Or",
"PtLogicalXor": "Pt Logical Xor",
"PtLt": "Pt Lt",
"PtMaskedSelect": "Pt Masked Select",
"PtMatMul": "Pt Mat Mul",
"PtMax": "Pt Max",
"PtMean": "Pt Mean",
"PtMedian": "Pt Median",
"PtMin": "Pt Min",
"PtMm": "Pt Mm",
"PtMul": "Pt Mul",
"PtNe": "Pt Ne",
@@ -555,10 +598,13 @@ NODE_DISPLAY_NAME_MAPPINGS: Dict[str, str] = {
"PtOnes": "Pt Ones",
"PtPermute": "Pt Permute",
"PtPow": "Pt Pow",
"PtProd": "Pt Prod",
"PtRand": "Pt Rand",
"PtRandInt": "Pt Rand Int",
"PtRandn": "Pt Randn",
"PtRemainder": "Pt Remainder",
"PtReshape": "Pt Reshape",
"PtScatter": "Pt Scatter",
"PtShowText": "Pt Show Text",
"PtSin": "Pt Sin",
"PtSinh": "Pt Sinh",
@@ -566,8 +612,11 @@ NODE_DISPLAY_NAME_MAPPINGS: Dict[str, str] = {
"PtSizeCreate": "Pt Size Create",
"PtSizeToNumpy": "Pt Size To Numpy",
"PtSizeToString": "Pt Size To String",
"PtSqrt": "Pt Sqrt",
"PtSqueeze": "Pt Squeeze",
"PtStd": "Pt Std",
"PtSub": "Pt Sub",
"PtSum": "Pt Sum",
"PtTan": "Pt Tan",
"PtTanh": "Pt Tanh",
"PtToImage": "Pt To Image",
@@ -575,7 +624,9 @@ NODE_DISPLAY_NAME_MAPPINGS: Dict[str, str] = {
"PtToNumpy": "Pt To Numpy",
"PtToRgbTensors": "Pt To Rgb Tensors",
"PtUnsqueeze": "Pt Unsqueeze",
"PtVar": "Pt Var",
"PtView": "Pt View",
"PtWhere": "Pt Where",
"PtZeros": "Pt Zeros",
"PyKvFloatCreate": "Py Kv Float Create",
"PyKvIntCreate": "Py Kv Int Create",
+23
View File
@@ -168,6 +168,14 @@
| [Pt Le](pt_le.md) | Tests whether elements in the first PyTorch tensor are less than or equal to the corresponding elements in the second tensor. |
| [Pt Lt](pt_lt.md) | Tests whether elements in the first PyTorch tensor are less than the corresponding elements in the second tensor. |
| [Pt Ne](pt_ne.md) | Tests whether two PyTorch tensors are not equal element-wise. |
## PyTorch wrapper - Indexing and Slicing Operations
| Node | Description |
| --- | --- |
| [Pt Gather](pt_gather.md) | Generates a tensor based on the index tensor using PyTorch's `gather` function. |
| [Pt Index Select](pt_index_select.md) | Extracts elements from the input tensor along a specified dimension using an index tensor. |
| [Pt Masked Select](pt_masked_select.md) | Extracts elements from the input tensor whose corresponding value in `masked_tens` is `True`. |
| [Pt Scatter](pt_scatter.md) | Generates a new tensor by replacing values at specified positions using an index tensor. |
| [Pt Where](pt_where.md) | Generates a new tensor by selecting values based on a condition tensor. |
## PyTorch wrapper - Logical operations
| Node | Description |
| --- | --- |
@@ -189,6 +197,7 @@
| [Pt Neg](pt_neg.md) | Computes the negation of each element in a PyTorch tensor. |
| [Pt Sin](pt_sin.md) | Computes the sine of a PyTorch tensor element-wise. |
| [Pt Sinh](pt_sinh.md) | Computes the hyperbolic sine of a PyTorch tensor element-wise. |
| [Pt Sqrt](pt_sqrt.md) | Computes the square root of each element in a PyTorch tensor. |
| [Pt Tan](pt_tan.md) | Computes the tangent of a PyTorch tensor element-wise. |
| [Pt Tanh](pt_tanh.md) | Computes the hyperbolic tangent of a PyTorch tensor element-wise. |
## PyTorch wrapper - Matrix operations
@@ -198,6 +207,19 @@
| [Pt Einsum](pt_einsum.md) | Performs Tensor operations specified in the Einstein summation equation. |
| [Pt Mat Mul](pt_matmul.md) | Performs matrix multiplication on two PyTorch tensors. |
| [Pt Mm](pt_mm.md) | Performs 2D matrix multiplication on two PyTorch tensors. |
## PyTorch wrapper - Reduction operation & Summary statistics
| Node | Description |
| --- | --- |
| [Pt Argmax](pt_argmax.md) | Computes the indices of the maximum values of a PyTorch tensor along the specified dimension(s). |
| [Pt Argmin](pt_argmin.md) | Computes the indices of the minimum values of a PyTorch tensor along the specified dimension(s). |
| [Pt Max](pt_max.md) | Computes the maximum values of a PyTorch tensor along the specified dimension(s). |
| [Pt Mean](pt_mean.md) | Computes the mean of a PyTorch tensor along the specified dimension(s). |
| [Pt Median](pt_median.md) | Computes the median of a PyTorch tensor along the specified dimension(s). |
| [Pt Min](pt_min.md) | Computes the minimum values of a PyTorch tensor along the specified dimension(s). |
| [Pt Prod](pt_prod.md) | Computes the product of a PyTorch tensor along the specified dimension(s). |
| [Pt Std](pt_std.md) | Computes the standard deviation of a PyTorch tensor along the specified dimension(s). |
| [Pt Sum](pt_sum.md) | Computes the sum of a PyTorch tensor along the specified dimension(s). |
| [Pt Var](pt_var.md) | Computes the variance of a PyTorch tensor along the specified dimension(s). |
## PyTorch wrapper - Size object support
| Node | Description |
| --- | --- |
@@ -231,6 +253,7 @@
| Node | Description |
| --- | --- |
| [Pt Permute](pt_permute.md) | Permutes the dimensions of a PyTorch tensor according to the specified order. |
| [Pt Reshape](pt_reshape.md) | Reshapes a PyTorch tensor into a specified shape using `torch.reshape()`. |
| [Pt Squeeze](pt_squeeze.md) | Removes a dimension at the specified position in the input tensor if it is of size 1. |
| [Pt Unsqueeze](pt_unsqueeze.md) | Adds a singleton dimension at the specified position in the input tensor. |
| [Pt View](pt_view.md) | Reshapes a PyTorch tensor into a specified shape using `torch.view()`. |
+24
View File
@@ -0,0 +1,24 @@
# Pt Argmax
Computes the indices of the maximum values of a PyTorch tensor along the specified dimension(s).
Specify the dimension in the `dim` field using an integer as shown below:
```
0
```
## Input
| Name | Data type |
|---|---|
| tens | Tensor |
| dim | String |
| keepdim | Boolean |
## Output
| Data type |
|---|
| Tensor |
<HR>
Category: PyTorch wrapper - Reduction operation & Summary statistics
ComfyUI Data Analysis Node Reference. © 2025 Hide Inada (HowToSD.com). All rights reserved.
+24
View File
@@ -0,0 +1,24 @@
# Pt Argmin
Computes the indices of the minimum values of a PyTorch tensor along the specified dimension(s).
Specify the dimension in the `dim` field using an integer as shown below:
```
0
```
## Input
| Name | Data type |
|---|---|
| tens | Tensor |
| dim | String |
| keepdim | Boolean |
## Output
| Data type |
|---|
| Tensor |
<HR>
Category: PyTorch wrapper - Reduction operation & Summary statistics
ComfyUI Data Analysis Node Reference. © 2025 Hide Inada (HowToSD.com). All rights reserved.
+59
View File
@@ -0,0 +1,59 @@
# Pt Gather
Generates a tensor based on the index tensor using PyTorch's `gather` function.
Specify the `dim` field using an integer to indicate the axis along which to gather elements.
Each input tensor element is selected based on the index tensor.
For example, consider the following input tensor:
```
[ [10, 20, 30, 40, 50],
[100, 200, 300, 400, 500]]
```
and the corresponding index tensor:
```
[ [0, 4, 3, 2, 0],
[4, 3, 0, 0, 0]]
```
If `dim = 0`, the function scans row-wise, treating each index value as a row index.
If `dim = 1`, it scans column-wise, interpreting each index value as a column index.
Let's analyze the case where `dim = 1`:
* Start at row 0, column 0.
* The index at this position is `index[0, 0] = 0`.
* PyTorch selects the value at position 0 from the array `[10, 20, 30, 40, 50]`, which is `10`.
* The result at this stage is:
```
[[10, ?, ?, ?, ?]
[?, ?, ?, ?, ?]]
```
* Moving to column 1: `index[0, 1] = 4`.
* The value at index 4 in `[10, 20, 30, 40, 50]` is `50`.
* The updated result is:
```
[[10, 50, ?, ?, ?]
[?, ?, ?, ?, ?]]
```
* Repeating this process for all elements constructs the final gathered tensor.
## Input
| Name | Data type |
|---|---|
| tens | Tensor |
| dim | Int |
| index | String |
## Output
| Data type |
|---|
| Tensor |
<HR>
Category: PyTorch wrapper - Indexing and Slicing Operations
ComfyUI Data Analysis Node Reference. © 2025 Hide Inada (HowToSD.com). All rights reserved.
+41
View File
@@ -0,0 +1,41 @@
# Pt Index Select
Extracts elements from the input tensor along a specified dimension using an index tensor.
`dim` specifies the dimension along which the selection occurs.
* For example, for 2D tensors:
** If `dim=0`, `index` selects rows.
** If `dim=1`, `index` selects columns (for 2D tensors).
`index` is a list of indices that will be converted into a tensor.
Example:
```
Input Tensor:
[[1, 2, 3],
[40, 50, 60],
[700, 800, 900]]
index = "[0, 2]"
dim = 0
Output Tensor:
[[1, 2, 3],
[700, 800, 900]]
```
## Input
| Name | Data type |
|---|---|
| tens | Tensor |
| dim | Int |
| index | String |
## Output
| Data type |
|---|
| Tensor |
<HR>
Category: PyTorch wrapper - Indexing and Slicing Operations
ComfyUI Data Analysis Node Reference. © 2025 Hide Inada (HowToSD.com). All rights reserved.
+34
View File
@@ -0,0 +1,34 @@
# Pt Masked Select
Extracts elements from the input tensor whose corresponding value in `masked_tens` is `True`.
Example:
```
Input Tensor:
[[1, 2, 3],
[40, 50, 60],
[700, 800, 900]]
Masked Tensor:
[[True, False, False],
[False, True, False],
[False, False, True]]
Output Tensor:
[1, 50, 900]
```
## Input
| Name | Data type |
|---|---|
| tens | Tensor |
| masked_tens | Tensor |
## Output
| Data type |
|---|
| Tensor |
<HR>
Category: PyTorch wrapper - Indexing and Slicing Operations
ComfyUI Data Analysis Node Reference. © 2025 Hide Inada (HowToSD.com). All rights reserved.
+24
View File
@@ -0,0 +1,24 @@
# Pt Max
Computes the maximum values of a PyTorch tensor along the specified dimension(s).
Specify the dimension(s) in the `dim` field using an integer, a list, or a tuple, as shown below:
```
0, [0], or (1, 2)
```
## Input
| Name | Data type |
|---|---|
| tens | Tensor |
| dim | String |
| keepdim | Boolean |
## Output
| Data type |
|---|
| Tensor |
<HR>
Category: PyTorch wrapper - Reduction operation & Summary statistics
ComfyUI Data Analysis Node Reference. © 2025 Hide Inada (HowToSD.com). All rights reserved.
+26
View File
@@ -0,0 +1,26 @@
# Pt Mean
Computes the mean of a PyTorch tensor along the specified dimension(s).
This node supports only floating-point or complex tensors as input.
Specify the dimension(s) in the `dim` field using an integer, a list, or a tuple, as shown below:
```
0, [0], or (1, 2)
```
## Input
| Name | Data type |
|---|---|
| tens | Tensor |
| dim | String |
| keepdim | Boolean |
## Output
| Data type |
|---|
| Tensor |
<HR>
Category: PyTorch wrapper - Reduction operation & Summary statistics
ComfyUI Data Analysis Node Reference. © 2025 Hide Inada (HowToSD.com). All rights reserved.
+29
View File
@@ -0,0 +1,29 @@
# Pt Median
Computes the median of a PyTorch tensor along the specified dimension(s).
Specify the dimension(s) in the `dim` field using an integer as shown below:
```
0
```
or
```
1
```
Note that PtMedian calls torch.median(), which returns the lower of the two numbers when the true median falls between them.
## Input
| Name | Data type |
|---|---|
| tens | Tensor |
| dim | String |
| keepdim | Boolean |
## Output
| Data type |
|---|
| Tensor |
<HR>
Category: PyTorch wrapper - Reduction operation & Summary statistics
ComfyUI Data Analysis Node Reference. © 2025 Hide Inada (HowToSD.com). All rights reserved.
+24
View File
@@ -0,0 +1,24 @@
# Pt Min
Computes the minimum values of a PyTorch tensor along the specified dimension(s).
Specify the dimension(s) in the `dim` field using an integer, a list, or a tuple, as shown below:
```
0, [0], or (1, 2)
```
## Input
| Name | Data type |
|---|---|
| tens | Tensor |
| dim | String |
| keepdim | Boolean |
## Output
| Data type |
|---|
| Tensor |
<HR>
Category: PyTorch wrapper - Reduction operation & Summary statistics
ComfyUI Data Analysis Node Reference. © 2025 Hide Inada (HowToSD.com). All rights reserved.
+28
View File
@@ -0,0 +1,28 @@
# Pt Prod
Computes the product of a PyTorch tensor along the specified dimension(s).
Specify the dimension(s) in the `dim` field using an integer as shown below:
```
0
```
or
```
1
```
## Input
| Name | Data type |
|---|---|
| tens | Tensor |
| dim | String |
| keepdim | Boolean |
## Output
| Data type |
|---|
| Tensor |
<HR>
Category: PyTorch wrapper - Reduction operation & Summary statistics
ComfyUI Data Analysis Node Reference. © 2025 Hide Inada (HowToSD.com). All rights reserved.
+27
View File
@@ -0,0 +1,27 @@
# Pt Reshape
Reshapes a PyTorch tensor into a specified shape using `torch.reshape()`.
The total number of elements must remain unchanged.
For example, if a tensor has shape (2, 3, 4), and `new_shape="[6, 4]"`,
the output will have shape (6, 4).
Use -1 to automatically infer a dimension, e.g.:
```
[2, -1]
```
## Input
| Name | Data type |
|---|---|
| tens | Tensor |
| new_shape | String |
## Output
| Data type |
|---|
| Tensor |
<HR>
Category: PyTorch wrapper - Transform
ComfyUI Data Analysis Node Reference. © 2025 Hide Inada (HowToSD.com). All rights reserved.
+24
View File
@@ -0,0 +1,24 @@
# Pt Scatter
Generates a new tensor by replacing values at specified positions using an index tensor.
* Target tensor (tens): Some of whose values are replaced.
* Source tensor (src): Values from this tensor replace a part or whole of the target tensor.
* Index tensor (index): Specifies the positions in the target tensor for replacement.
## Input
| Name | Data type |
|---|---|
| tens | Tensor |
| dim | Int |
| index | String |
| src | String |
## Output
| Data type |
|---|
| Tensor |
<HR>
Category: PyTorch wrapper - Indexing and Slicing Operations
ComfyUI Data Analysis Node Reference. © 2025 Hide Inada (HowToSD.com). All rights reserved.
+17
View File
@@ -0,0 +1,17 @@
# Pt Sqrt
Computes the square root of each element in a PyTorch tensor.
## Input
| Name | Data type |
|---|---|
| tens_a | Tensor |
## Output
| Data type |
|---|
| Tensor |
<HR>
Category: PyTorch wrapper - Math operations
ComfyUI Data Analysis Node Reference. © 2025 Hide Inada (HowToSD.com). All rights reserved.
+28
View File
@@ -0,0 +1,28 @@
# Pt Std
Computes the standard deviation of a PyTorch tensor along the specified dimension(s).
Specify 1 to use Bessel's correction (N-1) for sample standard deviation.
Specify 0 to compute population standard deviation.
Specify the dimension(s) in the `dim` field using an integer, a list, or a tuple, as shown below:
```
0, [0], or (1, 2)
```
## Input
| Name | Data type |
|---|---|
| tens | Tensor |
| correction | Int |
| dim | String |
| keepdim | Boolean |
## Output
| Data type |
|---|
| Tensor |
<HR>
Category: PyTorch wrapper - Reduction operation & Summary statistics
ComfyUI Data Analysis Node Reference. © 2025 Hide Inada (HowToSD.com). All rights reserved.
+24
View File
@@ -0,0 +1,24 @@
# Pt Sum
Computes the sum of a PyTorch tensor along the specified dimension(s).
Specify the dimension(s) in the `dim` field using an integer, a list, or a tuple, as shown below:
```
0, [0], or (1, 2)
```
## Input
| Name | Data type |
|---|---|
| tens | Tensor |
| dim | String |
| keepdim | Boolean |
## Output
| Data type |
|---|
| Tensor |
<HR>
Category: PyTorch wrapper - Reduction operation & Summary statistics
ComfyUI Data Analysis Node Reference. © 2025 Hide Inada (HowToSD.com). All rights reserved.
+28
View File
@@ -0,0 +1,28 @@
# Pt Var
Computes the variance of a PyTorch tensor along the specified dimension(s).
Specify 1 to use Bessel's correction (N-1) for sample variance.
Specify 0 to compute population variance.
Specify the dimension(s) in the `dim` field using an integer, a list, or a tuple, as shown below:
```
0, [0], or (1, 2)
```
## Input
| Name | Data type |
|---|---|
| tens | Tensor |
| correction | Int |
| dim | String |
| keepdim | Boolean |
## Output
| Data type |
|---|
| Tensor |
<HR>
Category: PyTorch wrapper - Reduction operation & Summary statistics
ComfyUI Data Analysis Node Reference. © 2025 Hide Inada (HowToSD.com). All rights reserved.
+23
View File
@@ -0,0 +1,23 @@
# Pt Where
Generates a new tensor by selecting values based on a condition tensor.
* `condition_tens` (torch.Tensor): Boolean tensor that determines which values are selected.
* `true_tens` (torch.Tensor): Values to use where the condition is `True`.
* `false_tens` (torch.Tensor): Values to use where the condition is `False`.
## Input
| Name | Data type |
|---|---|
| condition_tens | Tensor |
| true_tens | Tensor |
| false_tens | Tensor |
## Output
| Data type |
|---|
| Tensor |
<HR>
Category: PyTorch wrapper - Indexing and Slicing Operations
ComfyUI Data Analysis Node Reference. © 2025 Hide Inada (HowToSD.com). All rights reserved.
+59
View File
@@ -0,0 +1,59 @@
from typing import Any, Dict
import torch
from .utils import str_to_dim
class PtArgmax:
"""
Computes the indices of the maximum values of a PyTorch tensor along the specified dimension(s).
Specify the dimension in the `dim` field using an integer as shown below:
```
0
```
category: PyTorch wrapper - Reduction operation & Summary statistics
"""
@classmethod
def INPUT_TYPES(cls) -> Dict[str, Any]:
"""
Defines the input types for the function.
Returns:
Dict[str, Any]: A dictionary specifying required input types.
"""
return {
"required": {
"tens": ("TENSOR", {}),
"dim": ("STRING", {"multiline": True}),
"keepdim": ("BOOLEAN", {"default": False})
}
}
RETURN_TYPES: tuple = ("TENSOR",)
FUNCTION: str = "f"
CATEGORY: str = "Data Analysis"
def f(self, tens: torch.Tensor, dim: str, keepdim: bool) -> tuple:
"""
Computes the indices of the maximum values along the specified dimension(s).
Args:
tens (torch.Tensor): The input tensor.
dim (str): A string representation of the dimension(s) along which to compute argmax,
e.g., "0" or "".
keepdim (bool): Whether to retain reduced dimensions.
Returns:
tuple: A tuple containing the tensor with indices of maximum values.
"""
dim = str_to_dim(dim)
# Ensure keepdim is a boolean
if not isinstance(keepdim, bool):
raise TypeError(f"Invalid type for keepdim: {keepdim}. Must be a boolean.")
# Compute the argmax along the specified dimension(s)
tens = torch.argmax(tens, dim=dim, keepdim=keepdim)
return (tens,)
+59
View File
@@ -0,0 +1,59 @@
from typing import Any, Dict
import torch
from .utils import str_to_dim
class PtArgmin:
"""
Computes the indices of the minimum values of a PyTorch tensor along the specified dimension(s).
Specify the dimension in the `dim` field using an integer as shown below:
```
0
```
category: PyTorch wrapper - Reduction operation & Summary statistics
"""
@classmethod
def INPUT_TYPES(cls) -> Dict[str, Any]:
"""
Defines the input types for the function.
Returns:
Dict[str, Any]: A dictionary specifying required input types.
"""
return {
"required": {
"tens": ("TENSOR", {}),
"dim": ("STRING", {"multiline": True}),
"keepdim": ("BOOLEAN", {"default": False})
}
}
RETURN_TYPES: tuple = ("TENSOR",)
FUNCTION: str = "f"
CATEGORY: str = "Data Analysis"
def f(self, tens: torch.Tensor, dim: str, keepdim: bool) -> tuple:
"""
Computes the indices of the minimum values along the specified dimension(s).
Args:
tens (torch.Tensor): The input tensor.
dim (str): A string representation of the dimension(s) along which to compute argmin,
e.g., "0" or "".
keepdim (bool): Whether to retain reduced dimensions.
Returns:
tuple: A tuple containing the tensor with indices of minimum values.
"""
dim = str_to_dim(dim)
# Ensure keepdim is a boolean
if not isinstance(keepdim, bool):
raise TypeError(f"Invalid type for keepdim: {keepdim}. Must be a boolean.")
# Compute the argmin along the specified dimension(s)
tens = torch.argmin(tens, dim=dim, keepdim=keepdim)
return (tens,)
+94
View File
@@ -0,0 +1,94 @@
from typing import Any, Dict
import torch
import ast
class PtGather:
"""
Generates a tensor based on the index tensor using PyTorch's `gather` function.
Specify the `dim` field using an integer to indicate the axis along which to gather elements.
Each input tensor element is selected based on the index tensor.
For example, consider the following input tensor:
```
[ [10, 20, 30, 40, 50],
[100, 200, 300, 400, 500]]
```
and the corresponding index tensor:
```
[ [0, 4, 3, 2, 0],
[4, 3, 0, 0, 0]]
```
If `dim = 0`, the function scans row-wise, treating each index value as a row index.
If `dim = 1`, it scans column-wise, interpreting each index value as a column index.
Let's analyze the case where `dim = 1`:
* Start at row 0, column 0.
* The index at this position is `index[0, 0] = 0`.
* PyTorch selects the value at position 0 from the array `[10, 20, 30, 40, 50]`, which is `10`.
* The result at this stage is:
```
[[10, ?, ?, ?, ?]
[?, ?, ?, ?, ?]]
```
* Moving to column 1: `index[0, 1] = 4`.
* The value at index 4 in `[10, 20, 30, 40, 50]` is `50`.
* The updated result is:
```
[[10, 50, ?, ?, ?]
[?, ?, ?, ?, ?]]
```
* Repeating this process for all elements constructs the final gathered tensor.
category: PyTorch wrapper - Indexing and Slicing Operations
"""
@classmethod
def INPUT_TYPES(cls) -> Dict[str, Any]:
"""
Defines the input types for the function.
Returns:
Dict[str, Any]: A dictionary specifying required input types.
"""
return {
"required": {
"tens": ("TENSOR", {}),
"dim": ("INT", {"default": 0, "min": -10, "max": 10}),
"index": ("STRING", {"multiline": True})
}
}
RETURN_TYPES: tuple = ("TENSOR",)
FUNCTION: str = "f"
CATEGORY: str = "Data Analysis"
def f(self, tens: torch.Tensor, dim: int, index: str) -> tuple:
"""
Gathers elements along the specified dimension using an index tensor.
Args:
tens (torch.Tensor): The input tensor.
dim (int): The dimension along which to gather values.
index (str): A string representation of the index tensor, provided in list format.
Returns:
tuple: A tuple containing the gathered tensor.
"""
# Parse the index string to a Python list
index = ast.literal_eval(index)
# Convert the parsed list to a tensor
index_tensor = torch.tensor(index, dtype=torch.long, device=tens.device)
# Perform the gather operation
gathered_tens = torch.gather(tens, dim=dim, index=index_tensor)
return (gathered_tens,)
@@ -0,0 +1,78 @@
from typing import Any, Dict
import torch
import ast
class PtIndexSelect:
"""
Extracts elements from the input tensor along a specified dimension using an index tensor.
`dim` specifies the dimension along which the selection occurs.
* For example, for 2D tensors:
** If `dim=0`, `index` selects rows.
** If `dim=1`, `index` selects columns (for 2D tensors).
`index` is a list of indices that will be converted into a tensor.
Example:
```
Input Tensor:
[[1, 2, 3],
[40, 50, 60],
[700, 800, 900]]
index = "[0, 2]"
dim = 0
Output Tensor:
[[1, 2, 3],
[700, 800, 900]]
```
category: PyTorch wrapper - Indexing and Slicing Operations
"""
@classmethod
def INPUT_TYPES(cls) -> Dict[str, Any]:
"""
Defines the input types for the function.
Returns:
Dict[str, Any]: A dictionary specifying required input types.
"""
return {
"required": {
"tens": ("TENSOR", {}),
"dim": ("INT", {"default": 0, "min": -10, "max": 10}),
"index": ("STRING", {"multiline": True})
}
}
RETURN_TYPES: tuple = ("TENSOR",)
FUNCTION: str = "f"
CATEGORY: str = "Data Analysis"
def f(self, tens: torch.Tensor, dim: int, index: str) -> tuple:
"""
Extracts elements from the input tensor along the specified dimension.
Args:
tens (torch.Tensor): The input tensor.
dim (int): The dimension along which elements are selected.
index (str): A string representation of a list of indices, e.g., "[0, 2]".
Returns:
tuple: A tuple containing the selected tensor.
"""
# Convert the string index to a Python list
index_list = ast.literal_eval(index)
if not isinstance(index_list, list):
raise ValueError("Index must be a list of integers.")
# Convert the index list to a tensor
index_tensor = torch.tensor(index_list, dtype=torch.long)
# Perform index selection
result = torch.index_select(tens, dim=dim, index=index_tensor)
return (result,)
@@ -0,0 +1,62 @@
from typing import Any, Dict
import torch
class PtMaskedSelect:
"""
Extracts elements from the input tensor whose corresponding value in `masked_tens` is `True`.
Example:
```
Input Tensor:
[[1, 2, 3],
[40, 50, 60],
[700, 800, 900]]
Masked Tensor:
[[True, False, False],
[False, True, False],
[False, False, True]]
Output Tensor:
[1, 50, 900]
```
category: PyTorch wrapper - Indexing and Slicing Operations
"""
@classmethod
def INPUT_TYPES(cls) -> Dict[str, Any]:
"""
Defines the input types for the function.
Returns:
Dict[str, Any]: A dictionary specifying required input types.
"""
return {
"required": {
"tens": ("TENSOR", {}),
"masked_tens": ("TENSOR", {})
}
}
RETURN_TYPES: tuple = ("TENSOR",)
FUNCTION: str = "f"
CATEGORY: str = "Data Analysis"
def f(self, tens: torch.Tensor, masked_tens: torch.Tensor) -> tuple:
"""
Extracts elements from `tens` where `masked_tens` is `True`.
Args:
tens (torch.Tensor): The input tensor.
masked_tens (torch.Tensor): A boolean tensor specifying which elements to select.
Returns:
tuple: A tuple containing the selected tensor.
"""
if masked_tens.dtype != torch.bool:
raise ValueError("masked_tens must be a boolean tensor (dtype=torch.bool)")
result = torch.masked_select(tens, masked_tens)
return (result,)
+59
View File
@@ -0,0 +1,59 @@
from typing import Any, Dict
import torch
import ast
from .utils import str_to_dim
class PtMax:
"""
Computes the maximum values of a PyTorch tensor along the specified dimension(s).
Specify the dimension(s) in the `dim` field using an integer, a list, or a tuple, as shown below:
```
0, [0], or (1, 2)
```
category: PyTorch wrapper - Reduction operation & Summary statistics
"""
@classmethod
def INPUT_TYPES(cls) -> Dict[str, Any]:
"""
Defines the input types for the function.
Returns:
Dict[str, Any]: A dictionary specifying required input types.
"""
return {
"required": {
"tens": ("TENSOR", {}),
"dim": ("STRING", {"multiline": True}),
"keepdim": ("BOOLEAN", {"default": False})
}
}
RETURN_TYPES: tuple = ("TENSOR",)
FUNCTION: str = "f"
CATEGORY: str = "Data Analysis"
def f(self, tens: torch.Tensor, dim: str, keepdim: bool) -> tuple:
"""
Computes the maximum values along the specified dimension(s).
Args:
tens (torch.Tensor): The input tensor.
dim (str): A string representation of the dimension(s) along which to compute the maximum,
e.g., "0", "[0]", or "(1, 2)".
keepdim (bool): Whether to retain reduced dimensions.
Returns:
tuple: A tuple containing the tensor with maximum values.
"""
dim = str_to_dim(dim)
# Ensure keepdim is a boolean
if not isinstance(keepdim, bool):
raise TypeError(f"Invalid type for keepdim: {keepdim}. Must be a boolean.")
# Compute the max along the specified dimension(s)
tens = torch.amax(tens, dim=dim, keepdim=keepdim)
return (tens,)
+61
View File
@@ -0,0 +1,61 @@
from typing import Any, Dict
import torch
from .utils import str_to_dim
class PtMean:
"""
Computes the mean of a PyTorch tensor along the specified dimension(s).
This node supports only floating-point or complex tensors as input.
Specify the dimension(s) in the `dim` field using an integer, a list, or a tuple, as shown below:
```
0, [0], or (1, 2)
```
category: PyTorch wrapper - Reduction operation & Summary statistics
"""
@classmethod
def INPUT_TYPES(cls) -> Dict[str, Any]:
"""
Defines the input types for the function.
Returns:
Dict[str, Any]: A dictionary specifying required input types.
"""
return {
"required": {
"tens": ("TENSOR", {}),
"dim": ("STRING", {"multiline": True}),
"keepdim": ("BOOLEAN", {"default": False})
}
}
RETURN_TYPES: tuple = ("TENSOR",)
FUNCTION: str = "f"
CATEGORY: str = "Data Analysis"
def f(self, tens: torch.Tensor, dim: str, keepdim: bool) -> tuple:
"""
Computes the mean along the specified dimension(s).
Args:
tens (torch.Tensor): The input tensor.
dim (str): A string representation of the dimension(s) along which to compute the mean,
e.g., "0", "[0]", or "(1, 2)".
keepdim (bool): Whether to retain reduced dimensions.
Returns:
tuple: A tuple containing the mean tensor.
"""
dim = str_to_dim(dim)
# Ensure keepdim is a boolean
if not isinstance(keepdim, bool):
raise TypeError(f"Invalid type for keepdim: {keepdim}. Must be a boolean.")
# Compute the mean along the specified dimension(s)
tens = torch.mean(tens, dim=dim, keepdim=keepdim)
return (tens,)
+66
View File
@@ -0,0 +1,66 @@
from typing import Any, Dict
import torch
from .utils import str_to_dim
class PtMedian:
"""
Computes the median of a PyTorch tensor along the specified dimension(s).
Specify the dimension(s) in the `dim` field using an integer as shown below:
```
0
```
or
```
1
```
Note that PtMedian calls torch.median(), which returns the lower of the two numbers when the true median falls between them.
category: PyTorch wrapper - Reduction operation & Summary statistics
"""
@classmethod
def INPUT_TYPES(cls) -> Dict[str, Any]:
"""
Defines the input types for the function.
Returns:
Dict[str, Any]: A dictionary specifying required input types.
"""
return {
"required": {
"tens": ("TENSOR", {}),
"dim": ("STRING", {"multiline": True}),
"keepdim": ("BOOLEAN", {"default": False})
}
}
RETURN_TYPES: tuple = ("TENSOR",)
FUNCTION: str = "f"
CATEGORY: str = "Data Analysis"
def f(self, tens: torch.Tensor, dim: str, keepdim: bool) -> tuple:
"""
Computes the median along the specified dimension(s).
Args:
tens (torch.Tensor): The input tensor.
dim (str): A string representation of the dimension(s) along which to compute the median,
e.g., "0", or "1".
keepdim (bool): Whether to retain reduced dimensions.
Returns:
tuple: A tuple containing the median tensor.
"""
dim = str_to_dim(dim)
if not isinstance(dim, int):
raise TypeError(f"Invalid type for dim: {dim}. Must be a integer.")
# Ensure keepdim is a boolean
if not isinstance(keepdim, bool):
raise TypeError(f"Invalid type for keepdim: {keepdim}. Must be a boolean.")
# Compute the median along the specified dimension(s)
tens, _ = torch.median(tens, dim=dim, keepdim=keepdim)
return (tens,)
+59
View File
@@ -0,0 +1,59 @@
from typing import Any, Dict
import torch
from .utils import str_to_dim
class PtMin:
"""
Computes the minimum values of a PyTorch tensor along the specified dimension(s).
Specify the dimension(s) in the `dim` field using an integer, a list, or a tuple, as shown below:
```
0, [0], or (1, 2)
```
category: PyTorch wrapper - Reduction operation & Summary statistics
"""
@classmethod
def INPUT_TYPES(cls) -> Dict[str, Any]:
"""
Defines the input types for the function.
Returns:
Dict[str, Any]: A dictionary specifying required input types.
"""
return {
"required": {
"tens": ("TENSOR", {}),
"dim": ("STRING", {"multiline": True}),
"keepdim": ("BOOLEAN", {"default": False})
}
}
RETURN_TYPES: tuple = ("TENSOR",)
FUNCTION: str = "f"
CATEGORY: str = "Data Analysis"
def f(self, tens: torch.Tensor, dim: str, keepdim: bool) -> tuple:
"""
Computes the minimum values along the specified dimension(s).
Args:
tens (torch.Tensor): The input tensor.
dim (str): A string representation of the dimension(s) along which to compute the minimum,
e.g., "0", "[0]", or "(1, 2)".
keepdim (bool): Whether to retain reduced dimensions.
Returns:
tuple: A tuple containing the tensor with minimum values.
"""
dim = str_to_dim(dim)
# Ensure keepdim is a boolean
if not isinstance(keepdim, bool):
raise TypeError(f"Invalid type for keepdim: {keepdim}. Must be a boolean.")
# Compute the min along the specified dimension(s)
tens = torch.amin(tens, dim=dim, keepdim=keepdim)
return (tens,)
+65
View File
@@ -0,0 +1,65 @@
from typing import Any, Dict
import torch
from .utils import str_to_dim
class PtProd:
"""
Computes the product of a PyTorch tensor along the specified dimension(s).
Specify the dimension(s) in the `dim` field using an integer as shown below:
```
0
```
or
```
1
```
category: PyTorch wrapper - Reduction operation & Summary statistics
"""
@classmethod
def INPUT_TYPES(cls) -> Dict[str, Any]:
"""
Defines the input types for the function.
Returns:
Dict[str, Any]: A dictionary specifying required input types.
"""
return {
"required": {
"tens": ("TENSOR", {}),
"dim": ("STRING", {"multiline": True}),
"keepdim": ("BOOLEAN", {"default": False})
}
}
RETURN_TYPES: tuple = ("TENSOR",)
FUNCTION: str = "f"
CATEGORY: str = "Data Analysis"
def f(self, tens: torch.Tensor, dim: str, keepdim: bool) -> tuple:
"""
Computes the product along the specified dimension(s).
Args:
tens (torch.Tensor): The input tensor.
dim (str): A string representation of the dimension(s) along which to compute the product,
e.g., "0" or "1".
keepdim (bool): Whether to retain reduced dimensions.
Returns:
tuple: A tuple containing the product tensor.
"""
dim = str_to_dim(dim)
if not isinstance(dim, int):
raise TypeError(f"Invalid type for dim: {dim}. Must be a integer.")
# Ensure keepdim is a boolean
if not isinstance(keepdim, bool):
raise TypeError(f"Invalid type for keepdim: {keepdim}. Must be a boolean.")
# Compute the product along the specified dimension(s)
tens = torch.prod(tens, dim=dim, keepdim=keepdim)
return (tens,)
+62
View File
@@ -0,0 +1,62 @@
from typing import Any, Dict
import torch
import ast
class PtReshape:
"""
Reshapes a PyTorch tensor into a specified shape using `torch.reshape()`.
The total number of elements must remain unchanged.
For example, if a tensor has shape (2, 3, 4), and `new_shape="[6, 4]"`,
the output will have shape (6, 4).
Use -1 to automatically infer a dimension, e.g.:
```
[2, -1]
```
category: PyTorch wrapper - Transform
"""
@classmethod
def INPUT_TYPES(cls) -> Dict[str, Any]:
"""
Defines the input types for the function.
Returns:
Dict[str, Any]: A dictionary specifying required input types.
"""
return {
"required": {
"tens": ("TENSOR", {}),
"new_shape": ("STRING", {"multiline": True})
}
}
RETURN_TYPES: tuple = ("TENSOR",)
FUNCTION: str = "f"
CATEGORY: str = "Data Analysis"
def f(self, tens: torch.Tensor, new_shape: str) -> tuple:
"""
Reshapes the input tensor based on the specified shape.
Args:
tens (torch.Tensor): The input tensor.
new_shape (str): A string representation of the desired shape,
e.g., "[2, -1]" or "(6, 4)".
Returns:
tuple: A tuple containing the reshaped PyTorch tensor.
"""
# Convert string to a tuple of integers
shape_order = tuple(ast.literal_eval(new_shape))
# Ensure the parsed shape is valid
if not isinstance(shape_order, tuple) or not all(isinstance(i, int) for i in shape_order):
raise ValueError(f"Invalid format for new_shape: {new_shape}. Must be a tuple or list of integers.")
# Apply reshape operation
tens = tens.reshape(*shape_order)
return (tens,)
+69
View File
@@ -0,0 +1,69 @@
from typing import Any, Dict
import torch
import ast
class PtScatter:
"""
Generates a new tensor by replacing values at specified positions using an index tensor.
* Target tensor (tens): Some of whose values are replaced.
* Source tensor (src): Values from this tensor replace a part or whole of the target tensor.
* Index tensor (index): Specifies the positions in the target tensor for replacement.
category: PyTorch wrapper - Indexing and Slicing Operations
"""
@classmethod
def INPUT_TYPES(cls) -> Dict[str, Any]:
"""
Defines the input types for the function.
Returns:
Dict[str, Any]: A dictionary specifying required input types.
"""
return {
"required": {
"tens": ("TENSOR", {}),
"dim": ("INT", {"default": 0, "min": -10, "max": 10}),
"index": ("STRING", {"multiline": True}),
"src": ("STRING", {"multiline": True}),
}
}
RETURN_TYPES: tuple = ("TENSOR",)
FUNCTION: str = "f"
CATEGORY: str = "Data Analysis"
def f(self,
tens: torch.Tensor,
dim: int,
index: str,
src: str) -> tuple:
"""
Replaces elements in the target tensor along the specified dimension using an index tensor.
Args:
tens (torch.Tensor): The target tensor whose values will be replaced.
dim (int): The dimension along which values are replaced.
index (str): A string representation of the index tensor (list format).
src (str): A string representation of the source tensor (list format).
Returns:
tuple: A tuple containing the modified tensor.
"""
# Parse the input index and source tensors from string representations
index_list = ast.literal_eval(index)
src_list = ast.literal_eval(src)
# Convert to tensors
index_tensor = torch.tensor(index_list, dtype=torch.long, device=tens.device)
src_tensor = torch.tensor(src_list, dtype=tens.dtype, device=tens.device)
# Ensure index and src shapes match
if index_tensor.shape != src_tensor.shape:
raise ValueError(f"Shape mismatch: index tensor {index_tensor.shape} and src tensor {src_tensor.shape} must match.")
# Perform the scatter operation
updated_tens = torch.scatter(tens.clone(), dim=dim, index=index_tensor, src=src_tensor)
return (updated_tens,)
+39
View File
@@ -0,0 +1,39 @@
from typing import Any, Dict
import torch
class PtSqrt:
"""
Computes the square root of each element in a PyTorch tensor.
category: PyTorch wrapper - Math operations
"""
@classmethod
def INPUT_TYPES(cls) -> Dict[str, Any]:
"""
Defines the required input types.
Returns:
Dict[str, Any]: A dictionary of required input types.
"""
return {
"required": {
"tens_a": ("TENSOR", {})
}
}
RETURN_TYPES: tuple = ("TENSOR",)
FUNCTION: str = "f"
CATEGORY: str = "Data Analysis"
def f(self, tens_a: torch.Tensor) -> tuple:
"""
Computes the square root of a PyTorch tensor element-wise.
Args:
tens_a (torch.Tensor): A PyTorch tensor of numerical type.
Returns:
tuple: A tuple containing the resultant tensor after applying the square root function.
"""
return (torch.sqrt(tens_a),)
+68
View File
@@ -0,0 +1,68 @@
from typing import Any, Dict
import torch
from .utils import str_to_dim
class PtStd:
"""
Computes the standard deviation of a PyTorch tensor along the specified dimension(s).
Specify 1 to use Bessel's correction (N-1) for sample standard deviation.
Specify 0 to compute population standard deviation.
Specify the dimension(s) in the `dim` field using an integer, a list, or a tuple, as shown below:
```
0, [0], or (1, 2)
```
category: PyTorch wrapper - Reduction operation & Summary statistics
"""
@classmethod
def INPUT_TYPES(cls) -> Dict[str, Any]:
"""
Defines the input types for the function.
Returns:
Dict[str, Any]: A dictionary specifying required input types.
"""
return {
"required": {
"tens": ("TENSOR", {}),
"correction": ("INT", {"default": 1, "min": 0, "max": 1}),
"dim": ("STRING", {"multiline": True}),
"keepdim": ("BOOLEAN", {"default": False})
}
}
RETURN_TYPES: tuple = ("TENSOR",)
FUNCTION: str = "f"
CATEGORY: str = "Data Analysis"
def f(self, tens: torch.Tensor, dim: str, correction: int, keepdim: bool) -> tuple:
"""
Computes the standard deviation along the specified dimension(s).
Args:
tens (torch.Tensor): The input tensor.
dim (str): A string representation of the dimension(s) along which to compute the standard deviation,
e.g., "0", "[0]", or "(1, 2)".
correction (int): 0 for population standard deviation, 1 for sample standard deviation (Bessel's correction).
keepdim (bool): Whether to retain reduced dimensions.
Returns:
tuple: A tuple containing the standard deviation tensor.
"""
dim = str_to_dim(dim)
# Ensure correction is valid (0 or 1)
if correction not in (0, 1):
raise ValueError(f"Invalid correction value: {correction}. Must be 0 (population) or 1 (sample).")
# Ensure keepdim is a boolean
if not isinstance(keepdim, bool):
raise TypeError(f"Invalid type for keepdim: {keepdim}. Must be a boolean.")
# Compute the standard deviation along the specified dimension(s)
tens = torch.std(tens, dim=dim, correction=correction, keepdim=keepdim)
return (tens,)
+59
View File
@@ -0,0 +1,59 @@
from typing import Any, Dict
import torch
from .utils import str_to_dim
class PtSum:
"""
Computes the sum of a PyTorch tensor along the specified dimension(s).
Specify the dimension(s) in the `dim` field using an integer, a list, or a tuple, as shown below:
```
0, [0], or (1, 2)
```
category: PyTorch wrapper - Reduction operation & Summary statistics
"""
@classmethod
def INPUT_TYPES(cls) -> Dict[str, Any]:
"""
Defines the input types for the function.
Returns:
Dict[str, Any]: A dictionary specifying required input types.
"""
return {
"required": {
"tens": ("TENSOR", {}),
"dim": ("STRING", {"multiline": True}),
"keepdim": ("BOOLEAN", {"default": False})
}
}
RETURN_TYPES: tuple = ("TENSOR",)
FUNCTION: str = "f"
CATEGORY: str = "Data Analysis"
def f(self, tens: torch.Tensor, dim: str, keepdim: bool) -> tuple:
"""
Computes the sum along the specified dimension(s).
Args:
tens (torch.Tensor): The input tensor.
dim (str): A string representation of the dimension(s) along which to compute the sum,
e.g., "0", "[0]", or "(1, 2)".
keepdim (bool): Whether to retain reduced dimensions.
Returns:
tuple: A tuple containing the summed tensor.
"""
dim = str_to_dim(dim)
# Ensure keepdim is a boolean
if not isinstance(keepdim, bool):
raise TypeError(f"Invalid type for keepdim: {keepdim}. Must be a boolean.")
# Compute the sum along the specified dimension(s)
tens = torch.sum(tens, dim=dim, keepdim=keepdim)
return (tens,)
+68
View File
@@ -0,0 +1,68 @@
from typing import Any, Dict
import torch
from .utils import str_to_dim
class PtVar:
"""
Computes the variance of a PyTorch tensor along the specified dimension(s).
Specify 1 to use Bessel's correction (N-1) for sample variance.
Specify 0 to compute population variance.
Specify the dimension(s) in the `dim` field using an integer, a list, or a tuple, as shown below:
```
0, [0], or (1, 2)
```
category: PyTorch wrapper - Reduction operation & Summary statistics
"""
@classmethod
def INPUT_TYPES(cls) -> Dict[str, Any]:
"""
Defines the input types for the function.
Returns:
Dict[str, Any]: A dictionary specifying required input types.
"""
return {
"required": {
"tens": ("TENSOR", {}),
"correction": ("INT", {"default": 1, "min": 0, "max": 1}),
"dim": ("STRING", {"multiline": True}),
"keepdim": ("BOOLEAN", {"default": False})
}
}
RETURN_TYPES: tuple = ("TENSOR",)
FUNCTION: str = "f"
CATEGORY: str = "Data Analysis"
def f(self, tens: torch.Tensor, dim: str, correction: int, keepdim: bool) -> tuple:
"""
Computes the variance along the specified dimension(s).
Args:
tens (torch.Tensor): The input tensor.
dim (str): A string representation of the dimension(s) along which to compute the variance,
e.g., "0", "[0]", or "(1, 2)".
correction (int): 0 for population variance, 1 for sample variance (Bessel's correction).
keepdim (bool): Whether to retain reduced dimensions.
Returns:
tuple: A tuple containing the variance tensor.
"""
dim = str_to_dim(dim)
# Ensure correction is valid (0 or 1)
if correction not in (0, 1):
raise ValueError(f"Invalid correction value: {correction}. Must be 0 (population) or 1 (sample).")
# Ensure keepdim is a boolean
if not isinstance(keepdim, bool):
raise TypeError(f"Invalid type for keepdim: {keepdim}. Must be a boolean.")
# Compute the variance along the specified dimension(s)
tens = torch.var(tens, dim=dim, correction=correction, keepdim=keepdim)
return (tens,)
+61
View File
@@ -0,0 +1,61 @@
from typing import Any, Dict
import torch
class PtWhere:
"""
Generates a new tensor by selecting values based on a condition tensor.
* `condition_tens` (torch.Tensor): Boolean tensor that determines which values are selected.
* `true_tens` (torch.Tensor): Values to use where the condition is `True`.
* `false_tens` (torch.Tensor): Values to use where the condition is `False`.
category: PyTorch wrapper - Indexing and Slicing Operations
"""
@classmethod
def INPUT_TYPES(cls) -> Dict[str, Any]:
"""
Defines the input types for the function.
Returns:
Dict[str, Any]: A dictionary specifying required input types.
"""
return {
"required": {
"condition_tens": ("TENSOR", {}),
"true_tens": ("TENSOR", {}),
"false_tens": ("TENSOR", {}),
}
}
RETURN_TYPES: tuple = ("TENSOR",)
FUNCTION: str = "f"
CATEGORY: str = "Data Analysis"
def f(self,
condition_tens: torch.Tensor,
true_tens: torch.Tensor,
false_tens: torch.Tensor) -> tuple:
"""
Selects values from `true_tens` or `false_tens` based on `condition_tens`.
Args:
condition_tens (torch.Tensor): Boolean tensor indicating selection conditions.
true_tens (torch.Tensor): Values used where condition is True.
false_tens (torch.Tensor): Values used where condition is False.
Returns:
tuple: A tuple containing the modified tensor.
"""
# Ensure condition_tens is a boolean tensor
if condition_tens.dtype != torch.bool:
raise TypeError(f"Expected `condition_tens` to be a boolean tensor, got {condition_tens.dtype}")
# Ensure all tensors are compatible in shape
if not torch.broadcast_shapes(condition_tens.shape, true_tens.shape, false_tens.shape):
raise ValueError(f"Shape mismatch: condition {condition_tens.shape}, true {true_tens.shape}, false {false_tens.shape}")
# Perform the conditional selection
tens_out = torch.where(condition_tens, true_tens, false_tens)
return (tens_out,)
+39 -1
View File
@@ -1,3 +1,5 @@
import ast
from typing import Optional, Tuple, Union
import torch
DTYPE_MAPPING = {
@@ -11,4 +13,40 @@ DTYPE_MAPPING = {
"int32": torch.int32,
"int64": torch.int64,
"bool": torch.bool
}
}
def str_to_dim(dim: str) -> Optional[Union[int, Tuple[int, ...]]]:
"""
Converts a string representation of a dimension into an integer or a tuple of integers.
Args:
dim (str): A string representing an integer or a tuple of integers.
Returns:
Optional[Union[int, Tuple[int, ...]]]:
- An integer if the input represents a single integer.
- A tuple of integers if the input represents a list or tuple.
- None if the input is empty.
Raises:
ValueError: If the parsed value is not an integer or a tuple of integers.
"""
if dim:
# Convert string to an integer, list, or tuple of integers
parsed_dim = ast.literal_eval(dim)
# Convert list to tuple
if isinstance(parsed_dim, list):
parsed_dim = tuple(parsed_dim)
# Ensure the parsed dim is valid
if not isinstance(parsed_dim, (int, tuple)) or (
isinstance(parsed_dim, tuple) and not all(isinstance(i, int) for i in parsed_dim)
):
raise ValueError(f"Invalid format for dim: {parsed_dim}. Must be an integer or a tuple of integers.")
return parsed_dim
return None
+1 -1
View File
@@ -1,7 +1,7 @@
[project]
name = "data-analysis"
description = "Data analysis custom modules for ComfyUI - Use Pandas & Matplotlib within ComfyUI"
version = "1.4.1"
version = "1.4.2"
license = {file = "LICENSE"}
[project.urls]
+39
View File
@@ -0,0 +1,39 @@
import os
import sys
import unittest
import torch
import ast
PROJECT_ROOT = os.path.realpath(os.path.join(os.path.dirname(__file__), "..", ".."))
MODULE_ROOT = os.path.join(PROJECT_ROOT, "modules")
sys.path.append(MODULE_ROOT)
from pytorch_wrapper.pt_argmax import PtArgmax
class TestPtArgmax(unittest.TestCase):
def setUp(self):
"""Set up test instance."""
self.node = PtArgmax()
# Test cases: (Tensor, Dim (as string), keepdim, Expected result)
self.test_cases = [
(torch.tensor([[1, 3, 2], [4, 0, 5]]), "0", False, torch.tensor([1, 0, 1])),
(torch.tensor([[1, 3, 2], [4, 0, 5]]), "1", True, torch.tensor([[1], [2]])),
(torch.tensor([[10, 3, 2], [123, 0, 5]]), "", False, torch.tensor(3)),
]
def test_argmax(self):
"""Test torch.argmax behavior."""
for tens, dim, keepdim, expected in self.test_cases:
with self.subTest(tens=tens, dim=dim, keepdim=keepdim):
result, = self.node.f(tens, dim, keepdim)
# Ensure return value is a tensor
self.assertTrue(isinstance(result, torch.Tensor), "Output is not a tensor")
# Ensure result matches expected indices
self.assertTrue(torch.equal(result, expected), f"Expected {expected}, got {result}")
if __name__ == "__main__":
unittest.main()
+39
View File
@@ -0,0 +1,39 @@
import os
import sys
import unittest
import torch
import ast
PROJECT_ROOT = os.path.realpath(os.path.join(os.path.dirname(__file__), "..", ".."))
MODULE_ROOT = os.path.join(PROJECT_ROOT, "modules")
sys.path.append(MODULE_ROOT)
from pytorch_wrapper.pt_argmin import PtArgmin
class TestPtArgmin(unittest.TestCase):
def setUp(self):
"""Set up test instance."""
self.node = PtArgmin()
# Test cases: (Tensor, Dim (as string), keepdim, Expected result)
self.test_cases = [
(torch.tensor([[1, 3, 2], [4, 0, 5]]), "0", False, torch.tensor([0, 1, 0])),
(torch.tensor([[1, 3, 2], [4, 0, 5]]), "1", True, torch.tensor([[0], [1]])),
(torch.tensor([[1, 3, 2], [4, 0, 5]]), "", False, torch.tensor(4)),
]
def test_argmin(self):
"""Test torch.argmin behavior."""
for tens, dim, keepdim, expected in self.test_cases:
with self.subTest(tens=tens, dim=dim, keepdim=keepdim):
result, = self.node.f(tens, dim, keepdim)
# Ensure return value is a tensor
self.assertTrue(isinstance(result, torch.Tensor), "Output is not a tensor")
# Ensure result matches expected indices
self.assertTrue(torch.equal(result, expected), f"Expected {expected}, got {result}")
if __name__ == "__main__":
unittest.main()
+48
View File
@@ -0,0 +1,48 @@
import os
import sys
import unittest
import torch
import ast
PROJECT_ROOT = os.path.realpath(os.path.join(os.path.dirname(__file__), "..", ".."))
MODULE_ROOT = os.path.join(PROJECT_ROOT, "modules")
sys.path.append(MODULE_ROOT)
from pytorch_wrapper.pt_gather import PtGather
class TestPtGather(unittest.TestCase):
def setUp(self):
"""Set up test instance."""
self.node = PtGather()
# Test cases: (Tensor, Dim, Index (as string), Expected result)
self.test_cases = [
(
torch.tensor([[10, 20, 30, 40, 50], [100, 200, 300, 400, 500]]),
1,
"[[0, 4, 3, 2, 0], [4, 3, 0, 0, 0]]",
torch.tensor([[10, 50, 40, 30, 10], [500, 400, 100, 100, 100]])
),
(
torch.tensor([[1, 2], [3, 4]]),
0,
"[[1, 0], [0, 1]]",
torch.tensor([[3, 2], [1, 4]])
),
]
def test_gather(self):
"""Test torch.gather behavior."""
for tens, dim, index, expected in self.test_cases:
with self.subTest(tens=tens, dim=dim, index=index):
result, = self.node.f(tens, dim, index)
# Ensure return value is a tensor
self.assertTrue(isinstance(result, torch.Tensor), "Output is not a tensor")
# Ensure result matches expected gathered values
self.assertTrue(torch.equal(result, expected), f"Expected {expected}, got {result}")
if __name__ == "__main__":
unittest.main()
@@ -0,0 +1,48 @@
import os
import sys
import unittest
import torch
import ast
PROJECT_ROOT = os.path.realpath(os.path.join(os.path.dirname(__file__), "..", ".."))
MODULE_ROOT = os.path.join(PROJECT_ROOT, "modules")
sys.path.append(MODULE_ROOT)
from pytorch_wrapper.pt_index_select import PtIndexSelect
class TestPtIndexSelect(unittest.TestCase):
def setUp(self):
"""Set up test instance."""
self.node = PtIndexSelect()
# Test cases: (Tensor, Dim, Index (as string), Expected result)
self.test_cases = [
(
torch.tensor([[1, 2, 3], [40, 50, 60], [700, 800, 900]]),
0,
"[0, 2]",
torch.tensor([[1, 2, 3], [700, 800, 900]])
),
(
torch.tensor([[1, 2, 3], [40, 50, 60], [700, 800, 900]]),
1,
"[1, 2]",
torch.tensor([[2, 3], [50, 60], [800, 900]])
),
]
def test_index_select(self):
"""Test torch.index_select behavior."""
for tens, dim, index, expected in self.test_cases:
with self.subTest(tens=tens, dim=dim, index=index):
result, = self.node.f(tens, dim, index)
# Ensure return value is a tensor
self.assertTrue(isinstance(result, torch.Tensor), "Output is not a tensor")
# Ensure result matches expected selected values
self.assertTrue(torch.equal(result, expected), f"Expected {expected}, got {result}")
if __name__ == "__main__":
unittest.main()
@@ -0,0 +1,40 @@
import os
import sys
import unittest
import torch
PROJECT_ROOT = os.path.realpath(os.path.join(os.path.dirname(__file__), "..", ".."))
MODULE_ROOT = os.path.join(PROJECT_ROOT, "modules")
sys.path.append(MODULE_ROOT)
from pytorch_wrapper.pt_masked_select import PtMaskedSelect
class TestPtMaskedSelect(unittest.TestCase):
def setUp(self):
"""Set up test instance."""
self.node = PtMaskedSelect()
# Test cases: (Tensor, Masked Tensor, Expected result)
self.test_cases = [
(
torch.tensor([[1, 2, 3], [40, 50, 60], [700, 800, 900]]),
torch.tensor([[True, False, False], [False, True, False], [False, False, True]]),
torch.tensor([1, 50, 900])
),
]
def test_masked_select(self):
"""Test torch.masked_select behavior."""
for tens, masked_tens, expected in self.test_cases:
with self.subTest(tens=tens, masked_tens=masked_tens):
result, = self.node.f(tens, masked_tens)
# Ensure return value is a tensor
self.assertTrue(isinstance(result, torch.Tensor), "Output is not a tensor")
# Ensure result matches expected selected values
self.assertTrue(torch.equal(result, expected), f"Expected {expected}, got {result}")
if __name__ == "__main__":
unittest.main()
+61
View File
@@ -0,0 +1,61 @@
import os
import sys
import unittest
import torch
import ast
PROJECT_ROOT = os.path.realpath(os.path.join(os.path.dirname(__file__), "..", ".."))
MODULE_ROOT = os.path.join(PROJECT_ROOT, "modules")
sys.path.append(MODULE_ROOT)
from pytorch_wrapper.pt_max import PtMax
class TestPtMax(unittest.TestCase):
def setUp(self):
"""Set up test instance."""
self.node = PtMax()
# Test cases: (Tensor, Dim (as string), keepdim, Expected result)
self.test_cases = [
(
torch.tensor([[1, 3, 2], [4, 0, 5]]),
"0",
False,
torch.tensor([4, 3, 5])
),
(
torch.tensor([[1, 3, 2], [4, 0, 5]]),
"1",
True,
torch.tensor([[3], [5]])
),
(
torch.tensor([[[1, 5], [2, 8]], [[3, 4], [6, 7]]]),
"(0, 1)",
False,
torch.tensor([6, 8])
),
(
# Reduce across all axes
torch.tensor([[1, 3, 2], [4, 0, 5]]),
"", # Passing empty string to reduce across all dimensions
False,
torch.tensor(5)
)
]
def test_max(self):
"""Test torch.max behavior."""
for tens, dim, keepdim, expected in self.test_cases:
with self.subTest(tens=tens, dim=dim, keepdim=keepdim):
result, = self.node.f(tens, dim, keepdim)
# Ensure return value is a tensor
self.assertTrue(isinstance(result, torch.Tensor), "Output is not a tensor")
# Ensure result matches expected max values
self.assertTrue(torch.equal(result, expected), f"Expected {expected}, got {result}")
if __name__ == "__main__":
unittest.main()
+87
View File
@@ -0,0 +1,87 @@
import os
import sys
import unittest
import torch
PROJECT_ROOT = os.path.realpath(os.path.join(os.path.dirname(__file__), "..", ".."))
MODULE_ROOT = os.path.join(PROJECT_ROOT, "modules")
sys.path.append(MODULE_ROOT)
from pytorch_wrapper.pt_mean import PtMean
class TestPtMean(unittest.TestCase):
def setUp(self):
"""Set up test instance."""
self.node = PtMean()
# Test cases: (Tensor, Dim (as string), keepdim, Expected result)
self.test_cases = [
# Float tensor cases
(
torch.tensor([[1.0, 3.0, 2.0], [4.0, 0.0, 5.0]], dtype=torch.float),
"0",
False,
torch.tensor([2.5, 1.5, 3.5], dtype=torch.float)
),
(
torch.tensor([[1.0, 3.0, 2.0], [4.0, 0.0, 5.0]], dtype=torch.float),
"1",
True,
torch.tensor([[2.], [3.]], dtype=torch.float)
),
(
torch.tensor([[[1.0, 5.0], [2.0, 8.0]], [[3.0, 4.0], [6.0, 7.0]]], dtype=torch.float),
"(0, 1)",
False,
torch.tensor([3.0, 6.0], dtype=torch.float)
),
(
# Reduce across all axes
torch.tensor([[1.0, 3.0, 2.0], [4.0, 0.0, 5.0]], dtype=torch.float),
"", # Passing empty string to reduce across all dimensions
False,
torch.tensor(2.5, dtype=torch.float)
),
# Complex tensor cases
(
torch.tensor([[1 + 1j, 3 + 3j, 2 + 2j], [4 + 4j, 0 + 0j, 5 + 5j]], dtype=torch.complex64),
"0",
False,
torch.tensor([2.5 + 2.5j, 1.5 + 1.5j, 3.5 + 3.5j], dtype=torch.complex64)
),
(
torch.tensor([[1 + 1j, 3 + 3j, 2 + 2j], [4 + 4j, 0 + 0j, 5 + 5j]], dtype=torch.complex64),
"1",
True,
torch.tensor([[2. + 2j], [3. + 3j]], dtype=torch.complex64)
),
(
torch.tensor([[[1 + 1j, 5 + 5j], [2 + 2j, 8 + 8j]], [[3 + 3j, 4 + 4j], [6 + 6j, 7 + 7j]]], dtype=torch.complex64),
"(0, 1)",
False,
torch.tensor([3. + 3j, 6. + 6j], dtype=torch.complex64)
),
(
# Reduce across all axes
torch.tensor([[1 + 1j, 3 + 3j, 2 + 2j], [4 + 4j, 0 + 0j, 5 + 5j]], dtype=torch.complex64),
"", # Passing empty string to reduce across all dimensions
False,
torch.tensor(2.5 + 2.5j, dtype=torch.complex64)
)
]
def test_mean(self):
"""Test torch.mean behavior."""
for tens, dim, keepdim, expected in self.test_cases:
with self.subTest(tens=tens, dim=dim, keepdim=keepdim):
result, = self.node.f(tens, dim, keepdim)
# Ensure return value is a tensor
self.assertTrue(isinstance(result, torch.Tensor), "Output is not a tensor")
# Ensure result matches expected mean values
self.assertTrue(torch.allclose(result, expected), f"Expected {expected}, got {result}")
if __name__ == "__main__":
unittest.main()
+66
View File
@@ -0,0 +1,66 @@
import os
import sys
import unittest
import torch
import ast
PROJECT_ROOT = os.path.realpath(os.path.join(os.path.dirname(__file__), "..", ".."))
MODULE_ROOT = os.path.join(PROJECT_ROOT, "modules")
sys.path.append(MODULE_ROOT)
from pytorch_wrapper.pt_median import PtMedian
class TestPtMedian(unittest.TestCase):
def setUp(self):
"""Set up test instance."""
self.node = PtMedian()
# Test cases: (Tensor, Dim (as string), keepdim, Expected result)
self.test_cases = [
(
torch.tensor([[1, 3, 2], [2, 1, 3], [4, 0, 5]]),
"0",
False,
torch.tensor([2, 1, 3])
),
(
torch.tensor([[1, 3, 2], [4, 0, 5]]),
"0",
False,
torch.tensor([1, 0, 2]) # Fix: torch.median returns the lower value when between two numbers
),
(
torch.tensor([[1, 3, 2], [4, 0, 5]]),
"1",
True,
torch.tensor([[2], [4]])
),
(
torch.tensor([[1, 3, 2], [4, 0, 5], [7, 8, 6], [9, 2, 10]]),
"0",
False,
torch.tensor([4, 2, 5])
),
(
torch.tensor([[1, 3, 2, 8], [4, 0, 5, 6], [7, 8, 6, 9]]),
"1",
False,
torch.tensor([2, 4, 7])
)
]
def test_median(self):
"""Test torch.median behavior."""
for tens, dim, keepdim, expected in self.test_cases:
with self.subTest(tens=tens, dim=dim, keepdim=keepdim):
result, = self.node.f(tens, dim, keepdim)
# Ensure return value is a tensor
self.assertTrue(isinstance(result, torch.Tensor), "Output is not a tensor")
# Ensure result matches expected median values
self.assertTrue(torch.equal(result, expected), f"Expected {expected}, got {result}")
if __name__ == "__main__":
unittest.main()
+61
View File
@@ -0,0 +1,61 @@
import os
import sys
import unittest
import torch
PROJECT_ROOT = os.path.realpath(os.path.join(os.path.dirname(__file__), "..", ".."))
MODULE_ROOT = os.path.join(PROJECT_ROOT, "modules")
sys.path.append(MODULE_ROOT)
from pytorch_wrapper.pt_min import PtMin
class TestPtMin(unittest.TestCase):
def setUp(self):
"""Set up test instance."""
self.node = PtMin()
# Test cases: (Tensor, Dim (as string), keepdim, Expected result)
self.test_cases = [
(
torch.tensor([[1, 3, 2], [4, 0, 5]]),
"0",
False,
torch.tensor([1, 0, 2])
),
(
torch.tensor([[1, 3, 2], [4, 0, 5]]),
"1",
True,
torch.tensor([[1], [0]])
),
(
torch.tensor([[[1, 5], [2, 8]], [[3, 4], [6, 7]]]),
"(0, 1)",
False,
torch.tensor([1, 4])
),
(
# Reduce across all axes
torch.tensor([[1, 3, 2], [4, 0, 5]]),
"", # Passing empty string to reduce across all dimensions
False,
torch.tensor(0)
)
]
def test_amin(self):
"""Test torch.amin behavior."""
for tens, dim, keepdim, expected in self.test_cases:
with self.subTest(tens=tens, dim=dim, keepdim=keepdim):
result, = self.node.f(tens, dim, keepdim)
# Ensure return value is a tensor
self.assertTrue(isinstance(result, torch.Tensor), "Output is not a tensor")
# Ensure result matches expected min values
self.assertTrue(torch.equal(result, expected), f"Expected {expected}, got {result}")
if __name__ == "__main__":
unittest.main()
+59
View File
@@ -0,0 +1,59 @@
import os
import sys
import unittest
import torch
PROJECT_ROOT = os.path.realpath(os.path.join(os.path.dirname(__file__), "..", ".."))
MODULE_ROOT = os.path.join(PROJECT_ROOT, "modules")
sys.path.append(MODULE_ROOT)
from pytorch_wrapper.pt_prod import PtProd
class TestPtProd(unittest.TestCase):
def setUp(self):
"""Set up test instance."""
self.node = PtProd()
# Test cases: (Tensor, Dim (as string), keepdim, Expected result)
self.test_cases = [
(
torch.tensor([[1, 3, 2], [2, 1, 3]]),
"0",
False,
torch.tensor([2, 3, 6])
),
(
torch.tensor([[1, 3, 2], [4, 0, 5]]),
"1",
True,
torch.tensor([[6], [0]])
),
(
torch.tensor([[1, 3, 2], [4, 5, 6]]),
"0",
False,
torch.tensor([4, 15, 12])
),
(
torch.tensor([[1, 3, 2], [4, 5, 6], [7, 8, 9]]),
"1",
False,
torch.tensor([6, 120, 504])
)
]
def test_prod(self):
"""Test torch.prod behavior."""
for tens, dim, keepdim, expected in self.test_cases:
with self.subTest(tens=tens, dim=dim, keepdim=keepdim):
result, = self.node.f(tens, dim, keepdim)
# Ensure return value is a tensor
self.assertTrue(isinstance(result, torch.Tensor), "Output is not a tensor")
# Ensure result matches expected product values
self.assertTrue(torch.equal(result, expected), f"Expected {expected}, got {result}")
if __name__ == "__main__":
unittest.main()
+60
View File
@@ -0,0 +1,60 @@
import os
import sys
import unittest
import torch
PROJECT_ROOT = os.path.realpath(os.path.join(os.path.dirname(__file__), "..", ".."))
MODULE_ROOT = os.path.join(PROJECT_ROOT, "modules")
sys.path.append(MODULE_ROOT)
from pytorch_wrapper.pt_reshape import PtReshape # Importing the PtReshape class
class TestPtReshape(unittest.TestCase):
def setUp(self):
"""Set up test instance."""
self.node = PtReshape()
# Define test cases: (input_tensor, new_shape, expected_shape)
self.test_cases = [
# Case 1: Reshape a 2D tensor
(torch.ones(2, 3), "[3, 2]", (3, 2)), # (2,3) -> (3,2)
# Case 2: Reshape a 3D tensor
(torch.ones(2, 3, 4), "[6, 4]", (6, 4)), # (2,3,4) -> (6,4)
# Case 3: Using -1 for automatic inference
(torch.ones(2, 3, 4), "[2, -1]", (2, 12)), # (2,3,4) -> (2,12)
(torch.ones(6, 4), "[-1, 4]", (6, 4)), # (6,4) -> (6,4)
# Case 4: Flattening a multi-dimensional tensor
(torch.ones(2, 3, 4), "[-1]", (24,)), # (2,3,4) -> (24,)
# Case 5: Expanding dimensions
(torch.ones(6,), "[2, 3]", (2, 3)), # (6,) -> (2,3)
# Case 6: Reshaping a 4D tensor
(torch.ones(2, 3, 4, 5), "[6, -1, 5]", (6, 4, 5)), # (2,3,4,5) -> (6,4,5)
# Case 7: Edge case with 1D tensor
(torch.ones(10,), "[10, 1]", (10, 1)), # (10,) -> (10,1)
]
def test_reshape(self):
"""Test tensor reshape operation with various shape orders."""
for input_tensor, new_shape, expected_shape in self.test_cases:
with self.subTest(input_shape=input_tensor.shape, new_shape=new_shape):
result, = self.node.f(input_tensor, new_shape)
# Ensure return value is a tensor
self.assertTrue(isinstance(result, torch.Tensor), "Output is not a tensor")
# Ensure result has correct shape
self.assertEqual(result.shape, torch.Size(expected_shape), f"Expected shape {expected_shape}, got {result.shape}")
# Ensure result has the same dtype
self.assertEqual(result.dtype, input_tensor.dtype, "Dtype mismatch after reshape operation")
if __name__ == "__main__":
unittest.main()
+51
View File
@@ -0,0 +1,51 @@
import os
import sys
import unittest
import torch
import ast
PROJECT_ROOT = os.path.realpath(os.path.join(os.path.dirname(__file__), "..", ".."))
MODULE_ROOT = os.path.join(PROJECT_ROOT, "modules")
sys.path.append(MODULE_ROOT)
from pytorch_wrapper.pt_scatter import PtScatter
class TestPtScatter(unittest.TestCase):
def setUp(self):
"""Set up test instance."""
self.node = PtScatter()
# Test cases: (Target Tensor, Dim, Index Tensor (as string), Source Tensor (as string), Expected Output)
self.test_cases = [
(
torch.tensor([
[10, 11, 12],
[21, 22, 23],
[31, 32, 33],
]),
0,
"[[0, 1, 0], [1, 1, 2]]",
"[[2, 4, 6], [4, 4, 2]]",
torch.tensor([
[2, 11, 6],
[4, 4, 23],
[31, 32, 2],
])
)
]
def test_scatter(self):
"""Test torch.scatter behavior."""
for tens, dim, index, src, expected in self.test_cases:
with self.subTest(tens=tens, dim=dim, index=index, src=src):
result, = self.node.f(tens, dim, index, src)
# Ensure return value is a tensor
self.assertTrue(isinstance(result, torch.Tensor), "Output is not a tensor")
# Ensure result matches expected values
self.assertTrue(torch.equal(result, expected), f"Expected {expected}, got {result}")
if __name__ == "__main__":
unittest.main()
+39
View File
@@ -0,0 +1,39 @@
import os
import sys
import unittest
import torch
PROJECT_ROOT = os.path.realpath(os.path.join(os.path.dirname(__file__), "..", ".."))
MODULE_ROOT = os.path.join(PROJECT_ROOT, "modules")
sys.path.append(MODULE_ROOT)
from pytorch_wrapper.pt_sqrt import PtSqrt # Updated import to PtSqrt
class TestPtSqrt(unittest.TestCase):
def setUp(self):
"""Set up test instance."""
self.node = PtSqrt()
# Test cases: (Tensor A, Expected result)
self.test_cases = [
(torch.tensor([1.0, 4.0, 9.0], dtype=torch.float32), torch.sqrt(torch.tensor([1.0, 4.0, 9.0], dtype=torch.float32))), # Perfect squares
(torch.tensor([0.5, 2.0, 3.0], dtype=torch.float32), torch.sqrt(torch.tensor([0.5, 2.0, 3.0], dtype=torch.float32))), # Common values
(torch.tensor([[1.5, 3.2], [4.7, 5.9]], dtype=torch.float32), torch.sqrt(torch.tensor([[1.5, 3.2], [4.7, 5.9]], dtype=torch.float32))), # 2D case
(torch.full((2, 2), 2.5, dtype=torch.float32), torch.sqrt(torch.full((2, 2), 2.5, dtype=torch.float32))), # Constant matrix
]
def test_sqrt(self):
"""Test tensor element-wise square root using torch.sqrt()."""
for tens_a, expected in self.test_cases:
with self.subTest(tens_a=tens_a):
result, = self.node.f(tens_a)
# Ensure return value is a tensor
self.assertTrue(isinstance(result, torch.Tensor), "Output is not a tensor")
# Ensure result matches expected sqrt values
self.assertTrue(torch.allclose(result, expected, atol=1e-6), f"Expected {expected}, got {result}")
if __name__ == "__main__":
unittest.main()
+84
View File
@@ -0,0 +1,84 @@
import os
import sys
import unittest
import torch
PROJECT_ROOT = os.path.realpath(os.path.join(os.path.dirname(__file__), "..", ".."))
MODULE_ROOT = os.path.join(PROJECT_ROOT, "modules")
sys.path.append(MODULE_ROOT)
from pytorch_wrapper.pt_std import PtStd
class TestPtStd(unittest.TestCase):
def setUp(self):
"""Set up test instance."""
self.node = PtStd()
# Define test cases with string-based dimensions
self.test_cases = [
# Sample standard deviation (Bessel's correction) along dim=0
(
torch.tensor([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]),
"0",
1,
False,
torch.std(torch.tensor([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]), dim=0, correction=1, keepdim=False)
),
# Sample standard deviation along dim=1, with keepdim=True
(
torch.tensor([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]),
"1",
1,
True,
torch.std(torch.tensor([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]), dim=1, correction=1, keepdim=True)
),
# Sample standard deviation across both axes (dim=(0,1))
(
torch.tensor([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]),
"(0, 1)",
1,
False,
torch.std(torch.tensor([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]), dim=(0, 1), correction=1, keepdim=False)
),
# Sample standard deviation across all elements (dim=None) - Entire tensor
(
torch.tensor([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]),
"",
1,
False,
torch.std(torch.tensor([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]), correction=1, keepdim=False)
),
# Population standard deviation along dim=0 (correction=0)
(
torch.tensor([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]),
"0",
0,
False,
torch.std(torch.tensor([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]), dim=0, correction=0, keepdim=False)
),
# Population standard deviation along dim=1 with keepdim=True (correction=0)
(
torch.tensor([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]),
"1",
0,
True,
torch.std(torch.tensor([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]), dim=1, correction=0, keepdim=True)
)
]
def test_std(self):
"""Test torch.std behavior."""
for tens, dim, correction, keepdim, expected in self.test_cases:
with self.subTest(tens=tens, dim=dim, correction=correction, keepdim=keepdim):
result, = self.node.f(tens, dim, correction, keepdim)
# Ensure return value is a tensor
self.assertTrue(isinstance(result, torch.Tensor), "Output is not a tensor")
# Ensure result matches expected standard deviation values
self.assertTrue(torch.allclose(result, expected, atol=1e-6), f"Expected {expected}, got {result}")
if __name__ == "__main__":
unittest.main()
+71
View File
@@ -0,0 +1,71 @@
import os
import sys
import unittest
import torch
PROJECT_ROOT = os.path.realpath(os.path.join(os.path.dirname(__file__), "..", ".."))
MODULE_ROOT = os.path.join(PROJECT_ROOT, "modules")
sys.path.append(MODULE_ROOT)
from pytorch_wrapper.pt_sum import PtSum
class TestPtSum(unittest.TestCase):
def setUp(self):
"""Set up test instance."""
self.node = PtSum()
# Test cases: (Tensor, Dim (as string), keepdim, Expected result)
self.test_cases = [
(
torch.tensor([[1, 3, 2], [2, 1, 3]]),
"0",
False,
torch.tensor([3, 4, 5])
),
(
torch.tensor([[1, 3, 2], [4, 0, 5]]),
"1",
True,
torch.tensor([[6], [9]])
),
(
torch.tensor([[1, 3, 2], [4, 5, 6]]),
"0",
False,
torch.tensor([5, 8, 8])
),
(
torch.tensor([[1, 3, 2], [4, 5, 6], [7, 8, 9]]),
"1",
False,
torch.tensor([6, 15, 24])
),
(
torch.tensor([[1, 2], [3, 4]]),
"",
False,
torch.tensor(10) # Sum of all elements
),
(
torch.tensor([[[1, 2], [3, 4]], [[5, 6], [7, 8]]]),
"(0, 1)",
False,
torch.tensor([16, 20]) # Sum over dimensions (0, 1)
)
]
def test_sum(self):
"""Test torch.sum behavior."""
for tens, dim, keepdim, expected in self.test_cases:
with self.subTest(tens=tens, dim=dim, keepdim=keepdim):
result, = self.node.f(tens, dim, keepdim)
# Ensure return value is a tensor
self.assertTrue(isinstance(result, torch.Tensor), "Output is not a tensor")
# Ensure result matches expected sum values
self.assertTrue(torch.equal(result, expected), f"Expected {expected}, got {result}")
if __name__ == "__main__":
unittest.main()
+83
View File
@@ -0,0 +1,83 @@
import os
import sys
import unittest
import torch
PROJECT_ROOT = os.path.realpath(os.path.join(os.path.dirname(__file__), "..", ".."))
MODULE_ROOT = os.path.join(PROJECT_ROOT, "modules")
sys.path.append(MODULE_ROOT)
from pytorch_wrapper.pt_var import PtVar
class TestPtVar(unittest.TestCase):
def setUp(self):
"""Set up test instance."""
self.node = PtVar()
# Define test cases with string-based dimensions
self.test_cases = [
# Sample variance (Bessel's correction) along dim=0
(
torch.tensor([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]),
"0",
1,
False,
torch.var(torch.tensor([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]), dim=0, correction=1, keepdim=False)
),
# Sample variance along dim=1, with keepdim=True
(
torch.tensor([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]),
"1",
1,
True,
torch.var(torch.tensor([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]), dim=1, correction=1, keepdim=True)
),
# Sample variance across both axes (dim=(0,1))
(
torch.tensor([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]),
"(0, 1)",
1,
False,
torch.var(torch.tensor([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]), dim=(0, 1), correction=1, keepdim=False)
),
# Sample variance across all elements (dim=None) - Entire tensor
(
torch.tensor([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]),
"",
1,
False,
torch.var(torch.tensor([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]), correction=1, keepdim=False)
),
# Population variance along dim=0 (correction=0)
(
torch.tensor([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]),
"0",
0,
False,
torch.var(torch.tensor([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]), dim=0, correction=0, keepdim=False)
),
# Population variance along dim=1 with keepdim=True (correction=0)
(
torch.tensor([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]),
"1",
0,
True,
torch.var(torch.tensor([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]), dim=1, correction=0, keepdim=True)
)
]
def test_var(self):
"""Test torch.var behavior."""
for tens, dim, correction, keepdim, expected in self.test_cases:
with self.subTest(tens=tens, dim=dim, correction=correction, keepdim=keepdim):
result, = self.node.f(tens, dim, correction, keepdim)
# Ensure return value is a tensor
self.assertTrue(isinstance(result, torch.Tensor), "Output is not a tensor")
# Ensure result matches expected variance values
self.assertTrue(torch.allclose(result, expected, atol=1e-6), f"Expected {expected}, got {result}")
if __name__ == "__main__":
unittest.main()
+59
View File
@@ -0,0 +1,59 @@
import os
import sys
import unittest
import torch
PROJECT_ROOT = os.path.realpath(os.path.join(os.path.dirname(__file__), "..", ".."))
MODULE_ROOT = os.path.join(PROJECT_ROOT, "modules")
sys.path.append(MODULE_ROOT)
from pytorch_wrapper.pt_where import PtWhere
class TestPtWhere(unittest.TestCase):
def setUp(self):
"""Set up test instance."""
self.node = PtWhere()
# Test cases: (Condition Tensor, True Tensor, False Tensor, Expected Output)
self.test_cases = [
(
torch.tensor([[True, False], [False, True]]),
torch.tensor([[1, 2], [3, 4]]),
torch.tensor([[5, 6], [7, 8]]),
torch.tensor([[1, 6], [7, 4]])
),
(
torch.tensor([True, False, True]),
torch.tensor([10, 20, 30]),
torch.tensor([100, 200, 300]),
torch.tensor([10, 200, 30])
),
(
torch.tensor([[False, False], [True, True]]),
torch.tensor([[1.5, 2.5], [3.5, 4.5]]),
torch.tensor([[5.5, 6.5], [7.5, 8.5]]),
torch.tensor([[5.5, 6.5], [3.5, 4.5]])
),
(
torch.tensor([[[True, False], [False, True]], [[False, True], [True, False]]]),
torch.tensor([[[1, 2], [3, 4]], [[5, 6], [7, 8]]]),
torch.tensor([[[9, 10], [11, 12]], [[13, 14], [15, 16]]]),
torch.tensor([[[1, 10], [11, 4]], [[13, 6], [7, 16]]])
),
]
def test_where(self):
"""Test torch.where behavior."""
for condition_tens, true_tens, false_tens, expected in self.test_cases:
with self.subTest(condition_tens=condition_tens, true_tens=true_tens, false_tens=false_tens):
result, = self.node.f(condition_tens, true_tens, false_tens)
# Ensure return value is a tensor
self.assertTrue(isinstance(result, torch.Tensor), "Output is not a tensor")
# Ensure result matches expected values
self.assertTrue(torch.equal(result, expected), f"Expected {expected}, got {result}")
if __name__ == "__main__":
unittest.main()
+1 -1
View File
@@ -2,7 +2,7 @@ import os
import sys
import unittest
import torch
import ast
PROJECT_ROOT = os.path.realpath(os.path.join(os.path.dirname(__file__), "..", ".."))
MODULE_ROOT = os.path.join(PROJECT_ROOT, "modules")
+54
View File
@@ -0,0 +1,54 @@
import os
import sys
import unittest
PROJECT_ROOT = os.path.realpath(os.path.join(os.path.dirname(__file__), "..", ".."))
MODULE_ROOT = os.path.join(PROJECT_ROOT, "modules")
sys.path.append(MODULE_ROOT)
from pytorch_wrapper.utils import str_to_dim
class TestStrToDim(unittest.TestCase):
def setUp(self):
"""Set up test instance."""
pass
def test_valid_integer(self):
"""Test parsing a valid integer string."""
self.assertEqual(str_to_dim("5"), 5)
def test_valid_tuple(self):
"""Test parsing a valid tuple string."""
self.assertEqual(str_to_dim("(1, 2, 3)"), (1, 2, 3))
def test_valid_list(self):
"""Test parsing a valid list string (should be converted to tuple)."""
self.assertEqual(str_to_dim("[4, 5, 6]"), (4, 5, 6))
def test_empty_string(self):
"""Test empty string input should return None."""
self.assertIsNone(str_to_dim(""))
def test_invalid_string(self):
"""Test invalid string input should raise ValueError."""
with self.assertRaises(ValueError):
str_to_dim("invalid")
def test_mixed_type_list(self):
"""Test a list with mixed types should raise ValueError."""
with self.assertRaises(ValueError):
str_to_dim("[1, 'a', 3]")
def test_nested_tuple(self):
"""Test a nested tuple should raise ValueError."""
with self.assertRaises(ValueError):
str_to_dim("((1, 2), 3)")
def test_dict_input(self):
"""Test a dictionary input should raise ValueError."""
with self.assertRaises(ValueError):
str_to_dim("{'a': 1, 'b': 2}")
if __name__ == "__main__":
unittest.main()