<commit_before>// Harness utility that launches XIOSIM for a multiprogrammed workload. Muliple
// pintool processes for a workload are forked according to a configuration
// file. The harness creates a shared memory segment for XIOSIM to use as
// interprocess communication between the separate Pin processes.
//
// To use:
//   harness -benchmark_cfg <CONFIG_FILE> setarch i686 ... pin -pin_args ...
//
// The benchmarK_cfg flag is required.
//
// Author: Sam Xi

#include <iostream>
#include <sstream>
#include <stdlib.h>
#include <string>
#include <sstream>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

#include "boost_interprocess.h"

#include "ezOptionParser_clean.hpp"

#include "../interface.h"
#include "multiprocess_shared.h"
#include "ipc_queues.h"

boost::interprocess::managed_shared_memory *global_shm;
SHARED_VAR_DEFINE(XIOSIM_LOCK, printing_lock)
SHARED_VAR_DEFINE(int, num_processes)
SHARED_VAR_DEFINE(int, next_asid)

#include "confuse.h"  // For parsing config files.

// Until libconfuse switches to const char*
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wwrite-strings"

namespace xiosim {
namespace shared {

const std::string CFG_FILE_FLAG = "-benchmark_cfg";
const std::string HARNESS_PID_FLAG = "-harness_pid";
const char * LAST_PINTOOL_ARG = "-s";
const char * FIRST_PIN_ARG = "-pin";

pid_t *harness_pids;
int harness_num_processes = 0;

}  // namespace shared
}  // namespace xiosim

void remove_shared_memory() {
    using namespace boost::interprocess;
    using namespace xiosim::shared;

    std::stringstream harness_pid_stream;
    harness_pid_stream << getpid();
    std::string shared_memory_key =
        harness_pid_stream.str() + std::string(XIOSIM_SHARED_MEMORY_KEY);
    // Shared locks are actually named with the prefix "sem.".
    std::string init_counter_key = std::string("sem.") +
        harness_pid_stream.str() + std::string(XIOSIM_INIT_COUNTER_KEY);
    std::string shared_lock_key = std::string("sem.") +
        harness_pid_stream.str() + std::string(XIOSIM_INIT_SHARED_LOCK);

    if (!shared_memory_object::remove(shared_memory_key.c_str())) {
        std::cerr << "Warning: Could not remove shared memory object "
                  << shared_memory_key << std::endl;
    }
    if (!shared_memory_object::remove(init_counter_key.c_str())) {
        std::cerr << "Warning: Could not remove shared memory object "
                  << init_counter_key << std::endl;
    }
    if (!shared_memory_object::remove(shared_lock_key.c_str())) {
        std::cerr << "Warning: Could not remove shared memory object "
                  << shared_lock_key << std::endl;
    }
}

// Kills all child processes that were forked by the harness, including the
// timing simulator process. This sends the SIGKILL signal rather than SIGTERM
// in order to ensure process termination.
// This is called when the harness intercepts a SIGINT interrupt or when the
// harness detects that one of the child processes exited unnaturally.
void kill_children(int sig) {
    using namespace xiosim::shared;
    // Using a stringstream to avoid races on std::cout.
    std::stringstream output;
    if (WEXITSTATUS(sig) == SIGINT)
        output << "Caught SIGINT, ";
    else
        output << "Detected signal " << sig << ", ";
    output << "killing child processes." << std::endl;
    std::cout << output.str();
    for (int i = 0; i < harness_num_processes - 1; i++) {
        pid_t pgid = getpgid(harness_pids[i]);
        killpg(pgid, SIGKILL);
    }

    // Kill timing_sim too, but let it execute the cleanup handlers
    pid_t timing_pgid = getpgid(harness_pids[harness_num_processes - 1]);
    killpg(timing_pgid, SIGTERM);

    remove_shared_memory();
    exit(1);
}

// Modify the generic pintool command for the timing simulator.
std::string get_timing_sim_args(std::string harness_args) {
    size_t feeder_opt_end_pos = harness_args.find("-t ") +
                            strlen("-t ");
    size_t feeder_so_pos = harness_args.find("feeder_zesto.so");
    size_t feeder_so_end_pos = feeder_so_pos + strlen("feeder_zesto.so");

    std::string feeder_path = harness_args.substr(feeder_opt_end_pos, feeder_so_pos - feeder_opt_end_pos);
    std::string timing_fname = feeder_path + "timing_sim";

    // Remove everything before and including the "-t" feeder option
    harness_args.erase(0, feeder_so_end_pos);

    // Prefix with invoking timing_sim binary
    harness_args = timing_fname + " " + harness_args;

    // Remove trailing "--"
    auto pos = harness_args.rfind("--");
    harness_args.erase(pos);
    std::cout << "timing_sim args: " << harness_args << std::endl;
    return harness_args;
}

// Fork the timing simulator process and return its pid.
pid_t fork_timing_simulator(std::string run_str, bool debug_timing) {
    std::string timing_cmd = get_timing_sim_args(run_str);
    pid_t timing_sim_pid;

    if (!debug_timing) {
        timing_sim_pid = fork();
        switch (timing_sim_pid) {
          case 0: {   // child
            setpgid(0, 0);
            int ret = system(timing_cmd.c_str());
            if (WEXITSTATUS(ret) != 0) {
                std::cerr << "Execv failed: " << timing_cmd << std::endl;
                abort();
            }
            exit(0);
          }
          case 1: {
            perror("Fork failed.");
          }
          default: {  // parent
            std::cout << "Timing simulator: " << timing_sim_pid << std::endl;
          }
        }
    } else {
        std::cout << "Enter timing_sim PID:";
        std::cin >> timing_sim_pid;
    }
    return timing_sim_pid;
}

int main(int argc, const char *argv[]) {
    using namespace boost::interprocess;
    using namespace xiosim::shared;

    ez::ezOptionParser cmd_opts;
    cmd_opts.overview = "XIOSim harness options";
    cmd_opts.syntax = "-benchmark_cfg CFG_FILE [-debug_timing]";
    cmd_opts.add("benchmarks.cfg", 1, 1, 0, "Programs to simulate", CFG_FILE_FLAG.c_str());
    cmd_opts.add("", 0, 0, 0, "Debug timing_sim (start manually)", "-debug_timing");
    cmd_opts.parse(argc, argv);

    std::string cfg_filename;
    cmd_opts.get(CFG_FILE_FLAG.c_str())->getString(cfg_filename);
    bool debug_timing = cmd_opts.get("-debug_timing")->isSet;

    // Parse the benchmark configuration file.
    cfg_opt_t program_opts[] {
        CFG_STR("exec_path", "", CFGF_NONE),
        CFG_STR("command_line_args", "", CFGF_NONE),
        CFG_INT("instances", 1, CFGF_NONE),
        CFG_END()
    };
    cfg_opt_t opts[] {
        CFG_SEC("program", program_opts, CFGF_MULTI | CFGF_TITLE),
        CFG_END()
    };
    cfg_t *cfg = cfg_init(opts, 0);
    cfg_parse(cfg, cfg_filename.c_str());

    // Compute the total number of benchmark processes that will be forked.
    int num_programs = cfg_size(cfg, "program");
    for (int i = 0; i < num_programs; i++) {
        cfg_t *program_cfg = cfg_getnsec(cfg, "program", i);
        int instances = cfg_getint(program_cfg, "instances");
        harness_num_processes += instances;
    }
    harness_num_processes++;  // For the timing simulator.

    // Setup SIGINT handler to kill child processes as well.
    struct sigaction sig_int_handler;
    sig_int_handler.sa_handler = kill_children;
    sigemptyset(&sig_int_handler.sa_mask);
    sig_int_handler.sa_flags = 0;
    sigaction(SIGINT, &sig_int_handler, NULL);

    // Concatenate the flags into a single string to be executed when setup is
    // complete.
    std::stringstream command_stream;
    pid_t harness_pid = getpid();
    bool inserted_harness = false;
    bool found_pin = false;
    for (int i = 0; i < argc; i++) {
        // Look for "-pin" to signal end of harness args
        if ((strncmp(argv[i], FIRST_PIN_ARG, strlen(FIRST_PIN_ARG)) == 0) &&
            (strnlen(argv[i], strlen(FIRST_PIN_ARG) + 1) == strlen(FIRST_PIN_ARG))) {
            found_pin = true;
            continue;
        }

        // Bypass harness-specific arguments and start with pin
        if (!found_pin)
            continue;

        // If the next arg is "-s", add the harness_pid flag.
        if ((strncmp(argv[i], LAST_PINTOOL_ARG, strlen(LAST_PINTOOL_ARG)) == 0) &&
            (strnlen(argv[i], strlen(LAST_PINTOOL_ARG) + 1) == strlen(LAST_PINTOOL_ARG))) {
            command_stream << " " << HARNESS_PID_FLAG << " " << harness_pid << " ";
            inserted_harness = true;
        }

        // Pass down arg to children
        command_stream << argv[i] << " ";
    }
    if (!found_pin || !inserted_harness) {
        std:: cerr << "Failed to find -pin or -s args!" << std::endl;
        abort();
    }
    command_stream << "-- ";  // For appending the benchmark program arguments.

    std::cerr << "HARNESS CMD: " << command_stream.str() << std::endl;

    // Shared object keys are prefixed by the harness pid that created them.
    std::stringstream harness_pid_stream;
    harness_pid_stream << harness_pid;
    std::string shared_memory_key =
        harness_pid_stream.str() + std::string(XIOSIM_SHARED_MEMORY_KEY);
    std::string init_counter_key =
        harness_pid_stream.str() + std::string(XIOSIM_INIT_COUNTER_KEY);
    std::string shared_lock_key =
        harness_pid_stream.str() + std::string(XIOSIM_INIT_SHARED_LOCK);

    // Creates a new shared segment.
    global_shm = new managed_shared_memory(open_or_create, shared_memory_key.c_str(),
        DEFAULT_SHARED_MEMORY_SIZE);

    // Sets up a counter for multiprogramming. It is initialized to the number of
    // processes that are to be forked. When each forked process is about to start
    // the pin-instructed program, it will decrement this counter atomically. When
    // the counter reaches zero, the process is allowed to continue execution.
    // This also initializes a shared mutex for the forked processes to use for
    // synchronizing access to this counter.
    permissions perm;
    perm.set_unrestricted();
    int *counter =
        global_shm->find_or_construct<int>(init_counter_key.c_str())(harness_num_processes);
    (void) counter;
    named_mutex init_lock(open_or_create, shared_lock_key.c_str(), perm);

    SHARED_VAR_INIT(XIOSIM_LOCK, printing_lock);
    SHARED_VAR_INIT(int, num_processes, harness_num_processes - 1);
    SHARED_VAR_INIT(int, next_asid, 0);
    InitIPCQueues();
    init_lock.unlock();

    // Track the pids of all children.
    harness_pids = new pid_t[harness_num_processes];

    // Create a process for timing simulator and store its pid.
    harness_pids[harness_num_processes - 1] = fork_timing_simulator(
        command_stream.str(), debug_timing);

    // Fork all the benchmark child processes.
    int status;
    int nthprocess = 0;
    for (int program = 0; program < num_programs; program++) {
        cfg_t *program_cfg = cfg_getnsec(cfg, "program", program);
        int instances = cfg_getint(program_cfg, "instances");

        for (int process = 0; process < instances; process++) {
            harness_pids[nthprocess] = fork();
            std::string run_str = command_stream.str();

            // Append program command line arguments.
            std::stringstream ss;
            ss << cfg_getstr(program_cfg, "exec_path") << " " <<
                cfg_getstr(program_cfg, "command_line_args");
            run_str += ss.str();

            switch (harness_pids[nthprocess]) {
              case 0:  {  // child
                setpgid(0, 0);
                int ret = system(run_str.c_str());
                if (WIFSIGNALED(ret))
                    abort();
                exit(WEXITSTATUS(ret));
                break;
              }
              case -1:  {
                perror("Fork failed.");
                break;
              }
              default:  {  // parent
                std::cout << "New producer: " << harness_pids[nthprocess] << std::endl;
                nthprocess++;
                break;
              }
            }
        }
    }

    // Waits for the children process to finish. If any of the children, including
    // the timing simulator process, terminate unexpectedly, then ALL children are
    // killed immediately. Otherwise, it waits for only the producer children to
    // finish normally. The timing simulator is instructed to terminate
    // separately later.
    std::cout << "[HARNESS] Waiting for feeder children to finish." << std::endl;
    for (int i = 0; i < harness_num_processes-1; i++) {
        // Wait for any child process to finish.
        pid_t terminated_process = wait(&status);
        if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
            std::cerr << "Process " << terminated_process << " failed." << std::endl;
            kill_children(status);
        }
    }

    std::cout << "[HARNESS] Letting timing_sim finish" << std::endl;
    /* Tell timing simulator to die quietly */
    ipc_message_t msg;
    msg.StopSimulation(true);
    SendIPCMessage(msg);

    pid_t timing_pid = harness_pids[harness_num_processes-1];
    while (waitpid(timing_pid, &status, 0) != timing_pid);
    if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
        std::cerr << "Process " << timing_pid << " failed." << std::endl;
    }

    remove_shared_memory();
    std::cout << "[HARNESS] Parent exiting." << std::endl;
    delete[](harness_pids);
    return 0;
}

