
    2Vh	o                        d Z ddlmZ ddlmZ ddlmZ ddlmZ ddlmZ ddlm	Z	 ddl
mZ dd	lmZ  ej                         d
k(  rddlmZ n ej                         dk(  rddlmZ nl ej                         dk(  rddlmZ nQ ej                         dk(  rddlmZ n6 ej                         dk(  rddlmZ n ed ej                          d      dZ ed       G d de             Z	 ddZddZd Zy) z9Library for exporting SavedModel for Keras models/layers.    )backend)layers)tree)keras_export)get_input_signature)make_tf_tensor_spec)io_utils)
tensorflowr
   )TFExportArchivejax)JaxExportArchivetorch)TorchExportArchivenumpy)NumpyExportArchiveopenvino)OpenvinoExportArchivez	Backend 'z' must implement ExportArchive.servezkeras.export.ExportArchivec                        e Zd ZdZ fdZed        Zed        Zed        Zd Z	d fd	Z
 fdZd	 Zdd
Zd Zd Zd Zd Z xZS )ExportArchivea	  ExportArchive is used to write SavedModel artifacts (e.g. for inference).

    If you have a Keras model or layer that you want to export as SavedModel for
    serving (e.g. via TensorFlow-Serving), you can use `ExportArchive`
    to configure the different serving endpoints you need to make available,
    as well as their signatures. Simply instantiate an `ExportArchive`,
    use `track()` to register the layer(s) or model(s) to be used,
    then use the `add_endpoint()` method to register a new serving endpoint.
    When done, use the `write_out()` method to save the artifact.

    The resulting artifact is a SavedModel and can be reloaded via
    `tf.saved_model.load`.

    Examples:

    Here's how to export a model for inference.

    ```python
    export_archive = ExportArchive()
    export_archive.track(model)
    export_archive.add_endpoint(
        name="serve",
        fn=model.call,
        input_signature=[keras.InputSpec(shape=(None, 3), dtype="float32")],
    )
    export_archive.write_out("path/to/location")

    # Elsewhere, we can reload the artifact and serve it.
    # The endpoint we added is available as a method:
    serving_model = tf.saved_model.load("path/to/location")
    outputs = serving_model.serve(inputs)
    ```

    Here's how to export a model with one endpoint for inference and one
    endpoint for a training-mode forward pass (e.g. with dropout on).

    ```python
    export_archive = ExportArchive()
    export_archive.track(model)
    export_archive.add_endpoint(
        name="call_inference",
        fn=lambda x: model.call(x, training=False),
        input_signature=[keras.InputSpec(shape=(None, 3), dtype="float32")],
    )
    export_archive.add_endpoint(
        name="call_training",
        fn=lambda x: model.call(x, training=True),
        input_signature=[keras.InputSpec(shape=(None, 3), dtype="float32")],
    )
    export_archive.write_out("path/to/location")
    ```

    **Note on resource tracking:**

    `ExportArchive` is able to automatically track all `keras.Variables` used
    by its endpoints, so most of the time calling `.track(model)`
    is not strictly required. However, if your model uses lookup layers such
    as `IntegerLookup`, `StringLookup`, or `TextVectorization`,
    it will need to be tracked explicitly via `.track(model)`.

    Explicit tracking is also required if you need to be able to access
    the properties `variables`, `trainable_variables`, or
    `non_trainable_variables` on the revived archive.
    c                 j   t         |           t        j                         dvrt        d      g | _        i | _        t        j                  | _        t        j                  j                  j                         | _        g | j                  _        g | j                  _        g | j                  _        y )N)r
   r   r   zK`ExportArchive` is only compatible with TensorFlow, JAX and Torch backends.)super__init__r   NotImplementedError_endpoint_names_endpoint_signaturestf__version__tensorflow_version__internal__trackingAutoTrackable_tf_trackable	variablestrainable_variablesnon_trainable_variables)self	__class__s    L/home/dcms/DCMS/lib/python3.12/site-packages/keras/src/export/saved_model.pyr   zExportArchive.__init__l   s    ??$BB%" 
  "$&!"$..__55CCE')$13.572    c                 .    | j                   j                  S N)r#   r$   r'   s    r)   r$   zExportArchive.variables}   s    !!+++r*   c                 .    | j                   j                  S r,   )r#   r%   r-   s    r)   r%   z!ExportArchive.trainable_variables   s    !!555r*   c                 .    | j                   j                  S r,   )r#   r&   r-   s    r)   r&   z%ExportArchive.non_trainable_variables   s    !!999r*   c                    t        |t        j                        r|j                  st	        d      t        |t
        j                  j                  j                        r.t        | d      sg | _
        | j                  j                  |       t        |t        j                        r| j                  |       yt        |t
        j                  j                  j                        st	        d| dt        |       d      y)a+  Track the variables (of a layer or model) and other assets.

        By default, all variables used by an endpoint function are automatically
        tracked when you call `add_endpoint()`. However, non-variables assets
        such as lookup tables need to be tracked manually. Note that lookup
        tables used by built-in Keras layers (`TextVectorization`,
        `IntegerLookup`, `StringLookup`) are automatically tracked by
        `add_endpoint()`.

        Args:
            resource: A layer, model or a TensorFlow trackable resource.
        JThe layer provided has not yet been built. It must be built before export._trackedzoInvalid resource type. Expected a Keras `Layer` or `Model` or a TensorFlow `Trackable` object. Received object z
 of type 'z'. N)
