<commit_before>/****************************************************************************
 *
 *   Copyright (c) 2015 PX4 Development Team. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 * 3. Neither the name PX4 nor the names of its contributors may be
 *    used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 ****************************************************************************/

/**
 * @file camera_trigger.cpp
 *
 * External camera-IMU synchronisation and triggering via FMU auxillary pins.
 *
 * @author Mohammed Kabir <mhkabir98@gmail.com>
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <stdbool.h>
#include <nuttx/clock.h>
#include <nuttx/arch.h>
#include <nuttx/wqueue.h>
#include <systemlib/systemlib.h>
#include <systemlib/err.h>
#include <systemlib/param/param.h>
#include <uORB/uORB.h>
#include <uORB/topics/camera_trigger.h>
#include <uORB/topics/sensor_combined.h>
#include <uORB/topics/vehicle_command.h>
#include <poll.h>
#include <drivers/drv_gpio.h>
#include <drivers/drv_hrt.h>
#include <mavlink/mavlink_log.h>
#include <board_config.h>

#define TRIGGER_PIN_DEFAULT 1

extern "C" __EXPORT int camera_trigger_main(int argc, char *argv[]);

class CameraTrigger
{
public:
	/**
	 * Constructor
	 */
	CameraTrigger();

	/**
	 * Destructor, also kills task.
	 */
	~CameraTrigger();

	/**
	 * Set the trigger on / off
	 */
	void		control(bool on);

	/**
	 * Start the task.
	 */
	void		start();

	/**
	 * Stop the task.
	 */
	void		stop();

	/**
	 * Display info.
	 */
	void		info();

	int			_pin;

private:

	struct hrt_call		_engagecall;
	struct hrt_call		_disengagecall;
	static struct work_s	_work;

	int 			_gpio_fd;

	int 			_polarity;
	float 			_activation_time;
	float  			_interval;
	uint32_t 		_trigger_seq;
	bool	 		_trigger_enabled;

	int			_vcommand_sub;

	orb_advert_t		_trigger_pub;

	param_t polarity;
	param_t activation_time;
	param_t interval;
	param_t transfer_time ;

	static constexpr uint32_t _gpios[6] = {
		GPIO_GPIO0_OUTPUT,
		GPIO_GPIO1_OUTPUT,
		GPIO_GPIO2_OUTPUT,
		GPIO_GPIO3_OUTPUT,
		GPIO_GPIO4_OUTPUT,
		GPIO_GPIO5_OUTPUT
	};

	/**
	 * Vehicle command handler
	 */
	static void	cycle_trampoline(void *arg);
	/**
	 * Fires trigger
	 */
	static void	engage(void *arg);
	/**
	 * Resets trigger
	 */
	static void	disengage(void *arg);

};

struct work_s CameraTrigger::_work;
constexpr uint32_t CameraTrigger::_gpios[6];

namespace camera_trigger
{

CameraTrigger	*g_camera_trigger;
}

CameraTrigger::CameraTrigger() :
	_pin(TRIGGER_PIN_DEFAULT),
	_engagecall {},
	_disengagecall {},
	_gpio_fd(-1),
	_polarity(0),
	_activation_time(0.5f /* ms */),
	_interval(100.0f /* ms */),
	_trigger_seq(0),
	_trigger_enabled(false),
	_vcommand_sub(-1),
	_trigger_pub(nullptr)
{
	memset(&_work, 0, sizeof(_work));
	memset(&_engagecall, 0, sizeof(_engagecall));
	memset(&_disengagecall, 0, sizeof(_disengagecall));

	// Parameters
	polarity = param_find("TRIG_POLARITY");
	interval = param_find("TRIG_INTERVAL");
	activation_time = param_find("TRIG_ACT_TIME");
}

CameraTrigger::~CameraTrigger()
{
	camera_trigger::g_camera_trigger = nullptr;
}

void
CameraTrigger::control(bool on)
{
	// always execute, even if already on
	// to reset timings if necessary

	// schedule trigger on and off calls
	hrt_call_every(&_engagecall, 500, (_interval * 1000),
			       (hrt_callout)&CameraTrigger::engage, this);

	// schedule trigger on and off calls
	hrt_call_every(&_disengagecall, 500 + (_activation_time * 1000), (_interval * 1000),
			       (hrt_callout)&CameraTrigger::disengage, this);

	_trigger_enabled = on;
}

void
CameraTrigger::start()
{

	param_get(polarity, &_polarity);
	param_get(activation_time, &_activation_time);
	param_get(interval, &_interval);

	stm32_configgpio(_gpios[_pin - 1]);

	if (_polarity == 0) {
		stm32_gpiowrite(_gpios[_pin - 1], 1);	 	// GPIO pin pull high

	} else if (_polarity == 1) {
		stm32_gpiowrite(_gpios[_pin - 1], 0);	 	// GPIO pin pull low

	} else {
		warnx(" invalid trigger polarity setting. stopping.");
		stop();
	}

	// start to monitor at high rate for trigger enable command
	work_queue(LPWORK, &_work, (worker_t)&CameraTrigger::cycle_trampoline, this, USEC2TICK(1));
}

void
CameraTrigger::stop()
{
	work_cancel(LPWORK, &_work);
	hrt_cancel(&_engagecall);
	hrt_cancel(&_disengagecall);

	if (camera_trigger::g_camera_trigger != nullptr) {
		delete(camera_trigger::g_camera_trigger);
	}
}

void
CameraTrigger::cycle_trampoline(void *arg)
{

	CameraTrigger *trig = reinterpret_cast<CameraTrigger *>(arg);

	if (trig->_vcommand_sub < 0) {
		trig->_vcommand_sub = orb_subscribe(ORB_ID(vehicle_command));
	}

	bool updated;
	orb_check(trig->_vcommand_sub, &updated);

	// while the trigger is inactive it has to be ready
	// to become active instantaneously
	int poll_interval_usec = 5000;

	if (updated) {

		struct vehicle_command_s cmd;

		orb_copy(ORB_ID(vehicle_command), trig->_vcommand_sub, &cmd);

		if (cmd.command == vehicle_command_s::VEHICLE_CMD_DO_TRIGGER_CONTROL) {
			// Set trigger rate from command
			if (cmd.param2 > 0) {
				trig->_interval = cmd.param2;
				param_set(trig->interval, &(trig->_interval));
			}

			if (cmd.param1 < 1.0f) {
				trig->control(false);

			} else if (cmd.param1 >= 1.0f) {
				trig->control(true);
				// while the trigger is active there is no
				// need to poll at a very high rate
				poll_interval_usec = 100000;
			}
		}
	}

	work_queue(LPWORK, &_work, (worker_t)&CameraTrigger::cycle_trampoline,
		camera_trigger::g_camera_trigger, USEC2TICK(poll_interval_usec));
}

void
CameraTrigger::engage(void *arg)
{

	CameraTrigger *trig = reinterpret_cast<CameraTrigger *>(arg);

	stm32_configgpio(trig->_gpios[trig->_pin - 1]);

	struct camera_trigger_s	trigger;

	/* set timestamp the instant before the trigger goes off */
	trigger.timestamp = hrt_absolute_time();

	if (trig->_polarity == 0) {	// ACTIVE_LOW
		stm32_gpiowrite(trig->_gpios[trig->_pin - 1], 0);

	} else if (trig->_polarity == 1) {	// ACTIVE_HIGH
		stm32_gpiowrite(trig->_gpios[trig->_pin - 1], 1);
	}

	trigger.seq = trig->_trigger_seq++;

	orb_publish(ORB_ID(camera_trigger), trig->_trigger_pub, &trigger);
}

void
CameraTrigger::disengage(void *arg)
{

	CameraTrigger *trig = reinterpret_cast<CameraTrigger *>(arg);

	stm32_configgpio(trig->_gpios[trig->_pin - 1]);

	if (trig->_polarity == 0) {		// ACTIVE_LOW
		stm32_gpiowrite(trig->_gpios[trig->_pin - 1], 1);

	} else if (trig->_polarity == 1) {	// ACTIVE_HIGH
		stm32_gpiowrite(trig->_gpios[trig->_pin - 1], 0);
	}

}

void
CameraTrigger::info()
{
	warnx("state : %s", _trigger_enabled ? "enabled" : "disabled");
	warnx("pin : %i, polarity : %s", _pin, _polarity ? "ACTIVE_HIGH" : "ACTIVE_LOW");
	warnx("interval : %.2f", (double)_interval);
}

static void usage()
{
	errx(1, "usage: camera_trigger {start|stop|info} [-p <n>]\n"
	     "\t-p <n>\tUse specified AUX OUT pin number (default: %d)", TRIGGER_PIN_DEFAULT
	    );
}

int camera_trigger_main(int argc, char *argv[])
{
	if (argc < 2) {
		usage();
	}

	if (!strcmp(argv[1], "start")) {

		if (camera_trigger::g_camera_trigger != nullptr) {
			errx(0, "already running");
		}

		camera_trigger::g_camera_trigger = new CameraTrigger;

		if (camera_trigger::g_camera_trigger == nullptr) {
			errx(1, "alloc failed");
		}

		if (argc > 3) {

			camera_trigger::g_camera_trigger->_pin = (int)argv[3];

			if (atoi(argv[3]) > 0 && atoi(argv[3]) < 6) {
				warnx("starting trigger on pin : %li ", atoi(argv[3]));
				camera_trigger::g_camera_trigger->_pin = atoi(argv[3]);

			} else {
				usage();
			}
		}

		camera_trigger::g_camera_trigger->start();

		return 0;
	}

	if (camera_trigger::g_camera_trigger == nullptr) {
		errx(1, "not running");

	} else if (!strcmp(argv[1], "stop")) {
		camera_trigger::g_camera_trigger->stop();

	} else if (!strcmp(argv[1], "info")) {
		camera_trigger::g_camera_trigger->info();

	} else if (!strcmp(argv[1], "enable")) {
		camera_trigger::g_camera_trigger->control(true);

	} else if (!strcmp(argv[1], "disable")) {
		camera_trigger::g_camera_trigger->control(false);

	} else {
		usage();
	}

	return 0;
}

<commit_msg>Camera trigger: Launch publication in correct thread<commit_after>/****************************************************************************
 *
 *   Copyright (c) 2015 PX4 Development Team. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 * 3. Neither the name PX4 nor the names of its contributors may be
 *    used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 ****************************************************************************/

/**
 * @file camera_trigger.cpp
 *
 * External camera-IMU synchronisation and triggering via FMU auxillary pins.
 *
 * @author Mohammed Kabir <mhkabir98@gmail.com>
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <stdbool.h>
#include <nuttx/clock.h>
#include <nuttx/arch.h>
#include <nuttx/wqueue.h>
#include <systemlib/systemlib.h>
#include <systemlib/err.h>
#include <systemlib/param/param.h>
#include <uORB/uORB.h>
#include <uORB/topics/camera_trigger.h>
#include <uORB/topics/sensor_combined.h>
#include <uORB/topics/vehicle_command.h>
#include <poll.h>
#include <drivers/drv_gpio.h>
#include <drivers/drv_hrt.h>
#include <mavlink/mavlink_log.h>
#include <board_config.h>

#define TRIGGER_PIN_DEFAULT 1

extern "C" __EXPORT int camera_trigger_main(int argc, char *argv[]);

class CameraTrigger
{
public:
	/**
	 * Constructor
	 */
	CameraTrigger();

	/**
	 * Destructor, also kills task.
	 */
	~CameraTrigger();

	/**
	 * Set the trigger on / off
	 */
	void		control(bool on);

	/**
	 * Start the task.
	 */
	void		start();

	/**
	 * Stop the task.
	 */
	void		stop();

	/**
	 * Display info.
	 */
	void		info();

	int			_pin;

