// Copyright 2024 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

syntax = "proto3";

package google.cloud.aiplatform.v1;

import "google/api/field_behavior.proto";
import "google/cloud/aiplatform/v1/explanation_metadata.proto";
import "google/cloud/aiplatform/v1/io.proto";
import "google/protobuf/struct.proto";

option csharp_namespace = "Google.Cloud.AIPlatform.V1";
option go_package = "cloud.google.com/go/aiplatform/apiv1/aiplatformpb;aiplatformpb";
option java_multiple_files = true;
option java_outer_classname = "ExplanationProto";
option java_package = "com.google.cloud.aiplatform.v1";
option php_namespace = "Google\\Cloud\\AIPlatform\\V1";
option ruby_package = "Google::Cloud::AIPlatform::V1";

// Explanation of a prediction (provided in
// [PredictResponse.predictions][google.cloud.aiplatform.v1.PredictResponse.predictions])
// produced by the Model on a given
// [instance][google.cloud.aiplatform.v1.ExplainRequest.instances].
message Explanation {
  // Output only. Feature attributions grouped by predicted outputs.
  //
  // For Models that predict only one output, such as regression Models that
  // predict only one score, there is only one attibution that explains the
  // predicted output. For Models that predict multiple outputs, such as
  // multiclass Models that predict multiple classes, each element explains one
  // specific item.
  // [Attribution.output_index][google.cloud.aiplatform.v1.Attribution.output_index]
  // can be used to identify which output this attribution is explaining.
  //
  // By default, we provide Shapley values for the predicted class. However,
  // you can configure the explanation request to generate Shapley values for
  // any other classes too. For example, if a model predicts a probability of
  // `0.4` for approving a loan application, the model's decision is to reject
  // the application since `p(reject) = 0.6 > p(approve) = 0.4`, and the default
  // Shapley values would be computed for rejection decision and not approval,
  // even though the latter might be the positive class.
  //
  // If users set
  // [ExplanationParameters.top_k][google.cloud.aiplatform.v1.ExplanationParameters.top_k],
  // the attributions are sorted by
  // [instance_output_value][Attributions.instance_output_value] in descending
  // order. If
  // [ExplanationParameters.output_indices][google.cloud.aiplatform.v1.ExplanationParameters.output_indices]
  // is specified, the attributions are stored by
  // [Attribution.output_index][google.cloud.aiplatform.v1.Attribution.output_index]
  // in the same order as they appear in the output_indices.
  repeated Attribution attributions = 1
      [(google.api.field_behavior) = OUTPUT_ONLY];

  // Output only. List of the nearest neighbors for example-based explanations.
  //
  // For models deployed with the examples explanations feature enabled, the
  // attributions field is empty and instead the neighbors field is populated.
  repeated Neighbor neighbors = 2 [(google.api.field_behavior) = OUTPUT_ONLY];
}

// Aggregated explanation metrics for a Model over a set of instances.
message ModelExplanation {
  // Output only. Aggregated attributions explaining the Model's prediction
  // outputs over the set of instances. The attributions are grouped by outputs.
  //
  // For Models that predict only one output, such as regression Models that
  // predict only one score, there is only one attibution that explains the
  // predicted output. For Models that predict multiple outputs, such as
  // multiclass Models that predict multiple classes, each element explains one
  // specific item.
  // [Attribution.output_index][google.cloud.aiplatform.v1.Attribution.output_index]
  // can be used to identify which output this attribution is explaining.
  //
  // The
  // [baselineOutputValue][google.cloud.aiplatform.v1.Attribution.baseline_output_value],
  // [instanceOutputValue][google.cloud.aiplatform.v1.Attribution.instance_output_value]
  // and
  // [featureAttributions][google.cloud.aiplatform.v1.Attribution.feature_attributions]
  // fields are averaged over the test data.
  //
  // NOTE: Currently AutoML tabular classification Models produce only one
  // attribution, which averages attributions over all the classes it predicts.
  // [Attribution.approximation_error][google.cloud.aiplatform.v1.Attribution.approximation_error]
  // is not populated.
  repeated Attribution mean_attributions = 1
      [(google.api.field_behavior) = OUTPUT_ONLY];
}