isinstancer   Layerbuilt
ValueErrorr   r    r!   	Trackablehasattrr2   append_track_layertype)r'   resources     r)   trackzExportArchive.track   s     h-hnn2  h 8 8 B BC4, "MM  *h-h'Hboo&>&>&H&HI##+*JtH~6FcK  Jr*   c                    || j                   v rt        d| d      t        j                         dk7  r(d|v sd|v r t        dt        j                                |t        |t        j
                  j                  j                        r"|j                         st        d| d      |}nt        d	      t        | j                  ||       | j                   j                  |       |S t        j                  t        |      }t        | @  |||fi |}|| j"                  |<   t        | j                  ||       | j                   j                  |       |S )
a  Register a new serving endpoint.

        Args:
            name: `str`. The name of the endpoint.
            fn: A callable. It should only leverage resources
                (e.g. `keras.Variable` objects or `tf.lookup.StaticHashTable`
                objects) that are available on the models/layers tracked by the
                `ExportArchive` (you can call `.track(model)` to track a new
                model).
                The shape and dtype of the inputs to the function must be
                known. For that purpose, you can either 1) make sure that `fn`
                is a `tf.function` that has been called at least once, or 2)
                provide an `input_signature` argument that specifies the shape
                and dtype of the inputs (see below).
            input_signature: Optional. Specifies the shape and dtype of `fn`.
                Can be a structure of `keras.InputSpec`, `tf.TensorSpec`,
                `backend.KerasTensor`, or backend tensor (see below for an
                example showing a `Functional` model with 2 input arguments). If
                not provided, `fn` must be a `tf.function` that has been called
                at least once. Defaults to `None`.
            **kwargs: Additional keyword arguments:
                - Specific to the JAX backend:
                    - `is_static`: Optional `bool`. Indicates whether `fn` is
                        static. Set to `False` if `fn` involves state updates
                        (e.g., RNG seeds).
                    - `jax2tf_kwargs`: Optional `dict`. Arguments for
                        `jax2tf.convert`. See [`jax2tf.convert`](
                            https://github.com/google/jax/blob/main/jax/experimental/jax2tf/README.md).
                        If `native_serialization` and `polymorphic_shapes` are
                        not provided, they are automatically computed.

        Returns:
            The `tf.function` wrapping `fn` that was added to the archive.

        Example:

        Adding an endpoint using the `input_signature` argument when the
        model has a single input argument:

        ```python
        export_archive = ExportArchive()
        export_archive.track(model)
        export_archive.add_endpoint(
            name="serve",
            fn=model.call,
            input_signature=[keras.InputSpec(shape=(None, 3), dtype="float32")],
        )
        ```

        Adding an endpoint using the `input_signature` argument when the
        model has two positional input arguments:

        ```python
        export_archive = ExportArchive()
        export_archive.track(model)
        export_archive.add_endpoint(
            name="serve",
            fn=model.call,
            input_signature=[
                keras.InputSpec(shape=(None, 3), dtype="float32"),
                keras.InputSpec(shape=(None, 4), dtype="float32"),
            ],
        )
        ```

        Adding an endpoint using the `input_signature` argument when the
        model has one input argument that is a list of 2 tensors (e.g.
        a Functional model with 2 inputs):

        ```python
        model = keras.Model(inputs=[x1, x2], outputs=outputs)

        export_archive = ExportArchive()
        export_archive.track(model)
        export_archive.add_endpoint(
            name="serve",
            fn=model.call,
            input_signature=[
                [
                    keras.InputSpec(shape=(None, 3), dtype="float32"),
                    keras.InputSpec(shape=(None, 4), dtype="float32"),
                ],
            ],
        )
        ```

        This also works with dictionary inputs:

        ```python
        model = keras.Model(inputs={"x1": x1, "x2": x2}, outputs=outputs)

        export_archive = ExportArchive()
        export_archive.track(model)
        export_archive.add_endpoint(
            name="serve",
            fn=model.call,
            input_signature=[
                {
                    "x1": keras.InputSpec(shape=(None, 3), dtype="float32"),
                    "x2": keras.InputSpec(shape=(None, 4), dtype="float32"),
                },
            ],
        )
        ```

        Adding an endpoint that is a `tf.function`:

        ```python
        @tf.function()
        def serving_fn(x):
            return model(x)

        # The function must be traced, i.e. it must be called at least once.
        serving_fn(tf.random.normal(shape=(2, 3)))

        export_archive = ExportArchive()
        export_archive.track(model)
        export_archive.add_endpoint(name="serve", fn=serving_fn)
        ```

        Combining a model with some TensorFlow preprocessing, which can use
        TensorFlow resources:

        ```python
        lookup_table = tf.lookup.StaticHashTable(initializer, default_value=0.0)

        export_archive = ExportArchive()
        model_fn = export_archive.track_and_add_endpoint(
            "model_fn",
            model,
            input_signature=[tf.TensorSpec(shape=(None, 5), dtype=tf.float32)],
        )
        export_archive.track(lookup_table)

        @tf.function()
        def serving_fn(x):
            x = lookup_table.lookup(x)
            return model_fn(x)

        export_archive.add_endpoint(name="serve", fn=serving_fn)
        ```
        Endpoint name '' is already taken.r   jax2tf_kwargs	is_staticZ'jax2tf_kwargs' and 'is_static' are only supported with the jax backend. Current backend: zThe provided tf.function 'z' has never been called. To specify the expected shape and dtype of the function's arguments, you must either provide a function that has been called at least once, or alternatively pass an `input_signature` argument in `add_endpoint()`.ao  If the `fn` argument provided is not a `tf.function`, you must provide an `input_signature` argument to specify the shape and dtype of the function arguments. Example:

export_archive.add_endpoint(
    name='call',
    fn=model.call,
    input_signature=[
        keras.InputSpec(
            shape=(None, 224, 224, 3),
            dtype='float32',
        )
    ],
))r   r6   r   r3   r   typesexperimentalGenericFunction_list_all_concrete_functionssetattrr#   r9   r   map_structurer   r   add_endpointr   )r'   namefninput_signaturekwargsdecorated_fnr(   s         r)   rJ   zExportArchive.add_endpoint   sl   ^ 4'''tf4GHII??%&(K6,A 99@9J8KM  ""bhh33CCD668$4RD 9M M   "    D&&l;  ''-,,
 w+D"oPP*9!!$'""D,7##D)r*   c                    || j                   v rt        d| d      t        |t        j                        st        d| dt        |       d      |j                  st        d      t        j                         dk7  r(d|v sd	|v r t        d
