





import numpy as np
from matplotlib import pyplot as plt

def     a						(      _lowercase   :     Dict       )     ->List[str]:
  return 1 / (1 + np.exp(-1 * x       ))




class   _UpperCAmelCase	:




     def __init__(						self    ,      __lowerCAmelCase    ,      __lowerCAmelCase=None    ,      __lowerCAmelCase=None    ,      __lowerCAmelCase=False       )						->  List[str]:
       UpperCamelCase__         =	units
       UpperCamelCase__         =	None
       UpperCamelCase__         =	None
       UpperCamelCase__         =	activation
       if learning_rate is None:
         UpperCamelCase__         =	0.3
       UpperCamelCase__         =	learning_rate
       UpperCamelCase__         =	is_input_layer




     def 	snake_case_		(						self    ,      __lowerCAmelCase       )						->  Union[str, Any]:
       UpperCamelCase__         =	np.asmatrix(np.random.normal(0    ,      0.5    ,      (self.units, back_units)       )       )
       UpperCamelCase__         =	np.asmatrix(np.random.normal(0    ,      0.5    ,      self.units       )       ).T
       if self.activation is None:
         UpperCamelCase__         =	sigmoid




     def 	snake_case_		(						self       )						->  Any:
       # activation function may be sigmoid or linear
       if self.activation == sigmoid:
         UpperCamelCase__         =	np.dot(self.output    ,      (1 - self.output).T       )
         UpperCamelCase__         =	np.diag(np.diag(SCREAMING_SNAKE_CASE_       )       )
       else:
         UpperCamelCase__         =	1
       return gradient_activation




     def 	snake_case_		(						self    ,      __lowerCAmelCase       )						->  Optional[Any]:
       UpperCamelCase__         =	xdata
       if self.is_input_layer:
         # input layer
         UpperCamelCase__         =	xdata
         UpperCamelCase__         =	xdata
         return xdata
       else:
         UpperCamelCase__         =	np.dot(self.weight    ,      self.xdata       ) - self.bias
         UpperCamelCase__         =	self.activation(self.wx_plus_b       )
         return self.output




     def 	snake_case_		(						self    ,      __lowerCAmelCase       )						->  Dict:
       UpperCamelCase__         =	self.cal_gradient()  # i * i 维
       UpperCamelCase__         =	np.asmatrix(np.dot(gradient.T    ,      SCREAMING_SNAKE_CASE_       )       )

       UpperCamelCase__         =	np.asmatrix(self.xdata       )
       UpperCamelCase__         =	-1
       UpperCamelCase__         =	self.weight

       UpperCamelCase__         =	np.dot(gradient.T    ,      self._gradient_weight.T       )
       UpperCamelCase__         =	gradient * self._gradient_bias
       UpperCamelCase__         =	np.dot(SCREAMING_SNAKE_CASE_    ,      self._gradient_x       ).T
       # upgrade: the Negative gradient direction
       UpperCamelCase__         =	self.weight - self.learn_rate * self.gradient_weight
       UpperCamelCase__         =	self.bias - self.learn_rate * self.gradient_bias.T
       # updates the weights and bias according to learning rate (0.3 if undefined)
       return self.gradient




