Source code for skwdro.base.losses_torch.newsvendor
from typing import Optional
import torch as pt
import torch.nn as nn
from .base_loss import Loss
from skwdro.base.samplers.torch.base_samplers import NoLabelsSampler
from skwdro.base.samplers.torch.newsvendor_sampler import NewsVendorNormalSampler
[docs]
class NewsVendorLoss_torch(Loss):
r"""Loss associated with the newsvendor problem:
.. math::
k\theta - \mathbb{E}[u\min(\theta, \xi)]
Parameters
----------
sampler : Optional[NoLabelsSampler]
optional sampler to use for the demand
k : int
journal price
u : int
benefit per journal sold
name :
name of the loss, optional
Attributes
----------
sampler : NoLabelsSampler
k : nn.Parameter
u : nn.Parameter
theta : nn.Parameter
number of journal stocked
name : Optional[str]
"""
def __init__(
self,
sampler: NoLabelsSampler,
*,
k: float = 5,
u: float = 7,
l2reg: Optional[float] = None,
name: Optional[str] = "NewsVendor loss"
):
super(NewsVendorLoss_torch, self).__init__(sampler, False, l2reg=l2reg)
self.k = pt.tensor(float(k))
self.u = pt.tensor(float(u))
self.name = name
self._theta = nn.Parameter(pt.rand(1))
[docs]
def value_old(self, theta, xi):
return self.k * theta - self.u * pt.minimum(theta, xi)
[docs]
def value(
self, xi: pt.Tensor, xi_labels: Optional[pt.Tensor] = None
) -> pt.Tensor:
""" Forward pass of the loss on the data
Parameters
----------
xi : pt.Tensor
empirical observations of demand
xi_labels : NoneType
placeholder, do not touch
"""
assert xi_labels is not None
returns = self.k * self.theta
costs = self.u * pt.minimum(self.theta, xi).mean(dim=-1, keepdim=True)
return returns - costs
@property
def theta(self) -> pt.Tensor:
return self._theta
@property
def intercept(self) -> None:
return None
[docs]
@classmethod
def default_sampler(
cls,
xi,
xi_labels,
epsilon, seed: Optional[int]
) -> NewsVendorNormalSampler:
del xi_labels
return NewsVendorNormalSampler(xi, seed=seed, sigma=epsilon)