///////////////////////////////////////////////////////////////
//
// Invasive Species (Tamarisk) Domain 
//
// This is adapted from the RL Competition 2014 Invasive Species domain 
//
//   https://slots.google.com/slot/rlcompetition2014/domains/invasive-species 
//
// by Majid Alkaee Taleghan, Mark Crowley, and Thomas Dietterich 
// of Oregon State University, who have investigated reinforcement learning
// for this domain in 
//
//   Dietterich, T., Taleghan, M., Crowley, M. (2013). 
//   PAC Optimal Planning for Invasive Species Management: 
//     Improved Exploration for Reinforcement Learning from Simulator-Defined MDPs. 
//   In Proceedings of the AAAI Conference on Artificial Intelligence, AAAI-13.
//
// and based their Invasive Species (Tamarisk) model on the following work 
//
//   Muneepeerakul, R., Weitz, J. S., Levin, S. a, Rinaldo, A., & Rodriguez-Iturbe, I. (2007). 
//   A neutral metapopulation model of biodiversity in river networks. 
//   Journal of theoretical biology, 245(2), 351–63.
//
// Quick explanation: a stream system is organized into reaches with
// slots at each reach.  Each slot may have a native plot or invasive
// tamarisk plant (or be empty).  Tamarisk plants spread downstream
// and sometimes upstream and compete to replace native species that
// spread at a fixed rate.  One can manually intervene to eradicate
// tamarisk plants at a reach or restore them to their native species.
// Costs are assessed for eradication and restorative actions based on
// the amount of work that has to be done and penalties are assessed
// for invaded or vulnerable reaches.
//
// NOTE: this is a variation of the domain defined in the citations
//       above, points of variation or interpretation are noted below.
//
// RDDL translation by Scott Sanner (ssanner@gmail.com).
//
///////////////////////////////////////////////////////////////

