Save the model in Core ML format.
        The exported model calculates the distance between a query image and
        each row of the model's stored data. It does not sort and retrieve
        the k nearest neighbors of the query image.

        See Also
        --------
        save

        Examples
        --------
        >>> # Train an image similarity model
        >>> model = turicreate.image_similarity.create(data)
        >>>
        >>> # Query the model for similar images
        >>> similar_images = model.query(data)
        +-------------+-----------------+---------------+------+
        | query_label | reference_label |    distance   | rank |
        +-------------+-----------------+---------------+------+
        |      0      |        0        |      0.0      |  1   |
        |      0      |        2        | 24.9664942809 |  2   |
        |      0      |        1        | 28.4416069428 |  3   |
        |      1      |        1        |      0.0      |  1   |
        |      1      |        2        | 21.8715131191 |  2   |
        |      1      |        0        | 28.4416069428 |  3   |
        |      2      |        2        |      0.0      |  1   |
        |      2      |        1        | 21.8715131191 |  2   |
        |      2      |        0        | 24.9664942809 |  3   |
        +-------------+-----------------+---------------+------+
        [9 rows x 4 columns]
        >>>
        >>> # Export the model to Core ML format
        >>> model.export_coreml('myModel.mlmodel')
        >>>
        >>> # Load the Core ML model
        >>> import coremltools
        >>> ml_model = coremltools.models.MLModel('myModel.mlmodel')
        >>>
        >>> # Prepare the first image of reference data for consumption
        >>> # by the Core ML model
        >>> import PIL
        >>> image = tc.image_analysis.resize(data['image'][0], *reversed(model.input_image_shape))
        >>> image = PIL.Image.fromarray(image.pixel_data)
        >>>
        >>> # Calculate distances using the Core ML model
        >>> ml_model.predict(data={'image': image})
        {'distance': array([ 0., 28.453125, 24.96875 ])}