#pragma GCC diagnostic pop
<commit_msg>Check waitpid result for errors.<commit_after>// Harness utility that launches XIOSIM for a multiprogrammed workload. Muliple
// pintool processes for a workload are forked according to a configuration
// file. The harness creates a shared memory segment for XIOSIM to use as
// interprocess communication between the separate Pin processes.
//
// To use:
//   harness -benchmark_cfg <CONFIG_FILE> setarch i686 ... pin -pin_args ...
//
// The benchmarK_cfg flag is required.
//
// Author: Sam Xi

#include <iostream>
#include <sstream>
#include <stdlib.h>
#include <string>
#include <sstream>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

#include "boost_interprocess.h"

#include "ezOptionParser_clean.hpp"

#include "../interface.h"
#include "multiprocess_shared.h"
#include "ipc_queues.h"

boost::interprocess::managed_shared_memory *global_shm;
SHARED_VAR_DEFINE(XIOSIM_LOCK, printing_lock)
SHARED_VAR_DEFINE(int, num_processes)
SHARED_VAR_DEFINE(int, next_asid)

#include "confuse.h"  // For parsing config files.

// Until libconfuse switches to const char*
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wwrite-strings"

namespace xiosim {
namespace shared {

const std::string CFG_FILE_FLAG = "-benchmark_cfg";
const std::string HARNESS_PID_FLAG = "-harness_pid";
const char * LAST_PINTOOL_ARG = "-s";
const char * FIRST_PIN_ARG = "-pin";

pid_t *harness_pids;
int harness_num_processes = 0;

}  // namespace shared
}  // namespace xiosim

void remove_shared_memory() {
    using namespace boost::interprocess;
    using namespace xiosim::shared;

    std::stringstream harness_pid_stream;
    harness_pid_stream << getpid();
    std::string shared_memory_key =
        harness_pid_stream.str() + std::string(XIOSIM_SHARED_MEMORY_KEY);
    // Shared locks are actually named with the prefix "sem.".
    std::string init_counter_key = std::string("sem.") +
        harness_pid_stream.str() + std::string(XIOSIM_INIT_COUNTER_KEY);
    std::string shared_lock_key = std::string("sem.") +
        harness_pid_stream.str() + std::string(XIOSIM_INIT_SHARED_LOCK);

    if (!shared_memory_object::remove(shared_memory_key.c_str())) {
        std::cerr << "Warning: Could not remove shared memory object "
                  << shared_memory_key << std::endl;
    }
    if (!shared_memory_object::remove(init_counter_key.c_str())) {
        std::cerr << "Warning: Could not remove shared memory object "
                  << init_counter_key << std::endl;
    }
    if (!shared_memory_object::remove(shared_lock_key.c_str())) {
        std::cerr << "Warning: Could not remove shared memory object "
                  << shared_lock_key << std::endl;
    }
}

// Kills all child processes that were forked by the harness, including the
// timing simulator process. This sends the SIGKILL signal rather than SIGTERM
// in order to ensure process termination.
// This is called when the harness intercepts a SIGINT interrupt or when the
// harness detects that one of the child processes exited unnaturally.
void kill_children(int sig) {
    using namespace xiosim::shared;
    // Using a stringstream to avoid races on std::cout.
    std::stringstream output;
    if (WEXITSTATUS(sig) == SIGINT)
        output << "Caught SIGINT, ";
    else
        output << "Detected signal " << sig << ", ";
    output << "killing child processes." << std::endl;
    std::cout << output.str();
    for (int i = 0; i < harness_num_processes - 1; i++) {
        pid_t pgid = getpgid(harness_pids[i]);
        killpg(pgid, SIGKILL);
    }

    // Kill timing_sim too, but let it execute the cleanup handlers
    pid_t timing_pgid = getpgid(harness_pids[harness_num_processes - 1]);
    killpg(timing_pgid, SIGTERM);

    remove_shared_memory();
    exit(1);
}

// Modify the generic pintool command for the timing simulator.
std::string get_timing_sim_args(std::string harness_args) {
    size_t feeder_opt_end_pos = harness_args.find("-t ") +
                            strlen("-t ");
    size_t feeder_so_pos = harness_args.find("feeder_zesto.so");
    size_t feeder_so_end_pos = feeder_so_pos + strlen("feeder_zesto.so");

    std::string feeder_path = harness_args.substr(feeder_opt_end_pos, feeder_so_pos - feeder_opt_end_pos);
    std::string timing_fname = feeder_path + "timing_sim";

    // Remove everything before and including the "-t" feeder option
    harness_args.erase(0, feeder_so_end_pos);

    // Prefix with invoking timing_sim binary
    harness_args = timing_fname + " " + harness_args;

    // Remove trailing "--"
    auto pos = harness_args.rfind("--");
    harness_args.erase(pos);
    std::cout << "timing_sim args: " << harness_args << std::endl;
    return harness_args;
}

// Fork the timing simulator process and return its pid.
pid_t fork_timing_simulator(std::string run_str, bool debug_timing) {
    std::string timing_cmd = get_timing_sim_args(run_str);
    pid_t timing_sim_pid;

    if (!debug_timing) {
        timing_sim_pid = fork();
        switch (timing_sim_pid) {
          case 0: {   // child
            setpgid(0, 0);
            int ret = system(timing_cmd.c_str());
            if (WEXITSTATUS(ret) != 0) {
                std::cerr << "Execv failed: " << timing_cmd << std::endl;
                abort();
            }
            exit(0);
          }
          case 1: {
            perror("Fork failed.");
          }
          default: {  // parent
            std::cout << "Timing simulator: " << timing_sim_pid << std::endl;
          }
        }
    } else {
        std::cout << "Enter timing_sim PID:";
        std::cin >> timing_sim_pid;
    }
    return timing_sim_pid;
}

int main(int argc, const char *argv[]) {
    using namespace boost::interprocess;
    using namespace xiosim::shared;

    ez::ezOptionParser cmd_opts;
    cmd_opts.overview = "XIOSim harness options";
    cmd_opts.syntax = "-benchmark_cfg CFG_FILE [-debug_timing]";
    cmd_opts.add("benchmarks.cfg", 1, 1, 0, "Programs to simulate", CFG_FILE_FLAG.c_str());
    cmd_opts.add("", 0, 0, 0, "Debug timing_sim (start manually)", "-debug_timing");
    cmd_opts.parse(argc, argv);

    std::string cfg_filename;
    cmd_opts.get(CFG_FILE_FLAG.c_str())->getString(cfg_filename);
    bool debug_timing = cmd_opts.get("-debug_timing")->isSet;

    // Parse the benchmark configuration file.
    cfg_opt_t program_opts[] {
        CFG_STR("exec_path", "", CFGF_NONE),
        CFG_STR("command_line_args", "", CFGF_NONE),
        CFG_INT("instances", 1, CFGF_NONE),
        CFG_END()
    };
    cfg_opt_t opts[] {
        CFG_SEC("program", program_opts, CFGF_MULTI | CFGF_TITLE),
        CFG_END()
    };
    cfg_t *cfg = cfg_init(opts, 0);
    cfg_parse(cfg, cfg_filename.c_str());

    // Compute the total number of benchmark processes that will be forked.
    int num_programs = cfg_size(cfg, "program");
    for (int i = 0; i < num_programs; i++) {
        cfg_t *program_cfg = cfg_getnsec(cfg, "program", i);
        int instances = cfg_getint(program_cfg, "instances");
        harness_num_processes += instances;
    }
    harness_num_processes++;  // For the timing simulator.

    // Setup SIGINT handler to kill child processes as well.
    struct sigaction sig_int_handler;
    sig_int_handler.sa_handler = kill_children;
    sigemptyset(&sig_int_handler.sa_mask);
    sig_int_handler.sa_flags = 0;
    sigaction(SIGINT, &sig_int_handler, NULL);

    // Concatenate the flags into a single string to be executed when setup is
    // complete.
    std::stringstream command_stream;
    pid_t harness_pid = getpid();
    bool inserted_harness = false;
    bool found_pin = false;
    for (int i = 0; i < argc; i++) {
        // Look for "-pin" to signal end of harness args
        if ((strncmp(argv[i], FIRST_PIN_ARG, strlen(FIRST_PIN_ARG)) == 0) &&
            (strnlen(argv[i], strlen(FIRST_PIN_ARG) + 1) == strlen(FIRST_PIN_ARG))) {
            found_pin = true;
            continue;
        }

        // Bypass harness-specific arguments and start with pin
        if (!found_pin)
            continue;

        // If the next arg is "-s", add the harness_pid flag.
        if ((strncmp(argv[i], LAST_PINTOOL_ARG, strlen(LAST_PINTOOL_ARG)) == 0) &&
            (strnlen(argv[i], strlen(LAST_PINTOOL_ARG) + 1) == strlen(LAST_PINTOOL_ARG))) {
            command_stream << " " << HARNESS_PID_FLAG << " " << harness_pid << " ";
            inserted_harness = true;
        }

        // Pass down arg to children
        command_stream << argv[i] << " ";
    }
    if (!found_pin || !inserted_harness) {
        std:: cerr << "Failed to find -pin or -s args!" << std::endl;
        abort();
    }
    command_stream << "-- ";  // For appending the benchmark program arguments.

    std::cerr << "HARNESS CMD: " << command_stream.str() << std::endl;

    // Shared object keys are prefixed by the harness pid that created them.
    std::stringstream harness_pid_stream;
    harness_pid_stream << harness_pid;
    std::string shared_memory_key =
        harness_pid_stream.str() + std::string(XIOSIM_SHARED_MEMORY_KEY);
    std::string init_counter_key =
        harness_pid_stream.str() + std::string(XIOSIM_INIT_COUNTER_KEY);
    std::string shared_lock_key =
        harness_pid_stream.str() + std::string(XIOSIM_INIT_SHARED_LOCK);

    // Creates a new shared segment.
    global_shm = new managed_shared_memory(open_or_create, shared_memory_key.c_str(),
        DEFAULT_SHARED_MEMORY_SIZE);

    // Sets up a counter for multiprogramming. It is initialized to the number of
    // processes that are to be forked. When each forked process is about to start
    // the pin-instructed program, it will decrement this counter atomically. When
    // the counter reaches zero, the process is allowed to continue execution.
    // This also initializes a shared mutex for the forked processes to use for
    // synchronizing access to this counter.
    permissions perm;
    perm.set_unrestricted();
    int *counter =
        global_shm->find_or_construct<int>(init_counter_key.c_str())(harness_num_processes);
    (void) counter;
    named_mutex init_lock(open_or_create, shared_lock_key.c_str(), perm);

    SHARED_VAR_INIT(XIOSIM_LOCK, printing_lock);
    SHARED_VAR_INIT(int, num_processes, harness_num_processes - 1);
    SHARED_VAR_INIT(int, next_asid, 0);
    InitIPCQueues();
    init_lock.unlock();

    // Track the pids of all children.
    harness_pids = new pid_t[harness_num_processes];

    // Create a process for timing simulator and store its pid.
    harness_pids[harness_num_processes - 1] = fork_timing_simulator(
        command_stream.str(), debug_timing);

    // Fork all the benchmark child processes.
    int status;
    int nthprocess = 0;
    for (int program = 0; program < num_programs; program++) {
        cfg_t *program_cfg = cfg_getnsec(cfg, "program", program);
        int instances = cfg_getint(program_cfg, "instances");

        for (int process = 0; process < instances; process++) {
            harness_pids[nthprocess] = fork();
            std::string run_str = command_stream.str();

            // Append program command line arguments.
            std::stringstream ss;
            ss << cfg_getstr(program_cfg, "exec_path") << " " <<
                cfg_getstr(program_cfg, "command_line_args");
            run_str += ss.str();

            switch (harness_pids[nthprocess]) {
              case 0:  {  // child
                setpgid(0, 0);
                int ret = system(run_str.c_str());
                if (WIFSIGNALED(ret))
                    abort();
                exit(WEXITSTATUS(ret));
                break;
              }
              case -1:  {
                perror("Fork failed.");
                break;
              }
              default:  {  // parent
                std::cout << "New producer: " << harness_pids[nthprocess] << std::endl;
                nthprocess++;
                break;
              }
            }
        }
    }

    // Waits for the children process to finish. If any of the children, including
    // the timing simulator process, terminate unexpectedly, then ALL children are
    // killed immediately. Otherwise, it waits for only the producer children to
    // finish normally. The timing simulator is instructed to terminate
    // separately later.
    std::cout << "[HARNESS] Waiting for feeder children to finish." << std::endl;
    for (int i = 0; i < harness_num_processes-1; i++) {
        // Wait for any child process to finish.
        pid_t terminated_process = wait(&status);
        if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
            std::cerr << "Process " << terminated_process << " failed." << std::endl;
            kill_children(status);
        }
    }

    std::cout << "[HARNESS] Letting timing_sim finish" << std::endl;
    /* Tell timing simulator to die quietly */
    ipc_message_t msg;
    msg.StopSimulation(true);
    SendIPCMessage(msg);

    pid_t timing_pid = harness_pids[harness_num_processes-1];
    pid_t wait_res;
    do {
        wait_res = waitpid(timing_pid, &status, 0);
        if (wait_res == -1) {
            std::cout << "[HARNESS] waitpid(" << timing_pid << ") failed with: " << strerror(wait_res) << std::endl;
            break;
        }
    } while (wait_res != timing_pid);

    if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
        std::cerr << "Process " << timing_pid << " failed." << std::endl;
    }

    remove_shared_memory();
    std::cout << "[HARNESS] Parent exiting." << std::endl;
    delete[](harness_pids);
    return 0;
}