private:

	struct hrt_call		_engagecall;
	struct hrt_call		_disengagecall;
	static struct work_s	_work;

	int 			_gpio_fd;

	int 			_polarity;
	float 			_activation_time;
	float  			_interval;
	uint32_t 		_trigger_seq;
	bool	 		_trigger_enabled;

	int			_vcommand_sub;

	orb_advert_t		_trigger_pub;

	param_t polarity;
	param_t activation_time;
	param_t interval;
	param_t transfer_time ;

	static constexpr uint32_t _gpios[6] = {
		GPIO_GPIO0_OUTPUT,
		GPIO_GPIO1_OUTPUT,
		GPIO_GPIO2_OUTPUT,
		GPIO_GPIO3_OUTPUT,
		GPIO_GPIO4_OUTPUT,
		GPIO_GPIO5_OUTPUT
	};

	/**
	 * Vehicle command handler
	 */
	static void	cycle_trampoline(void *arg);
	/**
	 * Fires trigger
	 */
	static void	engage(void *arg);
	/**
	 * Resets trigger
	 */
	static void	disengage(void *arg);

};

struct work_s CameraTrigger::_work;
constexpr uint32_t CameraTrigger::_gpios[6];

namespace camera_trigger
{

CameraTrigger	*g_camera_trigger;
}

CameraTrigger::CameraTrigger() :
	_pin(TRIGGER_PIN_DEFAULT),
	_engagecall {},
	_disengagecall {},
	_gpio_fd(-1),
	_polarity(0),
	_activation_time(0.5f /* ms */),
	_interval(100.0f /* ms */),
	_trigger_seq(0),
	_trigger_enabled(false),
	_vcommand_sub(-1),
	_trigger_pub(nullptr)
{
	memset(&_work, 0, sizeof(_work));

	// Parameters
	polarity = param_find("TRIG_POLARITY");
	interval = param_find("TRIG_INTERVAL");
	activation_time = param_find("TRIG_ACT_TIME");

	struct camera_trigger_s	trigger = {};

	_trigger_pub = orb_advertise(ORB_ID(camera_trigger), &trigger);
}

CameraTrigger::~CameraTrigger()
{
	camera_trigger::g_camera_trigger = nullptr;
}

void
CameraTrigger::control(bool on)
{
	// always execute, even if already on
	// to reset timings if necessary

	// schedule trigger on and off calls
	hrt_call_every(&_engagecall, 500, (_interval * 1000),
			       (hrt_callout)&CameraTrigger::engage, this);

	// schedule trigger on and off calls
	hrt_call_every(&_disengagecall, 500 + (_activation_time * 1000), (_interval * 1000),
			       (hrt_callout)&CameraTrigger::disengage, this);

	_trigger_enabled = on;
}

void
CameraTrigger::start()
{

	param_get(polarity, &_polarity);
	param_get(activation_time, &_activation_time);
	param_get(interval, &_interval);

	stm32_configgpio(_gpios[_pin - 1]);

	if (_polarity == 0) {
		stm32_gpiowrite(_gpios[_pin - 1], 1);	 	// GPIO pin pull high

	} else if (_polarity == 1) {
		stm32_gpiowrite(_gpios[_pin - 1], 0);	 	// GPIO pin pull low

	} else {
		warnx(" invalid trigger polarity setting. stopping.");
		stop();
	}

	// start to monitor at high rate for trigger enable command
	work_queue(LPWORK, &_work, (worker_t)&CameraTrigger::cycle_trampoline, this, USEC2TICK(1));
}

void
CameraTrigger::stop()
{
	work_cancel(LPWORK, &_work);
	hrt_cancel(&_engagecall);
	hrt_cancel(&_disengagecall);

	if (camera_trigger::g_camera_trigger != nullptr) {
		delete(camera_trigger::g_camera_trigger);
	}
}

void
CameraTrigger::cycle_trampoline(void *arg)
{

	CameraTrigger *trig = reinterpret_cast<CameraTrigger *>(arg);

	if (trig->_vcommand_sub < 0) {
		trig->_vcommand_sub = orb_subscribe(ORB_ID(vehicle_command));
	}

	bool updated;
	orb_check(trig->_vcommand_sub, &updated);

	// while the trigger is inactive it has to be ready
	// to become active instantaneously
	int poll_interval_usec = 5000;

	if (updated) {

		struct vehicle_command_s cmd;

		orb_copy(ORB_ID(vehicle_command), trig->_vcommand_sub, &cmd);

		if (cmd.command == vehicle_command_s::VEHICLE_CMD_DO_TRIGGER_CONTROL) {
			// Set trigger rate from command
			if (cmd.param2 > 0) {
				trig->_interval = cmd.param2;
				param_set(trig->interval, &(trig->_interval));
			}

			if (cmd.param1 < 1.0f) {
				trig->control(false);

			} else if (cmd.param1 >= 1.0f) {
				trig->control(true);
				// while the trigger is active there is no
				// need to poll at a very high rate
				poll_interval_usec = 100000;
			}
		}
	}

	work_queue(LPWORK, &_work, (worker_t)&CameraTrigger::cycle_trampoline,
		camera_trigger::g_camera_trigger, USEC2TICK(poll_interval_usec));
}

void
CameraTrigger::engage(void *arg)
{

	CameraTrigger *trig = reinterpret_cast<CameraTrigger *>(arg);

	stm32_configgpio(trig->_gpios[trig->_pin - 1]);

	struct camera_trigger_s	trigger;

	/* set timestamp the instant before the trigger goes off */
	trigger.timestamp = hrt_absolute_time();

	if (trig->_polarity == 0) {	// ACTIVE_LOW
		stm32_gpiowrite(trig->_gpios[trig->_pin - 1], 0);

	} else if (trig->_polarity == 1) {	// ACTIVE_HIGH
		stm32_gpiowrite(trig->_gpios[trig->_pin - 1], 1);
	}

	trigger.seq = trig->_trigger_seq++;

	orb_publish(ORB_ID(camera_trigger), trig->_trigger_pub, &trigger);
}

void
CameraTrigger::disengage(void *arg)
{

	CameraTrigger *trig = reinterpret_cast<CameraTrigger *>(arg);

	stm32_configgpio(trig->_gpios[trig->_pin - 1]);

	if (trig->_polarity == 0) {		// ACTIVE_LOW
		stm32_gpiowrite(trig->_gpios[trig->_pin - 1], 1);

	} else if (trig->_polarity == 1) {	// ACTIVE_HIGH
		stm32_gpiowrite(trig->_gpios[trig->_pin - 1], 0);
	}

}

void
CameraTrigger::info()
{
	warnx("state : %s", _trigger_enabled ? "enabled" : "disabled");
	warnx("pin : %i, polarity : %s", _pin, _polarity ? "ACTIVE_HIGH" : "ACTIVE_LOW");
	warnx("interval : %.2f", (double)_interval);
}

static void usage()
{
	errx(1, "usage: camera_trigger {start|stop|info} [-p <n>]\n"
	     "\t-p <n>\tUse specified AUX OUT pin number (default: %d)", TRIGGER_PIN_DEFAULT
	    );
}

int camera_trigger_main(int argc, char *argv[])
{
	if (argc < 2) {
		usage();
	}

	if (!strcmp(argv[1], "start")) {

		if (camera_trigger::g_camera_trigger != nullptr) {
			errx(0, "already running");
		}

		camera_trigger::g_camera_trigger = new CameraTrigger;

		if (camera_trigger::g_camera_trigger == nullptr) {
			errx(1, "alloc failed");
		}

		if (argc > 3) {

			camera_trigger::g_camera_trigger->_pin = (int)argv[3];

			if (atoi(argv[3]) > 0 && atoi(argv[3]) < 6) {
				warnx("starting trigger on pin : %li ", atoi(argv[3]));
				camera_trigger::g_camera_trigger->_pin = atoi(argv[3]);

			} else {
				usage();
			}
		}

		camera_trigger::g_camera_trigger->start();

		return 0;
	}

	if (camera_trigger::g_camera_trigger == nullptr) {
		errx(1, "not running");

	} else if (!strcmp(argv[1], "stop")) {
		camera_trigger::g_camera_trigger->stop();

	} else if (!strcmp(argv[1], "info")) {
		camera_trigger::g_camera_trigger->info();

	} else if (!strcmp(argv[1], "enable")) {
		camera_trigger::g_camera_trigger->control(true);

	} else if (!strcmp(argv[1], "disable")) {
		camera_trigger::g_camera_trigger->control(false);

	} else {
		usage();
	}

	return 0;
}

<|endoftext|>


<commit_before>#include "training/graph_group_sync.h"

