<commit_before>/*
 * Copyright 2011-2013 Blender Foundation
 *
 * 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.
 */

#include "render/colorspace.h"

#include "util/util_color.h"
#include "util/util_image.h"
#include "util/util_half.h"
#include "util/util_logging.h"
#include "util/util_math.h"
#include "util/util_thread.h"
#include "util/util_vector.h"

#ifdef WITH_OCIO
#  include <OpenColorIO/OpenColorIO.h>
namespace OCIO = OCIO_NAMESPACE;
#endif

CCL_NAMESPACE_BEGIN

/* Builtin colorspaces. */
ustring u_colorspace_auto;
ustring u_colorspace_raw("__builtin_raw");
ustring u_colorspace_srgb("__builtin_srgb");

/* Cached data. */
#ifdef WITH_OCIO
static thread_mutex cache_colorspaces_mutex;
static thread_mutex cache_processors_mutex;
static unordered_map<ustring, ustring, ustringHash> cached_colorspaces;
static unordered_map<ustring, OCIO::ConstProcessorRcPtr, ustringHash> cached_processors;
#endif

ColorSpaceProcessor *ColorSpaceManager::get_processor(ustring colorspace)
{
#ifdef WITH_OCIO
  /* Only use this for OpenColorIO color spaces, not the builtin ones. */
  assert(colorspace != u_colorspace_srgb && colorspace != u_colorspace_auto);

  if (colorspace == u_colorspace_raw) {
    return NULL;
  }

  OCIO::ConstConfigRcPtr config = OCIO::GetCurrentConfig();
  if (!config) {
    return NULL;
  }

  /* Cache processor until free_memory(), memory overhead is expected to be
   * small and the processor is likely to be reused. */
  thread_scoped_lock cache_processors_lock(cache_processors_mutex);
  if (cached_processors.find(colorspace) == cached_processors.end()) {
    try {
      cached_processors[colorspace] = config->getProcessor(colorspace.c_str(), "scene_linear");
    }
    catch (OCIO::Exception &exception) {
      cached_processors[colorspace] = OCIO::ConstProcessorRcPtr();
      VLOG(1) << "Colorspace " << colorspace.c_str()
              << " can't be converted to scene_linear: " << exception.what();
    }
  }

  const OCIO::Processor *processor = cached_processors[colorspace].get();
  return (ColorSpaceProcessor *)processor;
#else
  /* No OpenColorIO. */
  (void)colorspace;
  return NULL;
#endif
}

bool ColorSpaceManager::colorspace_is_data(ustring colorspace)
{
  if (colorspace == u_colorspace_auto || colorspace == u_colorspace_raw ||
      colorspace == u_colorspace_srgb) {
    return false;
  }

#ifdef WITH_OCIO
  OCIO::ConstConfigRcPtr config = OCIO::GetCurrentConfig();
  if (!config) {
    return false;
  }

  try {
    OCIO::ConstColorSpaceRcPtr space = config->getColorSpace(colorspace.c_str());
    return space && space->isData();
  }
  catch (OCIO::Exception &exception) {
    return false;
  }
#else
  return false;
#endif
}

ustring ColorSpaceManager::detect_known_colorspace(ustring colorspace,
                                                   const char *file_format,
                                                   bool is_float)
{
  if (colorspace == u_colorspace_auto) {
    /* Auto detect sRGB or raw if none specified. */
    if (is_float) {
      bool srgb = (colorspace == "sRGB" || colorspace == "GammaCorrected" ||
                   (colorspace.empty() &&
                    (strcmp(file_format, "png") == 0 || strcmp(file_format, "tiff") == 0 ||
                     strcmp(file_format, "dpx") == 0 || strcmp(file_format, "jpeg2000") == 0)));
      return srgb ? u_colorspace_srgb : u_colorspace_raw;
    }
    else {
      return u_colorspace_srgb;
    }
  }
  else if (colorspace == u_colorspace_srgb || colorspace == u_colorspace_raw) {
    /* Builtin colorspaces. */
    return colorspace;
  }
  else {
    /* Use OpenColorIO. */
#ifdef WITH_OCIO
    {
      thread_scoped_lock cache_lock(cache_colorspaces_mutex);
      /* Cached lookup. */
      if (cached_colorspaces.find(colorspace) != cached_colorspaces.end()) {
        return cached_colorspaces[colorspace];
      }
    }

    /* Detect if it matches a simple builtin colorspace. */
    bool is_scene_linear, is_srgb;
    is_builtin_colorspace(colorspace, is_scene_linear, is_srgb);

    thread_scoped_lock cache_lock(cache_colorspaces_mutex);
    if (is_scene_linear) {
      VLOG(1) << "Colorspace " << colorspace.string() << " is no-op";
      cached_colorspaces[colorspace] = u_colorspace_raw;
      return u_colorspace_raw;
    }
    else if (is_srgb) {
      VLOG(1) << "Colorspace " << colorspace.string() << " is sRGB";
      cached_colorspaces[colorspace] = u_colorspace_srgb;
      return u_colorspace_srgb;
    }

    /* Verify if we can convert from the requested color space. */
    if (!get_processor(colorspace)) {
      OCIO::ConstConfigRcPtr config = OCIO::GetCurrentConfig();
      if (!config || !config->getColorSpace(colorspace.c_str())) {
        VLOG(1) << "Colorspace " << colorspace.c_str() << " not found, using raw instead";
      }
      else {
        VLOG(1) << "Colorspace " << colorspace.c_str()
                << " can't be converted to scene_linear, using raw instead";
      }
      cached_colorspaces[colorspace] = u_colorspace_raw;
      return u_colorspace_raw;
    }

    /* Convert to/from colorspace with OpenColorIO. */
    VLOG(1) << "Colorspace " << colorspace.string() << " handled through OpenColorIO";
    cached_colorspaces[colorspace] = colorspace;
    return colorspace;
#else
    VLOG(1) << "Colorspace " << colorspace.c_str() << " not available, built without OpenColorIO";
    return u_colorspace_raw;
#endif
  }
}

void ColorSpaceManager::is_builtin_colorspace(ustring colorspace,
                                              bool &is_scene_linear,
                                              bool &is_srgb)
{
#ifdef WITH_OCIO
  const OCIO::Processor *processor = (const OCIO::Processor *)get_processor(colorspace);
  if (!processor) {
    is_scene_linear = false;
    is_srgb = false;
    return;
  }

  is_scene_linear = true;
  is_srgb = true;
  for (int i = 0; i < 256; i++) {
    float v = i / 255.0f;

    float cR[3] = {v, 0, 0};
    float cG[3] = {0, v, 0};
    float cB[3] = {0, 0, v};
    float cW[3] = {v, v, v};
    processor->applyRGB(cR);
    processor->applyRGB(cG);
    processor->applyRGB(cB);
    processor->applyRGB(cW);

    /* Make sure that there is no channel crosstalk. */
    if (fabsf(cR[1]) > 1e-5f || fabsf(cR[2]) > 1e-5f || fabsf(cG[0]) > 1e-5f ||
        fabsf(cG[2]) > 1e-5f || fabsf(cB[0]) > 1e-5f || fabsf(cB[1]) > 1e-5f) {
      is_scene_linear = false;
      is_srgb = false;
      break;
    }
    /* Make sure that the three primaries combine linearly. */
    if (!compare_floats(cR[0], cW[0], 1e-6f, 64) || !compare_floats(cG[1], cW[1], 1e-6f, 64) ||
        !compare_floats(cB[2], cW[2], 1e-6f, 64)) {
      is_scene_linear = false;
      is_srgb = false;
      break;
    }
    /* Make sure that the three channels behave identically. */
    if (!compare_floats(cW[0], cW[1], 1e-6f, 64) || !compare_floats(cW[1], cW[2], 1e-6f, 64)) {
      is_scene_linear = false;
      is_srgb = false;
      break;
    }

    float out_v = average(make_float3(cW[0], cW[1], cW[2]));
    if (!compare_floats(v, out_v, 1e-6f, 64)) {
      is_scene_linear = false;
    }
    if (!compare_floats(color_srgb_to_linear(v), out_v, 1e-6f, 64)) {
      is_srgb = false;
    }
  }
#else
  (void)colorspace;
  is_scene_linear = false;
  is_srgb = false;
#endif
}

#ifdef WITH_OCIO

template<typename T> inline float4 cast_to_float4(T *data)
{
  return make_float4(util_image_cast_to_float(data[0]),
                     util_image_cast_to_float(data[1]),
                     util_image_cast_to_float(data[2]),
                     util_image_cast_to_float(data[3]));
}

template<typename T> inline void cast_from_float4(T *data, float4 value)
{
  data[0] = util_image_cast_from_float<T>(value.x);
  data[1] = util_image_cast_from_float<T>(value.y);
  data[2] = util_image_cast_from_float<T>(value.z);
  data[3] = util_image_cast_from_float<T>(value.w);
}

/* Slower versions for other all data types, which needs to convert to float and back. */
template<typename T, bool compress_as_srgb = false>
inline void processor_apply_pixels(const OCIO::Processor *processor,
                                   T *pixels,
                                   size_t width,
                                   size_t height)
{
  /* TODO: implement faster version for when we know the conversion
   * is a simple matrix transform between linear spaces. In that case
   * unpremultiply is not needed. */

  /* Process large images in chunks to keep temporary memory requirement down. */
  size_t y_chunk_size = max(1, 16 * 1024 * 1024 / (sizeof(float4) * width));
  vector<float4> float_pixels(y_chunk_size * width);

  for (size_t y0 = 0; y0 < height; y0 += y_chunk_size) {
    size_t y1 = std::min(y0 + y_chunk_size, height);
    size_t i = 0;

    for (size_t y = y0; y < y1; y++) {
      for (size_t x = 0; x < width; x++, i++) {
        float4 value = cast_to_float4(pixels + 4 * (y * width + x));

        if (!(value.w == 0.0f || value.w == 1.0f)) {
          float inv_alpha = 1.0f / value.w;
          value.x *= inv_alpha;
          value.y *= inv_alpha;
          value.z *= inv_alpha;
        }

        float_pixels[i] = value;
      }
    }

    OCIO::PackedImageDesc desc((float *)float_pixels.data(), width, y_chunk_size, 4);
    processor->apply(desc);

    i = 0;
    for (size_t y = y0; y < y1; y++) {
      for (size_t x = 0; x < width; x++, i++) {
        float4 value = float_pixels[i];

        value.x *= value.w;
        value.y *= value.w;
        value.z *= value.w;

        if (compress_as_srgb) {
          value = color_linear_to_srgb_v4(value);
        }

        cast_from_float4(pixels + 4 * (y * width + x), value);
      }
    }
  }
}
#endif

template<typename T>
void ColorSpaceManager::to_scene_linear(ustring colorspace,
                                        T *pixels,
                                        size_t width,
                                        size_t height,
                                        size_t depth,
                                        bool compress_as_srgb)
{
#ifdef WITH_OCIO
  const OCIO::Processor *processor = (const OCIO::Processor *)get_processor(colorspace);

  if (processor) {
    if (compress_as_srgb) {
      /* Compress output as sRGB. */
      for (size_t z = 0; z < depth; z++) {
        processor_apply_pixels<T, true>(processor, &pixels[z * width * height], width, height);
      }
    }
    else {
      /* Write output as scene linear directly. */
      for (size_t z = 0; z < depth; z++) {
        processor_apply_pixels<T>(processor, &pixels[z * width * height], width, height);
      }
    }
  }
#else
  (void)colorspace;
  (void)pixels;
  (void)width;
  (void)height;
  (void)depth;
  (void)compress_as_srgb;
#endif
}