// Attribution that explains a particular prediction output.
message Attribution {
  // Output only. Model predicted output if the input instance is constructed
  // from the baselines of all the features defined in
  // [ExplanationMetadata.inputs][google.cloud.aiplatform.v1.ExplanationMetadata.inputs].
  // The field name of the output is determined by the key in
  // [ExplanationMetadata.outputs][google.cloud.aiplatform.v1.ExplanationMetadata.outputs].
  //
  // If the Model's predicted output has multiple dimensions (rank > 1), this is
  // the value in the output located by
  // [output_index][google.cloud.aiplatform.v1.Attribution.output_index].
  //
  // If there are multiple baselines, their output values are averaged.
  double baseline_output_value = 1 [(google.api.field_behavior) = OUTPUT_ONLY];

  // Output only. Model predicted output on the corresponding [explanation
  // instance][ExplainRequest.instances]. The field name of the output is
  // determined by the key in
  // [ExplanationMetadata.outputs][google.cloud.aiplatform.v1.ExplanationMetadata.outputs].
  //
  // If the Model predicted output has multiple dimensions, this is the value in
  // the output located by
  // [output_index][google.cloud.aiplatform.v1.Attribution.output_index].
  double instance_output_value = 2 [(google.api.field_behavior) = OUTPUT_ONLY];

  // Output only. Attributions of each explained feature. Features are extracted
  // from the [prediction
  // instances][google.cloud.aiplatform.v1.ExplainRequest.instances] according
  // to [explanation metadata for
  // inputs][google.cloud.aiplatform.v1.ExplanationMetadata.inputs].
  //
  // The value is a struct, whose keys are the name of the feature. The values
  // are how much the feature in the
  // [instance][google.cloud.aiplatform.v1.ExplainRequest.instances] contributed
  // to the predicted result.
  //
  // The format of the value is determined by the feature's input format:
  //
  //   * If the feature is a scalar value, the attribution value is a
  //     [floating number][google.protobuf.Value.number_value].
  //
  //   * If the feature is an array of scalar values, the attribution value is
  //     an [array][google.protobuf.Value.list_value].
  //
  //   * If the feature is a struct, the attribution value is a
  //     [struct][google.protobuf.Value.struct_value]. The keys in the
  //     attribution value struct are the same as the keys in the feature
  //     struct. The formats of the values in the attribution struct are
  //     determined by the formats of the values in the feature struct.
  //
  // The
  // [ExplanationMetadata.feature_attributions_schema_uri][google.cloud.aiplatform.v1.ExplanationMetadata.feature_attributions_schema_uri]
  // field, pointed to by the
  // [ExplanationSpec][google.cloud.aiplatform.v1.ExplanationSpec] field of the
  // [Endpoint.deployed_models][google.cloud.aiplatform.v1.Endpoint.deployed_models]
  // object, points to the schema file that describes the features and their
  // attribution values (if it is populated).
  google.protobuf.Value feature_attributions = 3
      [(google.api.field_behavior) = OUTPUT_ONLY];

  // Output only. The index that locates the explained prediction output.
  //
  // If the prediction output is a scalar value, output_index is not populated.
  // If the prediction output has multiple dimensions, the length of the
  // output_index list is the same as the number of dimensions of the output.
  // The i-th element in output_index is the element index of the i-th dimension
  // of the output vector. Indices start from 0.
  repeated int32 output_index = 4 [(google.api.field_behavior) = OUTPUT_ONLY];

  // Output only. The display name of the output identified by
  // [output_index][google.cloud.aiplatform.v1.Attribution.output_index]. For
  // example, the predicted class name by a multi-classification Model.
  //
  // This field is only populated iff the Model predicts display names as a
  // separate field along with the explained output. The predicted display name
  // must has the same shape of the explained output, and can be located using
  // output_index.
  string output_display_name = 5 [(google.api.field_behavior) = OUTPUT_ONLY];

  // Output only. Error of
  // [feature_attributions][google.cloud.aiplatform.v1.Attribution.feature_attributions]
  // caused by approximation used in the explanation method. Lower value means
  // more precise attributions.
  //
  // * For Sampled Shapley
  // [attribution][google.cloud.aiplatform.v1.ExplanationParameters.sampled_shapley_attribution],
  // increasing
  // [path_count][google.cloud.aiplatform.v1.SampledShapleyAttribution.path_count]
  // might reduce the error.
  // * For Integrated Gradients
  // [attribution][google.cloud.aiplatform.v1.ExplanationParameters.integrated_gradients_attribution],
  // increasing
  // [step_count][google.cloud.aiplatform.v1.IntegratedGradientsAttribution.step_count]
  // might reduce the error.
  // * For [XRAI
  // attribution][google.cloud.aiplatform.v1.ExplanationParameters.xrai_attribution],
  // increasing
  // [step_count][google.cloud.aiplatform.v1.XraiAttribution.step_count] might
  // reduce the error.
  //
  // See [this introduction](/vertex-ai/docs/explainable-ai/overview)
  // for more information.
  double approximation_error = 6 [(google.api.field_behavior) = OUTPUT_ONLY];

  // Output only. Name of the explain output. Specified as the key in
  // [ExplanationMetadata.outputs][google.cloud.aiplatform.v1.ExplanationMetadata.outputs].
  string output_name = 7 [(google.api.field_behavior) = OUTPUT_ONLY];
}