t        j                                t        j                  t        |      }t        t        d      s0| j                  |        | j                  ||j                  |fi |S t!        | D  |||fi |}|| j$                  |<   t'        | j(                  ||       | j                   j+                  |       |S )a  Track the variables and register a new serving endpoint.

        This function combines the functionality of `track` and `add_endpoint`.
        It tracks the variables of the `resource` (either a layer or a model)
        and registers a serving endpoint using `resource.__call__`.

        Args:
            name: `str`. The name of the endpoint.
            resource: A trackable Keras resource, such as a layer or model.
            input_signature: Optional. Specifies the shape and dtype of `fn`.
                Can be a structure of `keras.InputSpec`, `tf.TensorSpec`,
                `backend.KerasTensor`, or backend tensor (see below for an
                example showing a `Functional` model with 2 input arguments). If
                not provided, `fn` must be a `tf.function` that has been called
                at least once. Defaults to `None`.
            **kwargs: Additional keyword arguments:
                - Specific to the JAX backend:
                    - `is_static`: Optional `bool`. Indicates whether `fn` is
                        static. Set to `False` if `fn` involves state updates
                        (e.g., RNG seeds).
                    - `jax2tf_kwargs`: Optional `dict`. Arguments for
                        `jax2tf.convert`. See [`jax2tf.convert`](
                            https://github.com/google/jax/blob/main/jax/experimental/jax2tf/README.md).
                        If `native_serialization` and `polymorphic_shapes` are
                        not provided, they are automatically computed.

        r?   r@   z^Invalid resource type. Expected an instance of a Keras `Layer` or `Model`. Received: resource=z
 (of type )r1   r   rA   rB   rC   track_and_add_endpoint)r   r6   r3   r   r4   r;   r5   r   r   rI   r   r8   BackendExportArchiver=   rJ   __call__r   rR   r   rH   r#   r9   )r'   rK   r<   rM   rN   rO   r(   s         r)   rR   z$ExportArchive.track_and_add_endpointr  sy   8 4'''tf4GHII(FLL1&&.Zz$x.9IL 
 ~~2  ??%&(K6,A 99@9J8KM 
 ,,
 +-EFJJx $4$$h''<B 
 !79h39L />D%%d+D&&l;  ''-r*   c           	         t        |t        t        t        f      st	        dt        |       d      t        d |D              s't	        dt        t        d |D                           t        j                         dk(  r3t        j                  t        j                  | j                  |            }t        | j                  |t        |             y)a  Register a set of variables to be retrieved after reloading.

        Arguments:
            name: The string name for the collection.
            variables: A tuple/list/set of `keras.Variable` instances.

        Example:

        ```python
        export_archive = ExportArchive()
        export_archive.track(model)
        # Register an endpoint
        export_archive.add_endpoint(
            name="serve",
            fn=model.call,
            input_signature=[keras.InputSpec(shape=(None, 3), dtype="float32")],
        )
        # Save a variable collection
        export_archive.add_variable_collection(
            name="optimizer_variables", variables=model.optimizer.variables)
        export_archive.write_out("path/to/location")

        # Reload the object
        revived_object = tf.saved_model.load("path/to/location")
        # Retrieve the variables
        optimizer_variables = revived_object.optimizer_variables
        ```
        zNExpected `variables` to be a list/tuple/set. Received instead object of type 'z'.c              3   p   K   | ].  }t        |t        j                  t        j                  f       0 y wr,   )r3   r   Variabler   .0vs     r)   	<genexpr>z8ExportArchive.add_variable_collection.<locals>.<genexpr>  s+      