void ColorSpaceManager::to_scene_linear(ColorSpaceProcessor *processor_,
                                        float *pixel,
                                        int channels)
{
#ifdef WITH_OCIO
  const OCIO::Processor *processor = (const OCIO::Processor *)processor_;

  if (processor) {
    if (channels == 3) {
      processor->applyRGB(pixel);
    }
    else if (channels == 4) {
      if (pixel[3] == 1.0f || pixel[3] == 0.0f) {
        /* Fast path for RGBA. */
        processor->applyRGB(pixel);
      }
      else {
        /* Unassociate and associate alpha since color management should not
         * be affected by transparency. */
        float alpha = pixel[3];
        float inv_alpha = 1.0f / alpha;

        pixel[0] *= inv_alpha;
        pixel[1] *= inv_alpha;
        pixel[2] *= inv_alpha;

        processor->applyRGB(pixel);

        pixel[0] *= alpha;
        pixel[1] *= alpha;
        pixel[2] *= alpha;
      }
    }
  }
#else
  (void)processor_;
  (void)pixel;
  (void)channels;
#endif
}

void ColorSpaceManager::free_memory()
{
#ifdef WITH_OCIO
  map_free_memory(cached_colorspaces);
  map_free_memory(cached_colorspaces);
#endif
}

/* Template instanstations so we don't have to inline functions. */
template void ColorSpaceManager::to_scene_linear(ustring, uchar *, size_t, size_t, size_t, bool);
template void ColorSpaceManager::to_scene_linear(ustring, ushort *, size_t, size_t, size_t, bool);
template void ColorSpaceManager::to_scene_linear(ustring, half *, size_t, size_t, size_t, bool);
template void ColorSpaceManager::to_scene_linear(ustring, float *, size_t, size_t, size_t, bool);

CCL_NAMESPACE_END
<commit_msg>cycles_render: Fix unused variable build warning with MSVC<commit_after>/*
 * Copyright 2011-2013 Blender Foundation
 *
 * 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.
 */

#include "render/colorspace.h"

#include "util/util_color.h"
#include "util/util_image.h"
#include "util/util_half.h"
#include "util/util_logging.h"
#include "util/util_math.h"
#include "util/util_thread.h"
#include "util/util_vector.h"

#ifdef WITH_OCIO
#  include <OpenColorIO/OpenColorIO.h>
namespace OCIO = OCIO_NAMESPACE;
#endif

CCL_NAMESPACE_BEGIN

/* Builtin colorspaces. */
ustring u_colorspace_auto;
ustring u_colorspace_raw("__builtin_raw");
ustring u_colorspace_srgb("__builtin_srgb");

/* Cached data. */
#ifdef WITH_OCIO
static thread_mutex cache_colorspaces_mutex;
static thread_mutex cache_processors_mutex;
static unordered_map<ustring, ustring, ustringHash> cached_colorspaces;
static unordered_map<ustring, OCIO::ConstProcessorRcPtr, ustringHash> cached_processors;
#endif

ColorSpaceProcessor *ColorSpaceManager::get_processor(ustring colorspace)
{
#ifdef WITH_OCIO
  /* Only use this for OpenColorIO color spaces, not the builtin ones. */
  assert(colorspace != u_colorspace_srgb && colorspace != u_colorspace_auto);

  if (colorspace == u_colorspace_raw) {
    return NULL;
  }

  OCIO::ConstConfigRcPtr config = OCIO::GetCurrentConfig();
  if (!config) {
    return NULL;
  }

  /* Cache processor until free_memory(), memory overhead is expected to be
   * small and the processor is likely to be reused. */
  thread_scoped_lock cache_processors_lock(cache_processors_mutex);
  if (cached_processors.find(colorspace) == cached_processors.end()) {
    try {
      cached_processors[colorspace] = config->getProcessor(colorspace.c_str(), "scene_linear");
    }
    catch (OCIO::Exception &exception) {
      cached_processors[colorspace] = OCIO::ConstProcessorRcPtr();
      VLOG(1) << "Colorspace " << colorspace.c_str()
              << " can't be converted to scene_linear: " << exception.what();
    }
  }

  const OCIO::Processor *processor = cached_processors[colorspace].get();
  return (ColorSpaceProcessor *)processor;
#else
  /* No OpenColorIO. */
  (void)colorspace;
  return NULL;
#endif
}

bool ColorSpaceManager::colorspace_is_data(ustring colorspace)
{
  if (colorspace == u_colorspace_auto || colorspace == u_colorspace_raw ||
      colorspace == u_colorspace_srgb) {
    return false;
  }

#ifdef WITH_OCIO
  OCIO::ConstConfigRcPtr config = OCIO::GetCurrentConfig();
  if (!config) {
    return false;
  }

  try {
    OCIO::ConstColorSpaceRcPtr space = config->getColorSpace(colorspace.c_str());
    return space && space->isData();
  }
  catch (OCIO::Exception &) {
    return false;
  }
#else
  return false;
#endif
}

ustring ColorSpaceManager::detect_known_colorspace(ustring colorspace,
                                                   const char *file_format,
                                                   bool is_float)
{
  if (colorspace == u_colorspace_auto) {
    /* Auto detect sRGB or raw if none specified. */
    if (is_float) {
      bool srgb = (colorspace == "sRGB" || colorspace == "GammaCorrected" ||
                   (colorspace.empty() &&
                    (strcmp(file_format, "png") == 0 || strcmp(file_format, "tiff") == 0 ||
                     strcmp(file_format, "dpx") == 0 || strcmp(file_format, "jpeg2000") == 0)));
      return srgb ? u_colorspace_srgb : u_colorspace_raw;
    }
    else {
      return u_colorspace_srgb;
    }
  }
  else if (colorspace == u_colorspace_srgb || colorspace == u_colorspace_raw) {
    /* Builtin colorspaces. */
    return colorspace;
  }
  else {
    /* Use OpenColorIO. */
#ifdef WITH_OCIO
    {
      thread_scoped_lock cache_lock(cache_colorspaces_mutex);
      /* Cached lookup. */
      if (cached_colorspaces.find(colorspace) != cached_colorspaces.end()) {
        return cached_colorspaces[colorspace];
      }
    }

    /* Detect if it matches a simple builtin colorspace. */
    bool is_scene_linear, is_srgb;
    is_builtin_colorspace(colorspace, is_scene_linear, is_srgb);

    thread_scoped_lock cache_lock(cache_colorspaces_mutex);
    if (is_scene_linear) {
      VLOG(1) << "Colorspace " << colorspace.string() << " is no-op";
      cached_colorspaces[colorspace] = u_colorspace_raw;
      return u_colorspace_raw;
    }
    else if (is_srgb) {
      VLOG(1) << "Colorspace " << colorspace.string() << " is sRGB";
      cached_colorspaces[colorspace] = u_colorspace_srgb;
      return u_colorspace_srgb;
    }

    /* Verify if we can convert from the requested color space. */
    if (!get_processor(colorspace)) {
      OCIO::ConstConfigRcPtr config = OCIO::GetCurrentConfig();
      if (!config || !config->getColorSpace(colorspace.c_str())) {
        VLOG(1) << "Colorspace " << colorspace.c_str() << " not found, using raw instead";
      }
      else {
        VLOG(1) << "Colorspace " << colorspace.c_str()
                << " can't be converted to scene_linear, using raw instead";
      }
      cached_colorspaces[colorspace] = u_colorspace_raw;
      return u_colorspace_raw;
    }

    /* Convert to/from colorspace with OpenColorIO. */
    VLOG(1) << "Colorspace " << colorspace.string() << " handled through OpenColorIO";
    cached_colorspaces[colorspace] = colorspace;
    return colorspace;
#else
    VLOG(1) << "Colorspace " << colorspace.c_str() << " not available, built without OpenColorIO";
    return u_colorspace_raw;
#endif
  }
}

void ColorSpaceManager::is_builtin_colorspace(ustring colorspace,
                                              bool &is_scene_linear,
                                              bool &is_srgb)
{
#ifdef WITH_OCIO
  const OCIO::Processor *processor = (const OCIO::Processor *)get_processor(colorspace);
  if (!processor) {
    is_scene_linear = false;
    is_srgb = false;
    return;
  }

  is_scene_linear = true;
  is_srgb = true;
  for (int i = 0; i < 256; i++) {
    float v = i / 255.0f;

    float cR[3] = {v, 0, 0};
    float cG[3] = {0, v, 0};
    float cB[3] = {0, 0, v};
    float cW[3] = {v, v, v};
    processor->applyRGB(cR);
    processor->applyRGB(cG);
    processor->applyRGB(cB);
    processor->applyRGB(cW);

    /* Make sure that there is no channel crosstalk. */
    if (fabsf(cR[1]) > 1e-5f || fabsf(cR[2]) > 1e-5f || fabsf(cG[0]) > 1e-5f ||
        fabsf(cG[2]) > 1e-5f || fabsf(cB[0]) > 1e-5f || fabsf(cB[1]) > 1e-5f) {
      is_scene_linear = false;
      is_srgb = false;
      break;
    }
    /* Make sure that the three primaries combine linearly. */
    if (!compare_floats(cR[0], cW[0], 1e-6f, 64) || !compare_floats(cG[1], cW[1], 1e-6f, 64) ||
        !compare_floats(cB[2], cW[2], 1e-6f, 64)) {
      is_scene_linear = false;
      is_srgb = false;
      break;
    }
    /* Make sure that the three channels behave identically. */
    if (!compare_floats(cW[0], cW[1], 1e-6f, 64) || !compare_floats(cW[1], cW[2], 1e-6f, 64)) {
      is_scene_linear = false;
      is_srgb = false;
      break;
    }

    float out_v = average(make_float3(cW[0], cW[1], cW[2]));
    if (!compare_floats(v, out_v, 1e-6f, 64)) {
      is_scene_linear = false;
    }
    if (!compare_floats(color_srgb_to_linear(v), out_v, 1e-6f, 64)) {
      is_srgb = false;
    }
  }
#else
  (void)colorspace;
  is_scene_linear = false;
  is_srgb = false;
#endif
}

#ifdef WITH_OCIO

template<typename T> inline float4 cast_to_float4(T *data)
{
  return make_float4(util_image_cast_to_float(data[0]),
                     util_image_cast_to_float(data[1]),
                     util_image_cast_to_float(data[2]),
                     util_image_cast_to_float(data[3]));
}

template<typename T> inline void cast_from_float4(T *data, float4 value)
{
  data[0] = util_image_cast_from_float<T>(value.x);
  data[1] = util_image_cast_from_float<T>(value.y);
  data[2] = util_image_cast_from_float<T>(value.z);
  data[3] = util_image_cast_from_float<T>(value.w);
}