domain tamarisk_mdp {

  types {
    reach : object;
    slot  : object;
  };

  pvariables {

	// Nonfluents: constant parameters
	ERADICATION-RATE       : { non-fluent, real, default = 0.9  }; // 0.85 in original
	RESTORATION-RATE       : { non-fluent, real, default = 0.9  }; // 0.65 in original
	DOWNSTREAM-SPREAD-RATE : { non-fluent, real, default = 0.6  }; // 0.5 in original
	UPSTREAM-SPREAD-RATE   : { non-fluent, real, default = 0.15 }; // 0.1 in original
	DEATH-RATE-TAMARISK    : { non-fluent, real, default = 0.05 }; // 0.2 in original
	DEATH-RATE-NATIVE      : { non-fluent, real, default = 0.05 }; // 0.2 in original
	
	// These are parameters I have added to address native spreading and
	// competition if a tamarisk and native both appear at a slot.
	EXOGENOUS-PROD-RATE-NATIVE    : { non-fluent, real, default = 0.1 };
	EXOGENOUS-PROD-RATE-TAMARISK  : { non-fluent, real, default = 0.1 };
	COMPETITION-WIN-RATE-NATIVE   : { non-fluent, real, default = 0.2 };
	COMPETITION-WIN-RATE-TAMARISK : { non-fluent, real, default = 0.8 };
		
	// Invasion and vulnerability costs (independent of actions)
		
	COST-PER-INVADED-REACH : { non-fluent, real, default = 5.0  }; // 5.0 in original
	COST-PER-TREE          : { non-fluent, real, default = 0.5  }; // 0.1 in original
	COST-PER-EMPTY-SLOT    : { non-fluent, real, default = 0.25 }; // 0.05 in original
	
	// From https://slots.google.com/slot/rlcompetition2014/domains/invasive-species:
	//
	// The following components of the cost function depend on the action being taken 
	// and are multiplied by the number of habitat slots being treated by that action.
	
	ERADICATION-COST                  : { non-fluent, real, default = 0.49 }; // 0.5 in original
	RESTORATION-COST                  : { non-fluent, real, default = 0.9  };
	RESTORATION-COST-FOR-EMPTY-SLOT   : { non-fluent, real, default = 0.4  };
	RESTORATION-COST-FOR-INVADED-SLOT : { non-fluent, real, default = 0.8  };
		
    // Nonfluents: topology
    SLOT-AT-REACH   (slot, reach)  : { non-fluent, bool, default = false }; // Is this slot located on this reach?
    DOWNSTREAM-REACH(reach, reach) : { non-fluent, bool, default = false }; // Is the first reach downstream from the second reach?

	// State
	tamarisk-at(slot) : { state-fluent, bool, default = false };
	native-at(slot)   : { state-fluent, bool, default = false };

	// From https://slots.google.com/slot/rlcompetition2014/domains/invasive-species:
	//
	// In each reach at each time step, a management action can be taken and applied to the 
	// habitats in that reach.  The available actions in each reach are [eradicate and restore
	// (neither, one, or both)... but in our formulation here, eradication only works when a
	// tamarisk is present and restoration only when nothing is present... you can do them
	// both for a reach, but each can only succeed on different slots].
	//
	// Both the eradicate and restore parts of an action can fail stochastically, and both 
	// have a cost that scales linearly with the number of affected slots.
	//
	// The following actions are not allowed:
	// - Doing any action other than null on a reach full of Native plants [would be suboptimal, allow]
	// - Eradicating an empty slot in an empty reach [effectively a noop, allow]
	// - Restoring a slot in a fully invaded reach (Tamarisk plants present in all habitats)
	
	// Actions
	eradicate(reach) : { action-fluent, bool, default = false }; // For simplicity this causes tamarisk-at to go false
	restore(reach)   : { action-fluent, bool, default = false }; // For simplicity this causes native-at to go true

  };

  cpfs {
	
	// A tamarisk vacates a slot by 
	// - dying
	// - being eradicated by intervention
	//   (stochastically succeeds *unless* the entire reach is filled by tamarisk)
	//
	// A tamarisk may invade a slot not occupied by a native species if
	// - it spreads downstream or upstream from an invaded slot
	//   (each upstream or downstream tamarisk spreads independently)
	//
	tamarisk-at'(?s) = 
		// Tamarisk and native competing?
		if (tamarisk-at(?s) ^ native-at(?s))
			then Bernoulli ( COMPETITION-WIN-RATE-TAMARISK ) 

		// If eradicated and no tamerisk at ?s, assume it won't spread to ?s on this iteration
		else if (~tamarisk-at(?s) ^ [exists_{?r : reach} [SLOT-AT-REACH(?s,?r) ^ eradicate(?r) ^ ~forall_{?s2 : slot} [SLOT-AT-REACH(?s2,?r) ^ tamarisk-at(?s2)]]])
			then false
		
		// Was it eradicated?
		else if (tamarisk-at(?s) ^ [exists_{?r : reach} [SLOT-AT-REACH(?s,?r) ^ eradicate(?r) ^ ~forall_{?s2 : slot} [SLOT-AT-REACH(?s2,?r) ^ tamarisk-at(?s2)]]])
			then Bernoulli( 1.0 - ERADICATION-RATE ) // eradication success leads to false

		// Did it die?
		else if (tamarisk-at(?s))
			then Bernoulli ( 1.0 - DEATH-RATE-TAMARISK ) // death leads to false

		// Modification: mix in EXOGENOUS-PROD-RATE-TAMARISK, previous standalone rule was preventing reaching the following
		// Did it spread from upstream or downstream?  (Eventually may want to look at transitive closure of up/downstream connections.)
		// Using UPSTREAM rate for spread within a reach (not specified).
		// Assume independent cascade epidemic model (=noisy-or model) that each upstream tamarisk independently infects this slot.
		else if (~tamarisk-at(?s) ^ ~native-at(?s))
			then Bernoulli( EXOGENOUS-PROD-RATE-TAMARISK + (1 - EXOGENOUS-PROD-RATE-TAMARISK) 
			                *(1.0 - // noisy-or product that all propagules fail to spread to this slot, take 1.0 - to get probability of at least one propagule spreading!
			                    [prod_{?r : reach, ?s2 : slot} [if (SLOT-AT-REACH(?s,?r) ^ SLOT-AT-REACH(?s2,?r) ^ (?s ~= ?s2) ^ tamarisk-at(?s2)) then (1.0 - DOWNSTREAM-SPREAD-RATE) else (1.0)]]
			                   *[prod_{?r : reach, ?r2 : reach, ?s2 : slot} [if (SLOT-AT-REACH(?s,?r) ^ DOWNSTREAM-REACH(?r,?r2) ^ SLOT-AT-REACH(?s2,?r2) ^ tamarisk-at(?s2)) then (1.0 - DOWNSTREAM-SPREAD-RATE) else (1.0)]] 
							   *[prod_{?r : reach, ?r2 : reach, ?s2 : slot} [if (SLOT-AT-REACH(?s,?r) ^ DOWNSTREAM-REACH(?r2,?r) ^ SLOT-AT-REACH(?s2,?r2) ^ tamarisk-at(?s2)) then (1.0 - UPSTREAM-SPREAD-RATE) else (1.0)]]) )

		// Did not experience competition, eradication, death, spreading, or an exogenous production, so the state persists
		else
			tamarisk-at(?s);
	
	// From the website description, it's not immediately clear how native plants spread.
	// Because there do not seem to be spread rates for these species, we'll instead simply
	// assume that they spontaneously generate at an empty slot with BIRTH-RATE-NATIVE.
	//
	// Note: nothing prevents a simultaneous spread of tamarisk and an exogenous production of a native species...
	//       we'll simply randomly let each compete with some probability of success until one
	//       of the species wins. 
	native-at'(?s) = 
		// Tamarisk and native competing?
		if (tamarisk-at(?s) ^ native-at(?s))
			then Bernoulli ( COMPETITION-WIN-RATE-NATIVE ) 

		// If it is restored and a native plant is already there, assume it must persist in next iteration
		else if (~tamarisk-at(?s) ^ native-at(?s) ^ [exists_{?r : reach} [SLOT-AT-REACH(?s,?r) ^ restore(?r)]])
			then true

		// Was it empty and restored?
		else if (~tamarisk-at(?s) ^ ~native-at(?s) ^ [exists_{?r : reach} [SLOT-AT-REACH(?s,?r) ^ restore(?r)]])
			then Bernoulli( RESTORATION-RATE )
		
		// Did it die?
		else if (native-at(?s))
			then Bernoulli ( 1.0 - DEATH-RATE-NATIVE ) // death leads to false
		
		// Was it produced by an exogenous arrival?
		else if (~tamarisk-at(?s) ^ ~native-at(?s))
			then Bernoulli ( EXOGENOUS-PROD-RATE-NATIVE ) // exogenous production leads to true
		
		// Did not experience competition, restoration, death, or an exogenous production, so the state persists
		else
			native-at(?s); 
	
  };

  // Add up all invasion and vulnerability costs with action costs 
  reward = 
	[sum_{?r : reach} [ // Invasion cost per reach
		-COST-PER-INVADED-REACH * (exists_{?s : slot} (SLOT-AT-REACH(?s,?r) ^ tamarisk-at(?s)))
	]] + 
	[sum_{?s : slot} [ // Individual Tamarisk cost per slot
		-COST-PER-TREE * (tamarisk-at(?s))
	]] + 
	[sum_{?s : slot} [ // Vulnerable empty slot cost
		-COST-PER-EMPTY-SLOT * (~tamarisk-at(?s) ^ ~native-at(?s))
	]] + 
	[sum_{?r : reach} [ // Restoration cost per reach
		-ERADICATION-COST * (eradicate(?r))
	]] + 
	[sum_{?r : reach} [ // Eradication cost per reach
		-RESTORATION-COST * (restore(?r))
	]] + 
	[sum_{?r : reach, ?s : slot} [ // Cost of restoring a slot if it is empty (no cost if it has a native plant already)
		-RESTORATION-COST-FOR-EMPTY-SLOT * (restore(?r) ^ SLOT-AT-REACH(?s,?r) ^ ~tamarisk-at(?s) ^ ~native-at(?s))
	]]; 

	// Cost of restoring a slot if it is invaded (ignoring this since we prevent restoring an invaded slot -- must eradicate first)
	//+ [sum_{?r : reach, ?s : slot} [ 
	//	-RESTORATION-COST-FOR-INVADED-SLOT * (restore(?r) ^ SLOT-AT-REACH(?s,?r) ^ tamarisk-at(?s))
	//]];

  state-action-constraints {
		
	// Ensure two different plants never at same slot -- *actually* this can happen if both simultaneously generate,
	// since we are not using a multivalued discrete variable in this model.
	// forall_{?s : slot} ~[ tamarisk-at(?s) ^ native-at(?s) ];
  };

}     