#pragma GCC diagnostic pop
<|endoftext|>


<commit_before>#include <SFML/Graphics.hpp>
#include <SFML/OpenGL.hpp>

#include "Test3DView.hpp"
#include "Main.hpp"

Test3DView::Test3DView(){}

// Calls to glColor3f no longer do anything when lighting is enabled.
// You have to set material properties. This does BOTH, for MAXIMUM
// COLORNESS and EXTREME FABULOSITY
#define COLOR_LIGHTING(r,g,b) {				\
	float color[] = { r, g, b, 1 };			\
	glMaterialfv(GL_FRONT, GL_DIFFUSE, color);	\
	glColor3f(r,g,b);				\
    }

void Test3DView::draw(sf::RenderTarget*rt){
    static sf::Clock clock;

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glTranslatef(0.f, 0.f, -200.f);

    // rotate world by 90 degrees widdershins about k hat
    //glRotatef(90,0,0,1);

    glRotatef(-45,1,0,0);
    glRotatef(-135,0,0,1);

    float pos[] = { 100, 100, 100, 1 };
    glLightfv(GL_LIGHT0, GL_POSITION, pos);
    //float cutoff[] = { 45 };
    //glLightfv(GL_LIGHT0, GL_SPOT_CUTOFF, cutoff);

    // Draw axes.
    glBegin(GL_LINES);
    {
	COLOR_LIGHTING(1,0,0);
	glVertex3f(0,   0,   0);
	glVertex3f(100, 0,   0);

    	COLOR_LIGHTING(0,1,0);
	glVertex3f(0,   0,   0);
	glVertex3f(0, 100,   0);

    	COLOR_LIGHTING(0,0,1);
	glVertex3f(0,   0,   0);
	glVertex3f(0,   0, 100);
    }
    glEnd();

    // ----------------------------------------------------------------
    // Enter box coordinates
    glPushMatrix();
    glRotatef(clock.getElapsedTime().asSeconds()*50, 1, 0, 0);
    glRotatef(clock.getElapsedTime().asSeconds()*30, 0, 1, 0);
    glRotatef(clock.getElapsedTime().asSeconds()*90, 0, 0, 1);

    glBegin(GL_QUADS);
    {
	float size = 50;

	// BLUE
	COLOR_LIGHTING(0,0,1);
	glNormal3f(0,0,-1);
	glVertex3f(-size,-size,-size);
	glVertex3f(-size, size,-size);
	glVertex3f( size, size,-size);
	glVertex3f( size,-size,-size);

	// GREEN
	COLOR_LIGHTING(0,1,0);
	glNormal3f(0,0,1);
	glVertex3f( size,-size, size);
	glVertex3f( size, size, size);
	glVertex3f(-size, size, size);
	glVertex3f(-size,-size, size);

	// BLUE-GREEN
	COLOR_LIGHTING(0,1,1);
	glNormal3d(-1,0,0);
	glVertex3f(-size,-size, size);
	glVertex3f(-size, size, size);
	glVertex3f(-size, size,-size);
	glVertex3f(-size,-size,-size);

	// RED
	COLOR_LIGHTING(1,0,0);
	glNormal3d(1,0,0);
	glVertex3f( size,-size,-size);
	glVertex3f( size, size,-size);
	glVertex3f( size, size, size);
	glVertex3f( size,-size, size);

	// PURPLE
	COLOR_LIGHTING(1,0,1);
	glNormal3d(0,-1,0);
	glVertex3f(-size,-size, size);
	glVertex3f(-size,-size,-size);
	glVertex3f( size,-size,-size);
	glVertex3f( size,-size, size);

	// YELLOW
	COLOR_LIGHTING(1,1,0);
	glNormal3d(0,1,0);
	glVertex3f( size, size, size);
	glVertex3f( size, size,-size);
	glVertex3f(-size, size,-size);
	glVertex3f(-size, size, size);
    }
    glEnd();

    // Leave box coordinates
    glPopMatrix();
    // ---------------------------------------------------------------------

    glFlush();
}

void Test3DView::pause(){}
void Test3DView::unpause(){}
<commit_msg>Now with knowledge of 2D HUD drawing over 3D scenes, SFML text drawing over opengl, and a pretty FPS graph!<commit_after>#include <SFML/Graphics.hpp>
#include <SFML/OpenGL.hpp>

#include <stdio.h>

#include "Test3DView.hpp"
#include "Main.hpp"

Test3DView::Test3DView(){}

// Calls to glColor3f no longer do anything when lighting is enabled.
// You have to set material properties. This does BOTH, for MAXIMUM
// COLORNESS and EXTREME FABULOSITY
#define COLOR_LIGHTING(r,g,b) {				\
	float color[] = { r, g, b, 1 };			\
	glMaterialfv(GL_FRONT, GL_DIFFUSE, color);	\
	glColor3f(r,g,b);				\
    }

void Test3DView::draw(sf::RenderTarget*rt){
    static sf::Clock clock;

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glTranslatef(0.f, 0.f, -200.f);

    // rotate world by 90 degrees widdershins about k hat
    //glRotatef(90,0,0,1);

    glRotatef(-45,1,0,0);
    glRotatef(-135,0,0,1);

    float pos[] = { 100, 100, 100, 1 };
    glLightfv(GL_LIGHT0, GL_POSITION, pos);
    //float cutoff[] = { 45 };
    //glLightfv(GL_LIGHT0, GL_SPOT_CUTOFF, cutoff);

    // Draw axes.
    glBegin(GL_LINES);
    {
	COLOR_LIGHTING(1,0,0);
	glVertex3f(0,   0,   0);
	glVertex3f(100, 0,   0);

    	COLOR_LIGHTING(0,1,0);
	glVertex3f(0,   0,   0);
	glVertex3f(0, 100,   0);

    	COLOR_LIGHTING(0,0,1);
	glVertex3f(0,   0,   0);
	glVertex3f(0,   0, 100);
    }
    glEnd();

    // ----------------------------------------------------------------
    // Enter box coordinates
    glPushMatrix();
    glRotatef(clock.getElapsedTime().asSeconds()*50, 1, 0, 0);
    glRotatef(clock.getElapsedTime().asSeconds()*30, 0, 1, 0);
    glRotatef(clock.getElapsedTime().asSeconds()*90, 0, 0, 1);

    glBegin(GL_QUADS);
    {
	float size = 50;

	// BLUE
	COLOR_LIGHTING(0,0,1);
	glNormal3f(0,0,-1);
	glVertex3f(-size,-size,-size);
	glVertex3f(-size, size,-size);
	glVertex3f( size, size,-size);
	glVertex3f( size,-size,-size);

	// GREEN
	COLOR_LIGHTING(0,1,0);
	glNormal3f(0,0,1);
	glVertex3f( size,-size, size);
	glVertex3f( size, size, size);
	glVertex3f(-size, size, size);
	glVertex3f(-size,-size, size);

	// BLUE-GREEN
	COLOR_LIGHTING(0,1,1);
	glNormal3d(-1,0,0);
	glVertex3f(-size,-size, size);
	glVertex3f(-size, size, size);
	glVertex3f(-size, size,-size);
	glVertex3f(-size,-size,-size);

	// RED
	COLOR_LIGHTING(1,0,0);
	glNormal3d(1,0,0);
	glVertex3f( size,-size,-size);
	glVertex3f( size, size,-size);
	glVertex3f( size, size, size);
	glVertex3f( size,-size, size);

	// PURPLE
	COLOR_LIGHTING(1,0,1);
	glNormal3d(0,-1,0);
	glVertex3f(-size,-size, size);
	glVertex3f(-size,-size,-size);
	glVertex3f( size,-size,-size);
	glVertex3f( size,-size, size);

	// YELLOW
	COLOR_LIGHTING(1,1,0);
	glNormal3d(0,1,0);
	glVertex3f( size, size, size);
	glVertex3f( size, size,-size);
	glVertex3f(-size, size,-size);
	glVertex3f(-size, size, size);
    }
    glEnd();

    // Leave box coordinates
    glPopMatrix();

    // ---------------------------------------------------------------------
    // We here draw debugging information on the screen.

    // Calculate FPS
    static sf::Time lastTime = clock.getElapsedTime();
    sf::Time dtime = clock.getElapsedTime()-lastTime;
    lastTime = clock.getElapsedTime();
    float fps = 1/dtime.asSeconds(); // 1/(seconds/frame) = frame/second

    // Draw fps graph
    {
	rt->pushGLStates();

	// Enter 2D Mode
	glMatrixMode(GL_PROJECTION);
	glPushMatrix();
	glLoadIdentity();
	glOrtho(0,Main::kWindowWidth,Main::kWindowHeight,0,-1,10);
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	glDisable(GL_CULL_FACE);
	glClear(GL_DEPTH_BUFFER_BIT);

	// ----------------------- do 2D drawing
	const int graphH = 100;
	const int graphW = 200;
	static int pastFPSs[graphW];
	static int pastFPSsIndex = 0;
	pastFPSsIndex = (pastFPSsIndex+1)%graphW;
	pastFPSs[pastFPSsIndex] = (int)fps;

	glTranslatef(Main::kWindowWidth-graphW,0,0);

	// draw background
	glColor4f(1,0,0,.2);
	glBegin(GL_QUADS);
	{
	    glVertex2f(0,0);
	    glVertex2f(graphW,0);
	    glVertex2f(graphW,graphH);
	    glVertex2f(0,graphH);
	}
	glEnd();

	// draw data
	glBegin(GL_LINES);
	glColor4f(0,0,1,.4);
	for(int i=0; i<graphW; i++){
	    int index = (pastFPSsIndex+i+1)%graphW;
	    glVertex2f(i,graphH);
	    glVertex2f(i,graphH-pastFPSs[index]);
	}
	glEnd();
	// ------------------------ done with 2D drawing

	// Bring back 3D Mode
	glMatrixMode(GL_PROJECTION);
	glPopMatrix();
	glMatrixMode(GL_MODELVIEW);

	rt->popGLStates();
    }

    glFlush();

    // -------------------------
    // Enter SFML drawing mode
    rt->pushGLStates();
    rt->resetGLStates();

    sf::Font font;
    if(!font.loadFromFile("res/lucida-console.ttf")){
	printf("Warning: couldn't load font for Test3DView\n");
    }

    // draw current fps
    char s[9];
    snprintf(s, 8, "%0.1ffps", fps);
    sf::Text text(s, font, 12);
    text.setPosition(Main::kWindowWidth-50, 0);
    rt->draw(text);

    rt->popGLStates();
    // Exit SFML drawing mode
    // -------------------------
}