/* Slower versions for other all data types, which needs to convert to float and back. */
template<typename T, bool compress_as_srgb = false>
inline void processor_apply_pixels(const OCIO::Processor *processor,
                                   T *pixels,
                                   size_t width,
                                   size_t height)
{
  /* TODO: implement faster version for when we know the conversion
   * is a simple matrix transform between linear spaces. In that case
   * unpremultiply is not needed. */

  /* Process large images in chunks to keep temporary memory requirement down. */
  size_t y_chunk_size = max(1, 16 * 1024 * 1024 / (sizeof(float4) * width));
  vector<float4> float_pixels(y_chunk_size * width);

  for (size_t y0 = 0; y0 < height; y0 += y_chunk_size) {
    size_t y1 = std::min(y0 + y_chunk_size, height);
    size_t i = 0;

    for (size_t y = y0; y < y1; y++) {
      for (size_t x = 0; x < width; x++, i++) {
        float4 value = cast_to_float4(pixels + 4 * (y * width + x));

        if (!(value.w == 0.0f || value.w == 1.0f)) {
          float inv_alpha = 1.0f / value.w;
          value.x *= inv_alpha;
          value.y *= inv_alpha;
          value.z *= inv_alpha;
        }

        float_pixels[i] = value;
      }
    }

    OCIO::PackedImageDesc desc((float *)float_pixels.data(), width, y_chunk_size, 4);
    processor->apply(desc);

    i = 0;
    for (size_t y = y0; y < y1; y++) {
      for (size_t x = 0; x < width; x++, i++) {
        float4 value = float_pixels[i];

        value.x *= value.w;
        value.y *= value.w;
        value.z *= value.w;

        if (compress_as_srgb) {
          value = color_linear_to_srgb_v4(value);
        }

        cast_from_float4(pixels + 4 * (y * width + x), value);
      }
    }
  }
}
#endif

template<typename T>
void ColorSpaceManager::to_scene_linear(ustring colorspace,
                                        T *pixels,
                                        size_t width,
                                        size_t height,
                                        size_t depth,
                                        bool compress_as_srgb)
{
#ifdef WITH_OCIO
  const OCIO::Processor *processor = (const OCIO::Processor *)get_processor(colorspace);

  if (processor) {
    if (compress_as_srgb) {
      /* Compress output as sRGB. */
      for (size_t z = 0; z < depth; z++) {
        processor_apply_pixels<T, true>(processor, &pixels[z * width * height], width, height);
      }
    }
    else {
      /* Write output as scene linear directly. */
      for (size_t z = 0; z < depth; z++) {
        processor_apply_pixels<T>(processor, &pixels[z * width * height], width, height);
      }
    }
  }
#else
  (void)colorspace;
  (void)pixels;
  (void)width;
  (void)height;
  (void)depth;
  (void)compress_as_srgb;
#endif
}

void ColorSpaceManager::to_scene_linear(ColorSpaceProcessor *processor_,
                                        float *pixel,
                                        int channels)
{
#ifdef WITH_OCIO
  const OCIO::Processor *processor = (const OCIO::Processor *)processor_;

  if (processor) {
    if (channels == 3) {
      processor->applyRGB(pixel);
    }
    else if (channels == 4) {
      if (pixel[3] == 1.0f || pixel[3] == 0.0f) {
        /* Fast path for RGBA. */
        processor->applyRGB(pixel);
      }
      else {
        /* Unassociate and associate alpha since color management should not
         * be affected by transparency. */
        float alpha = pixel[3];
        float inv_alpha = 1.0f / alpha;

        pixel[0] *= inv_alpha;
        pixel[1] *= inv_alpha;
        pixel[2] *= inv_alpha;

        processor->applyRGB(pixel);

        pixel[0] *= alpha;
        pixel[1] *= alpha;
        pixel[2] *= alpha;
      }
    }
  }
#else
  (void)processor_;
  (void)pixel;
  (void)channels;
#endif
}

void ColorSpaceManager::free_memory()
{
#ifdef WITH_OCIO
  map_free_memory(cached_colorspaces);
  map_free_memory(cached_colorspaces);
#endif
}

/* Template instanstations so we don't have to inline functions. */
template void ColorSpaceManager::to_scene_linear(ustring, uchar *, size_t, size_t, size_t, bool);
template void ColorSpaceManager::to_scene_linear(ustring, ushort *, size_t, size_t, size_t, bool);
template void ColorSpaceManager::to_scene_linear(ustring, half *, size_t, size_t, size_t, bool);
template void ColorSpaceManager::to_scene_linear(ustring, float *, size_t, size_t, size_t, bool);

CCL_NAMESPACE_END
<|endoftext|>


<commit_before>/*
 * Copyright 2015-2016 Joshua Nathaniel Pritikin and contributors
 *
 * 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.
 */

// Named in honor of Fellner (1987) "Sparse matrices, and the
// estimation of variance components by likelihood methods"
// Fellner was probably the first to apply sparse matrix algorithms
// to this kind of problem.

#include "glue.h"
#include <iterator>
#include <exception>
#include <stdexcept>
#include <Rconfig.h>
#include <Rmath.h>
#include "omxFitFunction.h"
#include "RAMInternal.h"
#include <Eigen/Cholesky>

namespace FellnerFitFunction {
	struct state {
		int verbose;
		int numProfiledOut;
		std::vector<int> olsVarNum;     // index into fc->est
		Eigen::MatrixXd olsDesign;      // a.k.a "X"

		int computeCov(RelationalRAMExpectation::independentGroup &ig);
		void compute(omxFitFunction *oo, int want, FitContext *fc);
		void setupProfiledParam(omxFitFunction *oo, FitContext *fc);
	};

	static void compute(omxFitFunction *oo, int want, FitContext *fc)
	{
		state *st = (state *) oo->argStruct;
		st->compute(oo, want, fc);
	}

	void state::setupProfiledParam(omxFitFunction *oo, FitContext *fc)
	{
		omxExpectation *expectation             = oo->expectation;
		omxRAMExpectation *ram = (omxRAMExpectation*) expectation->argStruct;

		if (numProfiledOut) ram->forceSingleGroup = true;
		omxExpectationCompute(fc, expectation, "nothing", "flat");

		if (numProfiledOut == 0) return;

		RelationalRAMExpectation::state &rram = ram->rram->getParent();
		if (rram.group.size() > 1) {
			Rf_error("Cannot profile out parameters when problem is split into independent groups");
		}

		RelationalRAMExpectation::independentGroup &ig = *rram.group[0];
		fc->profiledOut.assign(fc->numParam, false);

		olsVarNum.reserve(numProfiledOut);
		olsDesign.resize(ig.getParent().dataVec.size(), numProfiledOut);

		ProtectedSEXP Rprofile(R_do_slot(oo->rObj, Rf_install("profileOut")));
		for (int px=0; px < numProfiledOut; ++px) {
			const char *pname = CHAR(STRING_ELT(Rprofile, px));
			int vx = fc->varGroup->lookupVar(pname);
			if (vx < 0) {
				mxLog("Parameter [%s] not found", pname);
				continue;
			}

			omxFreeVar &fv = *fc->varGroup->vars[vx];
			olsVarNum.push_back(vx);
			bool found = false;
			bool moreThanOne;
			const omxFreeVarLocation *loc =
				fv.getOnlyOneLocation(ram->M, moreThanOne);
			if (loc) {
				if (moreThanOne) {
					mxLog("Parameter [%s] appears in more than one spot in %s",
					      pname, ram->M->name());
					continue;
				}
				found = true;
				int vnum = loc->row + loc->col;
				// Should ensure the loading is fixed and not a defvar TODO
				// Should ensure zero variance & no cross-level links TODO
				olsDesign.col(px) = (ig.dataColumn.array() == vnum).cast<double>();
			}
			loc = fv.getOnlyOneLocation(ram->A, moreThanOne);
			if (loc) {
				if (moreThanOne) {
					mxLog("Parameter [%s] appears in more than one spot in %s",
					      pname, ram->A->name());
					continue;
				}
				found = true;
				int vnum = loc->col;
				EigenMatrixAdaptor eA(ram->A);
				int rnum;
				eA.col(vnum).array().abs().maxCoeff(&rnum);
				// ensure only 1 nonzero in column TODO
				for (size_t ax=0; ax < ig.placements.size(); ++ax) {
					RelationalRAMExpectation::placement &pl = ig.placements[ax];
					RelationalRAMExpectation::addr &a1 = rram.layout[ ig.gMap[ax] ];
					if (a1.getExpNum() != expectation->expNum) continue;
					expectation->loadDefVars(a1.row);
					double weight = omxVectorElement(ram->M, vnum);
					olsDesign.col(px).segment(pl.obsStart, a1.numObs()) =
						weight * (ig.dataColumn.segment(pl.obsStart, a1.numObs()) == rnum).cast<double>();
				}
			}
			if (!found) Rf_error("oops");

			fc->profiledOut[vx] = true;
		}
	}

	int state::computeCov(RelationalRAMExpectation::independentGroup &ig)
	{
		if (0 == ig.getParent().dataVec.size()) return 0;

		ig.computeCov2();

		/*
		if (!ig.analyzedCov) {
			ig.fullCov.makeCompressed();
			ig.covDecomp.analyzePattern(ig.fullCov);
			ig.analyzedCov = true;
		}
		ig.covDecomp.factorize(ig.fullCov);
		*/

		Eigen::MatrixXd denseCov = ig.fullCov;
		ig.covDecomp.compute(denseCov);

		if (ig.covDecomp.info() != Eigen::Success || !(ig.covDecomp.vectorD().array() > 0.0).all()) return 1;

		ig.covDecomp.refreshInverse();
		return 0;
	}

