
    6biD                     x    S r SSKrSSKJs  Jr  SSKJr  SSKJ	r	  \" S/ S9 " S S\	R                  5      5       rg)	z#Base class for Python-based metrics    N)keras_export)base_metricz#keras.metrics.experimental.PyMetric)v1c                   X   ^  \ rS rSrSrS
U 4S jjrU 4S jrSS jrS rS r	S r
S	rU =r$ )PyMetric   aH
  Metric which runs in Python, compiled outside of the TensorFlow graph.

Args:
  name: (Optional) string name of the PyMetric instance.
  dtype: (Optional) data type of the PyMetric result.
  **kwargs: Additional layer keywords arguments.

Usage of `PyMetric` is generally identical to `keras.metrics.Metric`.
It can be used in isolation, or in tandem with the `compile()` API. For more
information about the usage of `PyMetric`, see `keras.metrics.Metric`.

Unlike regular metrics, `PyMetric` instances are outside-compiled
with respect to the TensorFlow graph during training or evaluation.
They have access to the same
inputs of a standard in-graph metric, but they run in a Python interpreter
on the host CPU. Any data stored in a `PyMetric` is located on the main
memory of the host CPU, and any TensorFlow ops used in a PyMetric are
run eagerly on the host CPU.

As a result, `PyMetric` instances are generally not as performant
as in-graph metrics, and should only be used in cases where computing
the metric inside of the TensorFlow graph is either impossible
or prohibitively expensive.

**Note:** Due to the use of `tf.py_function`, PyMetrics
are incompatible with XLA and therefore TPUs.

Methods to be implemented by subclasses:

* `update_state()`: Handles updates to internal state variables
* `result()`: Computes and returns a scalar value or a dict of scalar values
  for the metric from the state variables.
* `reset_state()`: Computes and returns a scalar value for the metric from
  the state variables.

This subclass implementation is similar to that of `keras.metrics.Metric`,
with two notable differences:

* Inputs to `update_state()` in a `PyMetric` are eager tensors, and both
`update_state()` and `result()` run outside of the TensorFlow graph,
executing any TensorFlow ops eagerly.
* `reset_state()` is also called at initialization time to initialize the
Python state of the metric.
* `result()` can only return a single scalar. It does not support returning
a dictionary of results like `keras.metrics.Metric`.

Example subclass implementation using sklearn's Jaccard Score:

```python
from sklearn.metrics import jaccard_score
import tensorflow as tf

class JaccardScore(tf.keras.metrics.experimental.PyMetric):

  def __init__(self, name='jaccard_score', **kwargs):
    super().__init__(name=name, **kwargs)

  def update_state(self, y_true, y_pred, sample_weight=None):
    self.jaccard_sum += jaccard_score(y_pred, y_true, average="macro")
    self.count += 1

  def reset_state(self):
    self.jaccard_sum = 0.
    self.count = 0.

  def result(self):
    return self.jaccard_sum / self.count
```
c                 J   > [         TU ]  " SXS.UD6  U R                  5         g )N)namedtype )super__init__reset_state)selfr
   r   kwargs	__class__s       X/home/james-whalen/.local/lib/python3.13/site-packages/tf_keras/src/metrics/py_metric.pyr   PyMetric.__init__a   s$    :d:6:    c                 6  >^^^	 [         [        R                  U ]  U 5      mTR                  m	SU	4S jjnUTl        SS jn[        R                  " UT5      Tl        TR                  mU4S jnUTl	        U4S jn[        R                  " UT5      Tl        T$ )Nc                 x   > [         R                  " S5         T" XU5      sS S S 5        $ ! , (       d  f       g = fNz/cpu:0tfdevice)y_truey_predsample_weightobj_update_states      r   update_state_on_cpu-PyMetric.__new__.<locals>.update_state_on_cpuk   s%    8$'F %$$s   	+
9c                 p    X/nUb  UR                  U5        [        R                  " U R                  U/ S9$ )N)funcinpTout)appendr   py_functionr    )r   r   r   r   eager_inputss        r   update_state_fn)PyMetric.__new__.<locals>.update_state_fnq   s<    "+L(##M2>>--<b r   c                  t   > [         R                  " S5         T " 5       sS S S 5        $ ! , (       d  f       g = fr   r   )
obj_results   r   result_on_host_cpu,PyMetric.__new__.<locals>.result_on_host_cpu~   s     8$!| %$$s   )
7c                 X   > [         R                  " U R                  / TR                  S9$ )N)r$   r%   )r   r'   r-   r   )r   objs    r   	result_fn#PyMetric.__new__.<locals>.result_fn   s%    >>''Rcii r   N)
r   r   Metric__new__update_stater    types
MethodTyperesultr-   )clsargsr   r    r)   r-   r1   r0   r,   r   r   s          @@@r   r5   PyMetric.__new__e   s    K&&4S9 ++	G #6	 !++OSA ZZ
	$ "4	
 %%i5

r   c                     [        S5      e)a  Accumulates statistics for the metric.

**Note:** This function is executed outside of the TensorFlow graph
on the CPU host.

This means:

a) Inputs are eager tensors.
b) Any TensorFlow ops run in this method are run eagerly.
c) Any Tensors created are allocated to the CPU's main memory.

Args:
  y_true: Target output
  y_pred: Predicted output
  sample_weight: (Optional) weights for the individual samples in
    `y_true` and `y_pred`
z*Subclasses should implement `update_state`NotImplementedError)r   r   r   r   s       r   r6   PyMetric.update_state   s    $ ""NOOr   c                     [        S5      e)zMerges the state from one or more metrics.

`PyMetric` instances that intend to support merging state must override
 this method, as the default implementation
in `keras.metrics.Metric` does not apply to `PyMetric`.
z)Subclasses should implement `merge_state`r>   )r   metricss     r   merge_statePyMetric.merge_state   s     ""MNNr   c                     [        S5      e)zResets all of the metric state variables.

This function is called between epochs when a metric is evaluated during
training. It's also called when the metric is initialized.
z)Subclasses should implement `reset_state`r>   r   s    r   r   PyMetric.reset_state   s     ""MNNr   c                     [        S5      e)aY  Computes and returns the scalar metric value.

**Note:** This function is executed outside of the TensorFlow graph
 on the CPU host. This means any TensorFlow ops run in this method
 are run eagerly.

Result computation is an idempotent operation that simply calculates the
metric value using the state variables.

Returns:
    A Python scalar.
z$Subclasses should implement `result`r>   rF   s    r   r9   PyMetric.result   s     ""HIIr   r   )NNr3   )__name__
__module____qualname____firstlineno____doc__r   r5   r6   rC   r   r9   __static_attributes____classcell__)r   s   @r   r   r      s3    DL&PP(OOJ Jr   r   )rN   r7   tensorflow.compat.v2compatv2r    tensorflow.python.util.tf_exportr   tf_keras.src.metricsr   r4   r   r   r   r   <module>rV      sH    *  ! ! 9 , 3;eJ{!! eJ <eJr   