?@Jq2;;(8(89:
s   46zgExpected all elements in `variables` to be `tf.Variable` instances. Found instead the following types: c              3   2   K   | ]  }t        |        y wr,   )r;   rX   s     r)   r[   z8ExportArchive.add_variable_collection.<locals>.<genexpr>  s     7DG7s   r   N)r3   listtuplesetr6   r;   allr   r   flattenrI   _convert_to_tf_variablerH   r#   )r'   rK   r$   s      r)   add_variable_collectionz%ExportArchive.add_variable_collection  s    : )dE3%78448O3DBH   
DM
 
 O7Y7789; 
 ??%""4#?#?KI 	""D$y/:r*   c                      j                   st        d       j                          i } j                   D ]  } j                  |      ||<    d j                   vr! j                   j                   d         |d<   t        j
                  j                   j                  |||       rAdj                   fd j                   D              }t        j                  d| d|        y	y	)
a"  Write the corresponding SavedModel to disk.

        Arguments:
            filepath: `str` or `pathlib.Path` object.
                Path where to save the artifact.
            options: `tf.saved_model.SaveOptions` object that specifies
                SavedModel saving options.
            verbose: whether to print all the variables of an
                exported SavedModel.

        **Note on TF-Serving**: all endpoints registered via `add_endpoint()`
        are made visible for TF-Serving in the SavedModel artifact. In addition,
        the first endpoint registered is made visible under the alias
        `"serving_default"` (unless an endpoint with the name
        `"serving_default"` was already registered manually),
        since TF-Serving requires this endpoint to be set.
        z4No endpoints have been set yet. Call add_endpoint().serving_defaultr   )options