void Test3DView::pause(){}
void Test3DView::unpause(){}
<|endoftext|>


<commit_before>#ifndef ARPACA_ARPACA_HPP_
#define ARPACA_ARPACA_HPP_

#include <algorithm>
#include <stdexcept>
#include <vector>
#include <eigen3/Eigen/Core>

// ARPACK interface
extern "C" {

// Lanczos method (double precision)
void dsaupd_(int* ido, char* bmat, int* n, char* which, int* nev, double* tol,
             double* resid, int* ncv, double* v, int* ldv, int* iparam,
             int* ipntr, double* workd, double* workl, int* lworkl, int* info);

// Lanczos method (single precision)
void ssaupd_(int* ido, char* bmat, int* n, char* which, int* nev, float* tol,
             float* resid, int* ncv, float* v, int* ldv, int* iparam,
             int* ipntr, float* workd, float* workl, int* lworkl, int* info);

// Eigenvalue problem solver for result of dsaupd_
void dseupd_(int* rvec, char* howmny, int* select, double* d, double* z,
             int* ldz, double* sigma, char* bmat, int* n, char* which, int* nev,
             double* tol, double* resid, int* ncv, double* v, int* ldv,
             int* iparam, int* ipntr, double* workd, double* workl, int* lworkl,
             int* info);

// Eigenvalue problem solver for result of fsaupd_
void sseupd_(int* rvec, char* howmny, int* select, float* d, float* z, int* ldz,
             float* sigma, char* bmat, int* n, char* which, int* nev,
             float* tol, float* resid, int* ncv, float* v, int* ldv,
             int* iparam, int* ipntr, float* workd, float* workl, int* lworkl,
             int* info);

}  // extern "C"

namespace arpaca {

// Type of eigenvalue to solve.
// Arpaca::SymmetricEigenSolver computes part of eigenvalues and eigenvectors.
// EigenvalueType indicates which part is to be solved.
enum EigenvalueType {
  ALGEBRAIC_LARGEST,  // Largest as real numbers
  ALGEBRAIC_SMALLEST,  // Smallest as real numbers
  ALGEBRAIC_BOTH_END,  // Both end as real numbers
  MAGNITUDE_LARGEST,  // Largest as absolute values
  MAGNITUDE_SMALLEST,  // Smallest as absolute values
};

namespace detail {

inline const char* GetNameOfEigenvalueType(EigenvalueType type)
{
  switch (type) {
    case ALGEBRAIC_LARGEST: return "LA";
    case ALGEBRAIC_SMALLEST: return "SA";
    case ALGEBRAIC_BOTH_END: return "BE";
    case MAGNITUDE_LARGEST: return "LM";
    case MAGNITUDE_SMALLEST: return "SM";
  }
  throw std::invalid_argument("Invalid eigenvalue type");
}

inline void GetNameOfEigenvalueType(EigenvalueType type, char* to)
{
  const char* from = GetNameOfEigenvalueType(type);
  std::copy(from, from + 3, to);
}

template<typename Scalar>
void saupd(int* ido, char* bmat, int* n, char* which, int* nev, Scalar* tol,
           Scalar* resid, int* ncv, Scalar* v, int* ldv, int* iparam,
           int* ipntr, Scalar* workd, Scalar* workl, int* lworkl, int* info)
{
  dsaupd_(ido, bmat, n, which, nev, tol, resid, ncv, v, ldv, iparam, ipntr,
          workd, workl, lworkl, info);
}

template<>
inline void saupd<float>(int* ido, char* bmat, int* n, char* which, int* nev,
                         float* tol, float* resid, int* ncv, float* v, int* ldv,
                         int* iparam, int* ipntr, float* workd, float* workl,
                         int* lworkl, int* info)
{
  ssaupd_(ido, bmat, n, which, nev, tol, resid, ncv, v, ldv, iparam, ipntr,
          workd, workl, lworkl, info);
}

template<typename Scalar>
void seupd(int* rvec, char* howmny, int* select, Scalar* d, Scalar* z, int* ldz,
           Scalar* sigma, char* bmat, int* n, char* which, int* nev,
           Scalar* tol, Scalar* resid, int* ncv, Scalar* v, int* ldv,
           int* iparam, int* ipntr, Scalar* workd, Scalar* workl, int* lworkl,
           int* info)
{
  dseupd_(rvec, howmny, select, d, z, ldz, sigma, bmat, n, which, nev, tol,
          resid, ncv, v, ldv, iparam, ipntr, workd, workl, lworkl, info);
}

template<>
void seupd<float>(int* rvec, char* howmny, int* select, float* d, float* z,
                  int* ldz, float* sigma, char* bmat, int* n, char* which,
                  int* nev, float* tol, float* resid, int* ncv, float* v,
                  int* ldv, int* iparam, int* ipntr, float* workd, float* workl,
                  int* lworkl, int* info)
{
  sseupd_(rvec, howmny, select, d, z, ldz, sigma, bmat, n, which, nev, tol,
          resid, ncv, v, ldv, iparam, ipntr, workd, workl, lworkl, info);
}

}  // namespace detail

// Eigenvalue problem solver for symmetric matrix (especially when sparse).
// It partly computes eigenvalues and eigenvectors of a symmetric matrix A using
// ARPACK, which implements Implicitly Restarted Arnoldi (IRA) method.
template<typename Scalar = double>
class SymmetricEigenSolver {
 public:
  typedef Eigen::Matrix<Scalar, Eigen::Dynamic, Eigen::Dynamic> Matrix;
  typedef Eigen::Matrix<Scalar, Eigen::Dynamic, 1> Vector;

  SymmetricEigenSolver()
      : num_lanczos_vectors_(100),
        max_iterations_(100),
        tolerance_(-1)
  {
    SetEigenvalueType(MAGNITUDE_LARGEST);
  }

  // Sets # of Lanczos vectors.
  // X * (# of objective eigenvalues) is recommended, where X is in [2, 4].
  //
  // NOTE: IRA iteratively improves subspace containing the objective
  // eigenvectors which consists of |num| basis vectors. Too small |num| causes
  // slow convergence, while too large |num| requires large size of memory and
  // even make each iteration too slow.
  void SetNumLanczosVectors(int num)
  {
    num_lanczos_vectors_ = num;
  }

  // Sets max # of Lanczos iterations.
  void SetMaxIterations(int num)
  {
    if (num <= 0)
      throw std::invalid_argument("Given max iterations is not positive");
    max_iterations_ = num;
  }

  // Sets precision tolerance for computation.
  // Negative value means machine precision.
  void SetTolerance(Scalar tolerance)
  {
    tolerance_ = tolerance;
  }

  // Sets the type of eigenvalues to solve.
  // See EigenvalueType for detail.
  void SetEigenvalueType(EigenvalueType type)
  {
    detail::GetNameOfEigenvalueType(type, eigenvalue_type_name_);
  }

  // Solves the eigenvalue problem "Ax = cx".
  // A must be a real symmetric matrix, which is not explicitly indicated.
  //
  // dimension:       Dimension of space (rows or cols of the matrix A).
  // num_eigenvalues: # of eigenvalues to compute.
  // op:              Functor that computes operation y = A * x. Signature is
  //                  op(x, y), where x and y are slices of Eigen::VectorX{d,f},
  //                  i.e. return values of Eigen::VectorX{d,f}::middleRows().
  template<typename OperatorATimes>
  void Solve(int dimension, int num_eigenvalues, OperatorATimes op)
  {
    if (dimension <= 0)
      throw std::invalid_argument("Given dimension is not positive");
    if (num_eigenvalues <= 0)
      throw std::invalid_argument("Given num_eigenvalues is not positive");

    // Eigenvalues (Lanczos)
    int info = 0;

    num_eigenvalues = std::max(std::min(num_eigenvalues, dimension), 0);

    int num_lanczos_vectors = std::min(num_lanczos_vectors_, dimension);
    if (num_lanczos_vectors <= 0)
      num_lanczos_vectors = num_eigenvalues * 3;
    else if (num_lanczos_vectors <= num_eigenvalues)
      num_lanczos_vectors = num_eigenvalues + 1;

    char bmat[2] = { 'I' };
    int leading_dimension = dimension;

    int iparam[11] = {}, ipntr[11] = {};
    iparam[0] = 1;  // Use effective shift
    iparam[2] = max_iterations_;
    iparam[3] = 1;  // block size
    iparam[6] = 1;  // Solve standard eigenvalue problem

    int lworkl = (num_lanczos_vectors + 8) * num_lanczos_vectors;
    Vector workd(3 * dimension), workl(lworkl);
    std::vector<Scalar> residue(dimension);

    Matrix lanczos_vectors(dimension, num_lanczos_vectors);

    for (int mode = 0; mode != 99; ) {
      detail::saupd(&mode, bmat, &dimension, eigenvalue_type_name_,
                    &num_eigenvalues, &tolerance_, &residue[0],
                    &num_lanczos_vectors, lanczos_vectors.data(),
                    &leading_dimension, iparam, ipntr, workd.data(),
                    workl.data(), &lworkl, &info);

      switch (mode) {
        case -1:
          op(workd.middleRows(ipntr[0] - 1, dimension),
             workd.middleRows(ipntr[1] - 1, dimension));
          break;
        case 1:
          op(workd.middleRows(ipntr[2] - 1, dimension),
             workd.middleRows(ipntr[1] - 1, dimension));
          workd.middleRows(ipntr[2] - 1, dimension) =
              workd.middleRows(ipntr[0] - 1, dimension);
          break;
        case 2:
          workd.middleRows(ipntr[1] - 1, dimension) =
              workd.middleRows(ipntr[0] - 1, dimension);
          break;
      }
    }

    info_ = info;
    num_actual_iterations_ = iparam[2];
    num_converged_eigenvalues_ = iparam[4];

    // Eigenvectors
    int rvec = 1;  // Compute eigenvectors
    // Computation of selected eigenvectors is not implemented yet in ARPACK
    char howmany[2] = { 'A' };
    std::vector<int> select(num_lanczos_vectors);
    Scalar sigma;  // not used

    eigenvectors_.resize(dimension, num_eigenvalues);
    eigenvalues_.resize(num_eigenvalues);

    detail::seupd(&rvec, howmany, &select[0], eigenvalues_.data(),
                  eigenvectors_.data(), &dimension, &sigma, bmat, &dimension,
                  eigenvalue_type_name_, &num_eigenvalues, &tolerance_,
                  &residue[0], &num_lanczos_vectors, lanczos_vectors.data(),
                  &leading_dimension, iparam, ipntr, workd.data(), workl.data(),
                  &lworkl, &info);
  }