	void state::compute(omxFitFunction *oo, int want, FitContext *fc)
	{
		omxExpectation *expectation             = oo->expectation;
		omxRAMExpectation *ram = (omxRAMExpectation*) expectation->argStruct;

		if (want & (FF_COMPUTE_INITIAL_FIT | FF_COMPUTE_PREOPTIMIZE)) {
			if (fc->isClone()) return;

			setupProfiledParam(oo, fc);

			RelationalRAMExpectation::state *rram   = ram->rram;
			if (verbose >= 1) {
				mxLog("%s: %d groups", oo->name(),
				      int(rram->group.size()));
			}
			return;
		}

		if (!(want & (FF_COMPUTE_FIT))) Rf_error("Not implemented");

		double lpOut = NA_REAL;
		try {
			if (!ram->rram) {
				// it is possible to skip FF_COMPUTE_PREOPTIMIZE (e.g. omxRecompute)
				omxExpectationCompute(fc, expectation, "nothing", "flat");
			}

			RelationalRAMExpectation::state &rram   = *ram->rram;
			double lp = 0.0;
			for (size_t gx=0; gx < rram.group.size(); ++gx) {
				rram.group[gx]->computeCov1(fc);
			}

			int covFailed = 0;
			for (size_t gx=0; gx < rram.group.size(); ++gx) {
				covFailed += computeCov(*rram.group[gx]);
			}
			if (covFailed) {
				throw std::runtime_error("Cholesky decomposition failed");
			}

			state *parent = this; // better to cache it TODO
			if (fc->isClone()) {
				omxMatrix *pfitMat = fc->getParentState()->getMatrixFromIndex(oo->matrix);
				parent = (state*) pfitMat->fitFunction->argStruct;
			}

			double remlAdj = 0.0;
			if (parent->numProfiledOut) {
				RelationalRAMExpectation::independentGroup &ig = *rram.group[0];
				const Eigen::MatrixXd &iV = ig.covDecomp.getInverse();
				Eigen::MatrixXd constCov =
					parent->olsDesign.transpose() * iV.selfadjointView<Eigen::Lower>() * parent->olsDesign;
				Eigen::LLT< Eigen::MatrixXd > cholConstCov;
				cholConstCov.compute(constCov);
				if(cholConstCov.info() != Eigen::Success){
					// ought to report error detail TODO
					throw std::exception();
				}
				remlAdj = 2*Eigen::MatrixXd(cholConstCov.matrixL()).diagonal().array().log().sum();

				Eigen::MatrixXd ident =
					Eigen::MatrixXd::Identity(parent->numProfiledOut, parent->numProfiledOut);
				Eigen::MatrixXd cholConstPrec = cholConstCov.solve(ident).triangularView<Eigen::Lower>();
				Eigen::VectorXd param =
					(cholConstPrec.selfadjointView<Eigen::Lower>() *
					 parent->olsDesign.transpose() * iV.selfadjointView<Eigen::Lower>() *
					 ig.getParent().dataVec);

				for (int px=0; px < parent->numProfiledOut; ++px) {
					fc->est[ parent->olsVarNum[px] ] = param[px];
					fc->varGroup->vars[ parent->olsVarNum[px] ]->copyToState(ram->M->currentState, param[px]);
				}
				lp += remlAdj - M_LN_2PI * parent->numProfiledOut;
			}

			omxExpectationCompute(fc, expectation, "mean", "flat");

			for (size_t gx=0; gx < rram.group.size(); ++gx) {
				RelationalRAMExpectation::independentGroup &ig = *rram.group[gx];
				if (0 == ig.getParent().dataVec.size()) continue;

				//mxPrintMat("dataVec", ig.dataVec);
				//mxPrintMat("fullMeans", ig.fullMeans);
				//ig.applyRotationPlan(ig.expectedVec);
				//mxPrintMat("expectedVec", ig.expectedVec);

				const Eigen::MatrixXd &iV = ig.covDecomp.getInverse();
				//mxPrintMat("iV", iV);
				int clumps = ig.numLooseClumps();
				if (clumps) {
					int residLen = clumps * ig.clumpObs;
					Eigen::VectorXd resid =
						ig.getParent().dataVec.segment(0,residLen) - ig.expectedVec.segment(0,residLen);
					//mxPrintMat("resid", resid);

					double logDet = clumps * ig.covDecomp.log_determinant();
				// Eigen::Map< Eigen::MatrixXd > iV(ig.covDecomp.getInverseData(),
				// 				 ig.fullCov.rows(), ig.fullCov.rows());
					double iqf = 0.0;
					for (int cx=0; cx < clumps; ++cx) {
						iqf += (resid.segment(cx*ig.clumpObs, ig.clumpObs).transpose() *
							iV.selfadjointView<Eigen::Lower>() *
							resid.segment(cx*ig.clumpObs, ig.clumpObs));
					}
					double cterm = M_LN_2PI * ig.getParent().dataVec.size();
					if (verbose >= 2) mxLog("log det %f iqf %f cterm %f", logDet, iqf, cterm);
					lp += logDet + iqf + cterm;
				}
				for (int sx=0; sx < (int)ig.getParent().sufficientSets.size(); ++sx) {
					RelationalRAMExpectation::sufficientSet &ss = ig.getParent().sufficientSets[sx];
					Eigen::VectorXd resid =
						ss.dataMean - ig.expectedVec.segment(ss.start * ig.clumpObs, ig.clumpObs);
					//mxPrintMat("resid", resid);
					double iqf = resid.transpose() * iV.selfadjointView<Eigen::Lower>() * resid;
					double tr1 = (iV.selfadjointView<Eigen::Lower>() * ss.dataCov).trace();
					double logDet = ig.covDecomp.log_determinant();
					double cterm = M_LN_2PI * ig.clumpObs;
					if (verbose >= 2) mxLog("iqf %f tr1 %f logDet %f cterm %f", iqf, tr1, logDet, cterm);
					lp += ss.length * (iqf + logDet + cterm) + (ss.length-1) * tr1;
				}
			}
			lpOut = lp;
		} catch (const std::exception& e) {
			if (fc) fc->recordIterationError("%s: %s", oo->name(), e.what());
		}
		oo->matrix->data[0] = lpOut;
	}

	static void popAttr(omxFitFunction *oo, SEXP algebra)
	{
		// use Eigen_cholmod_wrap to return a sparse matrix? TODO
		// always return it?

		/*
		state *st                               = (state *) oo->argStruct;
		SEXP expCovExt, expMeanExt;
		if (st->fullCov.rows() > 0) {
			Rf_protect(expCovExt = Rf_allocMatrix(REALSXP, expCovInt->rows, expCovInt->cols));
			memcpy(REAL(expCovExt), expCovInt->data, sizeof(double) * expCovInt->rows * expCovInt->cols);
			Rf_setAttrib(algebra, Rf_install("expCov"), expCovExt);
		}

		if (expMeanInt && expMeanInt->rows > 0) {
			Rf_protect(expMeanExt = Rf_allocMatrix(REALSXP, expMeanInt->rows, expMeanInt->cols));
			memcpy(REAL(expMeanExt), expMeanInt->data, sizeof(double) * expMeanInt->rows * expMeanInt->cols);
			Rf_setAttrib(algebra, Rf_install("expMean"), expMeanExt);
			}   */
	}

	static void destroy(omxFitFunction *oo)
	{
		state *st = (state*) oo->argStruct;
		delete st;
	}

	static void init(omxFitFunction *oo)
	{
		omxExpectation* expectation = oo->expectation;
		if(expectation == NULL) {
			omxRaiseErrorf("%s cannot fit without a model expectation", oo->fitType);
			return;
		}
		if (!strEQ(expectation->expType, "MxExpectationRAM")) {
			Rf_error("%s: only MxExpectationRAM is implemented", oo->matrix->name());
		}

		oo->computeFun = FellnerFitFunction::compute;
		oo->destructFun = FellnerFitFunction::destroy;
		oo->populateAttrFun = FellnerFitFunction::popAttr;
		FellnerFitFunction::state *st = new FellnerFitFunction::state;
		oo->argStruct = st;
		oo->canDuplicate = true;

		ProtectedSEXP Rprofile(R_do_slot(oo->rObj, Rf_install("profileOut")));
		st->numProfiledOut = Rf_length(Rprofile);

		{
			SEXP tmp;
			ScopedProtect p1(tmp, R_do_slot(oo->rObj, Rf_install("verbose")));
			st->verbose = Rf_asInteger(tmp) + OMX_DEBUG;
		}
	}
};

void InitFellnerFitFunction(omxFitFunction *oo)
{
	FellnerFitFunction::init(oo);
}
<commit_msg>Switch to trace_prod<commit_after>/*
 * Copyright 2015-2016 Joshua Nathaniel Pritikin and contributors
 *
 * 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.
 */

// Named in honor of Fellner (1987) "Sparse matrices, and the
// estimation of variance components by likelihood methods"
// Fellner was probably the first to apply sparse matrix algorithms
// to this kind of problem.

#include "glue.h"
#include <iterator>
#include <exception>
#include <stdexcept>
#include <Rconfig.h>
#include <Rmath.h>
#include "omxFitFunction.h"
#include "RAMInternal.h"
#include <Eigen/Cholesky>

namespace FellnerFitFunction {
	struct state {
		int verbose;
		int numProfiledOut;
		std::vector<int> olsVarNum;     // index into fc->est
		Eigen::MatrixXd olsDesign;      // a.k.a "X"

		int computeCov(RelationalRAMExpectation::independentGroup &ig);
		void compute(omxFitFunction *oo, int want, FitContext *fc);
		void setupProfiledParam(omxFitFunction *oo, FitContext *fc);
	};

	static void compute(omxFitFunction *oo, int want, FitContext *fc)
	{
		state *st = (state *) oo->argStruct;
		st->compute(oo, want, fc);
	}

	void state::setupProfiledParam(omxFitFunction *oo, FitContext *fc)
	{
		omxExpectation *expectation             = oo->expectation;
		omxRAMExpectation *ram = (omxRAMExpectation*) expectation->argStruct;

		if (numProfiledOut) ram->forceSingleGroup = true;
		omxExpectationCompute(fc, expectation, "nothing", "flat");

		if (numProfiledOut == 0) return;

		RelationalRAMExpectation::state &rram = ram->rram->getParent();
		if (rram.group.size() > 1) {
			Rf_error("Cannot profile out parameters when problem is split into independent groups");
		}

		RelationalRAMExpectation::independentGroup &ig = *rram.group[0];
		fc->profiledOut.assign(fc->numParam, false);

		olsVarNum.reserve(numProfiledOut);
		olsDesign.resize(ig.getParent().dataVec.size(), numProfiledOut);

		ProtectedSEXP Rprofile(R_do_slot(oo->rObj, Rf_install("profileOut")));
		for (int px=0; px < numProfiledOut; ++px) {
			const char *pname = CHAR(STRING_ELT(Rprofile, px));
			int vx = fc->varGroup->lookupVar(pname);
			if (vx < 0) {
				mxLog("Parameter [%s] not found", pname);
				continue;
			}

			omxFreeVar &fv = *fc->varGroup->vars[vx];
			olsVarNum.push_back(vx);
			bool found = false;
			bool moreThanOne;
			const omxFreeVarLocation *loc =
				fv.getOnlyOneLocation(ram->M, moreThanOne);
			if (loc) {
				if (moreThanOne) {
					mxLog("Parameter [%s] appears in more than one spot in %s",
					      pname, ram->M->name());
					continue;
				}
				found = true;
				int vnum = loc->row + loc->col;
				// Should ensure the loading is fixed and not a defvar TODO
				// Should ensure zero variance & no cross-level links TODO
				olsDesign.col(px) = (ig.dataColumn.array() == vnum).cast<double>();
			}
			loc = fv.getOnlyOneLocation(ram->A, moreThanOne);
			if (loc) {
				if (moreThanOne) {
					mxLog("Parameter [%s] appears in more than one spot in %s",
					      pname, ram->A->name());
					continue;
				}
				found = true;
				int vnum = loc->col;
				EigenMatrixAdaptor eA(ram->A);
				int rnum;
				eA.col(vnum).array().abs().maxCoeff(&rnum);
				// ensure only 1 nonzero in column TODO
				for (size_t ax=0; ax < ig.placements.size(); ++ax) {
					RelationalRAMExpectation::placement &pl = ig.placements[ax];
					RelationalRAMExpectation::addr &a1 = rram.layout[ ig.gMap[ax] ];
					if (a1.getExpNum() != expectation->expNum) continue;
					expectation->loadDefVars(a1.row);
					double weight = omxVectorElement(ram->M, vnum);
					olsDesign.col(px).segment(pl.obsStart, a1.numObs()) =
						weight * (ig.dataColumn.segment(pl.obsStart, a1.numObs()) == rnum).cast<double>();
				}
			}
			if (!found) Rf_error("oops");

			fc->profiledOut[vx] = true;
		}
	}

	int state::computeCov(RelationalRAMExpectation::independentGroup &ig)
	{
		if (0 == ig.getParent().dataVec.size()) return 0;

		ig.computeCov2();

		/*
		if (!ig.analyzedCov) {
			ig.fullCov.makeCompressed();
			ig.covDecomp.analyzePattern(ig.fullCov);
			ig.analyzedCov = true;
		}
		ig.covDecomp.factorize(ig.fullCov);
		*/

		Eigen::MatrixXd denseCov = ig.fullCov;
		ig.covDecomp.compute(denseCov);

		if (ig.covDecomp.info() != Eigen::Success || !(ig.covDecomp.vectorD().array() > 0.0).all()) return 1;

		ig.covDecomp.refreshInverse();
		return 0;
	}

