diff --git a/__init__.py b/__init__.py
index cfcd7d6..8e833cc 100644
--- a/__init__.py
+++ b/__init__.py
@@ -17,6 +17,7 @@ from .modules.pytorch_wrapper.pt_bitwise_right_shift import PtBitwiseRightShift
from .modules.pytorch_wrapper.pt_bitwise_xor import PtBitwiseXor
from .modules.pytorch_wrapper.pt_bmm import PtBmm
from .modules.pytorch_wrapper.pt_bool_create import PtBoolCreate
+from .modules.pytorch_wrapper.pt_compute_loss import PtComputeLoss
from .modules.pytorch_wrapper.pt_concat import PtConcat
from .modules.pytorch_wrapper.pt_cos import PtCos
from .modules.pytorch_wrapper.pt_cosh import PtCosh
@@ -147,17 +148,26 @@ from .modules.pytorch_wrapper.ptf_softplus import PtfSoftplus
from .modules.pytorch_wrapper.ptf_tanh import PtfTanh
from .modules.pytorch_wrapper.ptn_avg_pool2d import PtnAvgPool2d
from .modules.pytorch_wrapper.ptn_batch_norm2d import PtnBatchNorm2d
+from .modules.pytorch_wrapper.ptn_bce_loss import PtnBCELoss
+from .modules.pytorch_wrapper.ptn_bce_with_logits_loss import PtnBCEWithLogitsLoss
from .modules.pytorch_wrapper.ptn_chained_model import PtnChainedModel
from .modules.pytorch_wrapper.ptn_conv2d import PtnConv2d
from .modules.pytorch_wrapper.ptn_conv_model import PtnConvModel
+from .modules.pytorch_wrapper.ptn_cross_entropy_loss import PtnCrossEntropyLoss
+from .modules.pytorch_wrapper.ptn_huber_loss import PtnHuberLoss
from .modules.pytorch_wrapper.ptn_instance_norm2d import PtnInstanceNorm2d
+from .modules.pytorch_wrapper.ptn_kl_div_loss import PtnKLDivLoss
+from .modules.pytorch_wrapper.ptn_l1_loss import PtnL1Loss
from .modules.pytorch_wrapper.ptn_layer_norm import PtnLayerNorm
from .modules.pytorch_wrapper.ptn_linear import PtnLinear
from .modules.pytorch_wrapper.ptn_linear_model import PtnLinearModel
from .modules.pytorch_wrapper.ptn_max_pool2d import PtnMaxPool2d
+from .modules.pytorch_wrapper.ptn_mse_loss import PtnMSELoss
+from .modules.pytorch_wrapper.ptn_nll_loss import PtnNLLLoss
from .modules.pytorch_wrapper.ptn_pre_add_channel_axis import PtnPreAddChannelAxis
from .modules.pytorch_wrapper.ptn_pre_flatten import PtnPreFlatten
from .modules.pytorch_wrapper.ptn_resnet_model import PtnResnetModel
+from .modules.pytorch_wrapper.ptn_smooth_l1_loss import PtnSmoothL1Loss
from .modules.pytorch_wrapper.pto_adam import PtoAdam
from .modules.pytorch_wrapper.pto_adamw import PtoAdamW
from .modules.pytorch_wrapper.pto_lr_scheduler_cosine_annealing import PtoLrSchedulerCosineAnnealing
@@ -198,6 +208,7 @@ NODE_CLASS_MAPPINGS: Dict[str, Type[T]] = {
"PtBitwiseXor": PtBitwiseXor,
"PtBmm": PtBmm,
"PtBoolCreate": PtBoolCreate,
+ "PtComputeLoss": PtComputeLoss,
"PtConcat": PtConcat,
"PtCos": PtCos,
"PtCosh": PtCosh,
@@ -327,18 +338,27 @@ NODE_CLASS_MAPPINGS: Dict[str, Type[T]] = {
"PtfSoftplus": PtfSoftplus,
"PtfTanh": PtfTanh,
"PtnAvgPool2d": PtnAvgPool2d,
+ "PtnBCELoss": PtnBCELoss,
+ "PtnBCEWithLogitsLoss": PtnBCEWithLogitsLoss,
"PtnBatchNorm2d": PtnBatchNorm2d,
"PtnChainedModel": PtnChainedModel,
"PtnConv2d": PtnConv2d,
"PtnConvModel": PtnConvModel,
+ "PtnCrossEntropyLoss": PtnCrossEntropyLoss,
+ "PtnHuberLoss": PtnHuberLoss,
"PtnInstanceNorm2d": PtnInstanceNorm2d,
+ "PtnKLDivLoss": PtnKLDivLoss,
+ "PtnL1Loss": PtnL1Loss,
"PtnLayerNorm": PtnLayerNorm,
"PtnLinear": PtnLinear,
"PtnLinearModel": PtnLinearModel,
+ "PtnMSELoss": PtnMSELoss,
"PtnMaxPool2d": PtnMaxPool2d,
+ "PtnNLLLoss": PtnNLLLoss,
"PtnPreAddChannelAxis": PtnPreAddChannelAxis,
"PtnPreFlatten": PtnPreFlatten,
"PtnResnetModel": PtnResnetModel,
+ "PtnSmoothL1Loss": PtnSmoothL1Loss,
"PtoAdam": PtoAdam,
"PtoAdamW": PtoAdamW,
"PtoLrSchedulerCosineAnnealing": PtoLrSchedulerCosineAnnealing,
@@ -379,6 +399,7 @@ NODE_DISPLAY_NAME_MAPPINGS: Dict[str, str] = {
"PtBitwiseXor": "Pt Bitwise Xor",
"PtBmm": "Pt Bmm",
"PtBoolCreate": "Pt Bool Create",
+ "PtComputeLoss": "Pt Compute Loss",
"PtConcat": "Pt Concat",
"PtCos": "Pt Cos",
"PtCosh": "Pt Cosh",
@@ -508,18 +529,27 @@ NODE_DISPLAY_NAME_MAPPINGS: Dict[str, str] = {
"PtfSoftplus": "Ptf Softplus",
"PtfTanh": "Ptf Tanh",
"PtnAvgPool2d": "Ptn Avg Pool 2d",
+ "PtnBCELoss": "Ptn BCE Loss",
+ "PtnBCEWithLogitsLoss": "Ptn BCE With Logits Loss",
"PtnBatchNorm2d": "Ptn Batch Norm 2d",
"PtnChainedModel": "Ptn Chained Model",
"PtnConv2d": "Ptn Conv 2d",
"PtnConvModel": "Ptn Conv Model",
+ "PtnCrossEntropyLoss": "Ptn Cross Entropy Loss",
+ "PtnHuberLoss": "Ptn Huber Loss",
"PtnInstanceNorm2d": "Ptn Instance Norm 2d",
+ "PtnKLDivLoss": "Ptn KL Div Loss",
+ "PtnL1Loss": "Ptn L1 Loss",
"PtnLayerNorm": "Ptn Layer Norm",
"PtnLinear": "Ptn Linear",
"PtnLinearModel": "Ptn Linear Model",
+ "PtnMSELoss": "Ptn MSE Loss",
"PtnMaxPool2d": "Ptn Max Pool 2d",
+ "PtnNLLLoss": "Ptn NLL Loss",
"PtnPreAddChannelAxis": "Ptn Pre Add Channel Axis",
"PtnPreFlatten": "Ptn Pre Flatten",
"PtnResnetModel": "Ptn Resnet Model",
+ "PtnSmoothL1Loss": "Ptn Smooth L1 Loss",
"PtoAdam": "Pto Adam",
"PtoAdamW": "Pto AdamW",
"PtoLrSchedulerCosineAnnealing": "Pto Lr Scheduler Cosine Annealing",
diff --git a/dev/scripts/init_py_generate.py b/dev/scripts/init_py_generate.py
index 07d1f9b..020794b 100644
--- a/dev/scripts/init_py_generate.py
+++ b/dev/scripts/init_py_generate.py
@@ -83,6 +83,10 @@ def format_display_name(class_name):
("Si L U", "SiLU"),
("G E L U", "GELU"),
("S G D", "SGD"),
+ ("B C E", "BCE"),
+ ("K L", "KL"),
+ ("M S E", "MSE"),
+ ("N L L", "NLL"),
("Data Frame", "DataFrame"),
("Multi Index", "MultiIndex"),
("MPL Bar", "MPL Bar Chart"),
diff --git a/docs/reference/node_reference.md b/docs/reference/node_reference.md
index 076e81f..ecb0a63 100644
--- a/docs/reference/node_reference.md
+++ b/docs/reference/node_reference.md
@@ -111,6 +111,18 @@
| [Pt Logical Not](pt_logical_not.md) | Performs a logical NOT operation on a PyTorch tensor element-wise. |
| [Pt Logical Or](pt_logical_or.md) | Performs a logical OR operation on two PyTorch tensors element-wise. |
| [Pt Logical Xor](pt_logical_xor.md) | Performs a logical XOR operation on two PyTorch tensors element-wise. |
+## PyTorch wrapper - Loss function
+| Node | Description |
+| --- | --- |
+| [Ptn BCE Loss](ptn_bce_loss.md) | A class to compute the binary cross entropy loss. |
+| [Ptn BCE With Logits Loss](ptn_bce_with_logits_loss.md) | A class to compute the sigmoid then binary cross entropy loss. |
+| [Ptn Cross Entropy Loss](ptn_cross_entropy_loss.md) | A class to compute the cross entropy loss. |
+| [Ptn Huber Loss](ptn_huber_loss.md) | |
+| [Ptn KL Div Loss](ptn_kl_div_loss.md) | A class to compute the KL divergence loss. |
+| [Ptn L1 Loss](ptn_l1_loss.md) | A class to compute the L1 loss. |
+| [Ptn MSE Loss](ptn_mse_loss.md) | A class to compute the squared loss. |
+| [Ptn NLL Loss](ptn_nll_loss.md) | A model to compute the negative log likelihood (NLL) loss. |
+| [Ptn Smooth L1 Loss](ptn_smooth_l1_loss.md) | |
## PyTorch wrapper - Math operations
| Node | Description |
| --- | --- |
@@ -204,6 +216,7 @@
| Node | Description |
| --- | --- |
| [Pt Apply Function](pt_apply_function.md) | Applies a function to the input tensor. |
+| [Pt Compute Loss](pt_compute_loss.md) | Computes loss for the input with the target using the specified loss function. |
| [Pt Data Loader](pt_data_loader.md) | Loads data from a dataset node and creates a PyTorch DataLoader. |
| [Pt Data Loader From Tensors](pt_data_loader_from_tensors.md) | Creates a Torchvision Dataloader from a pair of tensors. |
| [Pt Evaluate Classification Model](pt_evaluate_classification_model.md) | Performs inference on test data and computes evaluation metrics. |
diff --git a/docs/reference/pt_compute_loss.md b/docs/reference/pt_compute_loss.md
new file mode 100644
index 0000000..2945304
--- /dev/null
+++ b/docs/reference/pt_compute_loss.md
@@ -0,0 +1,21 @@
+# Pt Compute Loss
+Computes loss for the input with the target using the specified loss function.
+
+The user does not normally need to use this node. Instead the user should use training nodes that accept a loss object.
+
+## Input
+| Name | Data type |
+|---|---|
+| input_tens | Tensor |
+| target_tens | Tensor |
+| loss | Ptloss |
+
+## Output
+| Data type |
+|---|
+| Tensor |
+
+
+Category: PyTorch wrapper - Training
+
+ComfyUI Pt Wrapper Node Reference. © 2025 Hide Inada (HowToSD.com). All rights reserved.
diff --git a/docs/reference/ptn_bce_loss.md b/docs/reference/ptn_bce_loss.md
new file mode 100644
index 0000000..7e1a432
--- /dev/null
+++ b/docs/reference/ptn_bce_loss.md
@@ -0,0 +1,25 @@
+# Ptn BCE Loss
+A class to compute the binary cross entropy loss.
+
+Unlike the Ptn BCE Loss With Logits node, which takes logits as input, this node expects probabilities. If the input consists of logits, apply a Sigmoid function first or use the Ptn BCE Loss With Logits node instead.
+
+ Args:
+ reduction (str): Reduction method.
+ `none`: no reduction will be applied done.
+ `mean`: computes the mean.
+ `sum`: computes the sum.
+
+## Input
+| Name | Data type |
+|---|---|
+| reduction | Mean |
+
+## Output
+| Data type |
+|---|
+| Ptloss |
+
+
+Category: PyTorch wrapper - Loss function
+
+ComfyUI Pt Wrapper Node Reference. © 2025 Hide Inada (HowToSD.com). All rights reserved.
diff --git a/docs/reference/ptn_bce_with_logits_loss.md b/docs/reference/ptn_bce_with_logits_loss.md
new file mode 100644
index 0000000..652d542
--- /dev/null
+++ b/docs/reference/ptn_bce_with_logits_loss.md
@@ -0,0 +1,23 @@
+# Ptn BCE With Logits Loss
+A class to compute the sigmoid then binary cross entropy loss.
+
+ Args:
+ reduction (str): Reduction method.
+ `none`: no reduction will be applied done.
+ `mean`: computes the mean.
+ `sum`: computes the sum.
+
+## Input
+| Name | Data type |
+|---|---|
+| reduction | Mean |
+
+## Output
+| Data type |
+|---|
+| Ptloss |
+
+
+Category: PyTorch wrapper - Loss function
+
+ComfyUI Pt Wrapper Node Reference. © 2025 Hide Inada (HowToSD.com). All rights reserved.
diff --git a/docs/reference/ptn_cross_entropy_loss.md b/docs/reference/ptn_cross_entropy_loss.md
new file mode 100644
index 0000000..fe8a00b
--- /dev/null
+++ b/docs/reference/ptn_cross_entropy_loss.md
@@ -0,0 +1,25 @@
+# Ptn Cross Entropy Loss
+A class to compute the cross entropy loss.
+
+Unlike Ptn NLL Loss node which takes log probabilities for input, input to this node needs to be logits. You can consider this node as a combination of log softmax and NLL.
+
+ Args:
+ reduction: (str): Reduction method.
+ `none`: no reduction will be applied done.
+ `mean`: computes the mean.
+ `sum`: computes the sum.
+
+## Input
+| Name | Data type |
+|---|---|
+| reduction | Mean |
+
+## Output
+| Data type |
+|---|
+| Ptloss |
+
+
+Category: PyTorch wrapper - Loss function
+
+ComfyUI Pt Wrapper Node Reference. © 2025 Hide Inada (HowToSD.com). All rights reserved.
diff --git a/docs/reference/ptn_huber_loss.md b/docs/reference/ptn_huber_loss.md
new file mode 100644
index 0000000..d718fd7
--- /dev/null
+++ b/docs/reference/ptn_huber_loss.md
@@ -0,0 +1,28 @@
+# Ptn Huber Loss
+A class to compute the Huber loss.
+
+Please see [PyTorch documentation](https://pytorch.org/docs/stable/generated/torch.nn.HuberLoss.html) for details.
+
+
+ Args:
+ reduction (str): Reduction method.
+ `none`: no reduction will be applied done.
+ `mean`: computes the mean.
+ `sum`: computes the sum.
+ delta (float): The threshold used to switch between adjusted MSE and L1 loss.
+
+## Input
+| Name | Data type |
+|---|---|
+| reduction | Mean |
+| delta | Float |
+
+## Output
+| Data type |
+|---|
+| Ptloss |
+
+
+Category: PyTorch wrapper - Loss function
+
+ComfyUI Pt Wrapper Node Reference. © 2025 Hide Inada (HowToSD.com). All rights reserved.
diff --git a/docs/reference/ptn_kl_div_loss.md b/docs/reference/ptn_kl_div_loss.md
new file mode 100644
index 0000000..24fbc29
--- /dev/null
+++ b/docs/reference/ptn_kl_div_loss.md
@@ -0,0 +1,28 @@
+# Ptn KL Div Loss
+A class to compute the KL divergence loss.
+
+ Args:
+ reduction (str): Reduction method.
+ `none`: no reduction will be applied done.
+ `batchmean`: computes the sum then divides by the input size (recommended in the PyTorch document).
+ `mean`: computes the mean.
+ `sum`: computes the sum.
+
+ log_target (bool): Set to `True` if the target is in log probability instead of probability.
+ The input (y_hat) must always be in log-probability form, regardless of this flag.
+
+## Input
+| Name | Data type |
+|---|---|
+| reduction | None |
+| log_target | Boolean |
+
+## Output
+| Data type |
+|---|
+| Ptloss |
+
+
+Category: PyTorch wrapper - Loss function
+
+ComfyUI Pt Wrapper Node Reference. © 2025 Hide Inada (HowToSD.com). All rights reserved.
diff --git a/docs/reference/ptn_l1_loss.md b/docs/reference/ptn_l1_loss.md
new file mode 100644
index 0000000..1fc8073
--- /dev/null
+++ b/docs/reference/ptn_l1_loss.md
@@ -0,0 +1,23 @@
+# Ptn L1 Loss
+A class to compute the L1 loss.
+
+ Args:
+ reduction: (str): Reduction method.
+ `none`: no reduction will be applied done.
+ `mean`: computes the mean.
+ `sum`: computes the sum.
+
+## Input
+| Name | Data type |
+|---|---|
+| reduction | Mean |
+
+## Output
+| Data type |
+|---|
+| Ptloss |
+
+
+Category: PyTorch wrapper - Loss function
+
+ComfyUI Pt Wrapper Node Reference. © 2025 Hide Inada (HowToSD.com). All rights reserved.
diff --git a/docs/reference/ptn_mse_loss.md b/docs/reference/ptn_mse_loss.md
new file mode 100644
index 0000000..6668235
--- /dev/null
+++ b/docs/reference/ptn_mse_loss.md
@@ -0,0 +1,23 @@
+# Ptn MSE Loss
+A class to compute the squared loss.
+
+ Args:
+ reduction: (str): Reduction method.
+ `none`: no reduction will be applied done.
+ `sum`: computes the sum.
+ `mean`: computes the mean.
+
+## Input
+| Name | Data type |
+|---|---|
+| reduction | Mean |
+
+## Output
+| Data type |
+|---|
+| Ptloss |
+
+
+Category: PyTorch wrapper - Loss function
+
+ComfyUI Pt Wrapper Node Reference. © 2025 Hide Inada (HowToSD.com). All rights reserved.
diff --git a/docs/reference/ptn_nll_loss.md b/docs/reference/ptn_nll_loss.md
new file mode 100644
index 0000000..ec0a11a
--- /dev/null
+++ b/docs/reference/ptn_nll_loss.md
@@ -0,0 +1,25 @@
+# Ptn NLL Loss
+A model to compute the negative log likelihood (NLL) loss.
+
+Unlike Ptn Cross Entropy which takes input in logits, input to this nodes needs to be in log-probabilities.
+
+ Args:
+ reduction: (str): Reduction method.
+ `none`: no reduction will be applied done.
+ `mean`: computes the mean.
+ `sum`: computes the sum.
+
+## Input
+| Name | Data type |
+|---|---|
+| reduction | Mean |
+
+## Output
+| Data type |
+|---|
+| Ptloss |
+
+
+Category: PyTorch wrapper - Loss function
+
+ComfyUI Pt Wrapper Node Reference. © 2025 Hide Inada (HowToSD.com). All rights reserved.
diff --git a/docs/reference/ptn_smooth_l1_loss.md b/docs/reference/ptn_smooth_l1_loss.md
new file mode 100644
index 0000000..d591879
--- /dev/null
+++ b/docs/reference/ptn_smooth_l1_loss.md
@@ -0,0 +1,28 @@
+# Ptn Smooth L1 Loss
+A class to compute the Smooth L1 loss.
+
+Please see [PyTorch documentation](https://pytorch.org/docs/stable/generated/torch.nn.SmoothL1Loss.html) for details and exact formulation.
+
+
+ Args:
+ reduction (str): Reduction method.
+ `none`: no reduction will be applied done.
+ `mean`: computes the mean.
+ `sum`: computes the sum.
+ beta (float): The threshold used to switch between adjusted L2 and L1 loss.
+
+## Input
+| Name | Data type |
+|---|---|
+| reduction | Mean |
+| beta | Float |
+
+## Output
+| Data type |
+|---|
+| Ptloss |
+
+
+Category: PyTorch wrapper - Loss function
+
+ComfyUI Pt Wrapper Node Reference. © 2025 Hide Inada (HowToSD.com). All rights reserved.
diff --git a/modules/pytorch_wrapper/pt_apply_function.py b/modules/pytorch_wrapper/pt_apply_function.py
index e48f6de..81cd17f 100644
--- a/modules/pytorch_wrapper/pt_apply_function.py
+++ b/modules/pytorch_wrapper/pt_apply_function.py
@@ -43,6 +43,6 @@ class PtApplyFunction:
closure (Callable): Differentiable function to apply to the tensor.
Returns:
- Tuple[nn.Module]: A tuple containing the instantiated model.
+ Tuple[nn.Module]: A tuple containing the tensor.
"""
return (closure(tens),)
diff --git a/modules/pytorch_wrapper/pt_compute_loss.py b/modules/pytorch_wrapper/pt_compute_loss.py
new file mode 100644
index 0000000..e1b587c
--- /dev/null
+++ b/modules/pytorch_wrapper/pt_compute_loss.py
@@ -0,0 +1,49 @@
+from typing import Any, Dict, Tuple
+import torch
+import torch.nn as nn
+
+class PtComputeLoss:
+ """
+ Computes loss for the input with the target using the specified loss function.
+
+ The user does not normally need to use this node. Instead the user should use training nodes that accept a loss object.
+
+ category: PyTorch wrapper - Training
+ """
+
+ @classmethod
+ def INPUT_TYPES(cls) -> Dict[str, Any]:
+ """
+ Defines the expected input types.
+
+ Returns:
+ Dict[str, Any]: A dictionary specifying required and optional input types.
+ """
+ return {
+ "required": {
+ "input_tens": ("TENSOR", {}),
+ "target_tens": ("TENSOR", {}),
+ "loss": ("PTLOSS", {})
+ }
+ }
+
+ RETURN_TYPES: Tuple[str, ...] = ("TENSOR",)
+ FUNCTION: str = "f"
+ CATEGORY: str = "Training"
+
+ def f(self,
+ input_tens: torch.Tensor,
+ target_tens: torch.Tensor,
+ loss: nn.Module) -> Tuple[torch.Tensor]:
+ """
+ Computes loss for the input with the target using the specified loss function.
+
+ Args:
+ input_tens (torch.Tensor): Tensor containing the model output or y_hat.
+ target_tens (torch.Tensor): Tensor containing the ground truth or the label.
+ loss (nn.Module): Loss object instance.
+
+ Returns:
+ Tuple[nn.Module]: A tuple containing the computed loss.
+ """
+ return (loss(input_tens, target_tens),)
diff --git a/modules/pytorch_wrapper/ptn_bce_loss.py b/modules/pytorch_wrapper/ptn_bce_loss.py
new file mode 100644
index 0000000..71f6399
--- /dev/null
+++ b/modules/pytorch_wrapper/ptn_bce_loss.py
@@ -0,0 +1,63 @@
+from typing import Any, Dict, Tuple
+import torch
+import torch.nn as nn
+
+
+class PtnBCELoss:
+ """
+ Ptn BCE Loss:
+ A class to compute the binary cross entropy loss.
+
+ Unlike the Ptn BCE Loss With Logits node, which takes logits as input, this node expects probabilities. If the input consists of logits, apply a Sigmoid function first or use the Ptn BCE Loss With Logits node instead.
+
+ Args:
+ reduction (str): Reduction method.
+ `none`: no reduction will be applied done.
+ `mean`: computes the mean.
+ `sum`: computes the sum.
+
+
+ category: PyTorch wrapper - Loss function
+ """
+
+ @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": {
+ "reduction": (("mean", "sum", "none"),)
+ }
+ }
+
+ RETURN_TYPES: Tuple[str] = ("PTLOSS",)
+ FUNCTION: str = "f"
+ CATEGORY: str = "Training"
+
+ @classmethod
+ def IS_CHANGED(cls, **kw):
+ return float("NaN")
+
+ def f(self,
+ reduction: str
+ ) -> Tuple[nn.Module]:
+ """
+ A model to compute the cross entropy loss.
+
+ Args:
+ reduction: (str): Reduction method.
+ `none`: no reduction will be applied done.
+ `sum`: computes the sum.
+ `mean`: computes the mean.
+
+ Returns:
+ Tuple[nn.Module]: A tuple containing the instantiated PyTorch model.
+ """
+ with torch.inference_mode(False):
+ model = nn.BCELoss(reduction=reduction)
+
+ return (model,)
diff --git a/modules/pytorch_wrapper/ptn_bce_with_logits_loss.py b/modules/pytorch_wrapper/ptn_bce_with_logits_loss.py
new file mode 100644
index 0000000..5d15bb2
--- /dev/null
+++ b/modules/pytorch_wrapper/ptn_bce_with_logits_loss.py
@@ -0,0 +1,61 @@
+from typing import Any, Dict, Tuple
+import torch
+import torch.nn as nn
+
+
+class PtnBCEWithLogitsLoss:
+ """
+ Ptn BCE With Logits Loss:
+ A class to compute the sigmoid then binary cross entropy loss.
+
+ Args:
+ reduction (str): Reduction method.
+ `none`: no reduction will be applied done.
+ `mean`: computes the mean.
+ `sum`: computes the sum.
+
+
+ category: PyTorch wrapper - Loss function
+ """
+
+ @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": {
+ "reduction": (("mean", "sum", "none"),)
+ }
+ }
+
+ RETURN_TYPES: Tuple[str] = ("PTLOSS",)
+ FUNCTION: str = "f"
+ CATEGORY: str = "Training"
+
+ @classmethod
+ def IS_CHANGED(cls, **kw):
+ return float("NaN")
+
+ def f(self,
+ reduction: str
+ ) -> Tuple[nn.Module]:
+ """
+ A class to compute the sigmoid then binary cross entropy loss.
+
+ Args:
+ reduction: (str): Reduction method.
+ `none`: no reduction will be applied done.
+ `sum`: computes the sum.
+ `mean`: computes the mean.
+
+ Returns:
+ Tuple[nn.Module]: A tuple containing the instantiated PyTorch model.
+ """
+ with torch.inference_mode(False):
+ model = nn.BCEWithLogitsLoss(reduction=reduction)
+
+ return (model,)
diff --git a/modules/pytorch_wrapper/ptn_cross_entropy_loss.py b/modules/pytorch_wrapper/ptn_cross_entropy_loss.py
new file mode 100644
index 0000000..2dbe40a
--- /dev/null
+++ b/modules/pytorch_wrapper/ptn_cross_entropy_loss.py
@@ -0,0 +1,63 @@
+from typing import Any, Dict, Tuple
+import torch
+import torch.nn as nn
+
+
+class PtnCrossEntropyLoss:
+ """
+ Ptn Cross Entropy Loss:
+ A class to compute the cross entropy loss.
+
+ Unlike Ptn NLL Loss node which takes log probabilities for input, input to this node needs to be logits. You can consider this node as a combination of log softmax and NLL.
+
+ Args:
+ reduction: (str): Reduction method.
+ `none`: no reduction will be applied done.
+ `mean`: computes the mean.
+ `sum`: computes the sum.
+
+
+ category: PyTorch wrapper - Loss function
+ """
+
+ @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": {
+ "reduction": (("mean", "sum", "none"),)
+ }
+ }
+
+ RETURN_TYPES: Tuple[str] = ("PTLOSS",)
+ FUNCTION: str = "f"
+ CATEGORY: str = "Training"
+
+ @classmethod
+ def IS_CHANGED(cls, **kw):
+ return float("NaN")
+
+ def f(self,
+ reduction: str
+ ) -> Tuple[nn.Module]:
+ """
+ A model to compute the cross entropy loss.
+
+ Args:
+ reduction: (str): Reduction method.
+ `none`: no reduction will be applied done.
+ `sum`: computes the sum.
+ `mean`: computes the mean.
+
+ Returns:
+ Tuple[nn.Module]: A tuple containing the instantiated PyTorch model.
+ """
+ with torch.inference_mode(False):
+ model = nn.CrossEntropyLoss(reduction=reduction)
+
+ return (model,)
diff --git a/modules/pytorch_wrapper/ptn_huber_loss.py b/modules/pytorch_wrapper/ptn_huber_loss.py
new file mode 100644
index 0000000..ddacc58
--- /dev/null
+++ b/modules/pytorch_wrapper/ptn_huber_loss.py
@@ -0,0 +1,68 @@
+from typing import Any, Dict, Tuple
+import torch
+import torch.nn as nn
+
+
+class PtnHuberLoss:
+ """
+ Ptn Huber Loss:
+
+ A class to compute the Huber loss.
+
+ Please see [PyTorch documentation](https://pytorch.org/docs/stable/generated/torch.nn.HuberLoss.html) for details.
+
+
+ Args:
+ reduction (str): Reduction method.
+ `none`: no reduction will be applied done.
+ `mean`: computes the mean.
+ `sum`: computes the sum.
+ delta (float): The threshold used to switch between adjusted MSE and L1 loss.
+
+ category: PyTorch wrapper - Loss function
+ """
+
+ @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": {
+ "reduction": (("mean", "sum", "none"),),
+ "delta": ("FLOAT", {"default":1.0, "min":0, "max":1e9, "step":0.0000000001}),
+ }
+ }
+
+ RETURN_TYPES: Tuple[str] = ("PTLOSS",)
+ FUNCTION: str = "f"
+ CATEGORY: str = "Training"
+
+ @classmethod
+ def IS_CHANGED(cls, **kw):
+ return float("NaN")
+
+ def f(self,
+ reduction: str,
+ delta: float
+ ) -> Tuple[nn.Module]:
+ """
+ A model to compute the Huber loss.
+
+ Args:
+ reduction: (str): Reduction method.
+ `none`: no reduction will be applied done.
+ `mean`: computes the mean.
+ `sum`: computes the sum.
+ beta (float): The threshold used to switch between adjusted MSE and L1 loss.
+
+ Returns:
+ Tuple[nn.Module]: A tuple containing the instantiated PyTorch model.
+ """
+ with torch.inference_mode(False):
+ model = nn.HuberLoss(reduction=reduction, delta=delta)
+
+ return (model,)
diff --git a/modules/pytorch_wrapper/ptn_kl_div_loss.py b/modules/pytorch_wrapper/ptn_kl_div_loss.py
new file mode 100644
index 0000000..ac7aa1a
--- /dev/null
+++ b/modules/pytorch_wrapper/ptn_kl_div_loss.py
@@ -0,0 +1,71 @@
+from typing import Any, Dict, Tuple
+import torch
+import torch.nn as nn
+
+
+class PtnKLDivLoss:
+ """
+ Ptn KL Div Loss:
+ A class to compute the KL divergence loss.
+
+ Args:
+ reduction (str): Reduction method.
+ `none`: no reduction will be applied done.
+ `batchmean`: computes the sum then divides by the input size (recommended in the PyTorch document).
+ `mean`: computes the mean.
+ `sum`: computes the sum.
+
+ log_target (bool): Set to `True` if the target is in log probability instead of probability.
+ The input (y_hat) must always be in log-probability form, regardless of this flag.
+
+ category: PyTorch wrapper - Loss function
+ """
+
+ @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": {
+ "reduction": (("batchmean", "mean", "sum", "none"),),
+ "log_target": ("BOOLEAN", {"default": False})
+ }
+ }
+
+ RETURN_TYPES: Tuple[str] = ("PTLOSS",)
+ FUNCTION: str = "f"
+ CATEGORY: str = "Training"
+
+ @classmethod
+ def IS_CHANGED(cls, **kw):
+ return float("NaN")
+
+ def f(self,
+ reduction: str,
+ log_target: bool
+ ) -> Tuple[nn.Module]:
+ """
+ A model to compute the cross entropy loss.
+
+ Args:
+ reduction: (str): Reduction method.
+ `none`: no reduction will be applied done.
+ `batchmean`: computes the sum then divides by the input size (recommended in the PyTorch document).
+ `sum`: computes the sum.
+ `mean`: computes the mean.
+ log_target (bool): Set to `True` if the target is in log probability instead of probability.
+ The input (y_hat) must always be in log-probability form, regardless of this flag.
+
+ Returns:
+ Tuple[nn.Module]: A tuple containing the instantiated PyTorch model.
+ """
+ with torch.inference_mode(False):
+ model = nn.KLDivLoss(
+ reduction=reduction,
+ log_target=log_target)
+
+ return (model,)
diff --git a/modules/pytorch_wrapper/ptn_l1_loss.py b/modules/pytorch_wrapper/ptn_l1_loss.py
new file mode 100644
index 0000000..9ad6f2e
--- /dev/null
+++ b/modules/pytorch_wrapper/ptn_l1_loss.py
@@ -0,0 +1,62 @@
+from typing import Any, Dict, Tuple
+import torch
+import torch.nn as nn
+
+
+class PtnL1Loss:
+ """
+ Ptn L1 Loss:
+ A class to compute the L1 loss.
+
+ Args:
+ reduction: (str): Reduction method.
+ `none`: no reduction will be applied done.
+ `mean`: computes the mean.
+ `sum`: computes the sum.
+
+
+ category: PyTorch wrapper - Loss function
+ """
+
+ @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": {
+ "reduction": (("mean", "sum", "none"),)
+ }
+ }
+
+ RETURN_TYPES: Tuple[str] = ("PTLOSS",)
+ FUNCTION: str = "f"
+ CATEGORY: str = "Training"
+
+ @classmethod
+ def IS_CHANGED(cls, **kw):
+ return float("NaN")
+
+ def f(self,
+ reduction: str
+ ) -> Tuple[nn.Module]:
+ """
+ A model to compute the L1 loss.
+
+ Args:
+ reduction: (str): Reduction method.
+ `none`: no reduction will be applied done.
+ `mean`: computes the mean.
+ `sum`: computes the sum.
+
+
+ Returns:
+ Tuple[nn.Module]: A tuple containing the instantiated PyTorch model.
+ """
+ with torch.inference_mode(False):
+ model = nn.L1Loss(reduction=reduction)
+
+ return (model,)
diff --git a/modules/pytorch_wrapper/ptn_mse_loss.py b/modules/pytorch_wrapper/ptn_mse_loss.py
new file mode 100644
index 0000000..6c474bb
--- /dev/null
+++ b/modules/pytorch_wrapper/ptn_mse_loss.py
@@ -0,0 +1,60 @@
+from typing import Any, Dict, Tuple
+import torch
+import torch.nn as nn
+
+
+class PtnMSELoss:
+ """
+ Ptn MSE Loss:
+ A class to compute the squared loss.
+
+ Args:
+ reduction: (str): Reduction method.
+ `none`: no reduction will be applied done.
+ `sum`: computes the sum.
+ `mean`: computes the mean.
+
+ category: PyTorch wrapper - Loss function
+ """
+
+ @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": {
+ "reduction": (("mean", "sum", "none"),)
+ }
+ }
+
+ RETURN_TYPES: Tuple[str] = ("PTLOSS",)
+ FUNCTION: str = "f"
+ CATEGORY: str = "Training"
+
+ @classmethod
+ def IS_CHANGED(cls, **kw):
+ return float("NaN")
+
+ def f(self,
+ reduction: str
+ ) -> Tuple[nn.Module]:
+ """
+ A model to compute the squared loss.
+
+ Args:
+ reduction: (str): Reduction method.
+ `none`: no reduction will be applied done.
+ `sum`: computes the sum.
+ `mean`: computes the mean.
+
+ Returns:
+ Tuple[nn.Module]: A tuple containing the instantiated PyTorch model.
+ """
+ with torch.inference_mode(False):
+ model = nn.MSELoss(reduction=reduction)
+
+ return (model,)
diff --git a/modules/pytorch_wrapper/ptn_nll_loss.py b/modules/pytorch_wrapper/ptn_nll_loss.py
new file mode 100644
index 0000000..0968275
--- /dev/null
+++ b/modules/pytorch_wrapper/ptn_nll_loss.py
@@ -0,0 +1,63 @@
+from typing import Any, Dict, Tuple
+import torch
+import torch.nn as nn
+
+
+class PtnNLLLoss:
+ """
+ Ptn NLL Loss:
+ A model to compute the negative log likelihood (NLL) loss.
+
+ Unlike Ptn Cross Entropy which takes input in logits, input to this nodes needs to be in log-probabilities.
+
+ Args:
+ reduction: (str): Reduction method.
+ `none`: no reduction will be applied done.
+ `mean`: computes the mean.
+ `sum`: computes the sum.
+
+
+ category: PyTorch wrapper - Loss function
+ """
+
+ @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": {
+ "reduction": (("mean", "sum", "none"),)
+ }
+ }
+
+ RETURN_TYPES: Tuple[str] = ("PTLOSS",)
+ FUNCTION: str = "f"
+ CATEGORY: str = "Training"
+
+ @classmethod
+ def IS_CHANGED(cls, **kw):
+ return float("NaN")
+
+ def f(self,
+ reduction: str
+ ) -> Tuple[nn.Module]:
+ """
+ A model to compute the NLL loss.
+
+ Args:
+ reduction: (str): Reduction method.
+ `none`: no reduction will be applied done.
+ `sum`: computes the sum.
+ `mean`: computes the mean.
+
+ Returns:
+ Tuple[nn.Module]: A tuple containing the instantiated PyTorch model.
+ """
+ with torch.inference_mode(False):
+ model = nn.NLLLoss(reduction=reduction)
+
+ return (model,)
diff --git a/modules/pytorch_wrapper/ptn_smooth_l1_loss.py b/modules/pytorch_wrapper/ptn_smooth_l1_loss.py
new file mode 100644
index 0000000..b6b75ab
--- /dev/null
+++ b/modules/pytorch_wrapper/ptn_smooth_l1_loss.py
@@ -0,0 +1,68 @@
+from typing import Any, Dict, Tuple
+import torch
+import torch.nn as nn
+
+
+class PtnSmoothL1Loss:
+ """
+ Ptn Smooth L1 Loss:
+
+ A class to compute the Smooth L1 loss.
+
+ Please see [PyTorch documentation](https://pytorch.org/docs/stable/generated/torch.nn.SmoothL1Loss.html) for details and exact formulation.
+
+
+ Args:
+ reduction (str): Reduction method.
+ `none`: no reduction will be applied done.
+ `mean`: computes the mean.
+ `sum`: computes the sum.
+ beta (float): The threshold used to switch between adjusted L2 and L1 loss.
+
+ category: PyTorch wrapper - Loss function
+ """
+
+ @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": {
+ "reduction": (("mean", "sum", "none"),),
+ "beta": ("FLOAT", {"default":1.0, "min":0, "max":1e9, "step":0.0000000001}),
+ }
+ }
+
+ RETURN_TYPES: Tuple[str] = ("PTLOSS",)
+ FUNCTION: str = "f"
+ CATEGORY: str = "Training"
+
+ @classmethod
+ def IS_CHANGED(cls, **kw):
+ return float("NaN")
+
+ def f(self,
+ reduction: str,
+ beta: float
+ ) -> Tuple[nn.Module]:
+ """
+ A model to compute the Smooth L1 loss.
+
+ Args:
+ reduction: (str): Reduction method.
+ `none`: no reduction will be applied done.
+ `mean`: computes the mean.
+ `sum`: computes the sum.
+ beta (float): The threshold used to switch between adjusted L2 and L1 loss.
+
+ Returns:
+ Tuple[nn.Module]: A tuple containing the instantiated PyTorch model.
+ """
+ with torch.inference_mode(False):
+ model = nn.SmoothL1Loss(reduction=reduction, beta=beta)
+
+ return (model,)
diff --git a/pyproject.toml b/pyproject.toml
index 607d206..eb83abe 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,7 +1,7 @@
[project]
name = "pt-wrapper"
description = "PyTorch extension for ComfyUI featuring extensive PyTorch wrapper nodes for seamless tensor operations and PyTorch model training."
-version = "3.1.4"
+version = "3.2.0"
license = {file = "LICENSE"}
[project.urls]
diff --git a/tests/pytorch_wrapper/pt_abs_test.py b/tests/pytorch_wrapper/pt_abs_test.py
index 046e88a..7fc8588 100644
--- a/tests/pytorch_wrapper/pt_abs_test.py
+++ b/tests/pytorch_wrapper/pt_abs_test.py
@@ -7,7 +7,7 @@ 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_abs import PtAbs # Updated import to PtAbs
+from pytorch_wrapper.pt_abs import PtAbs
class TestPtAbs(unittest.TestCase):
diff --git a/tests/pytorch_wrapper/pt_acos_test.py b/tests/pytorch_wrapper/pt_acos_test.py
index 528acf8..6cc132f 100644
--- a/tests/pytorch_wrapper/pt_acos_test.py
+++ b/tests/pytorch_wrapper/pt_acos_test.py
@@ -7,7 +7,7 @@ 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_acos import PtAcos # Updated import to PtAcos
+from pytorch_wrapper.pt_acos import PtAcos
class TestPtAcos(unittest.TestCase):
diff --git a/tests/pytorch_wrapper/pt_asin_test.py b/tests/pytorch_wrapper/pt_asin_test.py
index 92f8905..65706f7 100644
--- a/tests/pytorch_wrapper/pt_asin_test.py
+++ b/tests/pytorch_wrapper/pt_asin_test.py
@@ -7,7 +7,7 @@ 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_asin import PtAsin # Updated import to PtAsin
+from pytorch_wrapper.pt_asin import PtAsin
class TestPtAsin(unittest.TestCase):
diff --git a/tests/pytorch_wrapper/pt_atan_test.py b/tests/pytorch_wrapper/pt_atan_test.py
index 551d7a5..687c1dc 100644
--- a/tests/pytorch_wrapper/pt_atan_test.py
+++ b/tests/pytorch_wrapper/pt_atan_test.py
@@ -7,7 +7,7 @@ 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_atan import PtAtan # Updated import to PtAtan
+from pytorch_wrapper.pt_atan import PtAtan
class TestPtAtan(unittest.TestCase):
diff --git a/tests/pytorch_wrapper/pt_bitwise_and_test.py b/tests/pytorch_wrapper/pt_bitwise_and_test.py
index 630d459..43ad8de 100644
--- a/tests/pytorch_wrapper/pt_bitwise_and_test.py
+++ b/tests/pytorch_wrapper/pt_bitwise_and_test.py
@@ -7,7 +7,7 @@ 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_bitwise_and import PtBitwiseAnd # Updated import to PtBitwiseAnd
+from pytorch_wrapper.pt_bitwise_and import PtBitwiseAnd
class TestPtBitwiseAnd(unittest.TestCase):
diff --git a/tests/pytorch_wrapper/pt_bitwise_left_shift_test.py b/tests/pytorch_wrapper/pt_bitwise_left_shift_test.py
index 124b8bf..bf46ff5 100644
--- a/tests/pytorch_wrapper/pt_bitwise_left_shift_test.py
+++ b/tests/pytorch_wrapper/pt_bitwise_left_shift_test.py
@@ -7,7 +7,7 @@ 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_bitwise_left_shift import PtBitwiseLeftShift # Updated import to PtBitwiseLeftShift
+from pytorch_wrapper.pt_bitwise_left_shift import PtBitwiseLeftShift
class TestPtBitwiseLeftShift(unittest.TestCase):
diff --git a/tests/pytorch_wrapper/pt_bitwise_not_test.py b/tests/pytorch_wrapper/pt_bitwise_not_test.py
index a7d87d8..bad501c 100644
--- a/tests/pytorch_wrapper/pt_bitwise_not_test.py
+++ b/tests/pytorch_wrapper/pt_bitwise_not_test.py
@@ -7,7 +7,7 @@ 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_bitwise_not import PtBitwiseNot # Updated import to PtBitwiseNot
+from pytorch_wrapper.pt_bitwise_not import PtBitwiseNot
class TestPtBitwiseNot(unittest.TestCase):
diff --git a/tests/pytorch_wrapper/pt_bitwise_or_test.py b/tests/pytorch_wrapper/pt_bitwise_or_test.py
index 9b606fd..ad18a8d 100644
--- a/tests/pytorch_wrapper/pt_bitwise_or_test.py
+++ b/tests/pytorch_wrapper/pt_bitwise_or_test.py
@@ -7,7 +7,7 @@ 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_bitwise_or import PtBitwiseOr # Updated import to PtBitwiseOr
+from pytorch_wrapper.pt_bitwise_or import PtBitwiseOr
class TestPtBitwiseOr(unittest.TestCase):
diff --git a/tests/pytorch_wrapper/pt_bitwise_right_shift_test.py b/tests/pytorch_wrapper/pt_bitwise_right_shift_test.py
index 69a9b9b..26c97c5 100644
--- a/tests/pytorch_wrapper/pt_bitwise_right_shift_test.py
+++ b/tests/pytorch_wrapper/pt_bitwise_right_shift_test.py
@@ -7,7 +7,7 @@ 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_bitwise_right_shift import PtBitwiseRightShift # Updated import to PtBitwiseRightShift
+from pytorch_wrapper.pt_bitwise_right_shift import PtBitwiseRightShift
class TestPtBitwiseRightShift(unittest.TestCase):
diff --git a/tests/pytorch_wrapper/pt_bitwise_xor_test.py b/tests/pytorch_wrapper/pt_bitwise_xor_test.py
index 24ce242..4a63908 100644
--- a/tests/pytorch_wrapper/pt_bitwise_xor_test.py
+++ b/tests/pytorch_wrapper/pt_bitwise_xor_test.py
@@ -7,7 +7,7 @@ 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_bitwise_xor import PtBitwiseXor # Updated import to PtBitwiseXor
+from pytorch_wrapper.pt_bitwise_xor import PtBitwiseXor
class TestPtBitwiseXor(unittest.TestCase):
diff --git a/tests/pytorch_wrapper/pt_bmm_test.py b/tests/pytorch_wrapper/pt_bmm_test.py
index e1e1a87..ec58d49 100644
--- a/tests/pytorch_wrapper/pt_bmm_test.py
+++ b/tests/pytorch_wrapper/pt_bmm_test.py
@@ -7,7 +7,7 @@ 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_bmm import PtBmm # Updated import to PtBmm
+from pytorch_wrapper.pt_bmm import PtBmm
class TestPtBmm(unittest.TestCase):
diff --git a/tests/pytorch_wrapper/pt_compute_loss_test.py b/tests/pytorch_wrapper/pt_compute_loss_test.py
new file mode 100644
index 0000000..da2d7e4
--- /dev/null
+++ b/tests/pytorch_wrapper/pt_compute_loss_test.py
@@ -0,0 +1,86 @@
+import os
+import sys
+import unittest
+import torch
+from torch import Tensor
+from typing import List, Tuple
+
+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_compute_loss import PtComputeLoss
+from pytorch_wrapper.ptn_l1_loss import PtnL1Loss
+
+
+class TestComputeLoss(unittest.TestCase):
+ def setUp(self) -> None:
+ """
+ Set up the test instance by initializing the loss functions and test cases.
+ """
+ self.compute_node = PtComputeLoss()
+ self.loss_node = PtnL1Loss()
+ self.loss_fun_none = self.loss_node.f("none")[0]
+ self.loss_fun_mean = self.loss_node.f("mean")[0]
+ self.loss_fun_sum = self.loss_node.f("sum")[0]
+
+ # Test cases: (predicted values y_hat, ground truth y)
+ self.test_cases: List[Tuple[Tensor, Tensor]] = [
+ (
+ torch.tensor([0.1, -1.0, 10, -100, 1000], dtype=torch.float32),
+ torch.tensor([0.09, -1.1, 9, -101, 999], dtype=torch.float32),
+ )
+ ]
+
+ def unreduced_loss(self, y_hat: Tensor, y: Tensor) -> Tensor:
+ """
+ Compute the unreduced L1 loss.
+
+ Args:
+ y_hat (Tensor): Predicted values.
+ y (Tensor): Ground truth values.
+
+ Returns:
+ Tensor: Unreduced element-wise L1 loss.
+ """
+ return torch.abs(y_hat - y)
+
+ def test_none(self) -> None:
+ """
+ Test L1 loss with reduction='none'.
+ """
+ for y_hat, y in self.test_cases:
+ with self.subTest(y_hat=y_hat):
+ actual = self.compute_node.f(y_hat, y, self.loss_fun_none)[0]
+ expected = self.unreduced_loss(y_hat, y)
+ actual = self.loss_fun_none(y_hat, y)
+ self.assertTrue(torch.allclose(actual, expected, atol=1e-6),
+ f"Expected {expected}, got {actual}")
+
+ def test_mean(self) -> None:
+ """
+ Test L1 loss with reduction='mean'.
+ """
+ for y_hat, y in self.test_cases:
+ with self.subTest(y_hat=y_hat):
+ loss = self.unreduced_loss(y_hat, y)
+ expected = loss.mean(dim=0)
+ actual = self.compute_node.f(y_hat, y, self.loss_fun_mean)[0]
+ self.assertTrue(torch.equal(actual, expected),
+ f"Expected {expected}, got {actual}")
+
+ def test_sum(self) -> None:
+ """
+ Test L1 loss with reduction='sum'.
+ """
+ for y_hat, y in self.test_cases:
+ with self.subTest(y_hat=y_hat):
+ loss = self.unreduced_loss(y_hat, y)
+ expected = loss.sum(dim=0)
+ actual = self.compute_node.f(y_hat, y, self.loss_fun_sum)[0]
+ self.assertTrue(torch.equal(actual, expected),
+ f"Expected {expected}, got {actual}")
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/tests/pytorch_wrapper/pt_cos_test.py b/tests/pytorch_wrapper/pt_cos_test.py
index 9d03217..f853ed5 100644
--- a/tests/pytorch_wrapper/pt_cos_test.py
+++ b/tests/pytorch_wrapper/pt_cos_test.py
@@ -7,7 +7,7 @@ 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_cos import PtCos # Updated import to PtCos
+from pytorch_wrapper.pt_cos import PtCos
class TestPtCos(unittest.TestCase):
diff --git a/tests/pytorch_wrapper/pt_cosh_test.py b/tests/pytorch_wrapper/pt_cosh_test.py
index 299fec0..4963f41 100644
--- a/tests/pytorch_wrapper/pt_cosh_test.py
+++ b/tests/pytorch_wrapper/pt_cosh_test.py
@@ -7,7 +7,7 @@ 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_cosh import PtCosh # Updated import to PtCosh
+from pytorch_wrapper.pt_cosh import PtCosh
class TestPtCosh(unittest.TestCase):
diff --git a/tests/pytorch_wrapper/pt_div_test.py b/tests/pytorch_wrapper/pt_div_test.py
index 7e6e022..a7239a8 100644
--- a/tests/pytorch_wrapper/pt_div_test.py
+++ b/tests/pytorch_wrapper/pt_div_test.py
@@ -7,7 +7,7 @@ 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_div import PtDiv # Updated import to PtDiv
+from pytorch_wrapper.pt_div import PtDiv
class TestPtDiv(unittest.TestCase):
diff --git a/tests/pytorch_wrapper/pt_einsum_test.py b/tests/pytorch_wrapper/pt_einsum_test.py
index ef93fcb..f44bea8 100644
--- a/tests/pytorch_wrapper/pt_einsum_test.py
+++ b/tests/pytorch_wrapper/pt_einsum_test.py
@@ -7,7 +7,7 @@ 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_einsum import PtEinsum # Updated import to PtEinsum
+from pytorch_wrapper.pt_einsum import PtEinsum
class TestPtEinsum(unittest.TestCase):
diff --git a/tests/pytorch_wrapper/pt_eq_test.py b/tests/pytorch_wrapper/pt_eq_test.py
index 0943a8f..6d2c927 100644
--- a/tests/pytorch_wrapper/pt_eq_test.py
+++ b/tests/pytorch_wrapper/pt_eq_test.py
@@ -7,7 +7,7 @@ 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_eq import PtEq # Updated import to PtEq
+from pytorch_wrapper.pt_eq import PtEq
class TestPtEq(unittest.TestCase):
diff --git a/tests/pytorch_wrapper/pt_exp_test.py b/tests/pytorch_wrapper/pt_exp_test.py
index b88551c..51a491e 100644
--- a/tests/pytorch_wrapper/pt_exp_test.py
+++ b/tests/pytorch_wrapper/pt_exp_test.py
@@ -7,7 +7,7 @@ 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_exp import PtExp # Updated import to PtExp
+from pytorch_wrapper.pt_exp import PtExp
class TestPtExp(unittest.TestCase):
diff --git a/tests/pytorch_wrapper/pt_ge_test.py b/tests/pytorch_wrapper/pt_ge_test.py
index 92ab581..9165e63 100644
--- a/tests/pytorch_wrapper/pt_ge_test.py
+++ b/tests/pytorch_wrapper/pt_ge_test.py
@@ -7,7 +7,7 @@ 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_ge import PtGe # Updated import to PtGe
+from pytorch_wrapper.pt_ge import PtGe
class TestPtGe(unittest.TestCase):
diff --git a/tests/pytorch_wrapper/pt_gt_test.py b/tests/pytorch_wrapper/pt_gt_test.py
index 29603bf..a4fc56a 100644
--- a/tests/pytorch_wrapper/pt_gt_test.py
+++ b/tests/pytorch_wrapper/pt_gt_test.py
@@ -7,7 +7,7 @@ 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_gt import PtGt # Updated import to PtGt
+from pytorch_wrapper.pt_gt import PtGt
class TestPtGt(unittest.TestCase):
diff --git a/tests/pytorch_wrapper/pt_le_test.py b/tests/pytorch_wrapper/pt_le_test.py
index 4cb062d..7f32553 100644
--- a/tests/pytorch_wrapper/pt_le_test.py
+++ b/tests/pytorch_wrapper/pt_le_test.py
@@ -7,7 +7,7 @@ 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_le import PtLe # Updated import to PtLe
+from pytorch_wrapper.pt_le import PtLe
class TestPtLe(unittest.TestCase):
diff --git a/tests/pytorch_wrapper/pt_log_test.py b/tests/pytorch_wrapper/pt_log_test.py
index a2b644a..186212e 100644
--- a/tests/pytorch_wrapper/pt_log_test.py
+++ b/tests/pytorch_wrapper/pt_log_test.py
@@ -7,7 +7,7 @@ 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_log import PtLog # Updated import to PtLog
+from pytorch_wrapper.pt_log import PtLog
class TestPtLog(unittest.TestCase):
diff --git a/tests/pytorch_wrapper/pt_logical_and_test.py b/tests/pytorch_wrapper/pt_logical_and_test.py
index c5227fb..7def74f 100644
--- a/tests/pytorch_wrapper/pt_logical_and_test.py
+++ b/tests/pytorch_wrapper/pt_logical_and_test.py
@@ -7,7 +7,7 @@ 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_logical_and import PtLogicalAnd # Updated import to PtLogicalAnd
+from pytorch_wrapper.pt_logical_and import PtLogicalAnd
class TestPtLogicalAnd(unittest.TestCase):
diff --git a/tests/pytorch_wrapper/pt_logical_not_test.py b/tests/pytorch_wrapper/pt_logical_not_test.py
index de18c94..dc3917b 100644
--- a/tests/pytorch_wrapper/pt_logical_not_test.py
+++ b/tests/pytorch_wrapper/pt_logical_not_test.py
@@ -7,7 +7,7 @@ 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_logical_not import PtLogicalNot # Updated import to PtLogicalNot
+from pytorch_wrapper.pt_logical_not import PtLogicalNot
class TestPtLogicalNot(unittest.TestCase):
diff --git a/tests/pytorch_wrapper/pt_logical_or_test.py b/tests/pytorch_wrapper/pt_logical_or_test.py
index 97ae602..fd2ba26 100644
--- a/tests/pytorch_wrapper/pt_logical_or_test.py
+++ b/tests/pytorch_wrapper/pt_logical_or_test.py
@@ -7,7 +7,7 @@ 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_logical_or import PtLogicalOr # Updated import to PtLogicalOr
+from pytorch_wrapper.pt_logical_or import PtLogicalOr
class TestPtLogicalOr(unittest.TestCase):
diff --git a/tests/pytorch_wrapper/pt_logical_xor_test.py b/tests/pytorch_wrapper/pt_logical_xor_test.py
index 7756330..a93d186 100644
--- a/tests/pytorch_wrapper/pt_logical_xor_test.py
+++ b/tests/pytorch_wrapper/pt_logical_xor_test.py
@@ -7,7 +7,7 @@ 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_logical_xor import PtLogicalXor # Updated import to PtLogicalXor
+from pytorch_wrapper.pt_logical_xor import PtLogicalXor
class TestPtLogicalXor(unittest.TestCase):
diff --git a/tests/pytorch_wrapper/pt_lt_test.py b/tests/pytorch_wrapper/pt_lt_test.py
index 027c478..79ddfb0 100644
--- a/tests/pytorch_wrapper/pt_lt_test.py
+++ b/tests/pytorch_wrapper/pt_lt_test.py
@@ -7,7 +7,7 @@ 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_lt import PtLt # Updated import to PtLt
+from pytorch_wrapper.pt_lt import PtLt
class TestPtLt(unittest.TestCase):
diff --git a/tests/pytorch_wrapper/pt_matmul_test.py b/tests/pytorch_wrapper/pt_matmul_test.py
index d3f5245..9213716 100644
--- a/tests/pytorch_wrapper/pt_matmul_test.py
+++ b/tests/pytorch_wrapper/pt_matmul_test.py
@@ -7,7 +7,7 @@ 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_matmul import PtMatMul # Updated import to PtMatMul
+from pytorch_wrapper.pt_matmul import PtMatMul
class TestPtMatMul(unittest.TestCase):
diff --git a/tests/pytorch_wrapper/pt_mm_test.py b/tests/pytorch_wrapper/pt_mm_test.py
index 33fb78f..9fa9caa 100644
--- a/tests/pytorch_wrapper/pt_mm_test.py
+++ b/tests/pytorch_wrapper/pt_mm_test.py
@@ -7,7 +7,7 @@ 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_mm import PtMm # Updated import to PtMm
+from pytorch_wrapper.pt_mm import PtMm
class TestPtMm(unittest.TestCase):
diff --git a/tests/pytorch_wrapper/pt_mul_test.py b/tests/pytorch_wrapper/pt_mul_test.py
index 8bff3f9..22acd75 100644
--- a/tests/pytorch_wrapper/pt_mul_test.py
+++ b/tests/pytorch_wrapper/pt_mul_test.py
@@ -7,7 +7,7 @@ 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_mul import PtMul # Updated import to PtMul
+from pytorch_wrapper.pt_mul import PtMul
class TestPtMul(unittest.TestCase):
diff --git a/tests/pytorch_wrapper/pt_ne_test.py b/tests/pytorch_wrapper/pt_ne_test.py
index 4816b21..6cb1cd4 100644
--- a/tests/pytorch_wrapper/pt_ne_test.py
+++ b/tests/pytorch_wrapper/pt_ne_test.py
@@ -7,7 +7,7 @@ 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_ne import PtNe # Updated import to PtNe
+from pytorch_wrapper.pt_ne import PtNe
class TestPtNe(unittest.TestCase):
diff --git a/tests/pytorch_wrapper/pt_neg_test.py b/tests/pytorch_wrapper/pt_neg_test.py
index 57c66e1..6845822 100644
--- a/tests/pytorch_wrapper/pt_neg_test.py
+++ b/tests/pytorch_wrapper/pt_neg_test.py
@@ -7,7 +7,7 @@ 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_neg import PtNeg # Updated import to PtNeg
+from pytorch_wrapper.pt_neg import PtNeg
class TestPtNeg(unittest.TestCase):
diff --git a/tests/pytorch_wrapper/pt_rand_int_test.py b/tests/pytorch_wrapper/pt_rand_int_test.py
index 57c66e1..6845822 100644
--- a/tests/pytorch_wrapper/pt_rand_int_test.py
+++ b/tests/pytorch_wrapper/pt_rand_int_test.py
@@ -7,7 +7,7 @@ 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_neg import PtNeg # Updated import to PtNeg
+from pytorch_wrapper.pt_neg import PtNeg
class TestPtNeg(unittest.TestCase):
diff --git a/tests/pytorch_wrapper/pt_sin_test.py b/tests/pytorch_wrapper/pt_sin_test.py
index 25d0b3f..6f04aff 100644
--- a/tests/pytorch_wrapper/pt_sin_test.py
+++ b/tests/pytorch_wrapper/pt_sin_test.py
@@ -7,7 +7,7 @@ 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_sin import PtSin # Updated import to PtSin
+from pytorch_wrapper.pt_sin import PtSin
class TestPtSin(unittest.TestCase):
diff --git a/tests/pytorch_wrapper/pt_sinh_test.py b/tests/pytorch_wrapper/pt_sinh_test.py
index 9e9a360..23db120 100644
--- a/tests/pytorch_wrapper/pt_sinh_test.py
+++ b/tests/pytorch_wrapper/pt_sinh_test.py
@@ -7,7 +7,7 @@ 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_sinh import PtSinh # Updated import to PtSinh
+from pytorch_wrapper.pt_sinh import PtSinh
class TestPtSinh(unittest.TestCase):
diff --git a/tests/pytorch_wrapper/pt_sqrt_test.py b/tests/pytorch_wrapper/pt_sqrt_test.py
index 1254d07..11c00c1 100644
--- a/tests/pytorch_wrapper/pt_sqrt_test.py
+++ b/tests/pytorch_wrapper/pt_sqrt_test.py
@@ -7,7 +7,7 @@ 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
+from pytorch_wrapper.pt_sqrt import PtSqrt
class TestPtSqrt(unittest.TestCase):
diff --git a/tests/pytorch_wrapper/pt_tan_test.py b/tests/pytorch_wrapper/pt_tan_test.py
index 1346872..1590301 100644
--- a/tests/pytorch_wrapper/pt_tan_test.py
+++ b/tests/pytorch_wrapper/pt_tan_test.py
@@ -7,7 +7,7 @@ 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_tan import PtTan # Updated import to PtTan
+from pytorch_wrapper.pt_tan import PtTan
class TestPtTan(unittest.TestCase):
diff --git a/tests/pytorch_wrapper/pt_tanh_test.py b/tests/pytorch_wrapper/pt_tanh_test.py
index e2f0fc8..13d5e68 100644
--- a/tests/pytorch_wrapper/pt_tanh_test.py
+++ b/tests/pytorch_wrapper/pt_tanh_test.py
@@ -7,7 +7,7 @@ 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_tanh import PtTanh # Updated import to PtTanh
+from pytorch_wrapper.pt_tanh import PtTanh
class TestPtTanh(unittest.TestCase):
diff --git a/tests/pytorch_wrapper/pt_train_classification_conv_model_lr_test.py b/tests/pytorch_wrapper/pt_train_classification_conv_model_lr_test.py
index 28f6012..6df86d0 100644
--- a/tests/pytorch_wrapper/pt_train_classification_conv_model_lr_test.py
+++ b/tests/pytorch_wrapper/pt_train_classification_conv_model_lr_test.py
@@ -15,7 +15,7 @@ from pytorch_wrapper.ptv_transforms_to_tensor import PtvTransformsToTensor
from pytorch_wrapper.pt_data_loader import PtDataLoader
from pytorch_wrapper.pt_train_classification_model_lr import PtTrainClassificationModelLr
from pytorch_wrapper.pt_save_model import PtSaveModel
-from pytorch_wrapper.pto_lr_scheduler_step import PtoLrSchedulerStep # Updated import
+from pytorch_wrapper.pto_lr_scheduler_step import PtoLrSchedulerStep
class TestPtTrainClassificationConvModelLr(unittest.TestCase):
diff --git a/tests/pytorch_wrapper/ptn_batch_norm2d_test.py b/tests/pytorch_wrapper/ptn_batch_norm2d_test.py
index 5618cef..1c38075 100644
--- a/tests/pytorch_wrapper/ptn_batch_norm2d_test.py
+++ b/tests/pytorch_wrapper/ptn_batch_norm2d_test.py
@@ -29,6 +29,7 @@ class TestPtnBatchNorm2d(unittest.TestCase):
mean_after = out.mean(dim=(0, 2, 3))
# Ensure std is not one before normalization
+ # Reference value: [2.2758, 2.4911, 2.2218]
self.assertFalse(
torch.allclose(
std_before,
@@ -36,12 +37,14 @@ class TestPtnBatchNorm2d(unittest.TestCase):
atol=1e-1))
# Ensure normalization happened (std should be 1 unless elements are very close)
+ # Reference value: [1.0190, 1.0190, 1.0190]
self.assertTrue(
torch.allclose(
std_after,
torch.ones_like(std_after),
atol=1e-1))
-
+
+ # Reference value: [0.0000e+00, 0.0000e+00, 2.2076e-09]
self.assertTrue(
torch.allclose(
mean_after,
diff --git a/tests/pytorch_wrapper/ptn_bce_loss_test.py b/tests/pytorch_wrapper/ptn_bce_loss_test.py
new file mode 100644
index 0000000..d1cf103
--- /dev/null
+++ b/tests/pytorch_wrapper/ptn_bce_loss_test.py
@@ -0,0 +1,83 @@
+import os
+import sys
+import unittest
+import torch
+from torch import Tensor
+from typing import List, Tuple
+
+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.ptn_bce_loss import PtnBCELoss
+
+
+class TestPtnBCELoss(unittest.TestCase):
+ def setUp(self) -> None:
+ """
+ Set up the test instance by initializing the loss functions and test cases.
+ """
+ self.node = PtnBCELoss()
+ self.loss_fun_none = self.node.f("none")[0]
+ self.loss_fun_mean = self.node.f("mean")[0]
+ self.loss_fun_sum = self.node.f("sum")[0]
+
+ # Test cases: (predicted probabilities y_hat, ground truth y)
+ self.test_cases: List[Tuple[Tensor, Tensor]] = [
+ (
+ torch.tensor([0.1, 0.9], dtype=torch.float32),
+ torch.tensor([1, 0], dtype=torch.float32),
+ )
+ ]
+
+ def unreduced_loss(self, y_hat: Tensor, y: Tensor) -> Tensor:
+ """
+ Compute the unreduced binary cross-entropy loss.
+
+ Args:
+ y_hat (Tensor): Predicted probabilities.
+ y (Tensor): Ground truth binary labels.
+
+ Returns:
+ Tensor: Unreduced element-wise BCE loss.
+ """
+ return -(y * torch.log(y_hat) + (1 - y) * torch.log(1 - y_hat))
+
+ def test_none(self) -> None:
+ """
+ Test BCE loss with reduction='none'.
+ """
+ for y_hat, y in self.test_cases:
+ with self.subTest(y_hat=y_hat):
+ expected = self.unreduced_loss(y_hat, y)
+ actual = self.loss_fun_none(y_hat, y)
+ self.assertTrue(torch.allclose(actual, expected, atol=1e-4),
+ f"Expected {expected}, got {actual}")
+
+ def test_mean(self) -> None:
+ """
+ Test BCE loss with reduction='mean'.
+ """
+ for y_hat, y in self.test_cases:
+ with self.subTest(y_hat=y_hat):
+ loss = self.unreduced_loss(y_hat, y)
+ expected = loss.mean(dim=0)
+ actual = self.loss_fun_mean(y_hat, y)
+ self.assertTrue(torch.equal(actual, expected),
+ f"Expected {expected}, got {actual}")
+
+ def test_sum(self) -> None:
+ """
+ Test BCE loss with reduction='sum'.
+ """
+ for y_hat, y in self.test_cases:
+ with self.subTest(y_hat=y_hat):
+ loss = self.unreduced_loss(y_hat, y)
+ expected = loss.sum(dim=0)
+ actual = self.loss_fun_sum(y_hat, y)
+ self.assertTrue(torch.equal(actual, expected),
+ f"Expected {expected}, got {actual}")
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/tests/pytorch_wrapper/ptn_bce_with_logits_loss_test.py b/tests/pytorch_wrapper/ptn_bce_with_logits_loss_test.py
new file mode 100644
index 0000000..e81f2b0
--- /dev/null
+++ b/tests/pytorch_wrapper/ptn_bce_with_logits_loss_test.py
@@ -0,0 +1,84 @@
+import os
+import sys
+import unittest
+import torch
+from torch import Tensor
+from typing import List, Tuple
+
+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.ptn_bce_with_logits_loss import PtnBCEWithLogitsLoss
+
+
+class TestPtnBCEWithLogits(unittest.TestCase):
+ def setUp(self) -> None:
+ """
+ Set up the test instance by initializing the loss functions and test cases.
+ """
+ self.node = PtnBCEWithLogitsLoss()
+ self.loss_fun_none = self.node.f("none")[0]
+ self.loss_fun_mean = self.node.f("mean")[0]
+ self.loss_fun_sum = self.node.f("sum")[0]
+
+ # Probabilities: 0.1, 0.8 → Logits: log(0.1/0.9), log(0.8/0.2)
+ logits = torch.log(torch.tensor([0.1, 0.8]) / torch.tensor([0.9, 0.2]))
+ self.test_cases: List[Tuple[Tensor, Tensor]] = [
+ (
+ logits.to(dtype=torch.float32),
+ torch.tensor([1, 0], dtype=torch.float32),
+ )
+ ]
+
+ def unreduced_loss(self, logits: Tensor, y: Tensor) -> Tensor:
+ """
+ Compute the unreduced BCEWithLogits loss.
+
+ Args:
+ logits (Tensor): Raw logits.
+ y (Tensor): Ground truth binary labels.
+
+ Returns:
+ Tensor: Unreduced element-wise BCEWithLogits loss.
+ """
+ return torch.nn.functional.binary_cross_entropy_with_logits(logits, y, reduction='none')
+
+ def test_none(self) -> None:
+ """
+ Test BCEWithLogits loss with reduction='none'.
+ """
+ for logits, y in self.test_cases:
+ with self.subTest(logits=logits):
+ expected = self.unreduced_loss(logits, y)
+ actual = self.loss_fun_none(logits, y)
+ self.assertTrue(torch.allclose(actual, expected, atol=1e-4),
+ f"Expected {expected}, got {actual}")
+
+ def test_mean(self) -> None:
+ """
+ Test BCEWithLogits loss with reduction='mean'.
+ """
+ for logits, y in self.test_cases:
+ with self.subTest(logits=logits):
+ loss = self.unreduced_loss(logits, y)
+ expected = loss.mean(dim=0)
+ actual = self.loss_fun_mean(logits, y)
+ self.assertTrue(torch.equal(actual, expected),
+ f"Expected {expected}, got {actual}")
+
+ def test_sum(self) -> None:
+ """
+ Test BCEWithLogits loss with reduction='sum'.
+ """
+ for logits, y in self.test_cases:
+ with self.subTest(logits=logits):
+ loss = self.unreduced_loss(logits, y)
+ expected = loss.sum(dim=0)
+ actual = self.loss_fun_sum(logits, y)
+ self.assertTrue(torch.equal(actual, expected),
+ f"Expected {expected}, got {actual}")
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/tests/pytorch_wrapper/ptn_cross_entropy_loss_test.py b/tests/pytorch_wrapper/ptn_cross_entropy_loss_test.py
new file mode 100644
index 0000000..30904b7
--- /dev/null
+++ b/tests/pytorch_wrapper/ptn_cross_entropy_loss_test.py
@@ -0,0 +1,85 @@
+import os
+import sys
+import unittest
+import torch
+from torch import Tensor
+from typing import List, Tuple
+
+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.ptn_cross_entropy_loss import PtnCrossEntropyLoss
+
+
+class TestPtCrossEntropyLoss(unittest.TestCase):
+ def setUp(self) -> None:
+ """
+ Set up the test instance by initializing the loss functions and test cases.
+ """
+ self.node = PtnCrossEntropyLoss()
+ self.loss_fun_none = self.node.f("none")[0]
+ self.loss_fun_mean = self.node.f("mean")[0]
+ self.loss_fun_sum = self.node.f("sum")[0]
+
+ self.test_cases: List[Tuple[Tensor, Tensor]] = [
+ (
+ torch.tensor([
+ [-2.0, -1.0, 0.0, 1.0, 2.0],
+ [-40.0, -20.0, 0.0, 10.0, 20.0]
+ ], dtype=torch.float32),
+ torch.tensor([0, 2], dtype=torch.int64)
+ )
+ ]
+
+ def unreduced_loss(self, logits: Tensor, targets: Tensor) -> Tensor:
+ """
+ Compute the unreduced cross entropy loss.
+
+ Args:
+ logits (Tensor): Raw, unnormalized class scores (logits).
+ targets (Tensor): Ground truth class indices.
+
+ Returns:
+ Tensor: Unreduced element-wise cross entropy loss.
+ """
+ return torch.nn.functional.cross_entropy(logits, targets, reduction='none')
+
+ def test_none(self) -> None:
+ """
+ Test cross entropy loss with reduction='none'.
+ """
+ for logits, targets in self.test_cases:
+ with self.subTest(logits=logits):
+ expected = self.unreduced_loss(logits, targets)
+ actual = self.loss_fun_none(logits, targets)
+ self.assertTrue(torch.allclose(actual, expected, atol=1e-4),
+ f"Expected {expected}, got {actual}")
+
+ def test_mean(self) -> None:
+ """
+ Test cross entropy loss with reduction='mean'.
+ """
+ for logits, targets in self.test_cases:
+ with self.subTest(logits=logits):
+ loss = self.unreduced_loss(logits, targets)
+ expected = loss.mean(dim=0)
+ actual = self.loss_fun_mean(logits, targets)
+ self.assertTrue(torch.equal(actual, expected),
+ f"Expected {expected}, got {actual}")
+
+ def test_sum(self) -> None:
+ """
+ Test cross entropy loss with reduction='sum'.
+ """
+ for logits, targets in self.test_cases:
+ with self.subTest(logits=logits):
+ loss = self.unreduced_loss(logits, targets)
+ expected = loss.sum(dim=0)
+ actual = self.loss_fun_sum(logits, targets)
+ self.assertTrue(torch.equal(actual, expected),
+ f"Expected {expected}, got {actual}")
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/tests/pytorch_wrapper/ptn_huber_loss.py b/tests/pytorch_wrapper/ptn_huber_loss.py
new file mode 100644
index 0000000..147dc8e
--- /dev/null
+++ b/tests/pytorch_wrapper/ptn_huber_loss.py
@@ -0,0 +1,107 @@
+import os
+import sys
+import unittest
+import torch
+from torch import Tensor
+from typing import List, Tuple
+
+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.ptn_huber_loss import PtnHuberLoss
+
+
+class TestPtnHuberLoss(unittest.TestCase):
+ def setUp(self) -> None:
+ """
+ Set up the test instance by initializing the loss functions and test cases.
+ """
+ self.node = PtnHuberLoss()
+
+ self.loss_fun_none_1 = self.node.f("none", 0.01)[0]
+ self.loss_fun_none_2 = self.node.f("none", 1)[0]
+ self.loss_fun_none_3 = self.node.f("none", 100)[0]
+
+ self.loss_fun_mean_1 = self.node.f("mean", 0.01)[0]
+ self.loss_fun_mean_2 = self.node.f("mean", 1)[0]
+ self.loss_fun_mean_3 = self.node.f("mean", 100)[0]
+
+ self.loss_fun_sum_1 = self.node.f("sum", 0.01)[0]
+ self.loss_fun_sum_2 = self.node.f("sum", 1)[0]
+ self.loss_fun_sum_3 = self.node.f("sum", 100)[0]
+
+ # Test cases: (predicted values y_hat, ground truth y)
+ self.test_cases: List[Tuple[Tensor, Tensor]] = [
+ (
+ torch.tensor([0.1, -1.0, 10, -100, 1000], dtype=torch.float32),
+ torch.tensor([0.09, -1.1, 9, -101, 999], dtype=torch.float32),
+ )
+ ]
+
+ def unreduced_loss(self, y_hat: Tensor, y: Tensor, delta: float) -> Tensor:
+ return torch.nn.functional.huber_loss(y_hat, y, delta=delta, reduction="none")
+
+ def test_none_delta_001(self) -> None:
+ for y_hat, y in self.test_cases:
+ expected = self.unreduced_loss(y_hat, y, delta=0.01)
+ actual = self.loss_fun_none_1(y_hat, y)
+ self.assertTrue(torch.allclose(actual, expected, atol=1e-6))
+
+ def test_none_delta_1(self) -> None:
+ for y_hat, y in self.test_cases:
+ expected = self.unreduced_loss(y_hat, y, delta=1)
+ actual = self.loss_fun_none_2(y_hat, y)
+ self.assertTrue(torch.allclose(actual, expected, atol=1e-6))
+
+ def test_none_delta_100(self) -> None:
+ for y_hat, y in self.test_cases:
+ expected = self.unreduced_loss(y_hat, y, delta=100)
+ actual = self.loss_fun_none_3(y_hat, y)
+ self.assertTrue(torch.allclose(actual, expected, atol=1e-6))
+
+ def test_mean_delta_001(self) -> None:
+ for y_hat, y in self.test_cases:
+ loss = self.unreduced_loss(y_hat, y, delta=0.01)
+ expected = loss.mean(dim=0)
+ actual = self.loss_fun_mean_1(y_hat, y)
+ self.assertTrue(torch.equal(actual, expected))
+
+ def test_mean_delta_1(self) -> None:
+ for y_hat, y in self.test_cases:
+ loss = self.unreduced_loss(y_hat, y, delta=1)
+ expected = loss.mean(dim=0)
+ actual = self.loss_fun_mean_2(y_hat, y)
+ self.assertTrue(torch.equal(actual, expected))
+
+ def test_mean_delta_100(self) -> None:
+ for y_hat, y in self.test_cases:
+ loss = self.unreduced_loss(y_hat, y, delta=100)
+ expected = loss.mean(dim=0)
+ actual = self.loss_fun_mean_3(y_hat, y)
+ self.assertTrue(torch.equal(actual, expected))
+
+ def test_sum_delta_001(self) -> None:
+ for y_hat, y in self.test_cases:
+ loss = self.unreduced_loss(y_hat, y, delta=0.01)
+ expected = loss.sum(dim=0)
+ actual = self.loss_fun_sum_1(y_hat, y)
+ self.assertTrue(torch.equal(actual, expected))
+
+ def test_sum_delta_1(self) -> None:
+ for y_hat, y in self.test_cases:
+ loss = self.unreduced_loss(y_hat, y, delta=1)
+ expected = loss.sum(dim=0)
+ actual = self.loss_fun_sum_2(y_hat, y)
+ self.assertTrue(torch.equal(actual, expected))
+
+ def test_sum_delta_100(self) -> None:
+ for y_hat, y in self.test_cases:
+ loss = self.unreduced_loss(y_hat, y, delta=100)
+ expected = loss.sum(dim=0)
+ actual = self.loss_fun_sum_3(y_hat, y)
+ self.assertTrue(torch.equal(actual, expected))
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/tests/pytorch_wrapper/ptn_kl_div_loss_test.py b/tests/pytorch_wrapper/ptn_kl_div_loss_test.py
new file mode 100644
index 0000000..2b3e5d5
--- /dev/null
+++ b/tests/pytorch_wrapper/ptn_kl_div_loss_test.py
@@ -0,0 +1,130 @@
+import os
+import sys
+import unittest
+import torch
+from torch import Tensor
+from typing import List, Tuple
+
+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.ptn_kl_div_loss import PtnKLDivLoss
+
+
+class TestPtnKLDivLoss(unittest.TestCase):
+ def setUp(self) -> None:
+ """
+ Set up the test instance by initializing the loss functions and test cases.
+ """
+ self.node = PtnKLDivLoss()
+ self.loss_fun_none = self.node.f("none", log_target=False)[0]
+ self.loss_fun_batchmean = self.node.f("batchmean", log_target=False)[0]
+ self.loss_fun_mean = self.node.f("mean", log_target=False)[0]
+ self.loss_fun_sum = self.node.f("sum", log_target=False)[0]
+
+ self.loss_fun_none_log = self.node.f("none", log_target=True)[0]
+ self.loss_fun_batchmean_log = self.node.f("batchmean", log_target=True)[0]
+ self.loss_fun_mean_log = self.node.f("mean", log_target=True)[0]
+ self.loss_fun_sum_log = self.node.f("sum", log_target=True)[0]
+
+ # log_probs: log_softmax of logits
+ self.test_cases: List[Tuple[Tensor, Tensor]] = [
+ (
+ # input (log-probabilities)
+ torch.log_softmax(
+ torch.tensor([
+ [-2.0, -1.0, 0.0, 1.0, 2.0],
+ [-40.0, -20.0, 0.0, 10.0, 20.0]
+ ], dtype=torch.float32), dim=-1
+ ),
+ # target (probabilities for log_target=False)
+ torch.tensor([
+ [0.04, 0.06, 0.2, 0.3, 0.4],
+ [0.4, 0.3, 0.2, 0.06, 0.04]
+ ], dtype=torch.float32)
+ )
+ ]
+
+ self.log_target_cases: List[Tuple[Tensor, Tensor]] = [
+ (
+ # input
+ torch.log_softmax(
+ torch.tensor([
+ [-2.0, -1.0, 0.0, 1.0, 2.0],
+ [-40.0, -20.0, 0.0, 10.0, 20.0]
+ ], dtype=torch.float32), dim=-1
+ ),
+ # target (log-probabilities)
+ torch.log_softmax(
+ torch.tensor([
+ [0.04, 0.06, 0.2, 0.3, 0.4],
+ [0.4, 0.3, 0.2, 0.06, 0.04]
+ ], dtype=torch.float32), dim=-1
+ )
+ )
+ ]
+
+ def unreduced_loss(self, log_probs: Tensor, probs: Tensor) -> Tensor:
+ return torch.nn.functional.kl_div(log_probs, probs, reduction='none', log_target=False)
+
+ def batchmean_loss(self, log_probs: Tensor, probs: Tensor) -> Tensor:
+ return torch.nn.functional.kl_div(log_probs, probs, reduction='batchmean', log_target=False)
+
+ def unreduced_loss_log_target(self, log_probs: Tensor, log_targets: Tensor) -> Tensor:
+ return torch.nn.functional.kl_div(log_probs, log_targets, reduction='none', log_target=True)
+
+ def batchmean_loss_log_target(self, log_probs: Tensor, log_targets: Tensor) -> Tensor:
+ return torch.nn.functional.kl_div(log_probs, log_targets, reduction='batchmean', log_target=True)
+
+ def test_none(self) -> None:
+ for log_probs, probs in self.test_cases:
+ expected = self.unreduced_loss(log_probs, probs)
+ actual = self.loss_fun_none(log_probs, probs)
+ self.assertTrue(torch.allclose(actual, expected, atol=1e-4))
+
+ def test_mean(self) -> None:
+ for log_probs, probs in self.test_cases:
+ expected = self.unreduced_loss(log_probs, probs).mean()
+ actual = self.loss_fun_mean(log_probs, probs)
+ self.assertTrue(torch.allclose(actual, expected, atol=1e-6))
+
+ def test_batchmean(self) -> None:
+ for log_probs, probs in self.test_cases:
+ expected = self.batchmean_loss(log_probs, probs)
+ actual = self.loss_fun_batchmean(log_probs, probs)
+ self.assertTrue(torch.allclose(actual, expected, atol=1e-6))
+
+ def test_sum(self) -> None:
+ for log_probs, probs in self.test_cases:
+ expected = self.unreduced_loss(log_probs, probs).sum()
+ actual = self.loss_fun_sum(log_probs, probs)
+ self.assertTrue(torch.allclose(actual, expected, atol=1e-6))
+
+ def test_none_log_target(self) -> None:
+ for log_probs, log_targets in self.log_target_cases:
+ expected = self.unreduced_loss_log_target(log_probs, log_targets)
+ actual = self.loss_fun_none_log(log_probs, log_targets)
+ self.assertTrue(torch.allclose(actual, expected, atol=1e-4))
+
+ def test_mean_log_target(self) -> None:
+ for log_probs, log_targets in self.log_target_cases:
+ expected = self.unreduced_loss_log_target(log_probs, log_targets).mean()
+ actual = self.loss_fun_mean_log(log_probs, log_targets)
+ self.assertTrue(torch.allclose(actual, expected, atol=1e-6))
+
+ def test_batchmean_log_target(self) -> None:
+ for log_probs, log_targets in self.log_target_cases:
+ expected = self.batchmean_loss_log_target(log_probs, log_targets)
+ actual = self.loss_fun_batchmean_log(log_probs, log_targets)
+ self.assertTrue(torch.allclose(actual, expected, atol=1e-6))
+
+ def test_sum_log_target(self) -> None:
+ for log_probs, log_targets in self.log_target_cases:
+ expected = self.unreduced_loss_log_target(log_probs, log_targets).sum()
+ actual = self.loss_fun_sum_log(log_probs, log_targets)
+ self.assertTrue(torch.allclose(actual, expected, atol=1e-6))
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/tests/pytorch_wrapper/ptn_l1_loss_test.py b/tests/pytorch_wrapper/ptn_l1_loss_test.py
new file mode 100644
index 0000000..9a0de85
--- /dev/null
+++ b/tests/pytorch_wrapper/ptn_l1_loss_test.py
@@ -0,0 +1,83 @@
+import os
+import sys
+import unittest
+import torch
+from torch import Tensor
+from typing import List, Tuple
+
+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.ptn_l1_loss import PtnL1Loss
+
+
+class TestPtnL1Loss(unittest.TestCase):
+ def setUp(self) -> None:
+ """
+ Set up the test instance by initializing the loss functions and test cases.
+ """
+ self.node = PtnL1Loss()
+ self.loss_fun_none = self.node.f("none")[0]
+ self.loss_fun_mean = self.node.f("mean")[0]
+ self.loss_fun_sum = self.node.f("sum")[0]
+
+ # Test cases: (predicted values y_hat, ground truth y)
+ self.test_cases: List[Tuple[Tensor, Tensor]] = [
+ (
+ torch.tensor([0.1, -1.0, 10, -100, 1000], dtype=torch.float32),
+ torch.tensor([0.09, -1.1, 9, -101, 999], dtype=torch.float32),
+ )
+ ]
+
+ def unreduced_loss(self, y_hat: Tensor, y: Tensor) -> Tensor:
+ """
+ Compute the unreduced L1 loss.
+
+ Args:
+ y_hat (Tensor): Predicted values.
+ y (Tensor): Ground truth values.
+
+ Returns:
+ Tensor: Unreduced element-wise L1 loss.
+ """
+ return torch.abs(y_hat - y)
+
+ def test_none(self) -> None:
+ """
+ Test L1 loss with reduction='none'.
+ """
+ for y_hat, y in self.test_cases:
+ with self.subTest(y_hat=y_hat):
+ expected = self.unreduced_loss(y_hat, y)
+ actual = self.loss_fun_none(y_hat, y)
+ self.assertTrue(torch.allclose(actual, expected, atol=1e-6),
+ f"Expected {expected}, got {actual}")
+
+ def test_mean(self) -> None:
+ """
+ Test L1 loss with reduction='mean'.
+ """
+ for y_hat, y in self.test_cases:
+ with self.subTest(y_hat=y_hat):
+ loss = self.unreduced_loss(y_hat, y)
+ expected = loss.mean(dim=0)
+ actual = self.loss_fun_mean(y_hat, y)
+ self.assertTrue(torch.equal(actual, expected),
+ f"Expected {expected}, got {actual}")
+
+ def test_sum(self) -> None:
+ """
+ Test L1 loss with reduction='sum'.
+ """
+ for y_hat, y in self.test_cases:
+ with self.subTest(y_hat=y_hat):
+ loss = self.unreduced_loss(y_hat, y)
+ expected = loss.sum(dim=0)
+ actual = self.loss_fun_sum(y_hat, y)
+ self.assertTrue(torch.equal(actual, expected),
+ f"Expected {expected}, got {actual}")
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/tests/pytorch_wrapper/ptn_mse_loss_test.py b/tests/pytorch_wrapper/ptn_mse_loss_test.py
new file mode 100644
index 0000000..fead392
--- /dev/null
+++ b/tests/pytorch_wrapper/ptn_mse_loss_test.py
@@ -0,0 +1,83 @@
+import os
+import sys
+import unittest
+import torch
+from torch import Tensor
+from typing import List, Tuple
+
+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.ptn_mse_loss import PtnMSELoss
+
+
+class TestPtnMSELoss(unittest.TestCase):
+ def setUp(self) -> None:
+ """
+ Set up the test instance by initializing the loss functions and test cases.
+ """
+ self.node = PtnMSELoss()
+ self.loss_fun_none = self.node.f("none")[0]
+ self.loss_fun_mean = self.node.f("mean")[0]
+ self.loss_fun_sum = self.node.f("sum")[0]
+
+ # Test cases: (predicted values y_hat, ground truth y)
+ self.test_cases: List[Tuple[Tensor, Tensor]] = [
+ (
+ torch.tensor([0.1, -1.0, 10, -100, 1000], dtype=torch.float32),
+ torch.tensor([0.09, -1.1, 9, -101, 999], dtype=torch.float32),
+ )
+ ]
+
+ def unreduced_loss(self, y_hat: Tensor, y: Tensor) -> Tensor:
+ """
+ Compute the unreduced mean squared error loss.
+
+ Args:
+ y_hat (Tensor): Predicted values.
+ y (Tensor): Ground truth values.
+
+ Returns:
+ Tensor: Unreduced element-wise MSE loss.
+ """
+ return (y_hat - y) ** 2
+
+ def test_none(self) -> None:
+ """
+ Test MSE loss with reduction='none'.
+ """
+ for y_hat, y in self.test_cases:
+ with self.subTest(y_hat=y_hat):
+ expected = self.unreduced_loss(y_hat, y)
+ actual = self.loss_fun_none(y_hat, y)
+ self.assertTrue(torch.allclose(actual, expected, atol=1e-6),
+ f"Expected {expected}, got {actual}")
+
+ def test_mean(self) -> None:
+ """
+ Test MSE loss with reduction='mean'.
+ """
+ for y_hat, y in self.test_cases:
+ with self.subTest(y_hat=y_hat):
+ loss = self.unreduced_loss(y_hat, y)
+ expected = loss.mean(dim=0)
+ actual = self.loss_fun_mean(y_hat, y)
+ self.assertTrue(torch.equal(actual, expected),
+ f"Expected {expected}, got {actual}")
+
+ def test_sum(self) -> None:
+ """
+ Test MSE loss with reduction='sum'.
+ """
+ for y_hat, y in self.test_cases:
+ with self.subTest(y_hat=y_hat):
+ loss = self.unreduced_loss(y_hat, y)
+ expected = loss.sum(dim=0)
+ actual = self.loss_fun_sum(y_hat, y)
+ self.assertTrue(torch.equal(actual, expected),
+ f"Expected {expected}, got {actual}")
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/tests/pytorch_wrapper/ptn_nll_loss_test.py b/tests/pytorch_wrapper/ptn_nll_loss_test.py
new file mode 100644
index 0000000..e62a3c8
--- /dev/null
+++ b/tests/pytorch_wrapper/ptn_nll_loss_test.py
@@ -0,0 +1,89 @@
+import os
+import sys
+import unittest
+import torch
+from torch import Tensor
+from typing import List, Tuple
+
+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.ptn_nll_loss import PtnNLLLoss
+
+
+class TestPtnNLLLoss(unittest.TestCase):
+ def setUp(self) -> None:
+ """
+ Set up the test instance by initializing the loss functions and test cases.
+ """
+ self.node = PtnNLLLoss()
+ self.loss_fun_none = self.node.f("none")[0]
+ self.loss_fun_mean = self.node.f("mean")[0]
+ self.loss_fun_sum = self.node.f("sum")[0]
+
+ logits = torch.tensor([
+ [-2.0, -1.0, 0.0, 1.0, 2.0],
+ [-40.0, -20.0, 0.0, 10.0, 20.0]
+ ], dtype=torch.float32)
+
+ log_probs = torch.nn.functional.log_softmax(logits, dim=1)
+
+ self.test_cases: List[Tuple[Tensor, Tensor]] = [
+ (
+ log_probs,
+ torch.tensor([0, 2], dtype=torch.int64)
+ )
+ ]
+
+ def unreduced_loss(self, log_probs: Tensor, targets: Tensor) -> Tensor:
+ """
+ Compute the unreduced negative log-likelihood loss.
+
+ Args:
+ log_probs (Tensor): Log-probabilities from log_softmax.
+ targets (Tensor): Ground truth class indices.
+
+ Returns:
+ Tensor: Unreduced element-wise NLL loss.
+ """
+ return torch.nn.functional.nll_loss(log_probs, targets, reduction='none')
+
+ def test_none(self) -> None:
+ """
+ Test NLL loss with reduction='none'.
+ """
+ for log_probs, targets in self.test_cases:
+ with self.subTest(log_probs=log_probs):
+ expected = self.unreduced_loss(log_probs, targets)
+ actual = self.loss_fun_none(log_probs, targets)
+ self.assertTrue(torch.allclose(actual, expected, atol=1e-4),
+ f"Expected {expected}, got {actual}")
+
+ def test_mean(self) -> None:
+ """
+ Test NLL loss with reduction='mean'.
+ """
+ for log_probs, targets in self.test_cases:
+ with self.subTest(log_probs=log_probs):
+ loss = self.unreduced_loss(log_probs, targets)
+ expected = loss.mean(dim=0)
+ actual = self.loss_fun_mean(log_probs, targets)
+ self.assertTrue(torch.equal(actual, expected),
+ f"Expected {expected}, got {actual}")
+
+ def test_sum(self) -> None:
+ """
+ Test NLL loss with reduction='sum'.
+ """
+ for log_probs, targets in self.test_cases:
+ with self.subTest(log_probs=log_probs):
+ loss = self.unreduced_loss(log_probs, targets)
+ expected = loss.sum(dim=0)
+ actual = self.loss_fun_sum(log_probs, targets)
+ self.assertTrue(torch.equal(actual, expected),
+ f"Expected {expected}, got {actual}")
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/tests/pytorch_wrapper/ptn_smooth_l1_loss_test.py b/tests/pytorch_wrapper/ptn_smooth_l1_loss_test.py
new file mode 100644
index 0000000..45ad84b
--- /dev/null
+++ b/tests/pytorch_wrapper/ptn_smooth_l1_loss_test.py
@@ -0,0 +1,116 @@
+import os
+import sys
+import unittest
+import torch
+from torch import Tensor
+from typing import List, Tuple
+
+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.ptn_smooth_l1_loss import PtnSmoothL1Loss
+
+
+class TestPtnSmoothL1Loss(unittest.TestCase):
+ def setUp(self) -> None:
+ """
+ Set up the test instance by initializing the loss functions and test cases.
+ """
+ self.node = PtnSmoothL1Loss()
+
+ self.loss_fun_none_1 = self.node.f("none", 0.01)[0]
+ self.loss_fun_none_2 = self.node.f("none", 1)[0]
+ self.loss_fun_none_3 = self.node.f("none", 100)[0]
+
+ self.loss_fun_mean_1 = self.node.f("mean", 0.01)[0]
+ self.loss_fun_mean_2 = self.node.f("mean", 1)[0]
+ self.loss_fun_mean_3 = self.node.f("mean", 100)[0]
+
+ self.loss_fun_sum_1 = self.node.f("sum", 0.01)[0]
+ self.loss_fun_sum_2 = self.node.f("sum", 1)[0]
+ self.loss_fun_sum_3 = self.node.f("sum", 100)[0]
+
+ # Test cases: (predicted values y_hat, ground truth y)
+ self.test_cases: List[Tuple[Tensor, Tensor]] = [
+ (
+ torch.tensor([0.1, -1.0, 10, -100, 1000], dtype=torch.float32),
+ torch.tensor([0.09, -1.1, 9, -101, 999], dtype=torch.float32),
+ )
+ ]
+
+ def unreduced_loss(self, y_hat: Tensor, y: Tensor, beta: float) -> Tensor:
+ return torch.nn.functional.smooth_l1_loss(y_hat, y, beta=beta, reduction="none")
+
+ def test_none_beta_001(self) -> None:
+ for y_hat, y in self.test_cases:
+ expected = self.unreduced_loss(y_hat, y, beta=0.01)
+ actual = self.loss_fun_none_1(y_hat, y)
+ self.assertTrue(torch.allclose(actual, expected, atol=1e-6),
+ f"Expected {expected}, got {actual}")
+
+ def test_none_beta_1(self) -> None:
+ for y_hat, y in self.test_cases:
+ expected = self.unreduced_loss(y_hat, y, beta=1)
+ actual = self.loss_fun_none_2(y_hat, y)
+ self.assertTrue(torch.allclose(actual, expected, atol=1e-6),
+ f"Expected {expected}, got {actual}")
+
+ def test_none_beta_100(self) -> None:
+ for y_hat, y in self.test_cases:
+ expected = self.unreduced_loss(y_hat, y, beta=100)
+ actual = self.loss_fun_none_3(y_hat, y)
+ self.assertTrue(torch.allclose(actual, expected, atol=1e-6),
+ f"Expected {expected}, got {actual}")
+
+ def test_mean_beta_001(self) -> None:
+ for y_hat, y in self.test_cases:
+ loss = self.unreduced_loss(y_hat, y, beta=0.01)
+ expected = loss.mean(dim=0)
+ actual = self.loss_fun_mean_1(y_hat, y)
+ self.assertTrue(torch.equal(actual, expected),
+ f"Expected {expected}, got {actual}")
+
+ def test_mean_beta_1(self) -> None:
+ for y_hat, y in self.test_cases:
+ loss = self.unreduced_loss(y_hat, y, beta=1)
+ expected = loss.mean(dim=0)
+ actual = self.loss_fun_mean_2(y_hat, y)
+ self.assertTrue(torch.equal(actual, expected),
+ f"Expected {expected}, got {actual}")
+
+ def test_mean_beta_100(self) -> None:
+ for y_hat, y in self.test_cases:
+ loss = self.unreduced_loss(y_hat, y, beta=100)
+ expected = loss.mean(dim=0)
+ actual = self.loss_fun_mean_3(y_hat, y)
+ self.assertTrue(torch.equal(actual, expected),
+ f"Expected {expected}, got {actual}")
+
+ def test_sum_beta_001(self) -> None:
+ for y_hat, y in self.test_cases:
+ loss = self.unreduced_loss(y_hat, y, beta=0.01)
+ expected = loss.sum(dim=0)
+ actual = self.loss_fun_sum_1(y_hat, y)
+ self.assertTrue(torch.equal(actual, expected),
+ f"Expected {expected}, got {actual}")
+
+ def test_sum_beta_1(self) -> None:
+ for y_hat, y in self.test_cases:
+ loss = self.unreduced_loss(y_hat, y, beta=1)
+ expected = loss.sum(dim=0)
+ actual = self.loss_fun_sum_2(y_hat, y)
+ self.assertTrue(torch.equal(actual, expected),
+ f"Expected {expected}, got {actual}")
+
+ def test_sum_beta_100(self) -> None:
+ for y_hat, y in self.test_cases:
+ loss = self.unreduced_loss(y_hat, y, beta=100)
+ expected = loss.sum(dim=0)
+ actual = self.loss_fun_sum_3(y_hat, y)
+ self.assertTrue(torch.equal(actual, expected),
+ f"Expected {expected}, got {actual}")
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/tests/pytorch_wrapper/pto_lr_scheduler_reduce_on_plateau_test.py b/tests/pytorch_wrapper/pto_lr_scheduler_reduce_on_plateau_test.py
index 2a4ed34..ddeefdd 100644
--- a/tests/pytorch_wrapper/pto_lr_scheduler_reduce_on_plateau_test.py
+++ b/tests/pytorch_wrapper/pto_lr_scheduler_reduce_on_plateau_test.py
@@ -8,7 +8,7 @@ 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.pto_lr_scheduler_reduce_on_plateau import PtoLrSchedulerReduceOnPlateau # Updated import
+from pytorch_wrapper.pto_lr_scheduler_reduce_on_plateau import PtoLrSchedulerReduceOnPlateau
class TestPtoLrSchedulerReduceOnPlateau(unittest.TestCase):
diff --git a/tests/pytorch_wrapper/pto_lr_scheduler_step_test.py b/tests/pytorch_wrapper/pto_lr_scheduler_step_test.py
index 56ee104..c63bde7 100644
--- a/tests/pytorch_wrapper/pto_lr_scheduler_step_test.py
+++ b/tests/pytorch_wrapper/pto_lr_scheduler_step_test.py
@@ -8,7 +8,7 @@ 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.pto_lr_scheduler_step import PtoLrSchedulerStep # Updated import
+from pytorch_wrapper.pto_lr_scheduler_step import PtoLrSchedulerStep
class TestPtoLrSchedulerStep(unittest.TestCase):