namespace marian {

SyncGraphGroup::SyncGraphGroup(Ptr<Config> config)
    : GraphGroup(config),
      ExponentialSmoothing{options_->get<float>("exponential-smoothing")},
      delay_{options_->get<size_t>("optimizer-delay")} { // @TODO: rename to something else; delay means delayed updated, not accumulation

  mpi_ = initMPI(/*multiThreaded=*/false); // when not running under MPI, this will be a fake object that represents a one-MPI-process setup

  devices_ = options_->getDevices(mpi_->myMPIRank(), mpi_->numMPIProcesses());
  for(auto device : devices_) {
    auto graph = New<ExpressionGraph>();
    graph->setDevice(device);
    graph->reserveWorkspaceMB(options_->get<size_t>("workspace"));
    graph->getBackend()->setClip(options_->get<float>("clip-gemm"));

    graphs_.push_back(graph);
    shardOpt_.push_back(Optimizer(options_));
    builders_.push_back(models::from_config(options_, models::usage::training));
  }

  // Note: We may well end up with only one MPI process or only one graph per worker.
  // This part of the code will not special-case any of this here.
  // Rather, it is assumed that the communicator knows to reduce unnecessary transfers to no-ops.
  comm_ = createCommunicator(graphs_, /*noNccl=*/options_->get<bool>("no-nccl", false), /*mpi=*/mpi_);
}

void SyncGraphGroup::setScheduler(Ptr<Scheduler> scheduler) /*override*/ {
  scheduler_ = scheduler;
  // optimizer has to be registered last to see changes of learning rate
  // @TODO: ^^Fix this comment. Either it refers to the scheduler, or it should be moved. Which one?
  scheduler_->registerTrainingObserver(scheduler_);

  for(auto opt : shardOpt_)
    scheduler_->registerTrainingObserver(opt);
}

void SyncGraphGroup::initialize(const Ptr<data::Batch>& exampleBatch) {
  // Initialize 0th graph with random weights in one forward step
  // @TODO: Why do we need the THREAD_GUARD here? Why not run this on the main thread?
  THREAD_GUARD({
    builders_[0]->build(graphs_[0], exampleBatch);
    graphs_[0]->forward();
  });

  // Copy weights from 0th graph to all other graphs
  // to have equal weights across devices
  ThreadPool pool(graphs_.size() - 1, graphs_.size() - 1);
  for(size_t i = 1; i < graphs_.size(); ++i) {
    auto init = [&](size_t i) {
      // initialize t-th graph and weights
      builders_[i]->build(graphs_[i], exampleBatch);
      graphs_[i]->forward();
      // overwrite weights of t-th graph with weights from 0th graph
      graphs_[i]->params()->vals()->copyFrom(graphs_[0]->params()->vals());
    };
    pool.enqueue(init, i);
  }
  // ThreadPool destructor waits until completion of all tasks.
  // @TODO: can we use comm_->foreach()?
}

void SyncGraphGroup::initializeAvg() {
  Ptr<ExpressionGraph> graphAvg; // CPU-side temp
  std::string name = options_->get<std::string>("model");
  if(filesystem::exists(name + ".orig.npz")) {
    // Load the averaged parameters into a temporary graph
    graphAvg = New<ExpressionGraph>();
    graphAvg->setDevice({0, DeviceType::cpu});
    graphAvg->load(name, false);
    graphAvg->forward(); // initialize parameters if needed
  }

  auto init = [&](size_t localDeviceIndex, size_t begin, size_t end) {
    size_t size = end-begin;

    // get the device-specific allocator
    auto paramsAllocator = New<TensorAllocator>(graphs_[localDeviceIndex]->getBackend());
    paramsAllocs_[localDeviceIndex] = paramsAllocator;

    paramsAllocator->reserveExact(size * sizeof(float));

    Tensor paramAvg;
    paramsAllocator->allocate(paramAvg, {1, (int)size});
    paramsAvg_[localDeviceIndex] = paramAvg;

    if(graphAvg)
      paramAvg->copyFrom(graphAvg  ->params()->vals()->subtensor(begin, size));
    else
      paramAvg->copyFrom(graphs_[0]->params()->vals()->subtensor(begin, size));

    // note: for multi-node, graphAvg and graphs_[0] contain a complete copy, from which
    // each MPI process copies only part into its respective shard(s)
  };

  paramsAllocs_.resize(graphs_.size()); // allocators
  paramsAvg_.resize(graphs_.size());    // averaged parameters (shards; distributed over MPI processes if applicable)
  comm_->foreach(init, /*parallel=*/false); // @TODO: is sequential operation necessary here? (is the allocation stuff sufficiently reentrant or thread-separated?)
}

Ptr<data::BatchStats> SyncGraphGroup::collectStats() {
    size_t multiplier = devices_.size() * mpi_->numMPIProcesses() * delay_;
    return GraphGroup::collectStats(graphs_[0], builders_[0], multiplier);
}

void SyncGraphGroup::update(Ptr<data::Batch> batch) /*override*/ {
  ABORT_IF(finalized_, "Training has already finished.");

  // distribute the batch over (delay, local device, MPI rank)
  size_t numSubBatches = delay_ * devices_.size() * mpi_->numMPIProcesses();
  auto subBatches = batch->split(numSubBatches);
  subBatches.resize(numSubBatches); // pad with nullptrs if out of data

  // Helper to access the subBatches array
  auto getSubBatch = [&](size_t t, size_t localDeviceIndex, size_t rank) {
    // 't' (the delay) should be slowest changing dimension. If subBatches are sorted by
    // length, then grouping sentences of similar length into the same delay step can
    // reduce unnecessary time spent in padding.
    return subBatches[(t * mpi_->numMPIProcesses() + rank) * devices_.size() + localDeviceIndex];
  };

  // Upon very first execution, reset everything
  if(first_) {
    LOG(info, "[{}] Processing first minibatch. Batches are processed as {} processes x {} GPUs/process x {} delay steps.",
         mpi_->idStr(), mpi_->numMPIProcesses(), devices_.size(), delay_);
    initialize(subBatches.front());
    if(mvAvg_ && paramsAvg_.empty())
      initializeAvg();
    first_ = false;
  }

  // Compute gradients
  // This happens in multiple steps in case of delay_ > 1.
  std::vector<float> localDeviceCosts(devices_.size(), 0.f); // [local device index] aggregate cost for each local device
  for (size_t t = 0; t < delay_; t++) {
    // Execute single forward/backward step
    auto forwardBackward = [&](size_t localDeviceIndex, size_t /*begin*/, size_t /*end*/) {
      auto graph = graphs_[localDeviceIndex];
      auto subBatch = getSubBatch(t, localDeviceIndex, mpi_->myMPIRank());

      if(subBatch) {
        timer::Timer timer;
        auto costNode = builders_[localDeviceIndex]->build(graph, subBatch);
        LOG(info, timer.format(2, "after build: %ws"));
        graph->forward();
        LOG(info, timer.format(2, "after forward (no sync): %ws"));
        //localDeviceCosts[localDeviceIndex] += costNode->scalar();
        graph->backward(/*zero=*/t == 0); // only reset gradients to 0 if t = 0
        LOG(info, timer.format(2, "after backward (no sync): %ws"));
        localDeviceCosts[localDeviceIndex] += costNode->scalar(); // moved here for time measurements; @TODO: move this back
        LOG(info, timer.format(2, "after scalar() (that's a sync): %ws"));
      }
      else { // empty batch: execute do-nothing fw-bw step for proper inits and resets
        graph->forward();
        graph->backward(/*zero=*/t == 0);
      }
    };

    comm_->foreach(forwardBackward); // compute gradients in parallel on each device. Aggregate if delay_ > 1.
  }
  // At this point, each device on each MPI process has a gradient aggregated over a subset of the sub-batches.

  // Update parameter shard with gradient shard
  auto update = [&](size_t idx, size_t begin, size_t end) {
    auto curGrad = graphs_[idx]->params()->grads()->subtensor(begin, end-begin);
    auto curParam = graphs_[idx]->params()->vals()->subtensor(begin, end-begin);

    // if individual gradients were averages, then need to average again over all subBatches
    auto div = subBatches.size();
    if (options_->get<std::string>("cost-type") == "ce-sum")
      div = 1;
    if(div != 1) {
      using namespace functional;
      Element(_1 = _1 / (float)div, curGrad);
    }

    // actual model update
    shardOpt_[idx]->update(curParam, curGrad);

    if(mvAvg_)
      updateAvgParams(
          paramsAvg_[idx], curParam, scheduler_->numberOfBatches());
  };

  timer::Timer timer;
  comm_->scatterReduce(); // reduce gradients across all devices (globally) into shards
  LOG(info, timer.format(2, "after scatterReduce (has sync): %ws"));
  comm_->foreach(update); // per-shard model-update
  LOG(info, timer.format(2, "after model update (no sync): %ws"));
  comm_->allGather();     // distribute param value shards back
  LOG(info, timer.format(2, "after allGather (has sync): %ws"));

  // cost across all local devices (scheduler will aggregate cross-process)
  float localCost = 0;
  for(auto& c : localDeviceCosts) // localDeviceCosts is already summed up over delay steps
    localCost += c;

  // if localCost is average-based, we need to turn the sum over devices into an average as well
  if(options_->get<std::string>("cost-type") != "ce-sum")
    localCost /= numSubBatches;

  if(scheduler_) {
    // track and log localCost
    scheduler_->update(localCost, subBatches, mpi_);

    // save intermediate model (and optimizer state) to file
    if(scheduler_->saving())
      save();

    // process valid data set
    // This may save a model as well.
    if(scheduler_->validating()) {
      swapParamsAvg();
      if (isMainProcess())
        scheduler_->validate(graphs_);
      swapParamsAvg();
    }
  }
}

void SyncGraphGroup::load() /*override*/ {
  if(!options_->get<bool>("no-reload")) {
    std::string name = options_->get<std::string>("model");

    if(filesystem::exists(name)) {
      if(scheduler_)
        scheduler_->load(name);

      std::string nameGraph = name;
      if(mvAvg_ && filesystem::exists(name + ".orig.npz"))
        // Load the original parameters from model.npz.orig.npz
        nameGraph += ".orig.npz";

      size_t i = 0;
      for(auto graph : graphs_)
        builders_[i++]->load(graph, nameGraph); // we just load it N times from disk (it'll be in disk cache after the first)

      // @TODO: probably we want to have the list of DeviceIds as an attribute
      std::vector<Ptr<Backend>> backends;
      for(auto graph : graphs_)
        backends.push_back(graph->getBackend());
      shardOpt_[0]->load(name + ".optimizer.npz", shardOpt_, backends,
        [&](const std::vector<float>& optimizerStateVector, const OptimizerBase::ScatterStateSetFunc& setShardFn) {
          comm_->scatterState(optimizerStateVector, setShardFn);
        });
    } else if(options_->has("pretrained-model")) {
      std::string nameInit = options_->get<std::string>("pretrained-model");
      LOG(info,
          "Initialize model weights with the pre-trained model {}",
          nameInit);

      size_t i = 0;
      for(auto graph : graphs_)
        builders_[i++]->load(graph, nameInit, false);
    }
  }
}

void SyncGraphGroup::save(bool final) /*override*/ {
  barrier(); // (for better grouping of log messages)
  LOG(info, "[{}] save() line {}!", this->mpi_->idStr(), __LINE__);
  // do final validation
  if(final && scheduler_) {
    // bring the smoothed model in
    // Note that it is sharded. For multi-node, it is sharded over multiple machines, so this is a network access.
    // Also note that the swap must run on all MPI processes concurrently, although only one actually validates.
    LOG(info, "[{}] save() line {}", this->mpi_->idStr(), __LINE__);
    swapParamsAvg();
    LOG(info, "[{}] save() line {}", this->mpi_->idStr(), __LINE__);
    if (isMainProcess()) // in multi-node, only first MPI process saves the model (they are all identical)
      scheduler_->validate(graphs_, true);
    LOG(info, "[{}] save() line {}", this->mpi_->idStr(), __LINE__);
    swapParamsAvg();
    LOG(info, "[{}] save() line {}", this->mpi_->idStr(), __LINE__);
  }

  std::string name = options_->get<std::string>("model");

  LOG(info, "[{}] save() line {}", this->mpi_->idStr(), __LINE__);
  barrier(); // (for better grouping of log messages)
  LOG(info, "[{}] save() line {}", this->mpi_->idStr(), __LINE__);
  // if smoothing then save original (unsmoothed) parameters as well
  // @TODO: Check whether we are reloading the correct file (the unsmoothed one).
  if(mvAvg_ && paramsAvg_.size() > 0 && isMainProcess()) // only save from one MPI process
    // Save the original parameters in model.npz.orig.npz
    builders_[0]->save(graphs_[0], name + ".orig.npz", true);

  // Temporarily switch to the averaged parameters
  // Note: the smoothed model is sharded across GPUs, and across MPI processes if applicable. This brings it into MPI process[*].device[*]
  LOG(info, "[{}] save() line {}", this->mpi_->idStr(), __LINE__);
  swapParamsAvg();
  LOG(info, "[{}] save() line {}", this->mpi_->idStr(), __LINE__);

  // save main model file
  if (isMainProcess()) { // only save from one MPI process
    // if not overwrite then save a copy with number of updates in the model pathname
    LOG(info, "[{}] save() line {}", this->mpi_->idStr(), __LINE__);
    if(!options_->get<bool>("overwrite") && !final) {
      std::string numberOfBatches
          = scheduler_ ? std::to_string(scheduler_->numberOfBatches())
                       : "unknown";
      std::string nameOverwrite = name;
      nameOverwrite.replace(name.size() - 4, 4, ".iter" + numberOfBatches + ".npz"); // @TODO: use insert?
      builders_[0]->save(graphs_[0], nameOverwrite);
    }
    LOG(info, "[{}] save() line {}", this->mpi_->idStr(), __LINE__);
    // save main model file
    builders_[0]->save(graphs_[0], name, true);
    LOG(info, "[{}] save() line {}", this->mpi_->idStr(), __LINE__);
    // save scheduler-related state
    if (scheduler_)
      scheduler_->save(name);
    LOG(info, "[{}] save() line {}", this->mpi_->idStr(), __LINE__);
  }

  // Switch back to the original parameters
  LOG(info, "[{}] save() line {}", this->mpi_->idStr(), __LINE__);
  swapParamsAvg();
  LOG(info, "[{}] save() line {}", this->mpi_->idStr(), __LINE__);

#if 1 // temporary, for testing of saving distributed models; must be identical to .orig.npz
  if(mvAvg_ && paramsAvg_.size() > 0 && isMainProcess())
    builders_[0]->save(graphs_[0], name + ".orig_after_swapping.npz", true);
#endif
  LOG(info, "[{}] save() line {}", this->mpi_->idStr(), __LINE__);
  barrier(); // (for better grouping of log messages)
  LOG(info, "[{}] save() line {}", this->mpi_->idStr(), __LINE__);

  // persist optimizer state
  LOG(info, "[{}] save() line {}", this->mpi_->idStr(), __LINE__);
  shardOpt_[0]->save(name + ".optimizer.npz", shardOpt_,
    [&](const OptimizerBase::GatherStateGetFunc& getShardFn) {
      return comm_->gatherState(getShardFn);
    },
    isMainProcess());
  LOG(info, "[{}] save() line {}", this->mpi_->idStr(), __LINE__);

  barrier(); // (for better grouping of log messages)
  LOG(info, "[{}] save() line {}", this->mpi_->idStr(), __LINE__);
}

}  // namespace marian
<commit_msg>(added a comment)<commit_after>#include "training/graph_group_sync.h"