	void state::compute(omxFitFunction *oo, int want, FitContext *fc)
	{
		omxExpectation *expectation             = oo->expectation;
		omxRAMExpectation *ram = (omxRAMExpectation*) expectation->argStruct;

		if (want & (FF_COMPUTE_INITIAL_FIT | FF_COMPUTE_PREOPTIMIZE)) {
			if (fc->isClone()) return;

			setupProfiledParam(oo, fc);

			RelationalRAMExpectation::state *rram   = ram->rram;
			if (verbose >= 1) {
				mxLog("%s: %d groups", oo->name(),
				      int(rram->group.size()));
			}
			return;
		}

		if (!(want & (FF_COMPUTE_FIT))) Rf_error("Not implemented");

		double lpOut = NA_REAL;
		try {
			if (!ram->rram) {
				// it is possible to skip FF_COMPUTE_PREOPTIMIZE (e.g. omxRecompute)
				omxExpectationCompute(fc, expectation, "nothing", "flat");
			}

			RelationalRAMExpectation::state &rram   = *ram->rram;
			double lp = 0.0;
			for (size_t gx=0; gx < rram.group.size(); ++gx) {
				rram.group[gx]->computeCov1(fc);
			}

			int covFailed = 0;
			for (size_t gx=0; gx < rram.group.size(); ++gx) {
				covFailed += computeCov(*rram.group[gx]);
			}
			if (covFailed) {
				throw std::runtime_error("Cholesky decomposition failed");
			}

			state *parent = this; // better to cache it TODO
			if (fc->isClone()) {
				omxMatrix *pfitMat = fc->getParentState()->getMatrixFromIndex(oo->matrix);
				parent = (state*) pfitMat->fitFunction->argStruct;
			}

			double remlAdj = 0.0;
			if (parent->numProfiledOut) {
				RelationalRAMExpectation::independentGroup &ig = *rram.group[0];
				const Eigen::MatrixXd &iV = ig.covDecomp.getInverse();
				Eigen::MatrixXd constCov =
					parent->olsDesign.transpose() * iV.selfadjointView<Eigen::Lower>() * parent->olsDesign;
				Eigen::LLT< Eigen::MatrixXd > cholConstCov;
				cholConstCov.compute(constCov);
				if(cholConstCov.info() != Eigen::Success){
					// ought to report error detail TODO
					throw std::exception();
				}
				remlAdj = 2*Eigen::MatrixXd(cholConstCov.matrixL()).diagonal().array().log().sum();

				Eigen::MatrixXd ident =
					Eigen::MatrixXd::Identity(parent->numProfiledOut, parent->numProfiledOut);
				Eigen::MatrixXd cholConstPrec = cholConstCov.solve(ident).triangularView<Eigen::Lower>();
				Eigen::VectorXd param =
					(cholConstPrec.selfadjointView<Eigen::Lower>() *
					 parent->olsDesign.transpose() * iV.selfadjointView<Eigen::Lower>() *
					 ig.getParent().dataVec);

				for (int px=0; px < parent->numProfiledOut; ++px) {
					fc->est[ parent->olsVarNum[px] ] = param[px];
					fc->varGroup->vars[ parent->olsVarNum[px] ]->copyToState(ram->M->currentState, param[px]);
				}
				lp += remlAdj - M_LN_2PI * parent->numProfiledOut;
			}

			omxExpectationCompute(fc, expectation, "mean", "flat");

			for (size_t gx=0; gx < rram.group.size(); ++gx) {
				RelationalRAMExpectation::independentGroup &ig = *rram.group[gx];
				if (0 == ig.getParent().dataVec.size()) continue;

				//mxPrintMat("dataVec", ig.dataVec);
				//mxPrintMat("fullMeans", ig.fullMeans);
				//ig.applyRotationPlan(ig.expectedVec);
				//mxPrintMat("expectedVec", ig.expectedVec);

				const Eigen::MatrixXd &iV = ig.covDecomp.getInverse();
				//mxPrintMat("iV", iV);
				int clumps = ig.numLooseClumps();
				if (clumps) {
					int residLen = clumps * ig.clumpObs;
					Eigen::VectorXd resid =
						ig.getParent().dataVec.segment(0,residLen) - ig.expectedVec.segment(0,residLen);
					//mxPrintMat("resid", resid);

					double logDet = clumps * ig.covDecomp.log_determinant();
				// Eigen::Map< Eigen::MatrixXd > iV(ig.covDecomp.getInverseData(),
				// 				 ig.fullCov.rows(), ig.fullCov.rows());
					double iqf = 0.0;
					for (int cx=0; cx < clumps; ++cx) {
						iqf += (resid.segment(cx*ig.clumpObs, ig.clumpObs).transpose() *
							iV.selfadjointView<Eigen::Lower>() *
							resid.segment(cx*ig.clumpObs, ig.clumpObs));
					}
					double cterm = M_LN_2PI * ig.getParent().dataVec.size();
					if (verbose >= 2) mxLog("log det %f iqf %f cterm %f", logDet, iqf, cterm);
					lp += logDet + iqf + cterm;
				}
				for (int sx=0; sx < (int)ig.getParent().sufficientSets.size(); ++sx) {
					RelationalRAMExpectation::sufficientSet &ss = ig.getParent().sufficientSets[sx];
					Eigen::VectorXd resid =
						ss.dataMean - ig.expectedVec.segment(ss.start * ig.clumpObs, ig.clumpObs);
					//mxPrintMat("resid", resid);
					double iqf = resid.transpose() * iV.selfadjointView<Eigen::Lower>() * resid;
					double tr1 = trace_prod(iV, ss.dataCov);
					double logDet = ig.covDecomp.log_determinant();
					double cterm = M_LN_2PI * ig.clumpObs;
					if (verbose >= 2) mxLog("iqf %f tr1 %f logDet %f cterm %f", iqf, tr1, logDet, cterm);
					lp += ss.length * (iqf + logDet + cterm) + (ss.length-1) * tr1;
				}
			}
			lpOut = lp;
		} catch (const std::exception& e) {
			if (fc) fc->recordIterationError("%s: %s", oo->name(), e.what());
		}
		oo->matrix->data[0] = lpOut;
	}

	static void popAttr(omxFitFunction *oo, SEXP algebra)
	{
		// use Eigen_cholmod_wrap to return a sparse matrix? TODO
		// always return it?

		/*
		state *st                               = (state *) oo->argStruct;
		SEXP expCovExt, expMeanExt;
		if (st->fullCov.rows() > 0) {
			Rf_protect(expCovExt = Rf_allocMatrix(REALSXP, expCovInt->rows, expCovInt->cols));
			memcpy(REAL(expCovExt), expCovInt->data, sizeof(double) * expCovInt->rows * expCovInt->cols);
			Rf_setAttrib(algebra, Rf_install("expCov"), expCovExt);
		}

		if (expMeanInt && expMeanInt->rows > 0) {
			Rf_protect(expMeanExt = Rf_allocMatrix(REALSXP, expMeanInt->rows, expMeanInt->cols));
			memcpy(REAL(expMeanExt), expMeanInt->data, sizeof(double) * expMeanInt->rows * expMeanInt->cols);
			Rf_setAttrib(algebra, Rf_install("expMean"), expMeanExt);
			}   */
	}

	static void destroy(omxFitFunction *oo)
	{
		state *st = (state*) oo->argStruct;
		delete st;
	}

	static void init(omxFitFunction *oo)
	{
		omxExpectation* expectation = oo->expectation;
		if(expectation == NULL) {
			omxRaiseErrorf("%s cannot fit without a model expectation", oo->fitType);
			return;
		}
		if (!strEQ(expectation->expType, "MxExpectationRAM")) {
			Rf_error("%s: only MxExpectationRAM is implemented", oo->matrix->name());
		}

		oo->computeFun = FellnerFitFunction::compute;
		oo->destructFun = FellnerFitFunction::destroy;
		oo->populateAttrFun = FellnerFitFunction::popAttr;
		FellnerFitFunction::state *st = new FellnerFitFunction::state;
		oo->argStruct = st;
		oo->canDuplicate = true;

		ProtectedSEXP Rprofile(R_do_slot(oo->rObj, Rf_install("profileOut")));
		st->numProfiledOut = Rf_length(Rprofile);

		{
			SEXP tmp;
			ScopedProtect p1(tmp, R_do_slot(oo->rObj, Rf_install("verbose")));
			st->verbose = Rf_asInteger(tmp) + OMX_DEBUG;
		}
	}
};

void InitFellnerFitFunction(omxFitFunction *oo)
{
	FellnerFitFunction::init(oo);
}
<|endoftext|>


<commit_before>#ifndef PLATE2D_HPP
#define PLATE2D_HPP
#include <wx/wxprec.h>
#ifndef WX_PRECOMP
    #include <wx/wx.h>
#endif
#include <wx/datetime.h>

#include <vector>
#include <functional>
#include "Plater.hpp"
#include "ColorScheme.hpp"
#include "Settings.hpp"
#include "Plater/Plater2DObject.hpp"
#include "misc_ui.hpp"

#include "Log.hpp"

namespace Slic3r { namespace GUI {

// Setup for an Easter Egg with the canvas text.
const wxDateTime today_date {wxDateTime().GetDateOnly()};
const wxDateTime special_date {13, wxDateTime::Month::Sep, 2006, 0, 0, 0, 0};
const bool today_is_special = {today_date.GetDay() == special_date.GetDay() && today_date.GetMonth() == special_date.GetMonth()};

enum class MoveDirection {
    Up, Down, Left, Right
};

class Plate2D : public wxPanel {
public:
    Plate2D(wxWindow* parent, const wxSize& size, std::vector<Plater2DObject>& _objects, std::shared_ptr<Model> _model, std::shared_ptr<Config> _config, std::shared_ptr<Settings> _settings);

//    std::function<> on_select_object {};
private:
    std::vector<Plater2DObject>& objects;
    std::shared_ptr<Slic3r::Model> model;
    std::shared_ptr<Slic3r::Config> config;
    std::shared_ptr<Settings> settings;

    // Different brushes to draw with
    wxBrush objects_brush {};
    wxBrush instance_brush {};
    wxBrush selected_brush {};
    wxBrush bed_brush {};
    wxBrush dragged_brush {};
    wxBrush transparent_brush {};

    wxPen grid_pen {};
    wxPen print_center_pen {};
    wxPen clearance_pen {};
    wxPen skirt_pen {};
    wxPen dark_pen {};

    bool user_drawn_background {(the_os == OS::Mac ? false : true)};
    size_t selected_instance;

    /// Handle mouse-move events
    void mouse_drag(wxMouseEvent& e);

    /// Handle repaint events
    void repaint(wxPaintEvent& e);

    void nudge_key(wxKeyEvent& e);

    void nudge(MoveDirection dir);

    /// Set/Update all of the colors used by the various brushes in the panel.
    void set_colors();

    /// Convert a scale point array to a pixel polygon suitable for DrawPolygon
    std::vector<wxPoint> scaled_points_to_pixel(const Slic3r::Polygon& poly, bool unscale);
    std::vector<wxPoint> scaled_points_to_pixel(const Slic3r::Polyline& poly, bool unscale);

