import torch.nn as nn
import torch
import matplotlib.pyplot as plt 

"""
Explanation: <div class="alert alert-block alert-info" style="margin-top: 20px">
 <a href="http://cocl.us/pytorch_link_top"><img src = "http://cocl.us/Pytorch_top" width = 950, align = "center"></a>

<img src = "https://ibm.box.com/shared/static/ugcqz6ohbvff804xp84y4kqnvvk3bq1g.png" width = 200, align = "center">

<h1 align=center><font size = 5>Logistic Regression</font></h1> 

In this lab, you will cover logistic regression using Pytorch. 
# Table of Contents


<div class="alert alert-block alert-info" style="margin-top: 20px">
<li><a href="#ref0">Logistic Function</a></li>
<li><a href="#ref1">Build a Logistic Regression Using nn.Sequential</a></li>
<li><a href="#ref2">Build Custom Modules</a></li>

<br>
<p></p>
Estimated Time Needed: <strong>15 min</strong>
</div>

<hr>

Import the following modules:
End of explanation
"""


torch.manual_seed(2)

"""
Explanation: <a id="ref0"></a>
<h2> Logistic Function </h2>
End of explanation
"""


z=torch.arange(-100,100,0.1).view(-1, 1)
z

"""
Explanation: Create a tensor ranging from -100 to 100:
End of explanation
"""


sig=nn.Sigmoid()

"""
Explanation: Create a sigmoid object:
End of explanation
"""


yhat=sig(z)

"""
Explanation: Apply the element-wise function Sigmoid with the object:
End of explanation
"""


plt.plot(z.numpy(),yhat.numpy())
plt.xlabel('z')
plt.ylabel('yhat')

"""
Explanation: Plot the results:
End of explanation
"""


yhat=torch.sigmoid(z)
plt.plot(z.numpy(),yhat.numpy())

"""
Explanation: Apply the element-wise Sigmoid from the function module and plot the results:
End of explanation
"""


x=torch.tensor([[1.0]])
X=torch.tensor([[1.0],[100]])
print('x=',x)
print('X=',X)

"""
Explanation: <a id="ref0"></a>
<h2>Build a Logistic Regression Using nn.Sequential</h2>

Create a 1x1 tensor where x represents one data sample with one dimension, and 2x1 tensor X represents two data samples of one dimension:
End of explanation
"""


model=nn.Sequential(nn.Linear(1,1),nn.Sigmoid())

"""
Explanation: Create a logistic regression object with the <code>nn.Sequential</code> model with a one-dimensional input:
End of explanation
"""


print("list(model.parameters()):\n ", list(model.parameters()))
print( "\nmodel.state_dict():\n ",model.state_dict())

"""
Explanation: The object is represented in the following diagram: 
<img src = "https://ibm.box.com/shared/static/8yjc6pyix9ga1x9eeog49u38i59uctog.png" width = 800, align = "center">
In this case, the parameters are randomly insulated. You can view them the following ways:
End of explanation
"""


yhat=model(x)
yhat

"""
Explanation: Make a prediction with one sample:
End of explanation
"""


yhat=model(X)
yhat

"""
Explanation: Calling the object performed the following operation:
<img src = "https://ibm.box.com/shared/static/dwzrbmnhpmq6foy9fxtdhqt1samfdd0d.png" width = 400, align = "center">
Make a prediction with multiple samples:
End of explanation
"""


x=torch.tensor([[1.0,1.0]])
X=torch.tensor([[1.0,1.0],[1.0,2.0],[1.0,3.0]])
print('x=',x)
print('X=',X)

"""
Explanation: Calling the object performed the following operation: 
Create a 1x2 tensor where x represents one data sample with one dimension, and 2x3 tensor X represents one data sample of two dimension:
End of explanation
"""


model=nn.Sequential(nn.Linear(2,1),nn.Sigmoid())

"""
Explanation: Create a logistic regression object with the <code>nn.Sequentia</code>l model with a two-dimensional input:
End of explanation
"""


print("list(model.parameters()):\n ", list(model.parameters()))
print( "\nmodel.state_dict():\n ",model.state_dict())

"""
Explanation: The object will apply the Sigmoid function to the output of the linear function as shown in the following diagram:
<img src = "https://ibm.box.com/shared/static/nul06ttn4w40ogahvs9risc34suuymmg.png" width = 800, align = "center">
In this case, the parameters are randomly insulated. You can view them the following ways:
End of explanation
"""


yhat=model(x)
yhat

"""
Explanation: Make a prediction with one sample:
End of explanation
"""


yhat=model(X)
yhat

"""
Explanation: The operation is represented in the following diagram:
<img src = "https://ibm.box.com/shared/static/pnarlh41gl7epmy2f8ob3l58pd0zh9wq.png" width = 500, align = "center">
Make a prediction with multiple samples:
End of explanation
"""


class logistic_regression(nn.Module):
    def __init__(self,n_inputs):
        super(logistic_regression,self).__init__()
        self.linear=nn.Linear(n_inputs,1)
    def forward(self,x):
        yhat=torch.sigmoid(self.linear(x))
        return yhat

"""
Explanation: The operation is represented in the following diagram: 
<img src = "https://ibm.box.com/shared/static/1rpau4ggzepzxzu01p2j4506d5kvobbj.png" width = 800, align = "center">
<a id="ref2"></a>
<h2> Build Custom Modules</h2>

In this section, you will build a custom model. The model or object function is identical to  using nn.Sequential.
Create a logistic regression custom module:
End of explanation
"""


x=torch.tensor([[1.0]])
X=torch.tensor([[-100],[0],[100.0]])
print('x=',x)
print('X=',X)

"""
Explanation: Create a 1x1 tensor where x represents one data sample with one dimension, and 3x1 tensor X represents one data sample of one dimension:
End of explanation
"""


model=logistic_regression(1)

"""
Explanation: Create a model to predict one dimension:
End of explanation
"""


print("list(model.parameters()):\n ", list(model.parameters()))
print( "\nmodel.state_dict():\n ",model.state_dict())

"""
Explanation: In this case, the parameters are randomly insulated. You can view them the following ways:
End of explanation
"""


yhat=model(x)
yhat

"""
Explanation: Make a prediction with multiple samples:
End of explanation
"""


yhat=model(X)
yhat

"""
Explanation: Make a prediction with multiple samples:
End of explanation
"""


model=logistic_regression(2)

"""
Explanation: Create a logistic regression object with a function with two inputs:
End of explanation
"""


x=torch.tensor([[1.0,2.0]])
X=torch.tensor([[100,-100],[0.0,0.0],[-100,100]])
print('x=',x)
print('X=',X)

"""
Explanation: Create a 1x2 tensor where x represents one data sample with one dimension, and 3x2 tensor X represents one data sample of one dimension:
End of explanation
"""


yhat=model(x)
yhat

"""
Explanation: Make a prediction with one sample:
End of explanation
"""


yhat=model(X)
yhat

"""
Explanation: Make a prediction with multiple samples:
End of explanation
"""