namespace marian {

SyncGraphGroup::SyncGraphGroup(Ptr<Config> config)
    : GraphGroup(config),
      ExponentialSmoothing{options_->get<float>("exponential-smoothing")},
      delay_{options_->get<size_t>("optimizer-delay")} { // @TODO: rename to something else; delay means delayed updated, not accumulation

  mpi_ = initMPI(/*multiThreaded=*/false); // when not running under MPI, this will be a fake object that represents a one-MPI-process setup

  devices_ = options_->getDevices(mpi_->myMPIRank(), mpi_->numMPIProcesses());
  for(auto device : devices_) {
    auto graph = New<ExpressionGraph>();
    graph->setDevice(device);
    graph->reserveWorkspaceMB(options_->get<size_t>("workspace"));
    graph->getBackend()->setClip(options_->get<float>("clip-gemm"));

    graphs_.push_back(graph);
    shardOpt_.push_back(Optimizer(options_));
    builders_.push_back(models::from_config(options_, models::usage::training));
  }

  // Note: We may well end up with only one MPI process or only one graph per worker.
  // This part of the code will not special-case any of this here.
  // Rather, it is assumed that the communicator knows to reduce unnecessary transfers to no-ops.
  comm_ = createCommunicator(graphs_, /*noNccl=*/options_->get<bool>("no-nccl", false), /*mpi=*/mpi_);
}

void SyncGraphGroup::setScheduler(Ptr<Scheduler> scheduler) /*override*/ {
  scheduler_ = scheduler;
  // optimizer has to be registered last to see changes of learning rate
  // @TODO: ^^Fix this comment. Either it refers to the scheduler, or it should be moved. Which one?
  scheduler_->registerTrainingObserver(scheduler_);

  for(auto opt : shardOpt_)
    scheduler_->registerTrainingObserver(opt);
}

void SyncGraphGroup::initialize(const Ptr<data::Batch>& exampleBatch) {
  // Initialize 0th graph with random weights in one forward step
  // @TODO: Why do we need the THREAD_GUARD here? Why not run this on the main thread?
  THREAD_GUARD({
    builders_[0]->build(graphs_[0], exampleBatch);
    graphs_[0]->forward();
  });

  // Copy weights from 0th graph to all other graphs
  // to have equal weights across devices
  ThreadPool pool(graphs_.size() - 1, graphs_.size() - 1);
  for(size_t i = 1; i < graphs_.size(); ++i) {
    auto init = [&](size_t i) {
      // initialize t-th graph and weights
      builders_[i]->build(graphs_[i], exampleBatch);
      graphs_[i]->forward();
      // overwrite weights of t-th graph with weights from 0th graph
      graphs_[i]->params()->vals()->copyFrom(graphs_[0]->params()->vals());
    };
    pool.enqueue(init, i);
  }
  // ThreadPool destructor waits until completion of all tasks.
  // @TODO: can we use comm_->foreach()?
}

void SyncGraphGroup::initializeAvg() {
  Ptr<ExpressionGraph> graphAvg; // CPU-side temp
  std::string name = options_->get<std::string>("model");
  if(filesystem::exists(name + ".orig.npz")) {
    // Load the averaged parameters into a temporary graph
    graphAvg = New<ExpressionGraph>();
    graphAvg->setDevice({0, DeviceType::cpu});
    graphAvg->load(name, false);
    graphAvg->forward(); // initialize parameters if needed
  }

  auto init = [&](size_t localDeviceIndex, size_t begin, size_t end) {
    size_t size = end-begin;

    // get the device-specific allocator
    auto paramsAllocator = New<TensorAllocator>(graphs_[localDeviceIndex]->getBackend());
    paramsAllocs_[localDeviceIndex] = paramsAllocator;

    paramsAllocator->reserveExact(size * sizeof(float));

    Tensor paramAvg;
    paramsAllocator->allocate(paramAvg, {1, (int)size});
    paramsAvg_[localDeviceIndex] = paramAvg;

    if(graphAvg)
      paramAvg->copyFrom(graphAvg  ->params()->vals()->subtensor(begin, size));
    else
      paramAvg->copyFrom(graphs_[0]->params()->vals()->subtensor(begin, size));

    // note: for multi-node, graphAvg and graphs_[0] contain a complete copy, from which
    // each MPI process copies only part into its respective shard(s)
  };

  paramsAllocs_.resize(graphs_.size()); // allocators
  paramsAvg_.resize(graphs_.size());    // averaged parameters (shards; distributed over MPI processes if applicable)
  comm_->foreach(init, /*parallel=*/false); // @TODO: is sequential operation necessary here? (is the allocation stuff sufficiently reentrant or thread-separated?)
}

Ptr<data::BatchStats> SyncGraphGroup::collectStats() {
  // @TODO: This should only run on MPI process 0. Also we can share vv this vv expression with update().
  size_t multiplier = devices_.size() * mpi_->numMPIProcesses() * delay_;
  return GraphGroup::collectStats(graphs_[0], builders_[0], multiplier);
}

void SyncGraphGroup::update(Ptr<data::Batch> batch) /*override*/ {
  ABORT_IF(finalized_, "Training has already finished.");

  // distribute the batch over (delay, local device, MPI rank)
  size_t numSubBatches = delay_ * devices_.size() * mpi_->numMPIProcesses();
  auto subBatches = batch->split(numSubBatches);
  subBatches.resize(numSubBatches); // pad with nullptrs if out of data

  // Helper to access the subBatches array
  auto getSubBatch = [&](size_t t, size_t localDeviceIndex, size_t rank) {
    // 't' (the delay) should be slowest changing dimension. If subBatches are sorted by
    // length, then grouping sentences of similar length into the same delay step can
    // reduce unnecessary time spent in padding.
    return subBatches[(t * mpi_->numMPIProcesses() + rank) * devices_.size() + localDeviceIndex];
  };

  // Upon very first execution, reset everything
  if(first_) {
    LOG(info, "[{}] Processing first minibatch. Batches are processed as {} processes x {} GPUs/process x {} delay steps.",
         mpi_->idStr(), mpi_->numMPIProcesses(), devices_.size(), delay_);
    initialize(subBatches.front());
    if(mvAvg_ && paramsAvg_.empty())
      initializeAvg();
    first_ = false;
  }

  // Compute gradients
  // This happens in multiple steps in case of delay_ > 1.
  std::vector<float> localDeviceCosts(devices_.size(), 0.f); // [local device index] aggregate cost for each local device
  for (size_t t = 0; t < delay_; t++) {
    // Execute single forward/backward step
    auto forwardBackward = [&](size_t localDeviceIndex, size_t /*begin*/, size_t /*end*/) {
      auto graph = graphs_[localDeviceIndex];
      auto subBatch = getSubBatch(t, localDeviceIndex, mpi_->myMPIRank());

      if(subBatch) {
        timer::Timer timer;
        auto costNode = builders_[localDeviceIndex]->build(graph, subBatch);
        LOG(info, timer.format(2, "after build: %ws"));
        graph->forward();
        LOG(info, timer.format(2, "after forward (no sync): %ws"));
        //localDeviceCosts[localDeviceIndex] += costNode->scalar();
        graph->backward(/*zero=*/t == 0); // only reset gradients to 0 if t = 0
        LOG(info, timer.format(2, "after backward (no sync): %ws"));
        localDeviceCosts[localDeviceIndex] += costNode->scalar(); // moved here for time measurements; @TODO: move this back
        LOG(info, timer.format(2, "after scalar() (that's a sync): %ws"));
      }
      else { // empty batch: execute do-nothing fw-bw step for proper inits and resets
        graph->forward();
        graph->backward(/*zero=*/t == 0);
      }
    };

    comm_->foreach(forwardBackward); // compute gradients in parallel on each device. Aggregate if delay_ > 1.
  }
  // At this point, each device on each MPI process has a gradient aggregated over a subset of the sub-batches.

  // Update parameter shard with gradient shard
  auto update = [&](size_t idx, size_t begin, size_t end) {
    auto curGrad = graphs_[idx]->params()->grads()->subtensor(begin, end-begin);
    auto curParam = graphs_[idx]->params()->vals()->subtensor(begin, end-begin);

    // if individual gradients were averages, then need to average again over all subBatches
    auto div = subBatches.size();
    if (options_->get<std::string>("cost-type") == "ce-sum")
      div = 1;
    if(div != 1) {
      using namespace functional;
      Element(_1 = _1 / (float)div, curGrad);
    }

    // actual model update
    shardOpt_[idx]->update(curParam, curGrad);

    if(mvAvg_)
      updateAvgParams(
          paramsAvg_[idx], curParam, scheduler_->numberOfBatches());
  };

  timer::Timer timer;
  comm_->scatterReduce(); // reduce gradients across all devices (globally) into shards
  LOG(info, timer.format(2, "after scatterReduce (has sync): %ws"));
  comm_->foreach(update); // per-shard model-update
  LOG(info, timer.format(2, "after model update (no sync): %ws"));
  comm_->allGather();     // distribute param value shards back
  LOG(info, timer.format(2, "after allGather (has sync): %ws"));

  // cost across all local devices (scheduler will aggregate cross-process)
  float localCost = 0;
  for(auto& c : localDeviceCosts) // localDeviceCosts is already summed up over delay steps
    localCost += c;

  // if localCost is average-based, we need to turn the sum over devices into an average as well
  if(options_->get<std::string>("cost-type") != "ce-sum")
    localCost /= numSubBatches;

  if(scheduler_) {
    // track and log localCost
    scheduler_->update(localCost, subBatches, mpi_);

    // save intermediate model (and optimizer state) to file
    if(scheduler_->saving())
      save();

    // process valid data set
    // This may save a model as well.
    if(scheduler_->validating()) {
      swapParamsAvg();
      if (isMainProcess())
        scheduler_->validate(graphs_);
      swapParamsAvg();
    }
  }
}

void SyncGraphGroup::load() /*override*/ {
  if(!options_->get<bool>("no-reload")) {
    std::string name = options_->get<std::string>("model");

    if(filesystem::exists(name)) {
      if(scheduler_)
        scheduler_->load(name);

      std::string nameGraph = name;
      if(mvAvg_ && filesystem::exists(name + ".orig.npz"))
        // Load the original parameters from model.npz.orig.npz
        nameGraph += ".orig.npz";

      size_t i = 0;
      for(auto graph : graphs_)
        builders_[i++]->load(graph, nameGraph); // we just load it N times from disk (it'll be in disk cache after the first)

      // @TODO: probably we want to have the list of DeviceIds as an attribute
      std::vector<Ptr<Backend>> backends;
      for(auto graph : graphs_)
        backends.push_back(graph->getBackend());
      shardOpt_[0]->load(name + ".optimizer.npz", shardOpt_, backends,
        [&](const std::vector<float>& optimizerStateVector, const OptimizerBase::ScatterStateSetFunc& setShardFn) {
          comm_->scatterState(optimizerStateVector, setShardFn);
        });
    } else if(options_->has("pretrained-model")) {
      std::string nameInit = options_->get<std::string>("pretrained-model");
      LOG(info,
          "Initialize model weights with the pre-trained model {}",
          nameInit);

      size_t i = 0;
      for(auto graph : graphs_)
        builders_[i++]->load(graph, nameInit, false);
    }
  }
}

void SyncGraphGroup::save(bool final) /*override*/ {
  barrier(); // (for better grouping of log messages)
  LOG(info, "[{}] save() line {}!", this->mpi_->idStr(), __LINE__);
  // do final validation
  if(final && scheduler_) {
    // bring the smoothed model in
    // Note that it is sharded. For multi-node, it is sharded over multiple machines, so this is a network access.
    // Also note that the swap must run on all MPI processes concurrently, although only one actually validates.
    LOG(info, "[{}] save() line {}", this->mpi_->idStr(), __LINE__);
    swapParamsAvg();
    LOG(info, "[{}] save() line {}", this->mpi_->idStr(), __LINE__);
    if (isMainProcess()) // in multi-node, only first MPI process saves the model (they are all identical)
      scheduler_->validate(graphs_, true);
    LOG(info, "[{}] save() line {}", this->mpi_->idStr(), __LINE__);
    swapParamsAvg();
    LOG(info, "[{}] save() line {}", this->mpi_->idStr(), __LINE__);
  }

  std::string name = options_->get<std::string>("model");

  LOG(info, "[{}] save() line {}", this->mpi_->idStr(), __LINE__);
  barrier(); // (for better grouping of log messages)
  LOG(info, "[{}] save() line {}", this->mpi_->idStr(), __LINE__);
  // if smoothing then save original (unsmoothed) parameters as well
  // @TODO: Check whether we are reloading the correct file (the unsmoothed one).
  if(mvAvg_ && paramsAvg_.size() > 0 && isMainProcess()) // only save from one MPI process
    // Save the original parameters in model.npz.orig.npz
    builders_[0]->save(graphs_[0], name + ".orig.npz", true);

  // Temporarily switch to the averaged parameters
  // Note: the smoothed model is sharded across GPUs, and across MPI processes if applicable. This brings it into MPI process[*].device[*]
  LOG(info, "[{}] save() line {}", this->mpi_->idStr(), __LINE__);
  swapParamsAvg();
  LOG(info, "[{}] save() line {}", this->mpi_->idStr(), __LINE__);

  // save main model file
  if (isMainProcess()) { // only save from one MPI process
    // if not overwrite then save a copy with number of updates in the model pathname
    LOG(info, "[{}] save() line {}", this->mpi_->idStr(), __LINE__);
    if(!options_->get<bool>("overwrite") && !final) {
      std::string numberOfBatches
          = scheduler_ ? std::to_string(scheduler_->numberOfBatches())
                       : "unknown";
      std::string nameOverwrite = name;
      nameOverwrite.replace(name.size() - 4, 4, ".iter" + numberOfBatches + ".npz"); // @TODO: use insert?
      builders_[0]->save(graphs_[0], nameOverwrite);
    }
    LOG(info, "[{}] save() line {}", this->mpi_->idStr(), __LINE__);
    // save main model file
    builders_[0]->save(graphs_[0], name, true);
    LOG(info, "[{}] save() line {}", this->mpi_->idStr(), __LINE__);
    // save scheduler-related state
    if (scheduler_)
      scheduler_->save(name);
    LOG(info, "[{}] save() line {}", this->mpi_->idStr(), __LINE__);
  }

  // Switch back to the original parameters
  LOG(info, "[{}] save() line {}", this->mpi_->idStr(), __LINE__);
  swapParamsAvg();
  LOG(info, "[{}] save() line {}", this->mpi_->idStr(), __LINE__);

#if 1 // temporary, for testing of saving distributed models; must be identical to .orig.npz
  if(mvAvg_ && paramsAvg_.size() > 0 && isMainProcess())
    builders_[0]->save(graphs_[0], name + ".orig_after_swapping.npz", true);
#endif
  LOG(info, "[{}] save() line {}", this->mpi_->idStr(), __LINE__);
  barrier(); // (for better grouping of log messages)
  LOG(info, "[{}] save() line {}", this->mpi_->idStr(), __LINE__);

  // persist optimizer state
  LOG(info, "[{}] save() line {}", this->mpi_->idStr(), __LINE__);
  shardOpt_[0]->save(name + ".optimizer.npz", shardOpt_,
    [&](const OptimizerBase::GatherStateGetFunc& getShardFn) {
      return comm_->gatherState(getShardFn);
    },
    isMainProcess());
  LOG(info, "[{}] save() line {}", this->mpi_->idStr(), __LINE__);

  barrier(); // (for better grouping of log messages)
  LOG(info, "[{}] save() line {}", this->mpi_->idStr(), __LINE__);
}

}  // namespace marian
<|endoftext|>