// Neighbors for example-based explanations.
message Neighbor {
  // Output only. The neighbor id.
  string neighbor_id = 1 [(google.api.field_behavior) = OUTPUT_ONLY];

  // Output only. The neighbor distance.
  double neighbor_distance = 2 [(google.api.field_behavior) = OUTPUT_ONLY];
}

// Specification of Model explanation.
message ExplanationSpec {
  // Required. Parameters that configure explaining of the Model's predictions.
  ExplanationParameters parameters = 1 [(google.api.field_behavior) = REQUIRED];

  // Optional. Metadata describing the Model's input and output for explanation.
  ExplanationMetadata metadata = 2 [(google.api.field_behavior) = OPTIONAL];
}

// Parameters to configure explaining for Model's predictions.
message ExplanationParameters {
  oneof method {
    // An attribution method that approximates Shapley values for features that
    // contribute to the label being predicted. A sampling strategy is used to
    // approximate the value rather than considering all subsets of features.
    // Refer to this paper for model details: https://arxiv.org/abs/1306.4265.
    SampledShapleyAttribution sampled_shapley_attribution = 1;

    // An attribution method that computes Aumann-Shapley values taking
    // advantage of the model's fully differentiable structure. Refer to this
    // paper for more details: https://arxiv.org/abs/1703.01365
    IntegratedGradientsAttribution integrated_gradients_attribution = 2;

    // An attribution method that redistributes Integrated Gradients
    // attribution to segmented regions, taking advantage of the model's fully
    // differentiable structure. Refer to this paper for
    // more details: https://arxiv.org/abs/1906.02825
    //
    // XRAI currently performs better on natural images, like a picture of a
    // house or an animal. If the images are taken in artificial environments,
    // like a lab or manufacturing line, or from diagnostic equipment, like
    // x-rays or quality-control cameras, use Integrated Gradients instead.
    XraiAttribution xrai_attribution = 3;

    // Example-based explanations that returns the nearest neighbors from the
    // provided dataset.
    Examples examples = 7;
  }

  // If populated, returns attributions for top K indices of outputs
  // (defaults to 1). Only applies to Models that predicts more than one outputs
  // (e,g, multi-class Models). When set to -1, returns explanations for all
  // outputs.
  int32 top_k = 4;

  // If populated, only returns attributions that have
  // [output_index][google.cloud.aiplatform.v1.Attribution.output_index]
  // contained in output_indices. It must be an ndarray of integers, with the
  // same shape of the output it's explaining.
  //
  // If not populated, returns attributions for
  // [top_k][google.cloud.aiplatform.v1.ExplanationParameters.top_k] indices of
  // outputs. If neither top_k nor output_indices is populated, returns the
  // argmax index of the outputs.
  //
  // Only applicable to Models that predict multiple outputs (e,g, multi-class
  // Models that predict multiple classes).
  google.protobuf.ListValue output_indices = 5;
}