signaturesz

c              3   b   K   | ]&  }t        t        j                  |      |        ( yw)verboseN)_print_signaturegetattrr#   )rY   rK   r'   rj   s     r)   r[   z*ExportArchive.write_out.<locals>.<genexpr>  s9      $  !D..5tW $s   ,/zSaved artifact at 'z+'. The following endpoints are available:

N)r   r6   _filter_and_track_resources_get_concrete_fnr   saved_modelsaver#   joinr	   	print_msg)r'   filepathrf   rj   rg   rK   	endpointss   `  `   r)   	write_outzExportArchive.write_out  s   $ ##F  	((*
(( 	;D#44T:Jt	; D$8$88,0,A,A$$Q'-J() 	!	 	 	
  $ !00	$ I %hZ 0=+ r*   c                     t        |t        j                        st        d| dt	        |       d      t        j                  |j                  |j                  |j                  |j                        S )NzL`backend_variable` must be a `backend.Variable`. Recevied: backend_variable=z
 of type (rQ   )dtype	trainablerK   )
r3   r   rW   	TypeErrorr;   r   valuerw   rx   rK   )r'   backend_variables     r)   rb   z%ExportArchive._convert_to_tf_variable  s{    *G,<,<=..>-? @)*+1. 
 {{"""((&00!&&	
 	
r*   c                     || j                   v rt        | j                  |      S t        | j                  |      j                  d      }t	        |j                               d   S )z&Workaround for some SavedModel quirks.ro   r   )r   rl   r#   _trackable_childrenr]   values)r'   endpointtracess      r)   rn   zExportArchive._get_concrete_fn,  s[    t0004--x88T//:NNF (++r*   c                 r    | j                   D cg c]  }| j                  |       }}t        |      S c c}w r,   )r   rn   _list_variables_used_by_fns)r'   rK   fnss      r)    _get_variables_used_by_endpointsz.ExportArchive._get_variables_used_by_endpoints6  s7    7;7K7KLtt$$T*LL*3// Ms   4c                    | j                   D cg c]  }| j                  |       }}t        |      \  }}t        ||z         | j                  _        g | j                  _        ddlm} t        | d      rx| j                  D ]h  }t        j                  j                  |      j                         }|D ]4  }t        ||      s| j                  j                  j!                  |       6 j yyc c}w )zBTrack resources used by endpoints / referenced in `track()` calls.r   )TrackableResourcer2   N)r   rn   r   r]   r#   _all_variables_misc_assets#tensorflow.saved_model.experimentalr   r8   r2   r   trainTrackableViewdescendantsr3   r9   )	r'   rK   r   tvsntvsr   rootr   	trackables	            r)   rm   z)ExportArchive._filter_and_track_resources:  s     8<7K7KLtt$$T*LL/4	T,0t,<) +-'I4$ J hh44T:FFH!, JI!)->?**77>>yIJJ % Ms   C0r,   )NT)__name__
__module____qualname____doc__r   propertyr$   r%   r&   r=   rJ   rR   rc   ru   rb   rn   r   rm   __classcell__)r(   s   @r)   r   r   )   s    ?B8" , , 6 6 : :"HCJB H0;d4l
,0Jr*   r   Nc                     |d}t               }|t        |       } |j                  t        | |fi | |j	                  ||       y)a
  Export the model as a TensorFlow SavedModel artifact for inference.

    This method lets you export a model to a lightweight SavedModel artifact
    that contains the model's forward pass only (its `call()` method)
    and can be served via e.g. TensorFlow Serving. The forward pass is
    registered under the name `serve()` (see example below).

    The original code of the model (including any custom layers you may
    have used) is *no longer* necessary to reload the artifact -- it is
    entirely standalone.

    Args:
        filepath: `str` or `pathlib.Path` object. The path to save the artifact.
        verbose: `bool`. Whether to print a message during export. Defaults to
            `None`, which uses the default value set by different backends and
            formats.
        input_signature: Optional. Specifies the shape and dtype of the model
            inputs. Can be a structure of `keras.InputSpec`, `tf.TensorSpec`,
            `backend.KerasTensor`, or backend tensor. If not provided, it will
            be automatically computed. Defaults to `None`.
        **kwargs: Additional keyword arguments:
            - Specific to the JAX backend:
                - `is_static`: Optional `bool`. Indicates whether `fn` is
                    static. Set to `False` if `fn` involves state updates
                    (e.g., RNG seeds).
                - `jax2tf_kwargs`: Optional `dict`. Arguments for
                    `jax2tf.convert`. See [`jax2tf.convert`](
                        https://github.com/google/jax/blob/main/jax/experimental/jax2tf/README.md).
                    If `native_serialization` and `polymorphic_shapes` are not
                    provided, they are automatically computed.

    **Note:** This feature is currently supported only with TensorFlow, JAX and
    Torch backends. Support for the Torch backend is experimental.

    **Note:** The dynamic shape feature is not yet supported with Torch
    backend. As a result, you must fully define the shapes of the inputs using
    `input_signature`. If `input_signature` is not provided, all instances of
    `None` (such as the batch size) will be replaced with `1`.

    Example:

    ```python
    # Export the model as a TensorFlow SavedModel artifact
    model.export("path/to/location", format="tf_saved_model")

    # Load the artifact in a different process/environment
    reloaded_artifact = tf.saved_model.load("path/to/location")
    predictions = reloaded_artifact.serve(input_data)
    ```

    If you would like to customize your serving endpoints, you can
    use the lower-level `keras.export.ExportArchive` class. The
    `export()` method relies on `ExportArchive` internally.
    NTri   )r   r   rR   DEFAULT_ENDPOINT_NAMEru   )modelrs   rj   rM   rN   export_archives         r)   export_saved_modelr   N  sZ    r "_N-e4)N))uo9? Xw7r*   c                     | j                         d   }|j                  |      }|j                  d      }d| dg|dd  z   }dj                  |      }|S )Nr   ri   
z* Endpoint ''   )rG   pretty_printed_signaturesplitrq   )rL   rK   rj   concrete_fnpprinted_signaturelinesr   s          r)   rk   rk     sh    113A6K$==g=N$$T*ED6#$uQRy0EyyHOr*   c                    g }g }t               }t               }| D ]  }t        |d      r|j                  }n!t        |d      r|j                         g}n|g}|D ]  }|j                  D ];  }t        |      |vs|j                  |       |j                  t        |             = |j                  D ]I  }t        |      |vst        |      |vs|j                  |       |j                  t        |             K   ||fS )Nconcrete_functionsget_concrete_function)	r_   r8   r   r   r%   idr9   addr$   )	r   r%   r&   trainable_variables_idsnon_trainable_variables_idsrL   r   r   rZ   s	            r)   r   r     s    !e"%% ;2+,!#!6!6R01"$":":"<!="$- 	;K 44 7a5 77'..q1+//167
 !** ;qE!881%@@+2215/33BqE:;	;;(  777r*   )NN)T) r   	keras.srcr   r   r   keras.src.api_exportr   keras.src.export.export_utilsr   r   keras.src.utilsr	   keras.src.utils.module_utilsr
   r   #keras.src.backend.tensorflow.exportr   rS   keras.src.backend.jax.exportr   keras.src.backend.torch.exportr   keras.src.backend.numpy.exportr   !keras.src.backend.openvino.exportr   RuntimeErrorr   r   r   rk   r    r*   r)   <module>r      s   ?    - = = $ 97??$ W__% W__'! W__'! W__*$ 
OGOO%&&EF 
    *+aJ( aJ ,aJJ 48B8J8r*   