    // For a specific point, unscaled it
    wxPoint unscaled_point_to_pixel(const wxPoint& in) {
        const auto& canvas_height {this->GetSize().GetHeight()};
        const auto& zero = this->bed_origin;
        return wxPoint(in.x * this->scaling_factor + zero.x,
                       in.y * this->scaling_factor + (zero.y - canvas_height));
    }

    /// Read print bed size from config and calculate the scaled rendition of the bed given the draw canvas.
    void update_bed_size();

    /// private class variables to stash bits for drawing the print bed area.
    wxPoint bed_origin {};
    wxPoint print_center {};
    Slic3r::Polygon bed_polygon {};
    std::vector<wxPoint> grid {};

    /// Set up the 2D canvas blank canvas text.
    /// Easter egg: Sept. 13, 2006. The first part ever printed by a RepRap to make another RepRap.
    const wxString CANVAS_TEXT { today_is_special ? _(L"What do you want to print today?™") : _("Drag your objects here") };

    /// How much to scale the points to fit in the draw bounding box area.
    /// Expressed as pixel / mm
    double scaling_factor {1.0};

    const std::string LogChannel {"GUI_2D"};

};

} } // Namespace Slic3r::GUI
#endif // PLATE2D_HPP
<commit_msg>Added point_to_model_units() and clarified a couple comments.<commit_after>#ifndef PLATE2D_HPP
#define PLATE2D_HPP
#include <wx/wxprec.h>
#ifndef WX_PRECOMP
    #include <wx/wx.h>
#endif
#include <wx/datetime.h>

#include <vector>
#include <functional>
#include "Plater.hpp"
#include "ColorScheme.hpp"
#include "Settings.hpp"
#include "Plater/Plater2DObject.hpp"
#include "misc_ui.hpp"

#include "Log.hpp"

namespace Slic3r { namespace GUI {

// Setup for an Easter Egg with the canvas text.
const wxDateTime today_date {wxDateTime().GetDateOnly()};
const wxDateTime special_date {13, wxDateTime::Month::Sep, 2006, 0, 0, 0, 0};
const bool today_is_special = {today_date.GetDay() == special_date.GetDay() && today_date.GetMonth() == special_date.GetMonth()};

enum class MoveDirection {
    Up, Down, Left, Right
};

class Plate2D : public wxPanel {
public:
    Plate2D(wxWindow* parent, const wxSize& size, std::vector<Plater2DObject>& _objects, std::shared_ptr<Model> _model, std::shared_ptr<Config> _config, std::shared_ptr<Settings> _settings);

//    std::function<> on_select_object {};
private:
    std::vector<Plater2DObject>& objects;
    std::shared_ptr<Slic3r::Model> model;
    std::shared_ptr<Slic3r::Config> config;
    std::shared_ptr<Settings> settings;

    // Different brushes to draw with, initialized from settings->Color during the constructor
    wxBrush objects_brush {};
    wxBrush instance_brush {};
    wxBrush selected_brush {};
    wxBrush bed_brush {};
    wxBrush dragged_brush {};
    wxBrush transparent_brush {};

    wxPen grid_pen {};
    wxPen print_center_pen {};
    wxPen clearance_pen {};
    wxPen skirt_pen {};
    wxPen dark_pen {};

    bool user_drawn_background {(the_os == OS::Mac ? false : true)};
    size_t selected_instance;

    /// Handle mouse-move events
    void mouse_drag(wxMouseEvent& e);

    /// Handle repaint events
    void repaint(wxPaintEvent& e);

    void nudge_key(wxKeyEvent& e);

    void nudge(MoveDirection dir);

    /// Set/Update all of the colors used by the various brushes in the panel.
    void set_colors();

    /// Convert a scale point array to a pixel polygon suitable for DrawPolygon
    std::vector<wxPoint> scaled_points_to_pixel(const Slic3r::Polygon& poly, bool unscale);
    std::vector<wxPoint> scaled_points_to_pixel(const Slic3r::Polyline& poly, bool unscale);

    /// For a specific point, unscale it relative to the origin
    wxPoint unscaled_point_to_pixel(const wxPoint& in) {
        const auto& canvas_height {this->GetSize().GetHeight()};
        const auto& zero = this->bed_origin;
        return wxPoint(in.x * this->scaling_factor + zero.x,
                       in.y * this->scaling_factor + (zero.y - canvas_height));
    }

    /// Read print bed size from config and calculate the scaled rendition of the bed given the draw canvas.
    void update_bed_size();

    /// private class variables to stash bits for drawing the print bed area.
    wxPoint bed_origin {};
    wxPoint print_center {};
    Slic3r::Polygon bed_polygon {};
    std::vector<wxPoint> grid {};

    /// Set up the 2D canvas blank canvas text.
    /// Easter egg: Sept. 13, 2006. The first part ever printed by a RepRap to make another RepRap.
    const wxString CANVAS_TEXT { today_is_special ? _(L"What do you want to print today?™") : _("Drag your objects here") };

    /// How much to scale the points to fit in the draw bounding box area.
    /// Expressed as pixel / mm
    double scaling_factor {1.0};

    const std::string LogChannel {"GUI_2D"};

    Slic3r::Point point_to_model_units(coordf_t x, coordf_t y) {
        const auto& zero {this->bed_origin};
        return Slic3r::Point(
            scale_(x - zero.x) / this->scaling_factor,
            scale_(y - zero.y) / this->scaling_factor
        );
    }
    Slic3r::Point point_to_model_units(const wxPoint& pt) {
        return this->point_to_model_units(pt.x, pt.y);
    }
    Slic3r::Point point_to_model_units(const Pointf& pt) {
        return this->point_to_model_units(pt.x, pt.y);
    }

};

} } // Namespace Slic3r::GUI
#endif // PLATE2D_HPP
<|endoftext|>


<commit_before>/***************************************************************************
 *   Copyright (c) 2010 Werner Mayer <wmayer[at]users.sourceforge.net>     *
 *                                                                         *
 *   This file is part of the FreeCAD CAx development system.              *
 *                                                                         *
 *   This library is free software; you can redistribute it and/or         *
 *   modify it under the terms of the GNU Library General Public           *
 *   License as published by the Free Software Foundation; either          *
 *   version 2 of the License, or (at your option) any later version.      *
 *                                                                         *
 *   This library  is distributed in the hope that it will be useful,      *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU Library General Public License for more details.                  *
 *                                                                         *
 *   You should have received a copy of the GNU Library General Public     *
 *   License along with this library; see the file COPYING.LIB. If not,    *
 *   write to the Free Software Foundation, Inc., 59 Temple Place,         *
 *   Suite 330, Boston, MA  02111-1307, USA                                *
 *                                                                         *
 ***************************************************************************/

#include "PreCompiled.h"
#ifndef _PreComp_
# include <stack>
# include <boost/bind.hpp>
#endif
#include "MergeDocuments.h"
#include <Base/Console.h>
#include <Base/Reader.h>
#include <Base/Writer.h>
#include <App/Document.h>
#include <App/DocumentObject.h>
#include <Gui/Application.h>
#include <Gui/Document.h>
#include <Gui/ViewProvider.h>

using namespace Gui;

namespace Gui {

class XMLMergeReader : public Base::XMLReader
{
public:
    XMLMergeReader(std::map<std::string, std::string>& name, const char* FileName, std::istream& str)
      : Base::XMLReader(FileName, str), nameMap(name)
    {}

    void addName(const char* s1, const char* s2)
    {
        nameMap[s1] = s2;
    }
    const char* getName(const char* name) const
    {
        std::map<std::string, std::string>::const_iterator it = nameMap.find(name);
        if (it != nameMap.end())
            return it->second.c_str();
        else
            return name;
    }
protected:
    void startElement(const XMLCh* const uri, const XMLCh* const localname,
                      const XMLCh* const qname,
                      const XERCES_CPP_NAMESPACE_QUALIFIER Attributes& attrs)
    {
        Base::XMLReader::startElement(uri, localname, qname, attrs);
        if (LocalName == "Property")
            propertyStack.push(std::make_pair(AttrMap["name"],AttrMap["type"]));

        if (!propertyStack.empty()) {
            // replace the stored object name with the real one
            if (LocalName == "Link" || (LocalName == "String" && propertyStack.top().first == "Label")) {
                for (std::map<std::string, std::string>::iterator it = AttrMap.begin(); it != AttrMap.end(); ++it) {
                    std::map<std::string, std::string>::const_iterator jt = nameMap.find(it->second);
                    if (jt != nameMap.end())
                        it->second = jt->second;
                }
            }
        }
    }

    void endElement(const XMLCh* const uri, const XMLCh *const localname, const XMLCh *const qname)
    {
        Base::XMLReader::endElement(uri, localname, qname);
        if (LocalName == "Property")
            propertyStack.pop();
    }

private:
    std::map<std::string, std::string>& nameMap;
    typedef std::pair<std::string, std::string> PropertyTag;
    std::stack<PropertyTag> propertyStack;
};
}

MergeDocuments::MergeDocuments(App::Document* doc) : appdoc(doc)
{
    connectExport = doc->signalExportObjects.connect
        (boost::bind(&MergeDocuments::exportObject, this, _1, _2));
    connectImport = doc->signalImportObjects.connect
        (boost::bind(&MergeDocuments::importObject, this, _1, _2));
    document = Gui::Application::Instance->getDocument(doc);
}

MergeDocuments::~MergeDocuments()
{
    connectExport.disconnect();
    connectImport.disconnect();
}

unsigned int MergeDocuments::getMemSize (void) const
{
    return 0;
}

std::vector<App::DocumentObject*>
MergeDocuments::importObjects(std::istream& input)
{
    this->nameMap.clear();
    this->stream = new zipios::ZipInputStream(input);
    XMLMergeReader reader(this->nameMap,"<memory>", *stream);
    std::vector<App::DocumentObject*> objs = appdoc->importObjects(reader);

    delete this->stream;
    this->stream = 0;

    return objs;
}

void MergeDocuments::importObject(const std::vector<App::DocumentObject*>& o, Base::XMLReader & r)
{
    objects = o;
    for (std::vector<App::DocumentObject*>::iterator it = objects.begin(); it != objects.end(); ++it) {
        Gui::ViewProvider* vp = document->getViewProvider(*it);
        if (vp) vp->hide();
    }
    Restore(r);

    r.readFiles(*this->stream);
}

void MergeDocuments::exportObject(const std::vector<App::DocumentObject*>& o, Base::Writer & w)
{
    objects = o;
    Save(w);
}

void MergeDocuments::Save (Base::Writer & w) const
{
    w.addFile("GuiDocument.xml", this);
}

void MergeDocuments::Restore(Base::XMLReader &r)
{
    r.addFile("GuiDocument.xml", this);
}

void MergeDocuments::SaveDocFile (Base::Writer & w) const
{
    document->exportObjects(objects, w);
}