// An attribution method that approximates Shapley values for features that
// contribute to the label being predicted. A sampling strategy is used to
// approximate the value rather than considering all subsets of features.
message SampledShapleyAttribution {
  // Required. The number of feature permutations to consider when approximating
  // the Shapley values.
  //
  // Valid range of its value is [1, 50], inclusively.
  int32 path_count = 1 [(google.api.field_behavior) = REQUIRED];
}

// An attribution method that computes the Aumann-Shapley value taking advantage
// of the model's fully differentiable structure. Refer to this paper for
// more details: https://arxiv.org/abs/1703.01365
message IntegratedGradientsAttribution {
  // Required. The number of steps for approximating the path integral.
  // A good value to start is 50 and gradually increase until the
  // sum to diff property is within the desired error range.
  //
  // Valid range of its value is [1, 100], inclusively.
  int32 step_count = 1 [(google.api.field_behavior) = REQUIRED];

  // Config for SmoothGrad approximation of gradients.
  //
  // When enabled, the gradients are approximated by averaging the gradients
  // from noisy samples in the vicinity of the inputs. Adding
  // noise can help improve the computed gradients. Refer to this paper for more
  // details: https://arxiv.org/pdf/1706.03825.pdf
  SmoothGradConfig smooth_grad_config = 2;

  // Config for IG with blur baseline.
  //
  // When enabled, a linear path from the maximally blurred image to the input
  // image is created. Using a blurred baseline instead of zero (black image) is
  // motivated by the BlurIG approach explained here:
  // https://arxiv.org/abs/2004.03383
  BlurBaselineConfig blur_baseline_config = 3;
}

// An explanation method that redistributes Integrated Gradients
// attributions to segmented regions, taking advantage of the model's fully
// differentiable structure. Refer to this paper for more details:
// https://arxiv.org/abs/1906.02825
//
// Supported only by image Models.
message XraiAttribution {
  // Required. The number of steps for approximating the path integral.
  // A good value to start is 50 and gradually increase until the
  // sum to diff property is met within the desired error range.
  //
  // Valid range of its value is [1, 100], inclusively.
  int32 step_count = 1 [(google.api.field_behavior) = REQUIRED];

  // Config for SmoothGrad approximation of gradients.
  //
  // When enabled, the gradients are approximated by averaging the gradients
  // from noisy samples in the vicinity of the inputs. Adding
  // noise can help improve the computed gradients. Refer to this paper for more
  // details: https://arxiv.org/pdf/1706.03825.pdf
  SmoothGradConfig smooth_grad_config = 2;

  // Config for XRAI with blur baseline.
  //
  // When enabled, a linear path from the maximally blurred image to the input
  // image is created. Using a blurred baseline instead of zero (black image) is
  // motivated by the BlurIG approach explained here:
  // https://arxiv.org/abs/2004.03383
  BlurBaselineConfig blur_baseline_config = 3;
}