<commit_before>//
//
//
inline bool no_such_file()
{
    return no_file;
};
<commit_msg>Delete file_system__no_such_file.cpp<commit_after><|endoftext|>


<commit_before>#include "videosourcefactory.h"

namespace gg
{

VideoSourceFactory VideoSourceFactory::_factory_singleton;

VideoSourceFactory::VideoSourceFactory()
{
    // TODO
}

VideoSourceFactory::~VideoSourceFactory()
{
    // TODO
}

VideoSourceFactory & VideoSourceFactory::get_instance()
{
    return _factory_singleton;
}

IVideoSource * VideoSourceFactory::get_device(Device device,
                                              ColourSpace colour)
{
    // TODO
    return nullptr;
}

}
<commit_msg>Issue #121: implemented free_device function of VideoSourceFactory<commit_after>#include "videosourcefactory.h"

namespace gg
{

VideoSourceFactory VideoSourceFactory::_factory_singleton;

VideoSourceFactory::VideoSourceFactory()
{
    // TODO
}

VideoSourceFactory::~VideoSourceFactory()
{
    // TODO
}

VideoSourceFactory & VideoSourceFactory::get_instance()
{
    return _factory_singleton;
}

IVideoSource * VideoSourceFactory::get_device(Device device,
                                              ColourSpace colour)
{
    // TODO
}

void VideoSourceFactory::free_device(Device device)
{
    if (_devices[(int) device] != nullptr)
        delete _devices[(int) device];
    _devices[(int) device] = nullptr;
}

}
<|endoftext|>


<commit_before>//=======================================================================
// Copyright Baptiste Wicht 2011.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
//  http://www.boost.org/LICENSE_1_0.txt)
//=======================================================================

#include "SemanticalException.hpp"
#include "VisitorUtils.hpp"
#include "mangling.hpp"
#include "Options.hpp"
#include "Type.hpp"
#include "GlobalContext.hpp"
#include "FunctionContext.hpp"
#include "Variable.hpp"

#include "ast/FunctionsAnnotator.hpp"
#include "ast/SourceFile.hpp"
#include "ast/TypeTransformer.hpp"
#include "ast/ASTVisitor.hpp"
#include "ast/GetTypeVisitor.hpp"

using namespace eddic;

namespace {

class MemberFunctionAnnotator : public boost::static_visitor<> {
    private:
        std::shared_ptr<GlobalContext> context;
        std::string parent_struct;
        ast::Struct current_struct;

    public:
        void operator()(ast::SourceFile& program){
            context = program.Content->context;

            visit_each(*this, program.Content->blocks);
        }

        void operator()(ast::Struct& struct_){
            current_struct = struct_;
            parent_struct = struct_.Content->name;

            visit_each_non_variant(*this, struct_.Content->constructors);
            visit_each_non_variant(*this, struct_.Content->destructors);
            visit_each_non_variant(*this, struct_.Content->functions);

            parent_struct = "";
        }

        template<typename T>
        void annotate(T& declaration){
            if(!declaration.Content->marked){
                declaration.Content->struct_name = parent_struct;
                declaration.Content->struct_type = current_struct.Content->struct_type;

                ast::PointerType paramType;

                if(current_struct.Content->template_types.empty()){
                    ast::SimpleType struct_type;
                    struct_type.type = parent_struct;
                    struct_type.const_ = false;

                    paramType.type = struct_type;
                } else {
                    ast::TemplateType struct_type;
                    struct_type.type = parent_struct;
                    struct_type.template_types = current_struct.Content->template_types;
                    struct_type.resolved = true;

                    paramType.type = struct_type;
                }

                ast::FunctionParameter param;
                param.parameterName = "this";
                param.parameterType = paramType;

                declaration.Content->parameters.insert(declaration.Content->parameters.begin(), param);
            }
        }

        void operator()(ast::Constructor& constructor){
            annotate(constructor);
        }

        void operator()(ast::Destructor& destructor){
            annotate(destructor);
        }

        void operator()(ast::FunctionDeclaration& declaration){
            if(!parent_struct.empty()){
                annotate(declaration);
            }
        }

        AUTO_IGNORE_OTHERS()
};

class FunctionInserterVisitor : public boost::static_visitor<> {
    private:
        std::shared_ptr<GlobalContext> context;

    public:
        AUTO_RECURSE_STRUCT()

        void operator()(ast::SourceFile& program){
            context = program.Content->context;

            visit_each(*this, program.Content->blocks);
        }

        void operator()(ast::FunctionDeclaration& declaration){
            if(!declaration.Content->marked){
                auto return_type = visit(ast::TypeTransformer(context), declaration.Content->returnType);
                auto signature = std::make_shared<Function>(return_type, declaration.Content->functionName);

                if(return_type->is_array()){
                    throw SemanticalException("Cannot return array from function", declaration.Content->position);
                }

                if(return_type->is_custom_type()){
                    throw SemanticalException("Cannot return struct from function", declaration.Content->position);
                }

                for(auto& param : declaration.Content->parameters){
                    auto paramType = visit(ast::TypeTransformer(context), param.parameterType);
                    signature->parameters.push_back(ParameterType(param.parameterName, paramType));
                }

                signature->struct_ = declaration.Content->struct_name;
                signature->struct_type = declaration.Content->struct_type;
                signature->context = declaration.Content->context;

                declaration.Content->mangledName = signature->mangledName = mangle(signature);

                if(context->exists(signature->mangledName)){
                    throw SemanticalException("The function " + signature->mangledName + " has already been defined", declaration.Content->position);
                }

                context->addFunction(signature);
            }
        }

        void operator()(ast::Constructor& constructor){
            if(!constructor.Content->marked){
                auto signature = std::make_shared<Function>(VOID, "ctor");

                for(auto& param : constructor.Content->parameters){
                    auto paramType = visit(ast::TypeTransformer(context), param.parameterType);
                    signature->parameters.push_back(ParameterType(param.parameterName, paramType));
                }

                signature->struct_ = constructor.Content->struct_name;
                signature->struct_type = constructor.Content->struct_type;

                constructor.Content->mangledName = signature->mangledName = mangle_ctor(signature);

                if(context->exists(signature->mangledName)){
                    throw SemanticalException("The constructor " + signature->name + " has already been defined", constructor.Content->position);
                }

                context->addFunction(signature);
                context->getFunction(signature->mangledName)->context = constructor.Content->context;
            }
        }

        void operator()(ast::Destructor& destructor){
            if(!destructor.Content->marked){
                auto signature = std::make_shared<Function>(VOID, "dtor");

                for(auto& param : destructor.Content->parameters){
                    auto paramType = visit(ast::TypeTransformer(context), param.parameterType);
                    signature->parameters.push_back(ParameterType(param.parameterName, paramType));
                }

                signature->struct_ = destructor.Content->struct_name;
                signature->struct_type = destructor.Content->struct_type;

                destructor.Content->mangledName = signature->mangledName = mangle_dtor(signature);

                if(context->exists(signature->mangledName)){
                    throw SemanticalException("Only one destructor per struct is allowed", destructor.Content->position);
                }

                context->addFunction(signature);
                context->getFunction(signature->mangledName)->context = destructor.Content->context;
            }
        }

        AUTO_IGNORE_OTHERS()

    private:
        std::string parent_struct;
};

class FunctionCheckerVisitor : public boost::static_visitor<> {
    private:
        std::shared_ptr<Function> currentFunction;
        std::shared_ptr<GlobalContext> context;