  // Gets the computed eigenvectors.
  // Returned matrix contains each eigenvector in each column.
  const Matrix& eigenvectors() const
  {
    return eigenvectors_;
  }

  // Gets the computed eigenvalues.
  const Vector& eigenvalues() const
  {
    return eigenvalues_;
  }

  // Moves the eigenvectors.
  Matrix MoveEigenvectors()
  {
    Matrix eigenvectors;
    eigenvectors.swap(eigenvectors_);
    return eigenvectors;
  }

  // Moves the eigenvalues.
  Vector MoveEigenvalues()
  {
    Vector eigenvalues;
    eigenvalues.swap(eigenvalues_);
    return eigenvalues;
  }

  int num_actual_iterations() const
  {
    return num_actual_iterations_;
  }

  int num_converged_eigenvalues() const
  {
    return num_converged_eigenvalues_;
  }

  // Gets information message based on error code of ARPACK.
  const char* GetInfo() const
  {
    switch (info_) {
      case 0: return "Normal exit";
      case 1: return "Not converged";
      case 3: return "num_lanczos_vectors may be too small";
      case -8: return "Error on tridiagonal eigenvalue calculation";
      case -9999: return "Could not build an Arnoldi factorization";
    }
    return "Arpaca internal error";
  }

 private:
  Matrix eigenvectors_;
  Vector eigenvalues_;

  int num_lanczos_vectors_;
  int max_iterations_;
  Scalar tolerance_;

  int info_;
  int num_actual_iterations_;
  int num_converged_eigenvalues_;

  char eigenvalue_type_name_[3];
};

template<typename MatrixType>
class DefaultOperator {
 public:
  explicit DefaultOperator(MatrixType& m)
      : m_(m)
  {}

  template<typename X, typename Y>
  void operator()(X x, Y y) const
  {
    y = m_ * x;
  }

 private:
  MatrixType& m_;
};

template<typename Matrix>
inline DefaultOperator<Matrix> MakeDefaultOperator(Matrix& A)
{
  return DefaultOperator<Matrix>(A);
}

template<typename Matrix>
SymmetricEigenSolver<typename Matrix::Scalar> Solve(
    const Matrix& A,
    int num_eigenvalues,
    EigenvalueType type = MAGNITUDE_LARGEST,
    int num_lanczos_vectors = 0,
    int max_iterations = 10000,
    typename Matrix::Scalar tolerance = -1)
{
  SymmetricEigenSolver<typename Matrix::Scalar> solver;
  solver.SetEigenvalueType(type);
  solver.SetMaxIterations(max_iterations);
  solver.SetNumLanczosVectors(num_lanczos_vectors);
  solver.SetTolerance(tolerance);

  solver.Solve(A.rows(), num_eigenvalues, MakeDefaultOperator(A));

  return solver;
}

}  // namespace arpaca

#endif  // ARPACA_ARPACA_HPP_
<commit_msg>fix comments<commit_after>#ifndef ARPACA_ARPACA_HPP_
#define ARPACA_ARPACA_HPP_

#include <algorithm>
#include <stdexcept>
#include <vector>
#include <eigen3/Eigen/Core>

// ARPACK interface
extern "C" {

// Lanczos method (double precision)
void dsaupd_(int* ido, char* bmat, int* n, char* which, int* nev, double* tol,
             double* resid, int* ncv, double* v, int* ldv, int* iparam,
             int* ipntr, double* workd, double* workl, int* lworkl, int* info);

// Lanczos method (single precision)
void ssaupd_(int* ido, char* bmat, int* n, char* which, int* nev, float* tol,
             float* resid, int* ncv, float* v, int* ldv, int* iparam,
             int* ipntr, float* workd, float* workl, int* lworkl, int* info);

// Eigenvalue problem solver for result of dsaupd_
void dseupd_(int* rvec, char* howmny, int* select, double* d, double* z,
             int* ldz, double* sigma, char* bmat, int* n, char* which, int* nev,
             double* tol, double* resid, int* ncv, double* v, int* ldv,
             int* iparam, int* ipntr, double* workd, double* workl, int* lworkl,
             int* info);

// Eigenvalue problem solver for result of ssaupd_
void sseupd_(int* rvec, char* howmny, int* select, float* d, float* z, int* ldz,
             float* sigma, char* bmat, int* n, char* which, int* nev,
             float* tol, float* resid, int* ncv, float* v, int* ldv,
             int* iparam, int* ipntr, float* workd, float* workl, int* lworkl,
             int* info);

}  // extern "C"

namespace arpaca {

// Type of eigenvalue to solve.
// Arpaca::SymmetricEigenSolver computes part of eigenvalues and eigenvectors.
// EigenvalueType indicates which part is to be solved.
enum EigenvalueType {
  ALGEBRAIC_LARGEST,  // Largest as real numbers
  ALGEBRAIC_SMALLEST,  // Smallest as real numbers
  ALGEBRAIC_BOTH_END,  // Both end as real numbers
  MAGNITUDE_LARGEST,  // Largest as absolute values
  MAGNITUDE_SMALLEST,  // Smallest as absolute values
};

namespace detail {

inline const char* GetNameOfEigenvalueType(EigenvalueType type)
{
  switch (type) {
    case ALGEBRAIC_LARGEST: return "LA";
    case ALGEBRAIC_SMALLEST: return "SA";
    case ALGEBRAIC_BOTH_END: return "BE";
    case MAGNITUDE_LARGEST: return "LM";
    case MAGNITUDE_SMALLEST: return "SM";
  }
  throw std::invalid_argument("Invalid eigenvalue type");
}

inline void GetNameOfEigenvalueType(EigenvalueType type, char* to)
{
  const char* from = GetNameOfEigenvalueType(type);
  std::copy(from, from + 3, to);
}

template<typename Scalar>
void saupd(int* ido, char* bmat, int* n, char* which, int* nev, Scalar* tol,
           Scalar* resid, int* ncv, Scalar* v, int* ldv, int* iparam,
           int* ipntr, Scalar* workd, Scalar* workl, int* lworkl, int* info)
{
  dsaupd_(ido, bmat, n, which, nev, tol, resid, ncv, v, ldv, iparam, ipntr,
          workd, workl, lworkl, info);
}

template<>
inline void saupd<float>(int* ido, char* bmat, int* n, char* which, int* nev,
                         float* tol, float* resid, int* ncv, float* v, int* ldv,
                         int* iparam, int* ipntr, float* workd, float* workl,
                         int* lworkl, int* info)
{
  ssaupd_(ido, bmat, n, which, nev, tol, resid, ncv, v, ldv, iparam, ipntr,
          workd, workl, lworkl, info);
}

template<typename Scalar>
void seupd(int* rvec, char* howmny, int* select, Scalar* d, Scalar* z, int* ldz,
           Scalar* sigma, char* bmat, int* n, char* which, int* nev,
           Scalar* tol, Scalar* resid, int* ncv, Scalar* v, int* ldv,
           int* iparam, int* ipntr, Scalar* workd, Scalar* workl, int* lworkl,
           int* info)
{
  dseupd_(rvec, howmny, select, d, z, ldz, sigma, bmat, n, which, nev, tol,
          resid, ncv, v, ldv, iparam, ipntr, workd, workl, lworkl, info);
}

template<>
void seupd<float>(int* rvec, char* howmny, int* select, float* d, float* z,
                  int* ldz, float* sigma, char* bmat, int* n, char* which,
                  int* nev, float* tol, float* resid, int* ncv, float* v,
                  int* ldv, int* iparam, int* ipntr, float* workd, float* workl,
                  int* lworkl, int* info)
{
  sseupd_(rvec, howmny, select, d, z, ldz, sigma, bmat, n, which, nev, tol,
          resid, ncv, v, ldv, iparam, ipntr, workd, workl, lworkl, info);
}

}  // namespace detail

// Eigenvalue problem solver for symmetric matrix (especially sparse one).
// It partly computes eigenvalues and eigenvectors of a symmetric matrix A using
// ARPACK, which implements Implicitly Restarted Arnoldi (IRA) method.
template<typename Scalar = double>
class SymmetricEigenSolver {
 public:
  typedef Eigen::Matrix<Scalar, Eigen::Dynamic, Eigen::Dynamic> Matrix;
  typedef Eigen::Matrix<Scalar, Eigen::Dynamic, 1> Vector;

  SymmetricEigenSolver()
      : num_lanczos_vectors_(100),
        max_iterations_(100),
        tolerance_(-1)
  {
    SetEigenvalueType(MAGNITUDE_LARGEST);
  }

  // Sets # of Lanczos vectors.
  // From two to four times larger than # of objective eigenvalues is
  // recommended.
  //
  // NOTE: IRA approximates subspace containing the objective eigenvectors by
  // constructing |num| orthogonal basis vectors in each iteration, which is
  // improved for each iteration. Consequently, too small |num| causes slow
  // convergence, while too large |num| requires large size of memory and even
  // make each iteration too slow.
  void SetNumLanczosVectors(int num)
  {
    num_lanczos_vectors_ = num;
  }

  // Sets max # of Lanczos iterations.
  void SetMaxIterations(int num)
  {
    if (num <= 0)
      throw std::invalid_argument("Given # of iterations is not positive");
    max_iterations_ = num;
  }

  // Sets precision tolerance for computation.
  // Negative value means machine precision.
  void SetTolerance(Scalar tolerance)
  {
    tolerance_ = tolerance;
  }

  // Sets the type of eigenvalues to solve.
  // See EigenvalueType for detail.
  void SetEigenvalueType(EigenvalueType type)
  {
    detail::GetNameOfEigenvalueType(type, eigenvalue_type_name_);
  }