void MergeDocuments::RestoreDocFile(Base::Reader & reader)
{
    std::vector<App::DocumentObject*> obj = objects;
    // We must create an XML parser to read from the input stream
    Base::XMLReader xmlReader("GuiDocument.xml", reader);
    xmlReader.readElement("Document");
    long scheme = xmlReader.getAttributeAsInteger("SchemaVersion");

    // At this stage all the document objects and their associated view providers exist.
    // Now we must restore the properties of the view providers only.
    //
    // SchemeVersion "1"
    if (scheme == 1) {
        // read the viewproviders itself
        xmlReader.readElement("ViewProviderData");
        int Cnt = xmlReader.getAttributeAsInteger("Count");
        std::vector<App::DocumentObject*>::const_iterator it = obj.begin();
        for (int i=0;i<Cnt&&it!=obj.end();++i,++it) {
            // The stored name usually doesn't match with the current name anymore
            // thus we try to match by type. This should work because the order of
            // objects should not have changed
            xmlReader.readElement("ViewProvider");
            std::string name = xmlReader.getAttribute("name");
            name = nameMap[name];
            Gui::ViewProvider* pObj = document->getViewProviderByName(name.c_str());
            //Gui::ViewProvider* pObj = document->getViewProvider(*it);
            if (pObj)
                pObj->Restore(xmlReader);
            xmlReader.readEndElement("ViewProvider");
            if (it == obj.end())
                break;
        }
        xmlReader.readEndElement("ViewProviderData");
    }

    xmlReader.readEndElement("Document");

    // In the file GuiDocument.xml new data files might be added
    if (!xmlReader.getFilenames().empty())
        xmlReader.readFiles(static_cast<zipios::ZipInputStream&>(reader));
}
<commit_msg>0001161: Merged Project object being imported changes size when moved<commit_after>/***************************************************************************
 *   Copyright (c) 2010 Werner Mayer <wmayer[at]users.sourceforge.net>     *
 *                                                                         *
 *   This file is part of the FreeCAD CAx development system.              *
 *                                                                         *
 *   This library is free software; you can redistribute it and/or         *
 *   modify it under the terms of the GNU Library General Public           *
 *   License as published by the Free Software Foundation; either          *
 *   version 2 of the License, or (at your option) any later version.      *
 *                                                                         *
 *   This library  is distributed in the hope that it will be useful,      *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU Library General Public License for more details.                  *
 *                                                                         *
 *   You should have received a copy of the GNU Library General Public     *
 *   License along with this library; see the file COPYING.LIB. If not,    *
 *   write to the Free Software Foundation, Inc., 59 Temple Place,         *
 *   Suite 330, Boston, MA  02111-1307, USA                                *
 *                                                                         *
 ***************************************************************************/

#include "PreCompiled.h"
#ifndef _PreComp_
# include <stack>
# include <boost/bind.hpp>
#endif
#include "MergeDocuments.h"
#include <Base/Console.h>
#include <Base/Reader.h>
#include <Base/Writer.h>
#include <App/Document.h>
#include <App/DocumentObject.h>
#include <Gui/Application.h>
#include <Gui/Document.h>
#include <Gui/ViewProvider.h>

using namespace Gui;

namespace Gui {

class XMLMergeReader : public Base::XMLReader
{
public:
    XMLMergeReader(std::map<std::string, std::string>& name, const char* FileName, std::istream& str)
      : Base::XMLReader(FileName, str), nameMap(name)
    {}

    void addName(const char* s1, const char* s2)
    {
        nameMap[s1] = s2;
    }
    const char* getName(const char* name) const
    {
        std::map<std::string, std::string>::const_iterator it = nameMap.find(name);
        if (it != nameMap.end())
            return it->second.c_str();
        else
            return name;
    }
protected:
    void startElement(const XMLCh* const uri, const XMLCh* const localname,
                      const XMLCh* const qname,
                      const XERCES_CPP_NAMESPACE_QUALIFIER Attributes& attrs)
    {
        Base::XMLReader::startElement(uri, localname, qname, attrs);
        if (LocalName == "Property")
            propertyStack.push(std::make_pair(AttrMap["name"],AttrMap["type"]));

        if (!propertyStack.empty()) {
            // replace the stored object name with the real one
            if (LocalName == "Link" || LocalName == "LinkSub" || (LocalName == "String" && propertyStack.top().first == "Label")) {
                for (std::map<std::string, std::string>::iterator it = AttrMap.begin(); it != AttrMap.end(); ++it) {
                    std::map<std::string, std::string>::const_iterator jt = nameMap.find(it->second);
                    if (jt != nameMap.end())
                        it->second = jt->second;
                }
            }
        }
    }

    void endElement(const XMLCh* const uri, const XMLCh *const localname, const XMLCh *const qname)
    {
        Base::XMLReader::endElement(uri, localname, qname);
        if (LocalName == "Property")
            propertyStack.pop();
    }

private:
    std::map<std::string, std::string>& nameMap;
    typedef std::pair<std::string, std::string> PropertyTag;
    std::stack<PropertyTag> propertyStack;
};
}

MergeDocuments::MergeDocuments(App::Document* doc) : appdoc(doc)
{
    connectExport = doc->signalExportObjects.connect
        (boost::bind(&MergeDocuments::exportObject, this, _1, _2));
    connectImport = doc->signalImportObjects.connect
        (boost::bind(&MergeDocuments::importObject, this, _1, _2));
    document = Gui::Application::Instance->getDocument(doc);
}

MergeDocuments::~MergeDocuments()
{
    connectExport.disconnect();
    connectImport.disconnect();
}

unsigned int MergeDocuments::getMemSize (void) const
{
    return 0;
}

std::vector<App::DocumentObject*>
MergeDocuments::importObjects(std::istream& input)
{
    this->nameMap.clear();
    this->stream = new zipios::ZipInputStream(input);
    XMLMergeReader reader(this->nameMap,"<memory>", *stream);
    std::vector<App::DocumentObject*> objs = appdoc->importObjects(reader);

    delete this->stream;
    this->stream = 0;

    return objs;
}

void MergeDocuments::importObject(const std::vector<App::DocumentObject*>& o, Base::XMLReader & r)
{
    objects = o;
    for (std::vector<App::DocumentObject*>::iterator it = objects.begin(); it != objects.end(); ++it) {
        Gui::ViewProvider* vp = document->getViewProvider(*it);
        if (vp) vp->hide();
    }
    Restore(r);

    r.readFiles(*this->stream);
}

void MergeDocuments::exportObject(const std::vector<App::DocumentObject*>& o, Base::Writer & w)
{
    objects = o;
    Save(w);
}

void MergeDocuments::Save (Base::Writer & w) const
{
    w.addFile("GuiDocument.xml", this);
}

void MergeDocuments::Restore(Base::XMLReader &r)
{
    r.addFile("GuiDocument.xml", this);
}

void MergeDocuments::SaveDocFile (Base::Writer & w) const
{
    document->exportObjects(objects, w);
}

void MergeDocuments::RestoreDocFile(Base::Reader & reader)
{
    std::vector<App::DocumentObject*> obj = objects;
    // We must create an XML parser to read from the input stream
    Base::XMLReader xmlReader("GuiDocument.xml", reader);
    xmlReader.readElement("Document");
    long scheme = xmlReader.getAttributeAsInteger("SchemaVersion");

    // At this stage all the document objects and their associated view providers exist.
    // Now we must restore the properties of the view providers only.
    //
    // SchemeVersion "1"
    if (scheme == 1) {
        // read the viewproviders itself
        xmlReader.readElement("ViewProviderData");
        int Cnt = xmlReader.getAttributeAsInteger("Count");
        std::vector<App::DocumentObject*>::const_iterator it = obj.begin();
        for (int i=0;i<Cnt&&it!=obj.end();++i,++it) {
            // The stored name usually doesn't match with the current name anymore
            // thus we try to match by type. This should work because the order of
            // objects should not have changed
            xmlReader.readElement("ViewProvider");
            std::string name = xmlReader.getAttribute("name");
            name = nameMap[name];
            Gui::ViewProvider* pObj = document->getViewProviderByName(name.c_str());
            //Gui::ViewProvider* pObj = document->getViewProvider(*it);
            if (pObj)
                pObj->Restore(xmlReader);
            xmlReader.readEndElement("ViewProvider");
            if (it == obj.end())
                break;
        }
        xmlReader.readEndElement("ViewProviderData");
    }

    xmlReader.readEndElement("Document");

    // In the file GuiDocument.xml new data files might be added
    if (!xmlReader.getFilenames().empty())
        xmlReader.readFiles(static_cast<zipios::ZipInputStream&>(reader));
}
<|endoftext|>


<commit_before>// Brian Goldman

// When attached to a solution, the Improvement Harness
// keeps track of the fitness effects of flipping bits
// and allows for partial solution evaluation.
#include "ImprovementHarness.h"

using namespace std;

ImprovementHarness::ImprovementHarness(shared_ptr<GrayBox> evaluator_,
                                       size_t radius, Record& _recording)
    : improvements(radius),
      recording(_recording) {
  recording.start_clock();
  evaluator = evaluator_;
  // Construct neighborhood
  build_graph(evaluator, graph);
  moves_ = k_order_subgraphs(graph, radius);

  const auto& subfunctions = evaluator->epistasis();

  // Efficiency tool for converting a bit to the subfunctions it participates in
  vector<vector<size_t>> bit_to_sub(evaluator->length());
  for (size_t sub = 0; sub < subfunctions.size(); sub++) {
    for (const auto& bit : subfunctions[sub]) {
      bit_to_sub[bit].push_back(sub);
    }
  }

  sub_to_move.resize(subfunctions.size());
  move_to_sub.resize(moves_.size());
  single_bit_moves.resize(evaluator->length(), -1);

  for (size_t m = 0; m < moves_.size(); m++) {
    improvements[moves_[m].size() - 1].insert(m);
    if (moves_[m].size() == 1) {
      single_bit_moves[moves_[m][0]] = m;
    }
    for (const auto& bit : moves_[m]) {
      for (const auto& sub : bit_to_sub[bit]) {
        move_to_sub[m].insert(sub);
        sub_to_move[sub].insert(m);
      }
    }
  }

  // Table for tracking the fitness effects of making each move
  delta.resize(moves_.size(), 0);

  fitness = 0;
  check_point_fitness = 0;
  solution = nullptr;
}

// Connect the harness to the solution and calculate initial move quality information
int ImprovementHarness::attach(vector<bool>* solution_) {
  solution = solution_;
  fitness = 0;
  // fill delta with 0s
  delta.assign(delta.size(), 0);
  const size_t number_of_subs = evaluator->epistasis().size();
  for (size_t sub = 0; sub < number_of_subs; sub++) {
    auto score = evaluator->evaluate(sub, *solution);
    fitness += score;
    // Update the effect each move has on this subfunction
    for (const auto& move : sub_to_move[sub]) {
      delta[move] -= score;
      flip_move(move);
      delta[move] += evaluator->evaluate(sub, *solution);
      flip_move(move);
    }
  }
  // Initially all moves are on
  for (auto& options : improvements) {
    options.all_on();
  }
  recording.record(fitness);
  return fitness;
}

// Performs r-bit hamming ball hill climbing on the attached solution
int ImprovementHarness::optimize(Random & rand) {
  bool improvement_found = true;
  while (improvement_found) {
    improvement_found = false;
    // scan improvements until a step size has an improving move
    for (size_t step_size = 0;
        step_size < improvements.size() and not improvement_found;
        step_size++) {
      // scan potential improvements until an actual improving move is found
      while (improvements[step_size].size() and not improvement_found) {
        auto move = improvements[step_size].random(rand);
        if (0 < delta[move]) {
          make_move(move);  // Put in move
          improvement_found = true;
        }
        improvements[step_size].turn_off(move);
      }
    }
  }
  // no recording is necessary as that is done by make_move
  return fitness;
}