    public:
        AUTO_RECURSE_GLOBAL_DECLARATION()
        AUTO_RECURSE_MEMBER_VALUE()
        AUTO_RECURSE_STRUCT()

        void operator()(ast::Constructor& function){
            check_each(function.Content->instructions);
        }

        void operator()(ast::Destructor& function){
            check_each(function.Content->instructions);
        }

        void operator()(ast::DefaultCase& default_case){
            check_each(default_case.instructions);
        }

        void operator()(ast::Foreach& foreach_){
            check_each(foreach_.Content->instructions);
        }

        void operator()(ast::ForeachIn& foreach_){
            check_each(foreach_.Content->instructions);
        }

        void operator()(ast::If& if_){
            check_value(if_.Content->condition);
            check_each(if_.Content->instructions);
            visit_each_non_variant(*this, if_.Content->elseIfs);
            visit_optional_non_variant(*this, if_.Content->else_);
        }

        void operator()(ast::ElseIf& elseIf){
            check_value(elseIf.condition);
            check_each(elseIf.instructions);
        }

        void operator()(ast::Else& else_){
            check_each(else_.instructions);
        }

        template<typename T>
        void check_each(std::vector<T>& values){
            for(std::size_t i = 0; i < values.size(); ++i){
                check_value(values[i]);
            }
        }

        template<typename V>
        void check_value(V& value){
            if(auto* ptr = boost::get<ast::FunctionCall>(&value)){
                auto functionCall = *ptr;

                if(functionCall.Content->template_types.empty() || functionCall.Content->resolved){
                    check_each(functionCall.Content->values);

                    std::string name = functionCall.Content->function_name;

                    auto types = get_types(functionCall);

                    auto mangled = mangle(name, types);
                    auto original_mangled = mangled;

                    //If the function does not exists, try implicit conversions to pointers
                    if(!context->exists(mangled)){
                        auto perms = permutations(types);

                        for(auto& perm : perms){
                            mangled = mangle(name, perm);

                            if(context->exists(mangled)){
                                break;
                            }
                        }
                    }

                    if(context->exists(mangled)){
                        context->addReference(mangled);

                        functionCall.Content->mangled_name = mangled;
                        functionCall.Content->function = context->getFunction(mangled);
                    } else {
                        auto local_context = functionCall.Content->context->function();

                        if(local_context && local_context->struct_type && context->struct_exists(local_context->struct_type->mangle())){
                            auto struct_type = local_context->struct_type;

                            mangled = mangle(name, types, struct_type);

                            //If the function does not exists, try implicit conversions to pointers
                            if(!context->exists(mangled)){
                                auto perms = permutations(types);

                                for(auto& perm : perms){
                                    mangled = mangle(name, perm, struct_type);

                                    if(context->exists(mangled)){
                                        break;
                                    }
                                }
                            }

                            if(context->exists(mangled)){
                                context->addReference(mangled);

                                ast::MemberFunctionCall member_function_call;
                                member_function_call.Content->function = context->getFunction(mangled);
                                member_function_call.Content->context = functionCall.Content->context;
                                member_function_call.Content->mangled_name = mangled;
                                member_function_call.Content->position = functionCall.Content->position;
                                member_function_call.Content->object_name = "this";
                                member_function_call.Content->function_name = functionCall.Content->function_name;
                                member_function_call.Content->resolved = functionCall.Content->resolved;
                                member_function_call.Content->template_types = functionCall.Content->template_types;
                                member_function_call.Content->values = functionCall.Content->values;

                                value = member_function_call;

                                return;
                            }
                        }

                        throw SemanticalException("The function \"" + unmangle(original_mangled) + "\" does not exists", functionCall.Content->position);
                    }
                }
            } else {
                visit(*this, value);
            }
        }

        void operator()(ast::For& for_){
            if(for_.Content->start){
                check_value(*for_.Content->start);
            }

            if(for_.Content->condition){
                check_value(*for_.Content->condition);
            }

            if(for_.Content->repeat){
                check_value(*for_.Content->repeat);
            }

            check_each(for_.Content->instructions);
        }

        void operator()(ast::While& while_){
            check_value(while_.Content->condition);
            check_each(while_.Content->instructions);
        }

        void operator()(ast::DoWhile& while_){
            check_value(while_.Content->condition);
            check_each(while_.Content->instructions);
        }

        void operator()(ast::SourceFile& program){
            context = program.Content->context;

            visit_each(*this, program.Content->blocks);
        }

        void operator()(ast::FunctionDeclaration& declaration){
            currentFunction = context->getFunction(declaration.Content->mangledName);

            check_each(declaration.Content->instructions);
        }

        void permute(std::vector<std::vector<std::shared_ptr<const Type>>>& perms, std::vector<std::shared_ptr<const Type>>& types, int start){
            for(std::size_t i = start; i < types.size(); ++i){
                if(!types[i]->is_pointer() && !types[i]->is_array()){
                    std::vector<std::shared_ptr<const Type>> copy = types;

                    copy[i] = new_pointer_type(types[i]);

                    perms.push_back(copy);

                    permute(perms, copy, i + 1);
                }
            }
        }

        std::vector<std::vector<std::shared_ptr<const Type>>> permutations(std::vector<std::shared_ptr<const Type>>& types){
            std::vector<std::vector<std::shared_ptr<const Type>>> perms;

            permute(perms, types, 0);

            return perms;
        }

        template<typename T>
        std::vector<std::shared_ptr<const Type>> get_types(T& functionCall){
            std::vector<std::shared_ptr<const Type>> types;

            ast::GetTypeVisitor visitor;
            for(auto& value : functionCall.Content->values){
                types.push_back(visit(visitor, value));
            }

            return types;
        }

        void operator()(ast::FunctionCall&){
            ASSERT_PATH_NOT_TAKEN("Should be handled by check_value");
        }

        void operator()(ast::MemberFunctionCall& functionCall){
            if(functionCall.Content->template_types.empty() || functionCall.Content->resolved){
                auto var = functionCall.Content->context->getVariable(functionCall.Content->object_name);
                auto type = var->type();
                auto struct_type = type->is_pointer() ? type->data_type() : type;

                check_each(functionCall.Content->values);

                std::string name = functionCall.Content->function_name;

                auto types = get_types(functionCall);

                std::string mangled = mangle(name, types, struct_type);

                //If the function does not exists, try implicit conversions to pointers
                if(!context->exists(mangled)){
                    auto perms = permutations(types);

                    for(auto& perm : perms){
                        mangled = mangle(name, perm, struct_type);

                        if(context->exists(mangled)){
                            break;
                        }
                    }
                }

                if(context->exists(mangled)){
                    context->addReference(mangled);

                    functionCall.Content->mangled_name = mangled;
                    functionCall.Content->function = context->getFunction(mangled);
                } else {
                    throw SemanticalException("The member function \"" + unmangle(mangled) + "\" does not exists", functionCall.Content->position);
                }
            }
        }

        void operator()(ast::Switch& switch_){
            check_value(switch_.Content->value);
            visit_each_non_variant(*this, switch_.Content->cases);
            visit_optional_non_variant(*this, switch_.Content->default_case);
        }

        void operator()(ast::SwitchCase& switch_case){
            check_value(switch_case.value);
            visit_each(*this, switch_case.instructions);
        }

        void operator()(ast::Assignment& assignment){
            visit(*this, assignment.Content->left_value);
            check_value(assignment.Content->value);
        }

        void operator()(ast::VariableDeclaration& declaration){
            if(declaration.Content->value){
                check_value(*declaration.Content->value);
            }
        }

        void operator()(ast::Unary& value){
            check_value(value.Content->value);
        }

        void operator()(ast::BuiltinOperator& builtin){
            check_each(builtin.Content->values);
        }

        void operator()(ast::Expression& value){
            check_value(value.Content->first);

            for_each(value.Content->operations.begin(), value.Content->operations.end(),
                [&](ast::Operation& operation){ check_value(operation.get<1>()); });
        }

        void operator()(ast::Return& return_){
            return_.Content->function = currentFunction;

            check_value(return_.Content->value);
        }

        AUTO_IGNORE_OTHERS()
};

} //end of anonymous namespace

void ast::defineMemberFunctions(ast::SourceFile& program){
    MemberFunctionAnnotator annotator;
    annotator(program);
}

void ast::defineFunctions(ast::SourceFile& program){
    //First phase : Collect functions
    FunctionInserterVisitor inserterVisitor;
    inserterVisitor(program);

    //Second phase : Verify calls
    FunctionCheckerVisitor checkerVisitor;
    checkerVisitor(program);
}
<commit_msg>Fix recursion<commit_after>//=======================================================================
// Copyright Baptiste Wicht 2011.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
//  http://www.boost.org/LICENSE_1_0.txt)
//=======================================================================

#include "SemanticalException.hpp"
#include "VisitorUtils.hpp"
#include "mangling.hpp"
#include "Options.hpp"
#include "Type.hpp"
#include "GlobalContext.hpp"
#include "FunctionContext.hpp"
#include "Variable.hpp"

#include "ast/FunctionsAnnotator.hpp"
#include "ast/SourceFile.hpp"
#include "ast/TypeTransformer.hpp"
#include "ast/ASTVisitor.hpp"
#include "ast/GetTypeVisitor.hpp"

using namespace eddic;

namespace {

class MemberFunctionAnnotator : public boost::static_visitor<> {
    private:
        std::shared_ptr<GlobalContext> context;
        std::string parent_struct;
        ast::Struct current_struct;

    public:
        void operator()(ast::SourceFile& program){
            context = program.Content->context;

            visit_each(*this, program.Content->blocks);
        }

        void operator()(ast::Struct& struct_){
            current_struct = struct_;
            parent_struct = struct_.Content->name;

            visit_each_non_variant(*this, struct_.Content->constructors);
            visit_each_non_variant(*this, struct_.Content->destructors);
            visit_each_non_variant(*this, struct_.Content->functions);

            parent_struct = "";
        }

        template<typename T>
        void annotate(T& declaration){
            if(!declaration.Content->marked){
                declaration.Content->struct_name = parent_struct;
                declaration.Content->struct_type = current_struct.Content->struct_type;

                ast::PointerType paramType;

                if(current_struct.Content->template_types.empty()){
                    ast::SimpleType struct_type;
                    struct_type.type = parent_struct;
                    struct_type.const_ = false;

                    paramType.type = struct_type;
                } else {
                    ast::TemplateType struct_type;
                    struct_type.type = parent_struct;
                    struct_type.template_types = current_struct.Content->template_types;
                    struct_type.resolved = true;

                    paramType.type = struct_type;
                }

                ast::FunctionParameter param;
                param.parameterName = "this";
                param.parameterType = paramType;

                declaration.Content->parameters.insert(declaration.Content->parameters.begin(), param);
            }
        }

        void operator()(ast::Constructor& constructor){
            annotate(constructor);
        }

        void operator()(ast::Destructor& destructor){
            annotate(destructor);
        }

        void operator()(ast::FunctionDeclaration& declaration){
            if(!parent_struct.empty()){
                annotate(declaration);
            }
        }

        AUTO_IGNORE_OTHERS()
};

class FunctionInserterVisitor : public boost::static_visitor<> {
    private:
        std::shared_ptr<GlobalContext> context;

    public:
        AUTO_RECURSE_STRUCT()

        void operator()(ast::SourceFile& program){
            context = program.Content->context;

            visit_each(*this, program.Content->blocks);
        }