// Config for SmoothGrad approximation of gradients.
//
// When enabled, the gradients are approximated by averaging the gradients from
// noisy samples in the vicinity of the inputs. Adding noise can help improve
// the computed gradients. Refer to this paper for more details:
// https://arxiv.org/pdf/1706.03825.pdf
message SmoothGradConfig {
  // Represents the standard deviation of the gaussian kernel
  // that will be used to add noise to the interpolated inputs
  // prior to computing gradients.
  oneof GradientNoiseSigma {
    // This is a single float value and will be used to add noise to all the
    // features. Use this field when all features are normalized to have the
    // same distribution: scale to range [0, 1], [-1, 1] or z-scoring, where
    // features are normalized to have 0-mean and 1-variance. Learn more about
    // [normalization](https://developers.google.com/machine-learning/data-prep/transform/normalization).
    //
    // For best results the recommended value is about 10% - 20% of the standard
    // deviation of the input feature. Refer to section 3.2 of the SmoothGrad
    // paper: https://arxiv.org/pdf/1706.03825.pdf. Defaults to 0.1.
    //
    // If the distribution is different per feature, set
    // [feature_noise_sigma][google.cloud.aiplatform.v1.SmoothGradConfig.feature_noise_sigma]
    // instead for each feature.
    float noise_sigma = 1;

    // This is similar to
    // [noise_sigma][google.cloud.aiplatform.v1.SmoothGradConfig.noise_sigma],
    // but provides additional flexibility. A separate noise sigma can be
    // provided for each feature, which is useful if their distributions are
    // different. No noise is added to features that are not set. If this field
    // is unset,
    // [noise_sigma][google.cloud.aiplatform.v1.SmoothGradConfig.noise_sigma]
    // will be used for all features.
    FeatureNoiseSigma feature_noise_sigma = 2;
  }

  // The number of gradient samples to use for
  // approximation. The higher this number, the more accurate the gradient
  // is, but the runtime complexity increases by this factor as well.
  // Valid range of its value is [1, 50]. Defaults to 3.
  int32 noisy_sample_count = 3;
}

// Noise sigma by features. Noise sigma represents the standard deviation of the
// gaussian kernel that will be used to add noise to interpolated inputs prior
// to computing gradients.
message FeatureNoiseSigma {
  // Noise sigma for a single feature.
  message NoiseSigmaForFeature {
    // The name of the input feature for which noise sigma is provided. The
    // features are defined in
    // [explanation metadata
    // inputs][google.cloud.aiplatform.v1.ExplanationMetadata.inputs].
    string name = 1;

    // This represents the standard deviation of the Gaussian kernel that will
    // be used to add noise to the feature prior to computing gradients. Similar
    // to [noise_sigma][google.cloud.aiplatform.v1.SmoothGradConfig.noise_sigma]
    // but represents the noise added to the current feature. Defaults to 0.1.
    float sigma = 2;
  }

  // Noise sigma per feature. No noise is added to features that are not set.
  repeated NoiseSigmaForFeature noise_sigma = 1;
}

// Config for blur baseline.
//
// When enabled, a linear path from the maximally blurred image to the input
// image is created. Using a blurred baseline instead of zero (black image) is
// motivated by the BlurIG approach explained here:
// https://arxiv.org/abs/2004.03383
message BlurBaselineConfig {
  // The standard deviation of the blur kernel for the blurred baseline. The
  // same blurring parameter is used for both the height and the width
  // dimension. If not set, the method defaults to the zero (i.e. black for
  // images) baseline.
  float max_blur_sigma = 1;
}

// Example-based explainability that returns the nearest neighbors from the
// provided dataset.
message Examples {
  // The Cloud Storage input instances.
  message ExampleGcsSource {
    // The format of the input example instances.
    enum DataFormat {
      // Format unspecified, used when unset.
      DATA_FORMAT_UNSPECIFIED = 0;

      // Examples are stored in JSONL files.
      JSONL = 1;
    }

    // The format in which instances are given, if not specified, assume it's
    // JSONL format. Currently only JSONL format is supported.
    DataFormat data_format = 1;

    // The Cloud Storage location for the input instances.
    GcsSource gcs_source = 2;
  }

  oneof source {
    // The Cloud Storage input instances.
    ExampleGcsSource example_gcs_source = 5;
  }

  oneof config {
    // The full configuration for the generated index, the semantics are the
    // same as [metadata][google.cloud.aiplatform.v1.Index.metadata] and should
    // match
    // [NearestNeighborSearchConfig](https://cloud.google.com/vertex-ai/docs/explainable-ai/configuring-explanations-example-based#nearest-neighbor-search-config).
    google.protobuf.Value nearest_neighbor_search_config = 2;

    // Simplified preset configuration, which automatically sets configuration
    // values based on the desired query speed-precision trade-off and modality.
    Presets presets = 4;
  }

  // The number of neighbors to return when querying for examples.
  int32 neighbor_count = 3;
}