  // Solves the eigenvalue problem of a real symmetrix matrix A.
  // The matrix A is not set explicitly. Solve() only needs the operator
  // y = A * x.
  //
  // dimension:       Dimension of space (rows or cols of the matrix A).
  // num_eigenvalues: # of eigenvalues to compute.
  // op:              Functor that computes operation y = A * x. Signature is
  //                  op(x, y), where x and y are slices of Eigen::VectorX{d,f},
  //                  i.e. return values of Eigen::VectorX{d,f}::middleRows().
  template<typename OperatorATimes>
  void Solve(int dimension, int num_eigenvalues, OperatorATimes op)
  {
    if (dimension <= 0)
      throw std::invalid_argument("Given dimension is not positive");
    if (num_eigenvalues <= 0)
      throw std::invalid_argument("Given num_eigenvalues is not positive");

    // Eigenvalues (Lanczos)
    int info = 0;

    num_eigenvalues = std::max(std::min(num_eigenvalues, dimension), 0);

    int num_lanczos_vectors = std::min(num_lanczos_vectors_, dimension);
    if (num_lanczos_vectors <= 0)
      num_lanczos_vectors = num_eigenvalues * 3;
    else if (num_lanczos_vectors <= num_eigenvalues)
      num_lanczos_vectors = num_eigenvalues + 1;

    char bmat[2] = { 'I' };
    int leading_dimension = dimension;

    int iparam[11] = {}, ipntr[11] = {};
    iparam[0] = 1;  // Use effective shift
    iparam[2] = max_iterations_;
    iparam[3] = 1;  // block size
    iparam[6] = 1;  // Solve standard eigenvalue problem

    int lworkl = (num_lanczos_vectors + 8) * num_lanczos_vectors;
    Vector workd(3 * dimension), workl(lworkl);
    std::vector<Scalar> residue(dimension);

    Matrix lanczos_vectors(dimension, num_lanczos_vectors);

    for (int mode = 0; mode != 99; ) {
      detail::saupd(&mode, bmat, &dimension, eigenvalue_type_name_,
                    &num_eigenvalues, &tolerance_, &residue[0],
                    &num_lanczos_vectors, lanczos_vectors.data(),
                    &leading_dimension, iparam, ipntr, workd.data(),
                    workl.data(), &lworkl, &info);

      switch (mode) {
        case -1:
          op(workd.middleRows(ipntr[0] - 1, dimension),
             workd.middleRows(ipntr[1] - 1, dimension));
          break;
        case 1:
          op(workd.middleRows(ipntr[2] - 1, dimension),
             workd.middleRows(ipntr[1] - 1, dimension));
          workd.middleRows(ipntr[2] - 1, dimension) =
              workd.middleRows(ipntr[0] - 1, dimension);
          break;
        case 2:
          workd.middleRows(ipntr[1] - 1, dimension) =
              workd.middleRows(ipntr[0] - 1, dimension);
          break;
      }
    }

    info_ = info;
    num_actual_iterations_ = iparam[2];
    num_converged_eigenvalues_ = iparam[4];

    // Eigenvectors
    int rvec = 1;  // Compute eigenvectors
    // Computation of selected eigenvectors is not implemented yet in ARPACK
    char howmany[2] = { 'A' };
    std::vector<int> select(num_lanczos_vectors);
    Scalar sigma;  // not used

    eigenvectors_.resize(dimension, num_eigenvalues);
    eigenvalues_.resize(num_eigenvalues);

    detail::seupd(&rvec, howmany, &select[0], eigenvalues_.data(),
                  eigenvectors_.data(), &dimension, &sigma, bmat, &dimension,
                  eigenvalue_type_name_, &num_eigenvalues, &tolerance_,
                  &residue[0], &num_lanczos_vectors, lanczos_vectors.data(),
                  &leading_dimension, iparam, ipntr, workd.data(), workl.data(),
                  &lworkl, &info);
  }

  // Gets the computed eigenvectors.
  // Returned matrix contains each eigenvector in each column.
  const Matrix& eigenvectors() const
  {
    return eigenvectors_;
  }

  // Gets the computed eigenvalues.
  const Vector& eigenvalues() const
  {
    return eigenvalues_;
  }

  Matrix MoveEigenvectors()
  {
    Matrix eigenvectors;
    eigenvectors.swap(eigenvectors_);
    return eigenvectors;
  }

  Vector MoveEigenvalues()
  {
    Vector eigenvalues;
    eigenvalues.swap(eigenvalues_);
    return eigenvalues;
  }

  int num_actual_iterations() const
  {
    return num_actual_iterations_;
  }

  int num_converged_eigenvalues() const
  {
    return num_converged_eigenvalues_;
  }

  // Gets information message based on error code of ARPACK.
  const char* GetInfo() const
  {
    switch (info_) {
      case 0: return "Normal exit";
      case 1: return "Not converged";
      case 3: return "num_lanczos_vectors may be too small";
      case -8: return "Error on tridiagonal eigenvalue calculation";
      case -9999: return "Could not build an Arnoldi factorization";
    }
    return "Arpaca internal error";
  }

 private:
  Matrix eigenvectors_;
  Vector eigenvalues_;

  int num_lanczos_vectors_;
  int max_iterations_;
  Scalar tolerance_;

  int info_;
  int num_actual_iterations_;
  int num_converged_eigenvalues_;

  char eigenvalue_type_name_[3];
};

// Default operator for the case the matrix A is explicitly given.
template<typename MatrixType>
class DefaultOperator {
 public:
  explicit DefaultOperator(MatrixType& m)
      : m_(m)
  {}

  template<typename X, typename Y>
  void operator()(X x, Y y) const
  {
    y = m_ * x;
  }

 private:
  MatrixType& m_;
};

template<typename Matrix>
inline DefaultOperator<Matrix> MakeDefaultOperator(Matrix& A)
{
  return DefaultOperator<Matrix>(A);
}

// Solves the eigenvalue problem of a real symmetrix matrix A, where the matrix
// A is explicitly given.
template<typename Matrix>
SymmetricEigenSolver<typename Matrix::Scalar> Solve(
    const Matrix& A,
    int num_eigenvalues,
    EigenvalueType type = MAGNITUDE_LARGEST,
    int num_lanczos_vectors = 0,
    int max_iterations = 10000,
    typename Matrix::Scalar tolerance = -1)
{
  SymmetricEigenSolver<typename Matrix::Scalar> solver;
  solver.SetEigenvalueType(type);
  solver.SetMaxIterations(max_iterations);
  solver.SetNumLanczosVectors(num_lanczos_vectors);
  solver.SetTolerance(tolerance);

  solver.Solve(A.rows(), num_eigenvalues, MakeDefaultOperator(A));

  return solver;
}

}  // namespace arpaca

#endif  // ARPACA_ARPACA_HPP_
<|endoftext|>


<commit_before>// This file was developed by Thomas Müller <thomas94@gmx.net>.
// It is published under the BSD-style license contained in the LICENSE.txt file.

#include "../include/UberShader.h"

using namespace Eigen;
using namespace nanogui;
using namespace std;

TEV_NAMESPACE_BEGIN

UberShader::UberShader() {
    mShader.define("SRGB",        to_string(ETonemap::SRGB));
    mShader.define("GAMMA",       to_string(ETonemap::Gamma));
    mShader.define("FALSE_COLOR", to_string(ETonemap::FalseColor));

    mShader.define("ERROR",                   to_string(EMetric::Error));
    mShader.define("ABSOLUTE_ERROR",          to_string(EMetric::AbsoluteError));
    mShader.define("SQUARED_ERROR",           to_string(EMetric::SquaredError));
    mShader.define("RELATIVE_ABSOLUTE_ERROR", to_string(EMetric::RelativeAbsoluteError));
    mShader.define("RELATIVE_SQUARED_ERROR",  to_string(EMetric::RelativeSquaredError));

    mShader.init(
        "ubershader",

        // Vertex shader
        R"(#version 330
        uniform mat3 imageTransform;
        uniform mat3 referenceTransform;

        in vec2 position;

        out vec2 imageUv;
        out vec2 referenceUv;

        void main() {
            imageUv = (imageTransform * vec3(position, 1.0)).xy;
            referenceUv = (referenceTransform * vec3(position, 1.0)).xy;

            gl_Position = vec4(position, 1.0, 1.0);
        })",

        // Fragment shader
        R"(#version 330

        uniform sampler2D imageRed;
        uniform sampler2D imageGreen;
        uniform sampler2D imageBlue;
        uniform sampler2D imageAlpha;

        uniform sampler2D referenceRed;
        uniform sampler2D referenceGreen;
        uniform sampler2D referenceBlue;
        uniform sampler2D referenceAlpha;
        uniform bool hasReference;

        uniform float exposure;
        uniform int tonemap;
        uniform int metric;

        in vec2 imageUv;
        in vec2 referenceUv;

        out vec4 color;

        vec3 applyExposure(vec3 col) {
            return pow(2.0, exposure) * col;
        }

        float sRGB(float linear) {
            if (linear > 1.0) {
                return 1.0;
            } else if (linear < 0.0) {
                return 0.0;
            } else if (linear < 0.0031308) {
                return 12.92 * linear;
            } else {
                return 1.055 * pow(linear, 0.41666) - 0.055;
            }
        }

        vec3 applyTonemap(vec3 col) {
            switch (tonemap) {
                case SRGB:        return vec3(sRGB(col.r), sRGB(col.g), sRGB(col.b));
                case GAMMA:       return pow(col, vec3(1.0 / 2.2));
                case FALSE_COLOR: return pow(col, vec3(1.0 / 2.2));
            }
            return vec3(0.0);
        }

        vec3 applyMetric(vec3 col, vec3 reference) {
            switch (metric) {
                case ERROR:                   return col;
                case ABSOLUTE_ERROR:          return abs(col);
                case SQUARED_ERROR:           return col * col;
                case RELATIVE_ABSOLUTE_ERROR: return abs(col) / (reference + vec3(0.01));
                case RELATIVE_SQUARED_ERROR:  return col * col / (reference * reference + vec3(0.0001));
            }
            return vec3(0.0);
        }

        void main() {
            vec4 image = vec4(
                texture(imageRed, imageUv).x,
                texture(imageGreen, imageUv).x,
                texture(imageBlue, imageUv).x,
                texture(imageAlpha, imageUv).x
            );

            if (!hasReference) {
                color = vec4(applyTonemap(applyExposure(abs(image.rgb))), image.a);
                return;
            }

            vec4 reference = vec4(
                texture(referenceRed, referenceUv).x,
                texture(referenceGreen, referenceUv).x,
                texture(referenceBlue, referenceUv).x,
                texture(referenceAlpha, referenceUv).x
            );

            vec3 difference = image.rgb - reference.rgb;
            float alpha = (image.a + reference.a) * 0.5;
            color = vec4(applyTonemap(applyExposure(applyMetric(difference, reference.rgb))), alpha);
        })"
    );

    // 2 Triangles
    MatrixXu indices(3, 2);
    indices.col(0) << 0, 1, 2;
    indices.col(1) << 2, 3, 0;

    MatrixXf positions(2, 4);
    positions.col(0) << -1, -1;
    positions.col(1) <<  1, -1;
    positions.col(2) <<  1,  1;
    positions.col(3) << -1,  1;

    mShader.bind();
    mShader.uploadIndices(indices);
    mShader.uploadAttrib("position", positions);
}

UberShader::~UberShader() {
    mShader.free();
}

void UberShader::draw(
    std::array<const GlTexture*, 4> texturesImage,
    const Eigen::Matrix3f& transformImage,
    std::array<const GlTexture*, 4> texturesReference,
    const Eigen::Matrix3f& transformReference,
    float exposure,
    ETonemap tonemap,
    EMetric metric
) {
    for (int i = 0; i < 4; ++i) {
        glActiveTexture(GL_TEXTURE0 + i);
        glBindTexture(GL_TEXTURE_2D, texturesImage[i]->id());
    }

    for (int i = 0; i < 4; ++i) {
        glActiveTexture(GL_TEXTURE0 + 4 + i);
        glBindTexture(GL_TEXTURE_2D, texturesReference[i]->id());
    }

    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    mShader.bind();
    mShader.setUniform("imageRed",   0);
    mShader.setUniform("imageGreen", 1);
    mShader.setUniform("imageBlue",  2);
    mShader.setUniform("imageAlpha", 3);
    mShader.setUniform("imageTransform", transformImage);

    mShader.setUniform("referenceRed",   4);
    mShader.setUniform("referenceGreen", 5);
    mShader.setUniform("referenceBlue",  6);
    mShader.setUniform("referenceAlpha", 7);
    mShader.setUniform("referenceTransform", transformReference);

    mShader.setUniform("hasReference", true);

    mShader.setUniform("exposure", exposure);
    mShader.setUniform("tonemap", static_cast<int>(tonemap));
    mShader.setUniform("metric", static_cast<int>(metric));

    mShader.drawIndexed(GL_TRIANGLES, 0, 2);
}