        void operator()(ast::FunctionDeclaration& declaration){
            if(!declaration.Content->marked){
                auto return_type = visit(ast::TypeTransformer(context), declaration.Content->returnType);
                auto signature = std::make_shared<Function>(return_type, declaration.Content->functionName);

                if(return_type->is_array()){
                    throw SemanticalException("Cannot return array from function", declaration.Content->position);
                }

                if(return_type->is_custom_type()){
                    throw SemanticalException("Cannot return struct from function", declaration.Content->position);
                }

                for(auto& param : declaration.Content->parameters){
                    auto paramType = visit(ast::TypeTransformer(context), param.parameterType);
                    signature->parameters.push_back(ParameterType(param.parameterName, paramType));
                }

                signature->struct_ = declaration.Content->struct_name;
                signature->struct_type = declaration.Content->struct_type;
                signature->context = declaration.Content->context;

                declaration.Content->mangledName = signature->mangledName = mangle(signature);

                if(context->exists(signature->mangledName)){
                    throw SemanticalException("The function " + signature->mangledName + " has already been defined", declaration.Content->position);
                }

                context->addFunction(signature);
            }
        }

        void operator()(ast::Constructor& constructor){
            if(!constructor.Content->marked){
                auto signature = std::make_shared<Function>(VOID, "ctor");

                for(auto& param : constructor.Content->parameters){
                    auto paramType = visit(ast::TypeTransformer(context), param.parameterType);
                    signature->parameters.push_back(ParameterType(param.parameterName, paramType));
                }

                signature->struct_ = constructor.Content->struct_name;
                signature->struct_type = constructor.Content->struct_type;

                constructor.Content->mangledName = signature->mangledName = mangle_ctor(signature);

                if(context->exists(signature->mangledName)){
                    throw SemanticalException("The constructor " + signature->name + " has already been defined", constructor.Content->position);
                }

                context->addFunction(signature);
                context->getFunction(signature->mangledName)->context = constructor.Content->context;
            }
        }

        void operator()(ast::Destructor& destructor){
            if(!destructor.Content->marked){
                auto signature = std::make_shared<Function>(VOID, "dtor");

                for(auto& param : destructor.Content->parameters){
                    auto paramType = visit(ast::TypeTransformer(context), param.parameterType);
                    signature->parameters.push_back(ParameterType(param.parameterName, paramType));
                }

                signature->struct_ = destructor.Content->struct_name;
                signature->struct_type = destructor.Content->struct_type;

                destructor.Content->mangledName = signature->mangledName = mangle_dtor(signature);

                if(context->exists(signature->mangledName)){
                    throw SemanticalException("Only one destructor per struct is allowed", destructor.Content->position);
                }

                context->addFunction(signature);
                context->getFunction(signature->mangledName)->context = destructor.Content->context;
            }
        }

        AUTO_IGNORE_OTHERS()

    private:
        std::string parent_struct;
};

class FunctionCheckerVisitor : public boost::static_visitor<> {
    private:
        std::shared_ptr<Function> currentFunction;
        std::shared_ptr<GlobalContext> context;

    public:
        AUTO_RECURSE_GLOBAL_DECLARATION()
        AUTO_RECURSE_MEMBER_VALUE()
        AUTO_RECURSE_STRUCT()

        void operator()(ast::Constructor& function){
            check_each(function.Content->instructions);
        }

        void operator()(ast::Destructor& function){
            check_each(function.Content->instructions);
        }

        void operator()(ast::DefaultCase& default_case){
            check_each(default_case.instructions);
        }

        void operator()(ast::Foreach& foreach_){
            check_each(foreach_.Content->instructions);
        }

        void operator()(ast::ForeachIn& foreach_){
            check_each(foreach_.Content->instructions);
        }

        void operator()(ast::If& if_){
            check_value(if_.Content->condition);
            check_each(if_.Content->instructions);
            visit_each_non_variant(*this, if_.Content->elseIfs);
            visit_optional_non_variant(*this, if_.Content->else_);
        }

        void operator()(ast::ElseIf& elseIf){
            check_value(elseIf.condition);
            check_each(elseIf.instructions);
        }

        void operator()(ast::Else& else_){
            check_each(else_.instructions);
        }

        template<typename T>
        void check_each(std::vector<T>& values){
            for(std::size_t i = 0; i < values.size(); ++i){
                check_value(values[i]);
            }
        }

        template<typename V>
        void check_value(V& value){
            if(auto* ptr = boost::get<ast::FunctionCall>(&value)){
                auto functionCall = *ptr;

                if(functionCall.Content->template_types.empty() || functionCall.Content->resolved){
                    check_each(functionCall.Content->values);

                    std::string name = functionCall.Content->function_name;

                    auto types = get_types(functionCall);

                    auto mangled = mangle(name, types);
                    auto original_mangled = mangled;

                    //If the function does not exists, try implicit conversions to pointers
                    if(!context->exists(mangled)){
                        auto perms = permutations(types);

                        for(auto& perm : perms){
                            mangled = mangle(name, perm);

                            if(context->exists(mangled)){
                                break;
                            }
                        }
                    }

                    if(context->exists(mangled)){
                        context->addReference(mangled);

                        functionCall.Content->mangled_name = mangled;
                        functionCall.Content->function = context->getFunction(mangled);
                    } else {
                        auto local_context = functionCall.Content->context->function();

                        if(local_context && local_context->struct_type && context->struct_exists(local_context->struct_type->mangle())){
                            auto struct_type = local_context->struct_type;

                            mangled = mangle(name, types, struct_type);

                            //If the function does not exists, try implicit conversions to pointers
                            if(!context->exists(mangled)){
                                auto perms = permutations(types);

                                for(auto& perm : perms){
                                    mangled = mangle(name, perm, struct_type);

                                    if(context->exists(mangled)){
                                        break;
                                    }
                                }
                            }

                            if(context->exists(mangled)){
                                context->addReference(mangled);

                                ast::MemberFunctionCall member_function_call;
                                member_function_call.Content->function = context->getFunction(mangled);
                                member_function_call.Content->context = functionCall.Content->context;
                                member_function_call.Content->mangled_name = mangled;
                                member_function_call.Content->position = functionCall.Content->position;
                                member_function_call.Content->object_name = "this";
                                member_function_call.Content->function_name = functionCall.Content->function_name;
                                member_function_call.Content->resolved = functionCall.Content->resolved;
                                member_function_call.Content->template_types = functionCall.Content->template_types;
                                member_function_call.Content->values = functionCall.Content->values;

                                value = member_function_call;

                                return;
                            }
                        }

                        throw SemanticalException("The function \"" + unmangle(original_mangled) + "\" does not exists", functionCall.Content->position);
                    }
                }
            } else {
                visit(*this, value);
            }
        }

        void operator()(ast::For& for_){
            if(for_.Content->start){
                check_value(*for_.Content->start);
            }

            if(for_.Content->condition){
                check_value(*for_.Content->condition);
            }

            if(for_.Content->repeat){
                check_value(*for_.Content->repeat);
            }

            check_each(for_.Content->instructions);
        }

        void operator()(ast::While& while_){
            check_value(while_.Content->condition);
            check_each(while_.Content->instructions);
        }

        void operator()(ast::DoWhile& while_){
            check_value(while_.Content->condition);
            check_each(while_.Content->instructions);
        }

        void operator()(ast::SourceFile& program){
            context = program.Content->context;

            visit_each(*this, program.Content->blocks);
        }

        void operator()(ast::FunctionDeclaration& declaration){
            currentFunction = context->getFunction(declaration.Content->mangledName);

            check_each(declaration.Content->instructions);
        }

        void permute(std::vector<std::vector<std::shared_ptr<const Type>>>& perms, std::vector<std::shared_ptr<const Type>>& types, int start){
            for(std::size_t i = start; i < types.size(); ++i){
                if(!types[i]->is_pointer() && !types[i]->is_array()){
                    std::vector<std::shared_ptr<const Type>> copy = types;

                    copy[i] = new_pointer_type(types[i]);

                    perms.push_back(copy);

                    permute(perms, copy, i + 1);
                }
            }
        }

        std::vector<std::vector<std::shared_ptr<const Type>>> permutations(std::vector<std::shared_ptr<const Type>>& types){
            std::vector<std::vector<std::shared_ptr<const Type>>> perms;

            permute(perms, types, 0);

            return perms;
        }

        template<typename T>
        std::vector<std::shared_ptr<const Type>> get_types(T& functionCall){
            std::vector<std::shared_ptr<const Type>> types;

            ast::GetTypeVisitor visitor;
            for(auto& value : functionCall.Content->values){
                types.push_back(visit(visitor, value));
            }

            return types;
        }

        void operator()(ast::FunctionCall&){
            ASSERT_PATH_NOT_TAKEN("Should be handled by check_value");
        }

        void operator()(ast::MemberFunctionCall& functionCall){
            if(functionCall.Content->template_types.empty() || functionCall.Content->resolved){
                auto var = functionCall.Content->context->getVariable(functionCall.Content->object_name);
                auto type = var->type();
                auto struct_type = type->is_pointer() ? type->data_type() : type;

                check_each(functionCall.Content->values);

                std::string name = functionCall.Content->function_name;

                auto types = get_types(functionCall);

                std::string mangled = mangle(name, types, struct_type);

                //If the function does not exists, try implicit conversions to pointers
                if(!context->exists(mangled)){
                    auto perms = permutations(types);

                    for(auto& perm : perms){
                        mangled = mangle(name, perm, struct_type);

                        if(context->exists(mangled)){
                            break;
                        }
                    }
                }

                if(context->exists(mangled)){
                    context->addReference(mangled);

                    functionCall.Content->mangled_name = mangled;
                    functionCall.Content->function = context->getFunction(mangled);
                } else {
                    throw SemanticalException("The member function \"" + unmangle(mangled) + "\" does not exists", functionCall.Content->position);
                }
            }
        }

        void operator()(ast::Switch& switch_){
            check_value(switch_.Content->value);
            visit_each_non_variant(*this, switch_.Content->cases);
            visit_optional_non_variant(*this, switch_.Content->default_case);
        }

        void operator()(ast::SwitchCase& switch_case){
            check_value(switch_case.value);
            check_each(switch_case.instructions);
        }

        void operator()(ast::Assignment& assignment){
            visit(*this, assignment.Content->left_value);
            check_value(assignment.Content->value);
        }

        void operator()(ast::VariableDeclaration& declaration){
            if(declaration.Content->value){
                check_value(*declaration.Content->value);
            }
        }

        void operator()(ast::Unary& value){
            check_value(value.Content->value);
        }

        void operator()(ast::BuiltinOperator& builtin){
            check_each(builtin.Content->values);
        }

        void operator()(ast::Expression& value){
            check_value(value.Content->first);

            for_each(value.Content->operations.begin(), value.Content->operations.end(),
                [&](ast::Operation& operation){ check_value(operation.get<1>()); });
        }

        void operator()(ast::Return& return_){
            return_.Content->function = currentFunction;

            check_value(return_.Content->value);
        }

        AUTO_IGNORE_OTHERS()
};

} //end of anonymous namespace

void ast::defineMemberFunctions(ast::SourceFile& program){
    MemberFunctionAnnotator annotator;
    annotator(program);
}

void ast::defineFunctions(ast::SourceFile& program){
    //First phase : Collect functions
    FunctionInserterVisitor inserterVisitor;
    inserterVisitor(program);

    //Second phase : Verify calls
    FunctionCheckerVisitor checkerVisitor;
    checkerVisitor(program);
}
<|endoftext|>


<commit_before>#include "mainwindow.h"