// Preset configuration for example-based explanations
message Presets {
  // Preset option controlling parameters for query speed-precision trade-off
  enum Query {
    // More precise neighbors as a trade-off against slower response.
    PRECISE = 0;

    // Faster response as a trade-off against less precise neighbors.
    FAST = 1;
  }

  // Preset option controlling parameters for different modalities
  enum Modality {
    // Should not be set. Added as a recommended best practice for enums
    MODALITY_UNSPECIFIED = 0;

    // IMAGE modality
    IMAGE = 1;

    // TEXT modality
    TEXT = 2;

    // TABULAR modality
    TABULAR = 3;
  }

  // Preset option controlling parameters for speed-precision trade-off when
  // querying for examples. If omitted, defaults to `PRECISE`.
  optional Query query = 1;

  // The modality of the uploaded model, which automatically configures the
  // distance measurement and feature normalization for the underlying example
  // index and queries. If your model does not precisely fit one of these types,
  // it is okay to choose the closest type.
  Modality modality = 2;
}

// The [ExplanationSpec][google.cloud.aiplatform.v1.ExplanationSpec] entries
// that can be overridden at [online
// explanation][google.cloud.aiplatform.v1.PredictionService.Explain] time.
message ExplanationSpecOverride {
  // The parameters to be overridden. Note that the
  // attribution method cannot be changed. If not specified,
  // no parameter is overridden.
  ExplanationParameters parameters = 1;

  // The metadata to be overridden. If not specified, no metadata is overridden.
  ExplanationMetadataOverride metadata = 2;

  // The example-based explanations parameter overrides.
  ExamplesOverride examples_override = 3;
}

// The [ExplanationMetadata][google.cloud.aiplatform.v1.ExplanationMetadata]
// entries that can be overridden at [online
// explanation][google.cloud.aiplatform.v1.PredictionService.Explain] time.
message ExplanationMetadataOverride {
  // The [input
  // metadata][google.cloud.aiplatform.v1.ExplanationMetadata.InputMetadata]
  // entries to be overridden.
  message InputMetadataOverride {
    // Baseline inputs for this feature.
    //
    // This overrides the `input_baseline` field of the
    // [ExplanationMetadata.InputMetadata][google.cloud.aiplatform.v1.ExplanationMetadata.InputMetadata]
    // object of the corresponding feature's input metadata. If it's not
    // specified, the original baselines are not overridden.
    repeated google.protobuf.Value input_baselines = 1;
  }

  // Required. Overrides the [input
  // metadata][google.cloud.aiplatform.v1.ExplanationMetadata.inputs] of the
  // features. The key is the name of the feature to be overridden. The keys
  // specified here must exist in the input metadata to be overridden. If a
  // feature is not specified here, the corresponding feature's input metadata
  // is not overridden.
  map<string, InputMetadataOverride> inputs = 1
      [(google.api.field_behavior) = REQUIRED];
}

// Overrides for example-based explanations.
message ExamplesOverride {
  // Data format enum.
  enum DataFormat {
    // Unspecified format. Must not be used.
    DATA_FORMAT_UNSPECIFIED = 0;

    // Provided data is a set of model inputs.
    INSTANCES = 1;

    // Provided data is a set of embeddings.
    EMBEDDINGS = 2;
  }

  // The number of neighbors to return.
  int32 neighbor_count = 1;

  // The number of neighbors to return that have the same crowding tag.
  int32 crowding_count = 2;

  // Restrict the resulting nearest neighbors to respect these constraints.
  repeated ExamplesRestrictionsNamespace restrictions = 3;

  // If true, return the embeddings instead of neighbors.
  bool return_embeddings = 4;

  // The format of the data being provided with each call.
  DataFormat data_format = 5;
}

// Restrictions namespace for example-based explanations overrides.
message ExamplesRestrictionsNamespace {
  // The namespace name.
  string namespace_name = 1;

  // The list of allowed tags.
  repeated string allow = 2;

  // The list of deny tags.
  repeated string deny = 3;
}