// Allows for fast return to this solution state
void ImprovementHarness::set_check_point() {
  saved_delta.clear();
  flipped.clear();
  check_point_fitness = fitness;
  for (auto& options : improvements) {
    options.all_off();
  }
}

// Quickly returns to a previously saved state
int ImprovementHarness::revert() {
  for (const auto& changed : saved_delta) {
    delta[changed.first] = changed.second;
  }
  for (const auto& bit : flipped) {
    (*solution)[bit] = not (*solution)[bit];
  }
  fitness = check_point_fitness;
  set_check_point();
  return fitness;
}

// Given the index of a move, apply it to the solution
// and update auxiliary information.
int ImprovementHarness::make_move(size_t move) {
  // update fitness and record it
  fitness += delta[move];
  recording.record(fitness);
  // For each subfunction effected by this move
  for (const auto& sub : move_to_sub[move]) {
    auto pre_move = evaluator->evaluate(sub, *solution);
    flip_move(move);  // Put in move
    auto just_move = evaluator->evaluate(sub, *solution);
    flip_move(move);  // Take out move
    // for each move that overlaps the effected subfunction
    for (const auto& next : sub_to_move[sub]) {
      flip_move(next);  // Put in next
      auto just_next = evaluator->evaluate(sub, *solution);
      flip_move(move);  // Put in move
      auto move_next = evaluator->evaluate(sub, *solution);
      flip_move(move);  // Take out move
      flip_move(next);  // Take out next
      // Check point the delta if it hasn't been added already
      saved_delta.insert({next, delta[next]});
      // Take out old information and add in new information
      delta[next] += (pre_move - just_next + move_next - just_move);
      // turn the move on
      improvements[moves_[next].size() - 1].turn_on(next);
    }
  }
  // keep track of which bits have been flipped since the last checkpoint
  for (const auto& bit : moves_[move]) {
    if (flipped.count(bit)) {
      // two flips cancel out
      flipped.erase(bit);
    } else {
      flipped.insert(bit);
    }
  }
  flip_move(move);  // Put in move
  return fitness;
}

// Converts a bit index to a move index
int ImprovementHarness::modify_bit(size_t bit) {
  return make_move(single_bit_moves[bit]);
}

// Internal function to flip all bits associated with a move
void ImprovementHarness::flip_move(size_t move_index) {
  for (const auto& bit : moves_[move_index]) {
    (*solution)[bit] = not (*solution)[bit];
  }
}
<commit_msg>More minor improvements<commit_after>// Brian Goldman

// When attached to a solution, the Improvement Harness
// keeps track of the fitness effects of flipping bits
// and allows for partial solution evaluation.
#include "ImprovementHarness.h"

using namespace std;

ImprovementHarness::ImprovementHarness(shared_ptr<GrayBox> evaluator_,
                                       size_t radius, Record& _recording)
    : improvements(radius),
      recording(_recording) {
  recording.start_clock();
  evaluator = evaluator_;
  // Construct neighborhood
  build_graph(evaluator, graph);
  moves_ = k_order_subgraphs(graph, radius);

  const auto& subfunctions = evaluator->epistasis();

  // Efficiency tool for converting a bit to the subfunctions it participates in
  vector<vector<size_t>> bit_to_sub(evaluator->length());
  for (size_t sub = 0; sub < subfunctions.size(); sub++) {
    for (const auto& bit : subfunctions[sub]) {
      bit_to_sub[bit].push_back(sub);
    }
  }

  sub_to_move.resize(subfunctions.size());
  move_to_sub.resize(moves_.size());
  single_bit_moves.resize(evaluator->length(), -1);

  for (size_t m = 0; m < moves_.size(); m++) {
    improvements[moves_[m].size() - 1].insert(m);
    if (moves_[m].size() == 1) {
      single_bit_moves[moves_[m][0]] = m;
    }
    for (const auto& bit : moves_[m]) {
      for (const auto& sub : bit_to_sub[bit]) {
        move_to_sub[m].insert(sub);
        sub_to_move[sub].insert(m);
      }
    }
  }

  // Table for tracking the fitness effects of making each move
  delta.resize(moves_.size(), 0);

  fitness = 0;
  check_point_fitness = 0;
  solution = nullptr;
}

// Connect the harness to the solution and calculate initial move quality information
int ImprovementHarness::attach(vector<bool>* solution_) {
  solution = solution_;
  fitness = 0;
  // fill delta with 0s
  delta.assign(delta.size(), 0);
  const size_t number_of_subs = evaluator->epistasis().size();
  for (size_t sub = 0; sub < number_of_subs; sub++) {
    auto score = evaluator->evaluate(sub, *solution);
    fitness += score;
    // Update the effect each move has on this subfunction
    for (const auto& move : sub_to_move[sub]) {
      delta[move] -= score;
      flip_move(move);
      delta[move] += evaluator->evaluate(sub, *solution);
      flip_move(move);
    }
  }
  // Initially all moves are on
  for (auto& options : improvements) {
    options.all_on();
  }
  recording.record(fitness);
  return fitness;
}

// Performs r-bit hamming ball hill climbing on the attached solution
int ImprovementHarness::optimize(Random & rand) {
  bool improvement_found = true;
  while (improvement_found) {
    improvement_found = false;
    // scan improvements until a step size has an improving move
    for (auto & potential : improvements) {
      while (potential.size() and not improvement_found) {
        auto move = potential.random(rand);
        if (0 < delta[move]) {
          make_move(move);  // Put in move
          improvement_found = true;
        }
        potential.turn_off(move);
      }
      if (improvement_found) {
        break;
      }
    }
  }
  // no recording is necessary as that is done by make_move
  return fitness;
}

// Allows for fast return to this solution state
void ImprovementHarness::set_check_point() {
  saved_delta.clear();
  flipped.clear();
  check_point_fitness = fitness;
  for (auto& options : improvements) {
    options.all_off();
  }
}

// Quickly returns to a previously saved state
int ImprovementHarness::revert() {
  for (const auto& changed : saved_delta) {
    delta[changed.first] = changed.second;
  }
  for (const auto& bit : flipped) {
    (*solution)[bit] = not (*solution)[bit];
  }
  fitness = check_point_fitness;
  set_check_point();
  return fitness;
}

// Given the index of a move, apply it to the solution
// and update auxiliary information.
int ImprovementHarness::make_move(size_t move) {
  // update fitness and record it
  fitness += delta[move];
  recording.record(fitness);
  // For each subfunction effected by this move
  for (const auto& sub : move_to_sub[move]) {
    auto pre_move = evaluator->evaluate(sub, *solution);
    flip_move(move);  // Put in move
    auto just_move = evaluator->evaluate(sub, *solution);
    flip_move(move);  // Take out move
    // for each move that overlaps the effected subfunction
    for (const auto& next : sub_to_move[sub]) {
      flip_move(next);  // Put in next
      auto just_next = evaluator->evaluate(sub, *solution);
      flip_move(move);  // Put in move
      auto move_next = evaluator->evaluate(sub, *solution);
      flip_move(move);  // Take out move
      flip_move(next);  // Take out next
      // Check point the delta if it hasn't been added already
      saved_delta.insert({next, delta[next]});
      // Take out old information and add in new information
      delta[next] += (pre_move - just_next + move_next - just_move);
      // turn the move on
      improvements[moves_[next].size() - 1].turn_on(next);
    }
  }
  // keep track of which bits have been flipped since the last checkpoint
  for (const auto& bit : moves_[move]) {
    if (flipped.count(bit)) {
      // two flips cancel out
      flipped.erase(bit);
    } else {
      flipped.insert(bit);
    }
  }
  flip_move(move);  // Put in move
  return fitness;
}

// Converts a bit index to a move index
int ImprovementHarness::modify_bit(size_t bit) {
  return make_move(single_bit_moves[bit]);
}

// Internal function to flip all bits associated with a move
void ImprovementHarness::flip_move(size_t move_index) {
  for (const auto& bit : moves_[move_index]) {
    (*solution)[bit] = not (*solution)[bit];
  }
}
<|endoftext|>


<commit_before><commit_msg>Renaming a test.<commit_after><|endoftext|>


<commit_before>#include <cassert>

#include "Moves/Pawn_Capture.h"
#include "Pieces/Piece.h"
#include "Game/Board.h"
#include "Utility.h"

Pawn_Capture::Pawn_Capture(Color color_in, char dir) : Pawn_Move(color_in)
{
    if(dir == 'r')
    {
        d_file = 1;
    }
    else if(dir == 'l')
    {
        d_file = -1;
    }
    else
    {
        throw std::runtime_error(std::string("Invalid direction specification for Pawn_Capture: ")
                                + std::string().append(1, dir));
    }
}

Pawn_Capture::~Pawn_Capture()
{
}

bool Pawn_Capture::is_legal(const Board& board, char file_start, int rank_start) const
{
    char file_end = file_start + file_change();
    int  rank_end = rank_start + rank_change();

    auto attacking_piece = board.piece_on_square(file_start, rank_start);
    auto attacked_piece  = board.piece_on_square(file_end, rank_end);
    return rank_end != (rank_change() == 1 ? 8 : 1)
           && attacked_piece != nullptr
           && attacked_piece->color() != attacking_piece->color();
}

std::string Pawn_Capture::name() const
{
    return "Pawn Capture";
}

std::string Pawn_Capture::game_record_item(const Board&, char file_start, int rank_start) const
{
    return std::string(std::string(1, file_start))
            + "x"
            + std::string(std::string(1, char(file_start + file_change())))
            + std::string(std::to_string(rank_start + rank_change()));
}
<commit_msg>Comment added for Pawn_Capture::is_legal()<commit_after>#include <cassert>

#include "Moves/Pawn_Capture.h"
#include "Pieces/Piece.h"
#include "Game/Board.h"
#include "Utility.h"

Pawn_Capture::Pawn_Capture(Color color_in, char dir) : Pawn_Move(color_in)
{
    if(dir == 'r')
    {
        d_file = 1;
    }
    else if(dir == 'l')
    {
        d_file = -1;
    }
    else
    {
        throw std::runtime_error(std::string("Invalid direction specification for Pawn_Capture: ")
                                + std::string().append(1, dir));
    }
}

Pawn_Capture::~Pawn_Capture()
{
}

bool Pawn_Capture::is_legal(const Board& board, char file_start, int rank_start) const
{
    char file_end = file_start + file_change();
    int  rank_end = rank_start + rank_change();

    auto attacking_piece = board.piece_on_square(file_start, rank_start);
    auto attacked_piece  = board.piece_on_square(file_end, rank_end);
    return rank_end != (rank_change() == 1 ? 8 : 1) // not promoting (see Pawn_Promotion_by_Capture)
           && attacked_piece != nullptr // must be capturing a piece
           && attacked_piece->color() != attacking_piece->color();
}

std::string Pawn_Capture::name() const
{
    return "Pawn Capture";
}

std::string Pawn_Capture::game_record_item(const Board&, char file_start, int rank_start) const
{
    return std::string(std::string(1, file_start))
            + "x"
            + std::string(std::string(1, char(file_start + file_change())))
            + std::string(std::to_string(rank_start + rank_change()));
}
<|endoftext|>