class   _UpperCAmelCase	:




     def __init__(						self       )						->  List[Any]:
       UpperCamelCase__         =	[]
       UpperCamelCase__         =	[]
       UpperCamelCase__         =	plt.figure()
       UpperCamelCase__         =	self.fig_loss.add_subplot(1    ,      1    ,      1       )




     def 	snake_case_		(						self    ,      __lowerCAmelCase       )						->  Dict:
       self.layers.append(SCREAMING_SNAKE_CASE_       )




     def 	snake_case_		(						self       )						->  Union[str, Any]:
       for i, layer in enumerate(self.layers[:]       ):
         if i < 1:
           UpperCamelCase__         =	True
         else:
           layer.initializer(self.layers[i - 1].units       )




     def 	snake_case_		(						self       )						->  List[str]:
       for i, layer in enumerate(self.layers[:]       ):
         print(F"""------- layer {i} -------"""       )
         print("""weight.shape """    ,      np.shape(layer.weight       )       )
         print("""bias.shape """    ,      np.shape(layer.bias       )       )




     def 	snake_case_		(						self    ,      __lowerCAmelCase    ,      __lowerCAmelCase    ,      __lowerCAmelCase    ,      __lowerCAmelCase       )						->  Union[str, Any]:
       UpperCamelCase__         =	train_round
       UpperCamelCase__         =	accuracy

       self.ax_loss.hlines(self.accuracy    ,      0    ,      self.train_round * 1.1       )

       UpperCamelCase__         =	np.shape(SCREAMING_SNAKE_CASE_       )
       for _ in range(SCREAMING_SNAKE_CASE_       ):
         UpperCamelCase__         =	0
         for row in range(x_shape[0]       ):
           UpperCamelCase__         =	np.asmatrix(xdata[row, :]       ).T
           UpperCamelCase__         =	np.asmatrix(ydata[row, :]       ).T

           # forward propagation
           for layer in self.layers:
             UpperCamelCase__         =	layer.forward_propagation(_xdata       )

           UpperCamelCase__  ,			UpperCamelCase__         =	self.cal_loss(_ydata    ,      _xdata       )
           UpperCamelCase__         =	all_loss + loss

           # back propagation: the input_layer does not upgrade
           for layer in self.layers[:0:-1]:
             UpperCamelCase__         =	layer.back_propagation(SCREAMING_SNAKE_CASE_       )

         UpperCamelCase__         =	all_loss / x_shape[0]
         self.train_mse.append(SCREAMING_SNAKE_CASE_       )

         self.plot_loss()

         if mse < self.accuracy:
           print("""----达到精度----"""       )
           return mse
       return None




     def 	snake_case_		(						self    ,      __lowerCAmelCase    ,      __lowerCAmelCase       )						->  Optional[Any]:
       UpperCamelCase__         =	np.sum(np.power((ydata - ydata_)    ,      2       )       )
       UpperCamelCase__         =	2 * (ydata_ - ydata)
       # vector (shape is the same as _ydata.shape)
       return self.loss, self.loss_gradient




     def 	snake_case_		(						self       )						->  List[Any]:
       if self.ax_loss.lines:
         self.ax_loss.lines.remove(self.ax_loss.lines[0]       )
       self.ax_loss.plot(self.train_mse    ,      """r-"""       )
       plt.ion()
       plt.xlabel("""step"""       )
       plt.ylabel("""loss"""       )
       plt.show()
       plt.pause(0.1       )



def     a						(      )     ->List[str]:
  UpperCamelCase__         =	np.random.randn(1_0 ,						1_0       )
  UpperCamelCase__         =	np.asarray(
      [
          [0.8, 0.4],
          [0.4, 0.3],
          [0.34, 0.45],
          [0.67, 0.32],
          [0.88, 0.67],
          [0.78, 0.77],
          [0.55, 0.66],
          [0.55, 0.43],
          [0.54, 0.1],
          [0.1, 0.5],
      ]       )
  UpperCamelCase__         =	BPNN()
  for i in (1_0, 2_0, 3_0, 2):
    model.add_layer(DenseLayer(_UpperCAmelCase       )       )
  model.build()
  model.summary()
  model.train(xdata=_UpperCAmelCase ,						ydata=_UpperCAmelCase ,						train_round=1_0_0 ,						accuracy=0.01       )


if __name__ == "__main__":
    example()












__lowerCAmelCase     =			{
    "0": 1,
    "1": 1,
    "2": 2,
    "3": 6,
    "4": 24,
    "5": 120,
    "6": 720,
    "7": 5_040,
    "8": 40_320,
    "9": 362_880,
}

__lowerCAmelCase     =			{145: 145}

__lowerCAmelCase     =			{
    145: 0,
    169: 3,
    36_301: 3,
    1_454: 3,
    871: 2,
    45_361: 2,
    872: 2,
}

def     a						(      _lowercase   :     int       )     ->int:
		if n in CACHE_SUM_DIGIT_FACTORIALS:
				return CACHE_SUM_DIGIT_FACTORIALS[n]
		UpperCamelCase__         =	sum(DIGIT_FACTORIALS[let] for let in str(_lowercase       )       )
		UpperCamelCase__         =	ret
		return ret

def     a						(      _lowercase   :     int ,						_lowercase   :     set | None = None       )     ->int:
		UpperCamelCase__         =	previous or set()
		if n in CHAIN_LENGTH_CACHE:
				return CHAIN_LENGTH_CACHE[n]
		UpperCamelCase__         =	sum_digit_factorials(_lowercase       )
		if next_number in previous:
				UpperCamelCase__         =	0
				return 0
		else:
				previous.add(_lowercase       )
				UpperCamelCase__         =	1 + chain_length(_lowercase ,						_lowercase       )
				UpperCamelCase__         =	ret
				return ret

def     a						(      _lowercase   :     int = 6_0 ,						_lowercase   :     int = 1_0_0_0_0_0_0       )     ->int:
		return sum(1 for i in range(1 ,						_lowercase       ) if chain_length(_lowercase       ) == num_terms       )


if __name__ == "__main__":
				print(f"""{solution() = }""")