void UberShader::draw(
    std::array<const GlTexture*, 4> texturesImage,
    const Eigen::Matrix3f& transformImage,
    float exposure,
    ETonemap tonemap
) {
    for (int i = 0; i < 4; ++i) {
        glActiveTexture(GL_TEXTURE0 + i);
        glBindTexture(GL_TEXTURE_2D, texturesImage[i]->id());
    }

    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    mShader.bind();
    mShader.setUniform("imageRed", 0);
    mShader.setUniform("imageGreen", 1);
    mShader.setUniform("imageBlue", 2);
    mShader.setUniform("imageAlpha", 3);
    mShader.setUniform("imageTransform", transformImage);

    mShader.setUniform("hasReference", false);

    mShader.setUniform("exposure", exposure);
    mShader.setUniform("tonemap", static_cast<int>(tonemap));

    mShader.drawIndexed(GL_TRIANGLES, 0, 2);
}

TEV_NAMESPACE_END
<commit_msg>Initial implementation of false-color<commit_after>// This file was developed by Thomas Müller <thomas94@gmx.net>.
// It is published under the BSD-style license contained in the LICENSE.txt file.

#include "../include/UberShader.h"

using namespace Eigen;
using namespace nanogui;
using namespace std;

TEV_NAMESPACE_BEGIN

UberShader::UberShader() {
    mShader.define("SRGB",        to_string(ETonemap::SRGB));
    mShader.define("GAMMA",       to_string(ETonemap::Gamma));
    mShader.define("FALSE_COLOR", to_string(ETonemap::FalseColor));

    mShader.define("ERROR",                   to_string(EMetric::Error));
    mShader.define("ABSOLUTE_ERROR",          to_string(EMetric::AbsoluteError));
    mShader.define("SQUARED_ERROR",           to_string(EMetric::SquaredError));
    mShader.define("RELATIVE_ABSOLUTE_ERROR", to_string(EMetric::RelativeAbsoluteError));
    mShader.define("RELATIVE_SQUARED_ERROR",  to_string(EMetric::RelativeSquaredError));

    mShader.init(
        "ubershader",

        // Vertex shader
        R"(#version 330
        uniform mat3 imageTransform;
        uniform mat3 referenceTransform;

        in vec2 position;

        out vec2 imageUv;
        out vec2 referenceUv;

        void main() {
            imageUv = (imageTransform * vec3(position, 1.0)).xy;
            referenceUv = (referenceTransform * vec3(position, 1.0)).xy;

            gl_Position = vec4(position, 1.0, 1.0);
        })",

        // Fragment shader
        R"(#version 330

        uniform sampler2D imageRed;
        uniform sampler2D imageGreen;
        uniform sampler2D imageBlue;
        uniform sampler2D imageAlpha;

        uniform sampler2D referenceRed;
        uniform sampler2D referenceGreen;
        uniform sampler2D referenceBlue;
        uniform sampler2D referenceAlpha;
        uniform bool hasReference;

        uniform float exposure;
        uniform int tonemap;
        uniform int metric;

        in vec2 imageUv;
        in vec2 referenceUv;

        out vec4 color;

        vec3 applyExposure(vec3 col) {
            return pow(2.0, exposure) * col;
        }

        vec3 falseColor(float v) {
            vec3 c = vec3(1.0);
            v = clamp(v, 0.0, 1.0);

            if (v < 0.25) {
                c.r = 0.0;
                c.g = 4.0 * v;
            } else if (v < 0.5) {
                c.r = 0.0;
                c.b = 1.0 + 4.0 * (0.25 - v);
            } else if (v < 0.75) {
                c.r = 4.0 * (v - 0.5);
                c.b = 0.0;
            } else {
                c.g = 1.0 + 4.0 * (0.75 - v);
                c.b = 0.0;
            }

            return c;
        }

        float sRGB(float linear) {
            if (linear > 1.0) {
                return 1.0;
            } else if (linear < 0.0) {
                return 0.0;
            } else if (linear < 0.0031308) {
                return 12.92 * linear;
            } else {
                return 1.055 * pow(linear, 0.41666) - 0.055;
            }
        }

        vec3 applyTonemap(vec3 col) {
            switch (tonemap) {
                case SRGB:        return vec3(sRGB(col.r), sRGB(col.g), sRGB(col.b));
                case GAMMA:       return pow(col, vec3(1.0 / 2.2));
                case FALSE_COLOR:
                {
                    float grayscale = (col.r + col.g + col.b) / 3.0;
                    // Here grayscale is compressed such that a value of 1/1000th becomes 0 and 1000 becomes 1.
                    // Due to the usage of the logarithm, varying the exposure simply has an additive effect on
                    // the result of the following expression, and therefore the high range of values (a factor
                    // of ~1,000,000 is preserved).
                    float compressed = log2(grayscale) / 20.0 + 0.5;
                    return falseColor(compressed);
                }
            }
            return vec3(0.0);
        }

        vec3 applyMetric(vec3 col, vec3 reference) {
            switch (metric) {
                case ERROR:                   return col;
                case ABSOLUTE_ERROR:          return abs(col);
                case SQUARED_ERROR:           return col * col;
                case RELATIVE_ABSOLUTE_ERROR: return abs(col) / (reference + vec3(0.01));
                case RELATIVE_SQUARED_ERROR:  return col * col / (reference * reference + vec3(0.0001));
            }
            return vec3(0.0);
        }

        void main() {
            vec4 image = vec4(
                texture(imageRed, imageUv).x,
                texture(imageGreen, imageUv).x,
                texture(imageBlue, imageUv).x,
                texture(imageAlpha, imageUv).x
            );

            if (!hasReference) {
                color = vec4(applyTonemap(applyExposure(abs(image.rgb))), image.a);
                return;
            }

            vec4 reference = vec4(
                texture(referenceRed, referenceUv).x,
                texture(referenceGreen, referenceUv).x,
                texture(referenceBlue, referenceUv).x,
                texture(referenceAlpha, referenceUv).x
            );

            vec3 difference = image.rgb - reference.rgb;
            float alpha = (image.a + reference.a) * 0.5;
            color = vec4(applyTonemap(applyExposure(applyMetric(difference, reference.rgb))), alpha);
        })"
    );

    // 2 Triangles
    MatrixXu indices(3, 2);
    indices.col(0) << 0, 1, 2;
    indices.col(1) << 2, 3, 0;

    MatrixXf positions(2, 4);
    positions.col(0) << -1, -1;
    positions.col(1) <<  1, -1;
    positions.col(2) <<  1,  1;
    positions.col(3) << -1,  1;

    mShader.bind();
    mShader.uploadIndices(indices);
    mShader.uploadAttrib("position", positions);
}

UberShader::~UberShader() {
    mShader.free();
}

void UberShader::draw(
    std::array<const GlTexture*, 4> texturesImage,
    const Eigen::Matrix3f& transformImage,
    std::array<const GlTexture*, 4> texturesReference,
    const Eigen::Matrix3f& transformReference,
    float exposure,
    ETonemap tonemap,
    EMetric metric
) {
    for (int i = 0; i < 4; ++i) {
        glActiveTexture(GL_TEXTURE0 + i);
        glBindTexture(GL_TEXTURE_2D, texturesImage[i]->id());
    }

    for (int i = 0; i < 4; ++i) {
        glActiveTexture(GL_TEXTURE0 + 4 + i);
        glBindTexture(GL_TEXTURE_2D, texturesReference[i]->id());
    }

    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    mShader.bind();
    mShader.setUniform("imageRed",   0);
    mShader.setUniform("imageGreen", 1);
    mShader.setUniform("imageBlue",  2);
    mShader.setUniform("imageAlpha", 3);
    mShader.setUniform("imageTransform", transformImage);

    mShader.setUniform("referenceRed",   4);
    mShader.setUniform("referenceGreen", 5);
    mShader.setUniform("referenceBlue",  6);
    mShader.setUniform("referenceAlpha", 7);
    mShader.setUniform("referenceTransform", transformReference);

    mShader.setUniform("hasReference", true);

    mShader.setUniform("exposure", exposure);
    mShader.setUniform("tonemap", static_cast<int>(tonemap));
    mShader.setUniform("metric", static_cast<int>(metric));

    mShader.drawIndexed(GL_TRIANGLES, 0, 2);
}

void UberShader::draw(
    std::array<const GlTexture*, 4> texturesImage,
    const Eigen::Matrix3f& transformImage,
    float exposure,
    ETonemap tonemap
) {
    for (int i = 0; i < 4; ++i) {
        glActiveTexture(GL_TEXTURE0 + i);
        glBindTexture(GL_TEXTURE_2D, texturesImage[i]->id());
    }

    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    mShader.bind();
    mShader.setUniform("imageRed", 0);
    mShader.setUniform("imageGreen", 1);
    mShader.setUniform("imageBlue", 2);
    mShader.setUniform("imageAlpha", 3);
    mShader.setUniform("imageTransform", transformImage);

    mShader.setUniform("hasReference", false);

    mShader.setUniform("exposure", exposure);
    mShader.setUniform("tonemap", static_cast<int>(tonemap));

    mShader.drawIndexed(GL_TRIANGLES, 0, 2);
}

TEV_NAMESPACE_END
<|endoftext|>


<commit_before>// Copyright (c) 2009-2021 The Regents of the University of Michigan
// This file is part of the HOOMD-blue project, released under the BSD 3-Clause License.

// Maintainer: joaander

/*! \file BoxResizeUpdater.cc
    \brief Defines the BoxResizeUpdater class
*/

#include "BoxResizeUpdater.h"

#include <math.h>
#include <iostream>
#include <stdexcept>

using namespace std;
namespace py = pybind11;

/*! \param sysdef System definition containing the particle data to set the box size on
    \param Lx length of the x dimension over time
    \param Ly length of the y dimension over time
    \param Lz length of the z dimension over time

    The default setting is to scale particle positions along with the box.
*/

BoxResizeUpdater::BoxResizeUpdater(std::shared_ptr<SystemDefinition> sysdef,
                                   pybind11::object box1,
                                   pybind11::object box2,
                                   std::shared_ptr<Variant> variant,
                                   std::shared_ptr<ParticleGroup> scale_particles)
    : Updater(sysdef),
      m_py_box1(box1),
      m_py_box2(box2),
      m_box1(getBoxDimFromPyObject(box1)),
      m_box2(getBoxDimFromPyObject(box2)),
      m_variant(variant), m_scale_particles(scale_particles)
    {
    assert(m_pdata);
    assert(m_variant);
    m_exec_conf->msg->notice(5) << "Constructing BoxResizeUpdater" << endl;
    }

BoxResizeUpdater::~BoxResizeUpdater()
    {
    m_exec_conf->msg->notice(5) << "Destroying BoxResizeUpdater" << endl;
    }

/// Set a new initial box from a python object
void BoxResizeUpdater::setPyBox1(pybind11::object box1)
    {
    m_py_box1 = box1;
    m_box1 = getBoxDimFromPyObject(box1);
    }

/// Set a new final box from a python object
void BoxResizeUpdater::setPyBox2(pybind11::object box2)
    {
    m_py_box2 = box2;
    m_box2 = getBoxDimFromPyObject(box2);
    }

/// Get the current box based on the timestep
BoxDim BoxResizeUpdater::getCurrentBox(unsigned int timestep)
{
Scalar min = m_variant->min();
Scalar max = m_variant->max();
Scalar cur_value = (*m_variant)(timestep);
Scalar scale = 0;
if (cur_value == max)
{
scale = 1;
}
else if (cur_value > min)
{
scale = (cur_value - min) / (max - min);
}

Scalar3 new_L = m_box2.getL() * scale +
            m_box1.getL() * (1.0 - scale);
Scalar xy = m_box2.getTiltFactorXY() * scale +
        (1.0 - scale) * m_box1.getTiltFactorXY();
Scalar xz = m_box2.getTiltFactorXZ() * scale +
        (1.0 - scale) * m_box1.getTiltFactorXZ();
Scalar yz = m_box2.getTiltFactorYZ() * scale +
        (1.0 - scale) * m_box1.getTiltFactorYZ();

BoxDim new_box = BoxDim(new_L);
new_box.setTiltFactors(xy, xz, yz);
return new_box;
}