def export_coreml(self, filename):
        """
        Save the model in Core ML format.
        The exported model calculates the distance between a query image and
        each row of the model's stored data. It does not sort and retrieve
        the k nearest neighbors of the query image.

        See Also
        --------
        save

        Examples
        --------
        >>> # Train an image similarity model
        >>> model = turicreate.image_similarity.create(data)
        >>>
        >>> # Query the model for similar images
        >>> similar_images = model.query(data)
        +-------------+-----------------+---------------+------+
        | query_label | reference_label |    distance   | rank |
        +-------------+-----------------+---------------+------+
        |      0      |        0        |      0.0      |  1   |
        |      0      |        2        | 24.9664942809 |  2   |
        |      0      |        1        | 28.4416069428 |  3   |
        |      1      |        1        |      0.0      |  1   |
        |      1      |        2        | 21.8715131191 |  2   |
        |      1      |        0        | 28.4416069428 |  3   |
        |      2      |        2        |      0.0      |  1   |
        |      2      |        1        | 21.8715131191 |  2   |
        |      2      |        0        | 24.9664942809 |  3   |
        +-------------+-----------------+---------------+------+
        [9 rows x 4 columns]
        >>>
        >>> # Export the model to Core ML format
        >>> model.export_coreml('myModel.mlmodel')
        >>>
        >>> # Load the Core ML model
        >>> import coremltools
        >>> ml_model = coremltools.models.MLModel('myModel.mlmodel')
        >>>
        >>> # Prepare the first image of reference data for consumption
        >>> # by the Core ML model
        >>> import PIL
        >>> image = tc.image_analysis.resize(data['image'][0], *reversed(model.input_image_shape))
        >>> image = PIL.Image.fromarray(image.pixel_data)
        >>>
        >>> # Calculate distances using the Core ML model
        >>> ml_model.predict(data={'image': image})
        {'distance': array([ 0., 28.453125, 24.96875 ])}
        """
        import numpy as _np
        import coremltools as _cmt
        from coremltools.models import datatypes as _datatypes, neural_network as _neural_network
        from .._mxnet._mxnet_to_coreml import _mxnet_converter
        from turicreate.toolkits import _coreml_utils

        # Get the reference data from the model
        proxy = self.similarity_model.__proxy__
        reference_data = _np.array(_tc.extensions._nearest_neighbors._nn_get_reference_data(proxy))
        num_examples, embedding_size = reference_data.shape

        output_name = 'distance'
        output_features = [(output_name, _datatypes.Array(num_examples))]

        if self.model != 'VisionFeaturePrint_Scene':
            # Convert the MxNet model to Core ML
            ptModel = _pre_trained_models.MODELS[self.model]()
            feature_extractor = _image_feature_extractor.MXFeatureExtractor(ptModel)

            input_name = feature_extractor.data_layer
            input_features = [(input_name, _datatypes.Array(*(self.input_image_shape)))]

            # Create a neural network
            builder = _neural_network.NeuralNetworkBuilder(
                input_features, output_features, mode=None)

            # Convert the feature extraction network
            mx_feature_extractor = feature_extractor._get_mx_module(
                feature_extractor.ptModel.mxmodel,
                feature_extractor.data_layer,
                feature_extractor.feature_layer,
                feature_extractor.context,
                self.input_image_shape
            )
            batch_input_shape = (1, ) + self.input_image_shape
            _mxnet_converter.convert(mx_feature_extractor, mode=None,
                                     input_shape=[(input_name, batch_input_shape)],
                                     builder=builder, verbose=False)
            feature_layer = feature_extractor.feature_layer

        else:     # self.model == VisionFeaturePrint_Scene
            # Create a pipleline that contains a VisionFeaturePrint followed by a
            # neural network.
            BGR_VALUE = _cmt.proto.FeatureTypes_pb2.ImageFeatureType.ColorSpace.Value('BGR')
            DOUBLE_ARRAY_VALUE = _cmt.proto.FeatureTypes_pb2.ArrayFeatureType.ArrayDataType.Value('DOUBLE')
            INPUT_IMAGE_SHAPE = 299

            top_spec = _cmt.proto.Model_pb2.Model()
            top_spec.specificationVersion = 3
            desc = top_spec.description

            input = desc.input.add()
            input.name = self.feature
            input.type.imageType.width = INPUT_IMAGE_SHAPE
            input.type.imageType.height = INPUT_IMAGE_SHAPE
            input.type.imageType.colorSpace = BGR_VALUE

            output = desc.output.add()
            output.name = output_name
            output.type.multiArrayType.shape.append(num_examples)
            output.type.multiArrayType.dataType = DOUBLE_ARRAY_VALUE

            # VisionFeaturePrint extractor
            pipeline = top_spec.pipeline
            scene_print = pipeline.models.add()
            scene_print.specificationVersion = 3
            scene_print.visionFeaturePrint.scene.version = 1

            input = scene_print.description.input.add()
            input.name = self.feature
            input.type.imageType.width = 299
            input.type.imageType.height = 299
            input.type.imageType.colorSpace = BGR_VALUE

            feature_layer = 'VisionFeaturePrint_Scene_output'
            output = scene_print.description.output.add()
            output.name = feature_layer
            output.type.multiArrayType.dataType = DOUBLE_ARRAY_VALUE
            output.type.multiArrayType.shape.append(2048)

            # Neural network builder
            input_features = [(feature_layer, _datatypes.Array(2048))]
            builder = _neural_network.NeuralNetworkBuilder(input_features, output_features)

        # To add the nearest neighbors model we add calculation of the euclidean 
        # distance between the newly extracted query features (denoted by the vector u)
        # and each extracted reference feature (denoted by the rows of matrix V).
        # Calculation of sqrt((v_i-u)^2) = sqrt(v_i^2 - 2v_i*u + u^2) ensues.
        V = reference_data
        v_squared = (V * V).sum(axis=1)
        builder.add_inner_product('v^2-2vu', W=-2 * V, b=v_squared, has_bias=True,
                                  input_channels=embedding_size, output_channels=num_examples,
                                  input_name=feature_layer, output_name='v^2-2vu')

        builder.add_unary('element_wise-u^2', mode='power', alpha=2,
                          input_name=feature_layer, output_name='element_wise-u^2')

        # Produce a vector of length num_examples with all values equal to u^2
        builder.add_inner_product('u^2', W=_np.ones((embedding_size, num_examples)),
                                  b=None, has_bias=False,
                                  input_channels=embedding_size, output_channels=num_examples,
                                  input_name='element_wise-u^2', output_name='u^2')

        builder.add_elementwise('v^2-2vu+u^2', mode='ADD',
                                input_names=['v^2-2vu', 'u^2'],
                                output_name='v^2-2vu+u^2')

        # v^2-2vu+u^2=(v-u)^2 is non-negative but some computations on GPU may result in
        # small negative values. Apply RELU so we don't take the square root of negative values.
        builder.add_activation('relu', non_linearity='RELU',
                               input_name='v^2-2vu+u^2', output_name='relu')
        builder.add_unary('sqrt', mode='sqrt', input_name='relu', output_name=output_name)

        # Finalize model
        if self.model != 'VisionFeaturePrint_Scene':
            _mxnet_converter._set_input_output_layers(builder, [input_name], [output_name])
            builder.set_input([input_name], [self.input_image_shape])
            builder.set_output([output_name], [(num_examples,)])
            _cmt.models.utils.rename_feature(builder.spec, input_name, self.feature)
            builder.set_pre_processing_parameters(image_input_names=self.feature)
            mlmodel = _cmt.models.MLModel(builder.spec)
        else:
            top_spec.pipeline.models.extend([builder.spec])
            mlmodel = _cmt.models.MLModel(top_spec)

        # Add metadata
        model_type = 'image similarity'
        mlmodel.short_description = _coreml_utils._mlmodel_short_description(model_type)
        mlmodel.input_description[self.feature] = u'Input image'
        mlmodel.output_description[output_name] = u'Distances between the input and reference images'

        _coreml_utils._set_model_metadata(mlmodel, self.__class__.__name__, {
            'model': self.model,
            'num_examples': str(self.num_examples)
        }, version=ImageSimilarityModel._PYTHON_IMAGE_SIMILARITY_VERSION)

        mlmodel.save(filename)