#include <QLabel>
#include <QDockWidget>
#include <QSettings>
#include <QAction>
#include <QMenuBar>
#include <QMenu>
#include <QFile>
#include <QMessageBox>
#include <QFileDialog>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    // menu - file
    QMenu *menuFile = new QMenu("File", this);
    this->menuBar()->addMenu(menuFile);

    // action - new block
    QAction *actNewBlock = new QAction("new block", this);
    menuFile->addAction(actNewBlock);
    actNewBlock->setShortcut(Qt::Key_N | Qt::CTRL);
    connect(actNewBlock, SIGNAL(triggered(bool)), this, SLOT(slotActionNewBlock()));

    // action - save
    QAction *actSave = new QAction("save block", this);
    menuFile->addAction(actSave);
    actSave->setShortcut(Qt::Key_S | Qt::CTRL);
    connect(actSave, SIGNAL(triggered(bool)), this, SLOT(slotActionSave()));

    // action - quit
    QAction *actQuit = new QAction("quit", this);
    menuFile->addAction(actQuit);
    actQuit->setShortcut(Qt::Key_Q | Qt::CTRL);
    connect(actQuit, SIGNAL(triggered(bool)), this, SLOT(slotActionQuit()));

    // central widget
    this->widgetMain = new QTabWidget(this);
    this->setCentralWidget(this->widgetMain);

    // block browser
    this->blockBrowser = new BlockBrowser(this);
    connect(this->blockBrowser, SIGNAL(signalFileOpen(QString)), this, SLOT(slotFileOpen(QString)));

    // block browser dock
    QDockWidget *dock = new QDockWidget("", this);
    dock->setObjectName("BlockBrowser");
    dock->setAllowedAreas(Qt::LeftDockWidgetArea);
    dock->setWidget(this->blockBrowser);
    dock->setFloating(false);
    dock->setFeatures(QDockWidget::NoDockWidgetFeatures);
    this->addDockWidget(Qt::LeftDockWidgetArea, dock);

    // restore window state
    QSettings s;
    this->restoreState(s.value("Mainwindow/State").toByteArray());
    this->restoreGeometry(s.value("MainWindow/Geometry").toByteArray());
}

MainWindow::~MainWindow()
{
    // save window state
    QSettings s;
    s.setValue("MainWindow/State", this->saveState());
    s.setValue("MainWindow/Geometry", this->saveGeometry());
}

void MainWindow::slotFileOpen(QString filePath)
{
    // open file
    QFile f(filePath);
    if (!f.open(QIODevice::ReadOnly | QIODevice::Text)) {
        QMessageBox::critical(this, "Error", "Cannot open file!");
        return;
    }

    // parse block
    libblockdia::Block * block = libblockdia::Block::parseBlockDef(&f);
    f.close();
    if (!block) {
        QMessageBox::critical(this, "Error", "Error on parsing file!");
        return;
    }

    // remember opened file
    this->openFilePathHash[block] = filePath;

    // Ignore the first change signal for just created block
    // Because instantiating the block causes the signal.
    this->ignoreChangedBlocks.append(block);

    // open new tab for block
    QTabWidget *tw = (QTabWidget *) this->centralWidget();
    int index = tw->addTab(new libblockdia::ViewBlockEditor(block), block->typeId());
    tw->setCurrentIndex(index);

    // catch changes inside the block
    connect(block, SIGNAL(signalSomethingChanged(libblockdia::Block*)), this, SLOT(slotBlockChanged(libblockdia::Block*)));
}

void MainWindow::slotActionNewBlock()
{
    // create new block
    libblockdia::Block *block = new libblockdia::Block(this);
    block->setTypeName("New Block Type *");
    block->setTypeId("NBT");

    // create new block editor
    libblockdia::ViewBlockEditor *bEditor = new libblockdia::ViewBlockEditor(block);

    // add block editor to central widget
    QTabWidget *tw = (QTabWidget *) this->centralWidget();
    int index = tw->addTab(bEditor, block->typeId() + "*");
    tw->setCurrentIndex(index);

    // catch changes inside the block
    connect(block, SIGNAL(signalSomethingChanged(libblockdia::Block*)), this, SLOT(slotBlockChanged(libblockdia::Block*)));
}

void MainWindow::slotActionSave()
{
    // get objects
    QTabWidget *tw = (QTabWidget *) this->centralWidget();
    int currentIndex = tw->currentIndex();
    libblockdia::ViewBlockEditor *editor = static_cast<libblockdia::ViewBlockEditor*>(tw->currentWidget());
    libblockdia::Block *block = editor->block();

    QFile *f = Q_NULLPTR;

    // file path is already known
    if (this->openFilePathHash.contains(block)) {
        f = new QFile(this->openFilePathHash[block]);
    }

    // open file dialog
    else {
        QString fileName = QFileDialog::getSaveFileName(this, "Save Block", this->blockBrowser->currentRootPath(), "XML (*.xml)");
        this->openFilePathHash[block] = fileName;
        f = new QFile(fileName);
    }

    // check if file is valid
    if (!f || !f->open(QIODevice::WriteOnly | QIODevice::Text)) {
        QMessageBox::critical(this, "Error", "Cannot open file for writing!");
    }

    // export block
    block->exportBlockDef(f);

    // close file
    if (f) {
        f->close();
        f->deleteLater();
        f = Q_NULLPTR;
    }

    // update tab text
    tw->setTabText(currentIndex, block->typeId());
}

void MainWindow::slotActionQuit()
{
    this->close();
}

void MainWindow::slotBlockChanged(libblockdia::Block *block)
{
    // check if the block was just created
    // In this case ignore the change signal
    // because it is emitted because of instantiating the block
    if (this->ignoreChangedBlocks.contains(block)) {
        this->ignoreChangedBlocks.removeAll(block);
        return;
    }

    // add astresik to tab name if it contains unsaved changes
    QTabWidget *tw = (QTabWidget *) this->centralWidget();
    for (int i=0; i < tw->count(); ++i) {
        libblockdia::ViewBlockEditor *editor = static_cast<libblockdia::ViewBlockEditor*>(tw->widget(i));
        if (editor->block() == block) {
            tw->setTabText(i, block->typeId() + "*");
        }
    }
}
<commit_msg>catch situation when saving no open blocks<commit_after>#include "mainwindow.h"

#include <QLabel>
#include <QDockWidget>
#include <QSettings>
#include <QAction>
#include <QMenuBar>
#include <QMenu>
#include <QFile>
#include <QMessageBox>
#include <QFileDialog>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    // menu - file
    QMenu *menuFile = new QMenu("File", this);
    this->menuBar()->addMenu(menuFile);

    // action - new block
    QAction *actNewBlock = new QAction("new block", this);
    menuFile->addAction(actNewBlock);
    actNewBlock->setShortcut(Qt::Key_N | Qt::CTRL);
    connect(actNewBlock, SIGNAL(triggered(bool)), this, SLOT(slotActionNewBlock()));

    // action - save
    QAction *actSave = new QAction("save block", this);
    menuFile->addAction(actSave);
    actSave->setShortcut(Qt::Key_S | Qt::CTRL);
    connect(actSave, SIGNAL(triggered(bool)), this, SLOT(slotActionSave()));

    // action - quit
    QAction *actQuit = new QAction("quit", this);
    menuFile->addAction(actQuit);
    actQuit->setShortcut(Qt::Key_Q | Qt::CTRL);
    connect(actQuit, SIGNAL(triggered(bool)), this, SLOT(slotActionQuit()));

    // central widget
    this->widgetMain = new QTabWidget(this);
    this->setCentralWidget(this->widgetMain);

    // block browser
    this->blockBrowser = new BlockBrowser(this);
    connect(this->blockBrowser, SIGNAL(signalFileOpen(QString)), this, SLOT(slotFileOpen(QString)));

    // block browser dock
    QDockWidget *dock = new QDockWidget("", this);
    dock->setObjectName("BlockBrowser");
    dock->setAllowedAreas(Qt::LeftDockWidgetArea);
    dock->setWidget(this->blockBrowser);
    dock->setFloating(false);
    dock->setFeatures(QDockWidget::NoDockWidgetFeatures);
    this->addDockWidget(Qt::LeftDockWidgetArea, dock);

    // restore window state
    QSettings s;
    this->restoreState(s.value("Mainwindow/State").toByteArray());
    this->restoreGeometry(s.value("MainWindow/Geometry").toByteArray());
}

MainWindow::~MainWindow()
{
    // save window state
    QSettings s;
    s.setValue("MainWindow/State", this->saveState());
    s.setValue("MainWindow/Geometry", this->saveGeometry());
}

void MainWindow::slotFileOpen(QString filePath)
{
    // open file
    QFile f(filePath);
    if (!f.open(QIODevice::ReadOnly | QIODevice::Text)) {
        QMessageBox::critical(this, "Error", "Cannot open file!");
        return;
    }

    // parse block
    libblockdia::Block * block = libblockdia::Block::parseBlockDef(&f);
    f.close();
    if (!block) {
        QMessageBox::critical(this, "Error", "Error on parsing file!");
        return;
    }

    // remember opened file
    this->openFilePathHash[block] = filePath;

    // Ignore the first change signal for just created block
    // Because instantiating the block causes the signal.
    this->ignoreChangedBlocks.append(block);

    // open new tab for block
    QTabWidget *tw = (QTabWidget *) this->centralWidget();
    int index = tw->addTab(new libblockdia::ViewBlockEditor(block), block->typeId());
    tw->setCurrentIndex(index);

    // catch changes inside the block
    connect(block, SIGNAL(signalSomethingChanged(libblockdia::Block*)), this, SLOT(slotBlockChanged(libblockdia::Block*)));
}

void MainWindow::slotActionNewBlock()
{
    // create new block
    libblockdia::Block *block = new libblockdia::Block(this);
    block->setTypeName("New Block Type *");
    block->setTypeId("NBT");

    // create new block editor
    libblockdia::ViewBlockEditor *bEditor = new libblockdia::ViewBlockEditor(block);

    // add block editor to central widget
    QTabWidget *tw = (QTabWidget *) this->centralWidget();
    int index = tw->addTab(bEditor, block->typeId() + "*");
    tw->setCurrentIndex(index);

    // catch changes inside the block
    connect(block, SIGNAL(signalSomethingChanged(libblockdia::Block*)), this, SLOT(slotBlockChanged(libblockdia::Block*)));
}

void MainWindow::slotActionSave()
{
    // get tab widget
    QTabWidget *tw = (QTabWidget *) this->centralWidget();
    if (tw->count() == 0) return;

    // get objects
    int currentIndex = tw->currentIndex();
    libblockdia::ViewBlockEditor *editor = static_cast<libblockdia::ViewBlockEditor*>(tw->currentWidget());
    libblockdia::Block *block = editor->block();

    QFile *f = Q_NULLPTR;

    // file path is already known
    if (this->openFilePathHash.contains(block)) {
        f = new QFile(this->openFilePathHash[block]);
    }

    // open file dialog
    else {
        QString fileName = QFileDialog::getSaveFileName(this, "Save Block", this->blockBrowser->currentRootPath(), "XML (*.xml)");
        this->openFilePathHash[block] = fileName;
        f = new QFile(fileName);
    }

    // check if file is valid
    if (!f || !f->open(QIODevice::WriteOnly | QIODevice::Text)) {
        QMessageBox::critical(this, "Error", "Cannot open file for writing!");
    }

    // export block
    block->exportBlockDef(f);

    // close file
    if (f) {
        f->close();
        f->deleteLater();
        f = Q_NULLPTR;
    }

    // update tab text
    tw->setTabText(currentIndex, block->typeId());
}

void MainWindow::slotActionQuit()
{
    this->close();
}

void MainWindow::slotBlockChanged(libblockdia::Block *block)
{
    // check if the block was just created
    // In this case ignore the change signal
    // because it is emitted because of instantiating the block
    if (this->ignoreChangedBlocks.contains(block)) {
        this->ignoreChangedBlocks.removeAll(block);
        return;
    }

    // add astresik to tab name if it contains unsaved changes
    QTabWidget *tw = (QTabWidget *) this->centralWidget();
    for (int i=0; i < tw->count(); ++i) {
        libblockdia::ViewBlockEditor *editor = static_cast<libblockdia::ViewBlockEditor*>(tw->widget(i));
        if (editor->block() == block) {
            tw->setTabText(i, block->typeId() + "*");
        }
    }
}
<|endoftext|>