/** Perform the needed calculations to scale the box size
    \param timestep Current time step of the simulation
*/
void BoxResizeUpdater::update(unsigned int timestep)
    {
    m_exec_conf->msg->notice(10) << "Box resize update" << endl;
    if (m_prof) m_prof->push("BoxResize");

    // first, compute the new box
    BoxDim new_box = getCurrentBox(timestep);

    // check if the current box size is the same
    BoxDim cur_box = m_pdata->getGlobalBox();

    // only change the box if there is a change in the box dimensions
    if (new_box != cur_box)
        {
        // set the new box
        m_pdata->setGlobalBox(new_box);

        // scale the particle positions (if we have been asked to)
        // move the particles to be inside the new box
        ArrayHandle<Scalar4> h_pos(m_pdata->getPositions(),
                                   access_location::host,
                                   access_mode::readwrite);

        unsigned int n_particle_group = m_scale_particles->getNumMembersGlobal();

        for (unsigned int i = 0; i < n_particle_group; i++)
        {
        // obtain scaled coordinates in the old global box
        Scalar3 fractional_pos = cur_box.makeFraction(
        make_scalar3(h_pos.data[i].x,
                     h_pos.data[i].y,
                     h_pos.data[i].z));

        // intentionally scale both rigid body and free particles, this
        // may waste a few cycles but it enables the debug inBox checks
        // to be left as is (otherwise, setRV cannot fixup rigid body
        // positions without failing the check)
        Scalar3 scaled_pos = new_box.makeCoordinates(fractional_pos);
        h_pos.data[i].x = scaled_pos.x;
        h_pos.data[i].y = scaled_pos.y;
        h_pos.data[i].z = scaled_pos.z;
        }

        // otherwise, we need to ensure that the particles are still in their
        // local boxes by wrapping them if they are not
        ArrayHandle<int3> h_image(m_pdata->getImages(),
                                  access_location::host,
                                  access_mode::readwrite);

        const BoxDim& local_box = m_pdata->getBox();

        for (unsigned int i = 0; i < m_pdata->getN(); i++)
          {
          // need to update the image if we move particles from one side
          // of the box to the other
          local_box.wrap(h_pos.data[i], h_image.data[i]);
          }
          //TODO: Is it faster/better to do it like this, or one loop and if statement with isMember
        }
    if (m_prof) m_prof->pop();
    }

BoxDim& getBoxDimFromPyObject(pybind11::object box)
    {
    return box.attr("_cpp_obj").cast<BoxDim&>();
    }

void export_BoxResizeUpdater(py::module& m)
    {
    py::class_<BoxResizeUpdater, Updater,
               std::shared_ptr<BoxResizeUpdater> >(m,"BoxResizeUpdater")
    .def(pybind11::init<std::shared_ptr<SystemDefinition>,
                        pybind11::object, pybind11::object,
                        std::shared_ptr<Variant>,
                        std::shared_ptr<ParticleGroup> >())
//    .def_property("scale_particles",
//                  &BoxResizeUpdater::getScaleParticles,
//                  &BoxResizeUpdater::setScaleParticles)
    .def_property("box1",
                  &BoxResizeUpdater::getPyBox1,
                  &BoxResizeUpdater::setPyBox1)
    .def_property("box2",
                  &BoxResizeUpdater::getPyBox2,
                  &BoxResizeUpdater::setPyBox2)
    .def_property("variant",
                  &BoxResizeUpdater::getVariant,
                  &BoxResizeUpdater::setVariant)
    .def("get_current_box", &BoxResizeUpdater::getCurrentBox)
    ;
    }
<commit_msg>Use index rather than tag.<commit_after>// Copyright (c) 2009-2021 The Regents of the University of Michigan
// This file is part of the HOOMD-blue project, released under the BSD 3-Clause License.

// Maintainer: joaander

/*! \file BoxResizeUpdater.cc
    \brief Defines the BoxResizeUpdater class
*/

#include "BoxResizeUpdater.h"

#include <math.h>
#include <iostream>
#include <stdexcept>

using namespace std;
namespace py = pybind11;

/*! \param sysdef System definition containing the particle data to set the box size on
    \param Lx length of the x dimension over time
    \param Ly length of the y dimension over time
    \param Lz length of the z dimension over time

    The default setting is to scale particle positions along with the box.
*/

BoxResizeUpdater::BoxResizeUpdater(std::shared_ptr<SystemDefinition> sysdef,
                                   pybind11::object box1,
                                   pybind11::object box2,
                                   std::shared_ptr<Variant> variant,
                                   std::shared_ptr<ParticleGroup> scale_particles)
    : Updater(sysdef),
      m_py_box1(box1),
      m_py_box2(box2),
      m_box1(getBoxDimFromPyObject(box1)),
      m_box2(getBoxDimFromPyObject(box2)),
      m_variant(variant), m_scale_particles(scale_particles)
    {
    assert(m_pdata);
    assert(m_variant);
    m_exec_conf->msg->notice(5) << "Constructing BoxResizeUpdater" << endl;
    }

BoxResizeUpdater::~BoxResizeUpdater()
    {
    m_exec_conf->msg->notice(5) << "Destroying BoxResizeUpdater" << endl;
    }

/// Set a new initial box from a python object
void BoxResizeUpdater::setPyBox1(pybind11::object box1)
    {
    m_py_box1 = box1;
    m_box1 = getBoxDimFromPyObject(box1);
    }

/// Set a new final box from a python object
void BoxResizeUpdater::setPyBox2(pybind11::object box2)
    {
    m_py_box2 = box2;
    m_box2 = getBoxDimFromPyObject(box2);
    }

/// Get the current box based on the timestep
BoxDim BoxResizeUpdater::getCurrentBox(unsigned int timestep)
{
Scalar min = m_variant->min();
Scalar max = m_variant->max();
Scalar cur_value = (*m_variant)(timestep);
Scalar scale = 0;
if (cur_value == max)
{
scale = 1;
}
else if (cur_value > min)
{
scale = (cur_value - min) / (max - min);
}

Scalar3 new_L = m_box2.getL() * scale +
            m_box1.getL() * (1.0 - scale);
Scalar xy = m_box2.getTiltFactorXY() * scale +
        (1.0 - scale) * m_box1.getTiltFactorXY();
Scalar xz = m_box2.getTiltFactorXZ() * scale +
        (1.0 - scale) * m_box1.getTiltFactorXZ();
Scalar yz = m_box2.getTiltFactorYZ() * scale +
        (1.0 - scale) * m_box1.getTiltFactorYZ();

BoxDim new_box = BoxDim(new_L);
new_box.setTiltFactors(xy, xz, yz);
return new_box;
}

/** Perform the needed calculations to scale the box size
    \param timestep Current time step of the simulation
*/
void BoxResizeUpdater::update(unsigned int timestep)
    {
    m_exec_conf->msg->notice(10) << "Box resize update" << endl;
    if (m_prof) m_prof->push("BoxResize");

    // first, compute the new box
    BoxDim new_box = getCurrentBox(timestep);

    // check if the current box size is the same
    BoxDim cur_box = m_pdata->getGlobalBox();

    // only change the box if there is a change in the box dimensions
    if (new_box != cur_box)
        {
        // set the new box
        m_pdata->setGlobalBox(new_box);

        // scale the particle positions (if we have been asked to)
        // move the particles to be inside the new box
        ArrayHandle<Scalar4> h_pos(m_pdata->getPositions(),
                                   access_location::host,
                                   access_mode::readwrite);

        unsigned int n_particle_group = m_scale_particles->getNumMembersGlobal();

        for (unsigned int group_idx = 0; group_idx < n_particle_group; group_idx++)
        {
        unsigned int j = m_scale_particles->getMemberIndex(group_idx);
        // obtain scaled coordinates in the old global box
        Scalar3 fractional_pos = cur_box.makeFraction(
        make_scalar3(h_pos.data[j].x,
                     h_pos.data[j].y,
                     h_pos.data[j].z));

        // intentionally scale both rigid body and free particles, this
        // may waste a few cycles but it enables the debug inBox checks
        // to be left as is (otherwise, setRV cannot fixup rigid body
        // positions without failing the check)
        Scalar3 scaled_pos = new_box.makeCoordinates(fractional_pos);
        h_pos.data[j].x = scaled_pos.x;
        h_pos.data[j].y = scaled_pos.y;
        h_pos.data[j].z = scaled_pos.z;
        }

        // otherwise, we need to ensure that the particles are still in their
        // local boxes by wrapping them if they are not
        ArrayHandle<int3> h_image(m_pdata->getImages(),
                                  access_location::host,
                                  access_mode::readwrite);

        const BoxDim& local_box = m_pdata->getBox();

        for (unsigned int i = 0; i < m_pdata->getN(); i++)
          {
          // need to update the image if we move particles from one side
          // of the box to the other
          local_box.wrap(h_pos.data[i], h_image.data[i]);
          }
          //TODO: Is it faster/better to do it like this, or one loop and if statement with isMember
        }
    if (m_prof) m_prof->pop();
    }

BoxDim& getBoxDimFromPyObject(pybind11::object box)
    {
    return box.attr("_cpp_obj").cast<BoxDim&>();
    }

void export_BoxResizeUpdater(py::module& m)
    {
    py::class_<BoxResizeUpdater, Updater,
               std::shared_ptr<BoxResizeUpdater> >(m,"BoxResizeUpdater")
    .def(pybind11::init<std::shared_ptr<SystemDefinition>,
                        pybind11::object, pybind11::object,
                        std::shared_ptr<Variant>,
                        std::shared_ptr<ParticleGroup> >())
//    .def_property("scale_particles",
//                  &BoxResizeUpdater::getScaleParticles,
//                  &BoxResizeUpdater::setScaleParticles)
    .def_property("box1",
                  &BoxResizeUpdater::getPyBox1,
                  &BoxResizeUpdater::setPyBox1)
    .def_property("box2",
                  &BoxResizeUpdater::getPyBox2,
                  &BoxResizeUpdater::setPyBox2)
    .def_property("variant",
                  &BoxResizeUpdater::getVariant,
                  &BoxResizeUpdater::setVariant)
    .def("get_current_box", &BoxResizeUpdater::getCurrentBox)
    ;
    }
<|endoftext|>


<commit_before>class Solution {
public:
    int kthSmallest(vector<vector<int>>& matrix, int k) {
        auto comp = [](int a, int b) {
            return a < b;
        };
        std::priority_queue<int, std::vector<int>, decltype(comp)> q(comp);
        for (const auto& v: matrix) {
            for (const auto& i : v) {
                if (q.size() < k) {
                    q.push(i);
                } else {
                    if (q.top() > i) {
                        q.pop();
                        q.push(i);
                    }
                }
            }
        }
        return q.top();
    }
};
<commit_msg>Kth Smallest Element in a Sorted Matrix<commit_after>class Solution {
public:
    int kthSmallest(vector<vector<int>>& matrix, int k) {
        auto comp = [](int a, int b) {
            return a < b;
        };
        std::priority_queue<int, std::vector<int>, decltype(comp)> q(comp);
        for (const auto& v: matrix) {
            for (const auto& i : v) {
                if (q.size() < k) {
                    q.push(i);
                } else {
                    if (q.top() > i) {
                        q.pop();
                        q.push(i);
                    }
                }
            }
        }
        return q.top();
    }
};

class Solution {
public:
    int kthSmallest(vector<vector<int>>& matrix, int k) {
        auto cmp = [](int a, int b) {
            return a > b;
        };
        std::priority_queue<int, std::vector<int>, decltype(cmp)> q(cmp);
        for (const auto& line : matrix) {
            for (const auto& num : line) {
                q.emplace(num);
            }
        }
        while (k > 1) {
            q.pop();
            --k;
        }
        return q.top();
    }
};
<|endoftext|>


<commit_before><commit_msg>Fix another timeout bug in Agency.<commit_after><|endoftext|>