All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.metacsp.time.APSPSolver Maven / Gradle / Ivy

There is a newer version: 1.3.5
Show newest version
/*******************************************************************************
 * Copyright (c) 2010-2013 Federico Pecora 
 * 
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 * 
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 ******************************************************************************/
package org.metacsp.time;

import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.Toolkit;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Vector;
import java.util.logging.Logger;

import javax.swing.JFrame;

import org.metacsp.framework.Constraint;
import org.metacsp.framework.ConstraintNetwork;
import org.metacsp.framework.ConstraintSolver;
import org.metacsp.framework.Domain;
import org.metacsp.framework.ValueChoiceFunction;
import org.metacsp.framework.Variable;
import org.metacsp.throwables.ConstraintNotFound;
import org.metacsp.throwables.time.MalformedSimpleDistanceConstraint;
import org.metacsp.utility.UI.PlotSTPTemporalModule;
import org.metacsp.utility.logging.MetaCSPLogging;

/**
 * Simple Temporal Problem solver which uses the Floyd-Warshall
 * all-pairs shortest-path algorithm for constraint propagation.
 * Derived from original implementation by
 * the Planning and Scheduling Team (ISTC-CNR) under project APSI.
 * 
 * @author Federico Pecora and Planning and Scheduling Team
 */
public class APSPSolver extends ConstraintSolver {

	/**
	 * 
	 */
	private static final long serialVersionUID = -5029122662268797937L;
	
	private boolean doFromScratchInsteadOfIncremental = false;
	private boolean addingIndependentConstraints = false;

	private boolean backupDMatrixSimple = false;
	private int cubePropCount = 0;
	private int quadPropCount = 0;

	/**
	 * Public access class variable denoting the value infinity.
	 */
	public static final long INF = Long.MAX_VALUE-1;

	public static final int DEFAULT_MAX_TPS = 2000;

	//MAX number of TPs in temporal network.
	private final int MAX_TPS;

	//All TimePoints in the STP
	private TimePoint[] tPoints = null;

	//Envelope of used timepoints so we can keep size of F-W loops small
	//without too much housekeeping.
	private int MAX_USED = 2;

	//Distance Matrix
	private long[][] distance;
	private long[][] distanceBackupInternal;
	private Vector backups = new Vector();
	private Vector backupConstraints = new Vector();
	private Vector backupMaxUsed = new Vector();

	// Roll-back data structures
	private ArrayList tPointsRollback = new ArrayList();
	private ArrayList distanceRollback = new ArrayList();	
	private ArrayList maxUsedRollback = new ArrayList();
	private ArrayList networkRollback = new ArrayList();

	//Temporal Horizon
	private long H;

	//Constraint between source (tPoints[0]) and sink (tPoints[1])
	private SimpleDistanceConstraint horizonConstraint;

	//Origin
	private long O;

	//Time Point Counter
	private int tpCounter = 0;


	//rigidity matrix
	private double[] rigidity;

	/**
	 * Create a new APSPSolver with given temporal horizon.
	 * @param origin The start time of the horizon.
	 * @param horizon The end time of the horizon. 
	 */
	public APSPSolver(long origin,long horizon) {
		this(origin, horizon, DEFAULT_MAX_TPS);
	}


	/**
	 * Create a new APSPSolver with given temporal horizon and given maximum number of timepoints.
	 * @param origin The start time of the horizon.
	 * @param horizon The end time of the horizon. 
	 * @param maxTPs maximum number of timepoints in the network 
	 * (excluding the Origin (O) and Horizon (H) timepoint)
	 * (propagation is O((maxTPs+2)^3)). 
	 */
	public APSPSolver (long origin, long horizon, int maxTPs) {
		super(new Class[]{SimpleDistanceConstraint.class}, TimePoint.class);
		this.setOptions(OPTIONS.MANUAL_PROPAGATE);
		this.MAX_TPS = maxTPs+2; //+2 To account for O and H
		tPoints = new TimePoint[MAX_TPS];
		distance = new long[MAX_TPS][MAX_TPS];


		//Init
		H = horizon;
		O = origin;
		
//		for (int i = 0; i < MAX_TPS; i++) {
//			for (int j = 0; j < MAX_TPS; j++) {
//				if (i == j) distance[i][j] = 0;
//				else distance[i][j] = APSPSolver.INF;
//			}
//		}

		for (int i = 0; i < MAX_TPS; i++) {
			if (i == 0) {
				for (int j = 1; j < MAX_TPS; j++) distance[i][j] = H;
			}
			else if (i == 1) {
				distance[i][0] = -H;
				for (int j = 1; j < MAX_TPS; j++) distance[i][j] = 0; 
			}
			else {
				distance[i][0] = 0;
				for (int j = 1; j < MAX_TPS; j++) {
					if (i == j) distance[i][j] = 0;
					else distance[i][j] = H;
				}
			}
		}

		//Edges between reference and horizon
		tPoints[0] = new TimePoint(tpCounter,MAX_TPS,this,O,H);
		tpCounter++;
		tPoints[1] = new TimePoint(tpCounter,MAX_TPS,this,O,H);
		tpCounter++;

		this.theNetwork.addVariable(tPoints[0]);
		this.theNetwork.addVariable(tPoints[1]);

		SimpleDistanceConstraint con = new SimpleDistanceConstraint();
		horizonConstraint = con;

		con.setFrom(this.getVariable(0));
		con.setTo(this.getVariable(1));
		con.setMinimum(H-O);
		con.setMaximum(H-O);
		con.addInterval(new Bounds(H-O,H-O));

		//[lb,ub] = [-di0,d0i]
		tPoints[0].setUsed(true);
		tPoints[0].setLowerBound(O);
		tPoints[0].setUpperBound(O);
		tPoints[1].setUsed(true);
		tPoints[1].setLowerBound(H);
		tPoints[1].setUpperBound(H);

		//Add edge to TimePoints
		tPoints[0].setOut(1,con);

		//Create all time points
		for (int i = 2; i < MAX_TPS; i++) {
			TimePoint tp = new TimePoint(tpCounter,MAX_TPS,this);
			tPoints[i] = tp;
			tpCounter++;

			//Create edge from Origin and Horizon to all TPs	
			SimpleDistanceConstraint conO = new SimpleDistanceConstraint();
			SimpleDistanceConstraint conH = new SimpleDistanceConstraint();
			conO.setFrom(this.getVariable(0));
			conO.setTo(this.getVariable(i));
			conH.setFrom(this.getVariable(i));
			conH.setTo(this.getVariable(1));

			conO.setMinimum(0);
			conO.setMaximum(H-O);
			conH.setMinimum(0);
			conH.setMaximum(H-O);

			conO.addInterval(new Bounds(0,H-O));
			conH.addInterval(new Bounds(0,H-O));

			//[lb,ub] = [-di0,d0i]
			tPoints[i].setLowerBound(O);
			tPoints[i].setUpperBound(H);

			//Add interval to TimePoints
			tPoints[0].setOut(i,conO);
			tPoints[i].setOut(1,conH);
		}
		
//		System.out.println("Init print:\n" + this.printDist());
	}

	//TP creation
	private int tpCreate() {
		logger.finest("Creating 1 TP");
		int i = 2;
		boolean found = false;
		while (i < MAX_TPS && !found) {
			if (!tPoints[i].isUsed()) {
				tPoints[i].setUsed(true);
				found = true;
				//reusing a timepoint, check!
				//System.out.println("REUSING TP " + i + " and with Origin->" + i + " is " + tPoints[0].getOut(i));
				//System.out.println("REUSING TP " + i + " and " + Arrays.toString(theNetwork.getIncidentEdges(tPoints[i])));
				if (i == MAX_USED+1) MAX_USED = i;
			} else i++;
		}
		for (int l = 2; l <= MAX_USED; l++) {
			distance[i][l] = H;
			distance[l][i] = H;			
		}
		
		distance[i][i] = 0;
		distance[i][0] = 0;
		distance[i][1] = H;//APSPSolver.INF;
		
		distance[0][i] = H; // Needed for bookmark/revert (Uwe)
		distance[1][i] = 0; // Needed for bookmark/revert (Uwe)
		
		return i;
	}


	//Batch TP creation
	private int[] tpCreate(int n) {
		if (n > MAX_TPS) return null;
		int[] ret = new int[n];
		for (int i = 0; i < n; i++) ret[i] = tpCreate();
		return ret;
	}

	private void saveDMatrix(Constraint[] con) {
		long[][] distanceBackup = new long[MAX_USED+1][MAX_USED+1];
		for (int i = 0; i < MAX_USED+1; i++) {
			for (int j = 0; j < MAX_USED+1; j++) {
				distanceBackup[i][j] = distance[i][j];
			}
		}
		backupConstraints.add(con);
		backups.add(distanceBackup);
		backupMaxUsed.add(MAX_USED);
	}

	private void restoreDMatrix() {
		long[][] distanceBackup = backups.lastElement();
		for (int i = 0; i < backupMaxUsed.lastElement()+1; i++) {
			for (int j = 0; j < backupMaxUsed.lastElement()+1; j++) {
				distance[i][j] = distanceBackup[i][j];
			}			
		}
		backupConstraints.remove(backupConstraints.size()-1);
		backups.remove(backups.size()-1);
		backupMaxUsed.remove(backupMaxUsed.size()-1);
	}
	
	private void saveDMatrixInternal() {
		distanceBackupInternal = new long[MAX_USED+1][MAX_USED+1];
		for (int i = 0; i < MAX_USED+1; i++) {
			for (int j = 0; j < MAX_USED+1; j++) {
				distanceBackupInternal[i][j] = distance[i][j];
			}			
		}
	}

	private void restoreDMatrixInternal() {
		for (int i = 0; i < MAX_USED+1; i++) {
			for (int j = 0; j < MAX_USED+1; j++) {
				distance[i][j] = distanceBackupInternal[i][j];
			}			
		}
		distanceBackupInternal = null;
	}


	//Batch Time point erase
	private void tpDelete(int[] IDtimePoint) {
		logger.finest("Deleting " + IDtimePoint.length + " TP");
		for (int i = 0; i < IDtimePoint.length; i++) {
			tPoints[IDtimePoint[i]].setUsed(false);

			if (IDtimePoint[i] == MAX_USED) MAX_USED--;

			SimpleDistanceConstraint conO = new SimpleDistanceConstraint();
			SimpleDistanceConstraint conH = new SimpleDistanceConstraint();
			conO.setFrom(this.getVariable(0));
			conO.setTo(this.getVariable(IDtimePoint[i]));
			conH.setFrom(this.getVariable(IDtimePoint[i]));
			conH.setTo(this.getVariable(1));

			conO.setMinimum(0);
			conO.setMaximum(H-O);
			conH.setMinimum(0);
			conH.setMaximum(H-O);

			conO.addInterval(new Bounds(0,H-O));
			conH.addInterval(new Bounds(0,H-O));

			//[lb,ub] = [-di0,d0i]
			tPoints[IDtimePoint[i]].setLowerBound(O);
			tPoints[IDtimePoint[i]].setUpperBound(H);
			tPoints[0].setOut(IDtimePoint[i],conO);
			tPoints[IDtimePoint[i]].setOut(1,conH);
		}

		fromScratchDistanceMatrixComputation();
	}

	//Create an interval for a constraint
	private boolean cCreateFromScratch(Bounds i, int from, int to) {
		//Conversion
		long max = i.max;
		long min = i.min;
		if (i.max == APSPSolver.INF) max = H-O;
		if (i.min == -APSPSolver.INF) min = -1 * (H - O);
		i = new Bounds(min,max);

		// Checks
		if (i.min > i.max) return false; 
		if (from == to) return false;
		if (tPoints[from] == null) return false;

		//Already existing edge
		SimpleDistanceConstraint con = tPoints[from].getOut(to);
		if (con != null) {
			//check intersection between active con and new con
			if ( (con.getMinimum() > i.max) || (con.getMaximum() < i.min) ) return false;
			//check con does not contain active con
			if ( (con.getMinimum() > i.min) && (con.getMaximum() < i.max) ) {
				//OK it is. I save con without doing anything else
				if (!con.addInterval(i)) return false;
				return true;
			}
			//Update active con
			long oldd = con.getMinimum();
			long oldD = con.getMaximum();
			if (con.getMinimum() < i.min) con.setMinimum(i.min);
			if (con.getMaximum() > i.max) con.setMaximum(i.max);

			if (!con.addInterval(i)) return false;
			
			if (backupDMatrixSimple) saveDMatrixInternal();
			if (!fromScratchDistanceMatrixComputation()) {
				//Inconsistency. Rollback
				con.removeInterval(i);
				if (backupDMatrixSimple) restoreDMatrixInternal();
				return false;
			}

			//[lb,ub] = [-di0,d0i]
			for (int j = 0; j < MAX_USED+1; j++)
				if (tPoints[j].isUsed()) {
					tPoints[j].setLowerBound(sum(-distance[j][0],O));
					tPoints[j].setUpperBound(sum(distance[0][j],O));
				}
		}				
		else {
			con = new SimpleDistanceConstraint();
			con.setFrom(this.getVariable(from));
			con.setTo(this.getVariable(to));
			con.setMinimum(i.min);
			con.setMaximum(i.max);
			con.addInterval(new Bounds(i.min,i.max));
			//Add edge to tp
			tPoints[from].setOut(to,con);

			if (backupDMatrixSimple) saveDMatrixInternal();
			if (!fromScratchDistanceMatrixComputation()) {
				tPoints[from].setOut(to,null);
				if (backupDMatrixSimple) restoreDMatrixInternal();
				return false;
			}

			//[lb,ub] = [-di0,d0i]
			for (int j = 0; j < MAX_USED+1; j++) {
				if (tPoints[j].isUsed() == true) {
					tPoints[j].setLowerBound (sum(-distance[j][0],O));
					tPoints[j].setUpperBound (sum(distance[0][j],O));
				}
			}
		}
		return true;	
	}

	
	//Create an interval for a constraint
	private boolean cCreate(Bounds i, int from, int to, boolean noPropagation) {
		if (!noPropagation && doFromScratchInsteadOfIncremental) return cCreateFromScratch(i, from, to);
		//Conversion
		long max = i.max;
		long min = i.min;
		if (i.max == APSPSolver.INF) max = H-O;
		if (i.min == -APSPSolver.INF) min = -1 * (H - O);
		i = new Bounds(min,max);

		// Checks
		if (i.min > i.max) return false; 
		if (from == to) return false;
		if (tPoints[from] == null) return false;

		if (noPropagation) {
			distance[from][to] = max;
			distance[to][from] = -min;
		}
		
		//Already existing edge
		SimpleDistanceConstraint con = tPoints[from].getOut(to);
		if (con != null) {
			//check intersection between active con and new con
			if ( (con.getMinimum() > i.max) || (con.getMaximum() < i.min) ) return false;
			//check con does not contain active con
			if ( (con.getMinimum() > i.min) && (con.getMaximum() < i.max) ) {
				//OK it is. I save con without doing anything else
				if (!con.addInterval(i)) return false;
				return true;
			}
			//Update active con
			long oldd = con.getMinimum();
			long oldD = con.getMaximum();
			if (con.getMinimum() < i.min) con.setMinimum(i.min);
			if (con.getMaximum() > i.max) con.setMaximum(i.max);

//			saveDMatrix();
			if (!noPropagation && !incrementalDistanceMatrixComputation(from,to,i)) {
				//Inconsistency. Rollback
				con.setMinimum(oldd);
				con.setMaximum(oldD);
//				restoreDMatrix();
				return false;
			}

			//Ok update
			if (!con.addInterval(i)) return false;

			//[lb,ub] = [-di0,d0i]
			for (int j = 0; j < MAX_USED+1; j++)
				if (tPoints[j].isUsed()) {
					tPoints[j].setLowerBound(sum(-distance[j][0],O));
					tPoints[j].setUpperBound(sum(distance[0][j],O));
				}
		}				
		else {
//			saveDMatrix();
			if (!noPropagation && !incrementalDistanceMatrixComputation(from,to,i)) {
//				restoreDMatrix();
				return false;
			}
			//Ok no inconsistency
			con = new SimpleDistanceConstraint();
			con.setFrom(this.getVariable(from));
			con.setTo(this.getVariable(to));
			con.setMinimum(i.min);
			con.setMaximum(i.max);
			con.addInterval(new Bounds(i.min,i.max));

			//Add edge to tp
			tPoints[from].setOut(to,con);

			//[lb,ub] = [-di0,d0i]
			for (int j = 0; j < MAX_USED+1; j++) {
				if (tPoints[j].isUsed() == true) {
					tPoints[j].setLowerBound (sum(-distance[j][0],O));
					tPoints[j].setUpperBound (sum(distance[0][j],O));
				}
			}
		}
		return true;	
	}

    //Batch create intervals (for many constraints)
	private boolean cCreate(Bounds[] in, int[] from, int[] to) {
		long[] old_d = new long[in.length];
		long[] old_D = new long[in.length];
		boolean[] added = new boolean[in.length];
		for (int i = 0; i < added.length; i++) added[i] = false;
		boolean rollback = false;
		int rollBackPoint = -1;

		for (int i = 0; i < in.length; i++) {
			//Conversion
			long min = in[i].min;
			long max = in[i].max;
			if (in[i].max == APSPSolver.INF) max = H-O;
			if (in[i].min == -APSPSolver.INF) min = -1 * (H - O);
			in[i] = new Bounds(min,max);
			//Checks
			if (in[i].min > in[i].max) { rollback = true; rollBackPoint = i; break; /*return false;*/ }
			if (from[i] == to[i]) { rollback = true; rollBackPoint = i; break; /*return false;*/ }

			SimpleDistanceConstraint con = tPoints[from[i]].getOut(to[i]);
			if (con != null) {
				//Already existing edge
				//check intersection between active con and new con
				//added[i] = false;
				if ( (con.getMinimum() > in[i].max) || (con.getMaximum() < in[i].min) ) { rollback = true; rollBackPoint = i; break; /*return false;*/ }
				//Update active con
				old_d[i] = con.getMinimum();
				old_D[i] = con.getMaximum();
				if (con.getMinimum() < in[i].min) con.setMinimum(in[i].min);
				if (con.getMaximum() > in[i].max) con.setMaximum(in[i].max);
			} 
			else {          
				//Non-existing arc
				//Adding tentative constraint
				added[i] = true;
				con = new SimpleDistanceConstraint();
				con.setFrom(this.getVariable(from[i]));
				con.setTo(this.getVariable(to[i]));
				con.setMinimum(in[i].min);
				con.setMaximum(in[i].max);
				con.addInterval(new Bounds(in[i].min,in[i].max));
				tPoints[from[i]].setOut(to[i], con);
			}
		}

		if (rollback) {
			for (int i = rollBackPoint-1; i >= 0; i--) {
				SimpleDistanceConstraint con = tPoints[from[i]].getOut(to[i]);
				if (!added[i]) {
					//Rollback in case of already existing edge
					con.setMinimum(old_d[i]);
					con.setMaximum(old_D[i]);
				}
				else {
					//Rollback in case of new edge
					con.removeInterval(in[i]);
					tPoints[from[i]].setOut(to[i], null);
				}
			}
			return false;
		}

		if (backupDMatrixSimple) saveDMatrixInternal();
		if (!this.fromScratchDistanceMatrixComputation()) {
			if (backupDMatrixSimple) restoreDMatrixInternal();
			return false;
		}
		
		//Ok update
		for (int i = in.length-1; i >= 0; i--) {
			if (!added[i]) {
				SimpleDistanceConstraint con = tPoints[from[i]].getOut(to[i]);
				con.addInterval(in[i]);
			}
		}

		//[lb,ub] = [-di0,d0i]
		for (int j = 0; j < MAX_USED+1; j++)
			if (tPoints[j].isUsed() == true) {
				tPoints[j].setLowerBound(sum(-distance[j][0],O));
				tPoints[j].setUpperBound(sum(distance[0][j],O));
			}
		
		return true;    
	}
	
//	//Delete a constraint...
//	//throw error in case of parameter inconsistency
//	private boolean cDelete(Bounds i, int from, int to) throws ConstraintNotFound, MalformedSimpleDistanceConstraint {
//		//Conversion
//		long min = i.min;
//		long max = i.max;
//		if (i.max == Long.MAX_VALUE - 1) max = H-O;
//		if (i.min == Long.MIN_VALUE + 1) min = -1 * (H - O);
//		i = new Bounds(min,max);
//
//		SimpleDistanceConstraint con = tPoints[from].getOut(to);
//
//		if (con == null) throw new ConstraintNotFound(String.format("Interval %s, from %d, to %d", i.toString(), from, to));
//
//		if (con.getCounter() == 1) { 
//			if (con.removeInterval(i)) {
//				tPoints[from].setOut(to,null);
//			}
//			else throw new MalformedSimpleDistanceConstraint(con, 3);
//		}
//		else if (!con.removeInterval(i)) throw new MalformedSimpleDistanceConstraint(con, 4);
//
//		fromScratchDistanceMatrixComputation();
//
//		for (int j = 0; j < MAX_USED+1; j++)
//			if (tPoints[j].isUsed()) {
//				tPoints[j].setLowerBound(sum(-distance[j][0],O));
//				tPoints[j].setUpperBound(sum(distance[0][j],O));
//			}
//
//		return true;
//	}

	//Delete many constraints...
	//throw error in case of parameter inconsistency
	private boolean cDelete(Bounds[] in, int[] from, int[] to, boolean canRestore) throws ConstraintNotFound, MalformedSimpleDistanceConstraint {
		for (int i = 0; i < in.length; i++) {
			//Conversion
			long min = in[i].min;
			long max = in[i].max;
			if (in[i].max == Long.MAX_VALUE - 1) max = H-O;
			if (in[i].min == Long.MIN_VALUE + 1) min = -1 * (H - O);
			in[i] = new Bounds(min,max);

			SimpleDistanceConstraint con = tPoints[from[i]].getOut(to[i]);

			if (con == null) {
				throw new ConstraintNotFound(String.format("Interval %s, from %d, to %d", in[i].toString(), from[i], to[i]));
			}

			if (con.getCounter() == 1) { 
				if (con.removeInterval(in[i])) tPoints[from[i]].setOut(to[i],null);
				else throw new MalformedSimpleDistanceConstraint(con, 1);
			}
			else if (!con.removeInterval(in[i])) throw new MalformedSimpleDistanceConstraint(con, 2);
		}

		if (!canRestore) fromScratchDistanceMatrixComputation();
		else {
			logger.finest("QuickRestoring distance matrix, no propagation");
			if (backupDMatrixSimple) restoreDMatrix();
		}

		for (int j = 0; j < MAX_USED+1; j++)
			if (tPoints[j].isUsed() == true) {
				tPoints[j].setLowerBound(sum(-distance[j][0],O));
				tPoints[j].setUpperBound(sum(distance[0][j],O));
			}

		return true;
	}


	//Access methods

	/**
	 * Get a timepoint given its ID.
	 * @param Id The ID of the timepoint.
	 * @return The timepoint referenced by passed ID. 
	 */
	public TimePoint getTimePoint(int Id) {
		if (Id >= MAX_TPS) return null;
		if (tPoints[Id] == null) return null;
		if (!tPoints[Id].isUsed()) return null;
		return tPoints[Id];
	}


	//Printing functions

	/**
	 * Generates a String representation of the Simple Temporal Network (STN).
	 * @return A String describing the STN.
	 */
	@Override
	public String toString() {
		StringBuilder strb = new StringBuilder();
		strb.append("Temporal Network (" + MAX_TPS +" time points): \n");
		for (int i = 0; i < MAX_TPS; i++) {
			if (tPoints[i].isUsed()) {
				strb.append(tPoints[i] + "\n");
			}
		}
		return strb.toString();
	}

	//"from scratch" re-computation
	private boolean fromScratchDistanceMatrixComputation() {
		logger.fine("Propagating (cube) with (#TPs,#cons) = (" + this.MAX_USED + "," + this.theNetwork.getConstraints().length + ") (call num.: " + (++cubePropCount) + ")");
		//*
		//This code is not tested thoroughly but seems to work
		for (int i = 0; i < MAX_USED+1; i++) {
			for (int j = i; j < MAX_USED+1; j++) {
				if (i != j) {
					long dij = H;//APSPSolver.INF;
					long dji = H;//APSPSolver.INF;

					if(tPoints[i].getOut(j) != null) {
						dij = Math.min(dij, +tPoints[i].getOut(j).getMaximum());
						dji = Math.min(dji, -tPoints[i].getOut(j).getMinimum());
					}
					if(tPoints[j].getOut(i) != null) {
						dij = Math.min(dij, -tPoints[j].getOut(i).getMinimum());
						dji = Math.min(dji, +tPoints[j].getOut(i).getMaximum());
					}

					if(-dji > +dij) {
						return false;
					}

					distance[i][j] = dij;
					distance[j][i] = dji;

				}
				else distance[i][j] = 0;
			}
		}

		for (int k = 0; k < MAX_USED+1; k++) {
			if (tPoints[k].isUsed() == true) {
				for (int i = 0; i < MAX_USED+1; i++) {
					if (tPoints[i].isUsed() == true) {
						for (int j = 0; j < MAX_USED+1; j++) { 
							if (tPoints[j].isUsed() == true) {
								long temp = sum(distance[i][k],distance[k][j]);
								if (distance[i][j] > temp)
									distance[i][j] = temp;
							}
							if (i == j && distance[i][j] < 0) return false;
						}
					}
				}
			}
		}

		return true;
	} 

	//Gd graph propagation function
	private boolean incrementalDistanceMatrixComputation(int from,int to,Bounds i) {
		logger.fine("Propagating (quad) with (#TPs,#cons) = (" + this.MAX_USED + "," + this.theNetwork.getConstraints().length + ") (call num.: " + (++quadPropCount) + ")");

		if (distance[to][from] != APSPSolver.INF && sum(i.max,distance[to][from]) < 0) return false;
		if (distance[from][to] != APSPSolver.INF && sum(-i.min,distance[from][to]) < 0) return false;
		
//		System.out.println("a)" + sum(i.max,distance[to][from]));
//		System.out.println("b)" + sum(-i.min,distance[from][to]));
		
		long sum1;
		long sum2;
		long sum3;
		long sum4;
		long temp;
		
		for (int u = 0; u < MAX_USED+1; u++) {
			if (tPoints[u].isUsed()) {
				for (int v = 0; v < MAX_USED+1;v++) {
					if (tPoints[v].isUsed()) {
						//min{distance[u][v];(distance[u][from]+i.max+distance[to][v]);(distance[u][to]-i.minl+distance[from][v])}
						sum1 = sum(distance[u][to],-i.min);
						sum2 = sum(sum1,distance[from][v]);
						sum3 = sum(distance[u][from],i.max);
						sum4 = sum(sum3,distance[to][v]);
						temp = Math.min(sum2,sum4);
												
						if (distance[u][v] > temp) {
							//long oldD = distance[u][v];
							distance[u][v] = temp;
							if (u == v && distance[u][v] != 0) {
								//logger.info("==================> Updated distance[" + u + "][" + v + "] from " + oldD + " to " + temp);
								//throw new Error("Found negative cycle in incremental propagation while adding (from,to,i) (" + from + "," + to + "," + i + ")");
								return false;
							}
						}
					}
				}
			}
		}
		return true;
	}


	//Interface to framework classes 

	//Create many new variables (batch) - i.e., many timepoints.
	@Override
	protected Variable[] createVariablesSub(int num) {
		int[] tp = tpCreate(num);
		Variable[] ret = new Variable[num];
		for (int i = 0; i < tp.length; i++) {
			ret[i] = tPoints[tp[i]];			
		}
		return ret;
	}


	//Remove many variables (timepoints).
	@Override
	protected void removeVariablesSub(Variable[] ti) {
		int[] Ids = new int[ti.length];
		for (int i = 0; i < ti.length;i ++) 
		{
			if (ti[i] instanceof TimePoint) {
				Ids[i] = ((TimePoint)ti[i]).getID();
			}
		}
		//super.removeVariable(ti);
		tpDelete(Ids);
	}
	
	public void setAddingIndependentConstraints() {
		addingIndependentConstraints = true;
	}
	
	@Override
	protected boolean addConstraintsSub(Constraint[] con) {
		if (con == null || con.length == 0) return true;
		if (backupDMatrixSimple && backupConstraints.size() > 50) resetDMatrixBackups();
		Bounds[] tot = new Bounds[con.length];
		int[] from = new int[con.length];
		int[] to = new int[con.length];

		for (int i = 0; i < con.length; i++) {
			if (con[i] instanceof SimpleDistanceConstraint) {
				SimpleDistanceConstraint c = (SimpleDistanceConstraint)con[i];
				tot[i] = new Bounds(c.getMinimum(),c.getMaximum());                             
				from[i] = ((TimePoint)c.getFrom()).getID();
				to[i] = ((TimePoint)c.getTo()).getID();
			}
		}

		logger.finest("Trying to add constraints " + Arrays.toString(con) + "...");
		Vector added = new Vector();

		if (backupDMatrixSimple) saveDMatrix(con);
		
		if (addingIndependentConstraints) {
			addingIndependentConstraints = false;
			//Do special adding
			for (int i = 0; i < con.length; i++) {
				//add cons, one shot
				if (!cCreate(tot[i],from[i],to[i],true)) {
					logger.info("Critical error in new constraint propagation!");
					return false;
				}
			}
			return true;
		}
		
		if (con.length > MAX_USED) {
			logger.finest("From scratch prop is more convenient (MAX_USED = " + MAX_USED + " < " + con.length + " = #constraintsToAdd)...");
			return cCreate(tot, from, to);
		}
		
		logger.finest("Incremental prop is more convenient (MAX_USED = " + MAX_USED + " >= " + con.length + " = #constraintsToAdd)...");
		ConstraintNetwork cn = new ConstraintNetwork(null);
		for (int i = 0; i < con.length; i++) {
			//System.out.println("TOT: " + tot[i] + " FROM: " + printLong(from[i]) + " TO: " + printLong(to[i]));
			if (cCreate(tot[i],from[i],to[i],false)) {
				added.add(con[i]);
			}
			else {
				logger.finest("Failed to add " + con[i]);
				Bounds[] toDeleteBounds = new Bounds[added.size()];
				int[] toDeleteFrom = new int[added.size()];
				int[] toDeleteTo = new int[added.size()];
				for (int j = 0; j < added.size(); j++) {
					toDeleteBounds[j] = new Bounds(((SimpleDistanceConstraint)added.get(j)).getMinimum(),((SimpleDistanceConstraint)added.get(j)).getMaximum());
					toDeleteFrom[j] = ((TimePoint)((SimpleDistanceConstraint)added.get(j)).getFrom()).getID();
					toDeleteTo[j] = ((TimePoint)((SimpleDistanceConstraint)added.get(j)).getTo()).getID();
				}
				cDelete(toDeleteBounds, toDeleteFrom, toDeleteTo, true);
				return false;
			}
		}
		
		return true;
	}

	private void resetDMatrixBackups() {
		backups = new Vector();
		backupConstraints = new Vector();
		backupMaxUsed = new Vector();
	}
	
	private boolean canRestoreDMatrix(Constraint[] con) {
		if (!backupDMatrixSimple) return false;
		if (backupConstraints.isEmpty()) return false;
		for (Constraint c : backupConstraints.lastElement()) {
			boolean found = false;
			for (Constraint c1 : con) {
				if (c1.equals(c)) {
					found = true;
					break;
				}
			}
			if (!found) return false;
		}
		return true;
	}

	//Remove a constraint (SimpleDistanceConstraint)
	@Override
	protected void removeConstraintsSub(Constraint[] con) {
		logger.finest("Trying to remove constraints " + Arrays.toString(con) + "...");
		if (con != null && con.length != 0) {
			Bounds[] tot = new Bounds[con.length];
			int[] from = new int[con.length];
			int[] to = new int[con.length];

			for (int i = 0; i < con.length; i++) {
				if (con[i] instanceof SimpleDistanceConstraint) {
					SimpleDistanceConstraint c = (SimpleDistanceConstraint)con[i];
					tot[i] = new Bounds(c.getMinimum(),c.getMaximum());
					from[i] = ((TimePoint)c.getFrom()).getID();
					to[i] = ((TimePoint)c.getTo()).getID();				
				}
			}
			if (canRestoreDMatrix(con)) cDelete(tot,from,to,true);
			else {
				if (backupDMatrixSimple) resetDMatrixBackups();
				cDelete(tot,from,to,false);
			}
		}
	}

	/**
	 * Perform complete propagation.  Computational cost is O(n^3) where n is the number of
	 * timepoints in the Simple Temporal Network.
	 * @return {@code True} if propagation was successful, {@code False} otherwise.
	 */
	@Override
	public boolean propagate(){
		return fromScratchDistanceMatrixComputation();
	}

	/**
	 * Get the time origin of this {@link APSPSolver}. 
	 * @return The time origin of this {@link APSPSolver}.
	 */
	public long getO() {
		return O;
	}

	/**
	 * Get the horizon of this {@link APSPSolver}. 
	 * @return The horizon of this {@link APSPSolver}.
	 */
	public long getH() {
		return H;
	}

	/**
	 * Set a new horizon for this network.
	 * @param val The new horizon.
	 * @return {@code True} If the operation succeeded, {@code False} otherwise.
	 */
	public boolean changeHorizon(long val)
	{
		this.removeConstraint(horizonConstraint);
		SimpleDistanceConstraint sdc = new SimpleDistanceConstraint();
		sdc.setFrom(this.getVariable(0));
		sdc.setTo(this.getVariable(1));
		sdc.setMinimum(val);
		sdc.setMaximum(val);
		if (this.addConstraint(sdc)) {
			this.H = val;
			horizonConstraint = sdc;
			return true;
		}
		return false;
	}

	/**
	 * Get the source timepoint of the Simple Temporal Network (anchored to origin of time).
	 * @return the source timepoint of the Simple Temporal Network.
	 */
	public TimePoint getSource() {
		return tPoints[0];
	}

	/**
	 * Get the sink timepoint of the Simple Temporal Network (anchored to horizon).
	 * @return the sink timepoint of the Simple Temporal Network.
	 */
	public TimePoint getSink() {
		return tPoints[1];
	}

	/**
	 * Class method for pretty-printing longs (substitutes +/-INF appropriately). 
	 * @param l The long value to pretty-print.
	 * @return a String representation of the given long value. 
	 */
	public static String printLong(long l) {
		if (l >= 0) return "" + ((l == APSPSolver.INF ? "INF" : l));
		return "" + (-l == APSPSolver.INF ? "-INF" : l);
	}

	/**
	 * Get active constraint between two {@link TimePoint}s.
	 * @param tpFrom The source {@link TimePoint}.
	 * @param tpTo The destination {@link TimePoint}.
	 * @return The active {@link SimpleDistanceConstraint} between the
	 * two {@link TimePoint}s (null if none exists).
	 */
	public SimpleDistanceConstraint getConstraint(TimePoint tpFrom, TimePoint tpTo) {
		if (this.distance[tpFrom.getID()][tpTo.getID()] != INF)
			return tPoints[tpFrom.getID()].getOut(tpTo.getID());
		return null;
	}

	/**
	 * Gets the effective bounds between a pair of {@link TimePoint}s.
	 * (After propagation, considering all constraints in the network)
	 */
	public Bounds getDistanceBounds(TimePoint tpFrom, TimePoint tpTo) {
		final long max = distance[tpFrom.getID()][tpTo.getID()];
		final long min = -distance[tpTo.getID()][tpFrom.getID()];
		return new Bounds(min, max);
	}

	/**
	 * @return The maximum number of timepoints that can be added to this STP network
	 * (excluding the Origin (O) and Horizon (H) timepoint).
	 * @see APSPSolver#APSPSolver(long, long, int)
	 */
	public int getMaxTps() {
		//Subtract 2 (O and H) 
		return this.MAX_TPS - 2;
	}

	/**
	 * Draw a graph representing this {@link APSPSolver}'s {@link ConstraintNetwork}.  This method depends on the Prefuse library.
	 */
	public void draw() {
		//=================================================================
		//Now plot the STP with the Utility.PlotSTPTemporalModule class
		//=================================================================
		PlotSTPTemporalModule view;
		Toolkit tk = Toolkit.getDefaultToolkit();
		int xSize = ((int) tk.getScreenSize().getWidth());
		int ySize = ((int) tk.getScreenSize().getHeight());
		//int ySize = 500;

		//GIVE tm TO VISIUALIZER		
		view = new PlotSTPTemporalModule(this, xSize, ySize);
		JFrame myFrame = new JFrame("Simple Temporal Network");

		Container pane = myFrame.getContentPane();
		pane.setLayout(new BorderLayout());
		pane.add(view, BorderLayout.NORTH);
		myFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);

		myFrame.setSize(xSize,ySize);

		myFrame.pack();
		myFrame.setVisible(true);

		view.touchLBUBNodes();
		//=================================================================
		//End STP plotting
		//=================================================================
	}
	/**
	 * This method computes the root mean square rigidity of a consistent STN (the inverse concept of flexibility of a STN).
	 * If the STN is completely rigid, then its rigidity is 1. If the STN has no constraints, its rigidity is 0.
	 * This measure in proposed in [Luke Hunsberger, 2002]. 
	 * @return Root mean square rigidity of a consistent STN.
	 */
	public double getRMSRigidity(){

		rigidity = new double[this.getVariables().length];
		for (int i = 0; i < this.getVariables().length; i++) {
			if ( ((TimePoint)this.getVariables()[i]).isUsed() ) {
			rigidity[i] = (
					((double)1 / 
							((double)(1 + ((TimePoint)this.getVariables()[i]).getUpperBound() - ((TimePoint)this.getVariables()[i]).getLowerBound()))
							));
			//System.out.println(i + " " + j + " -> " + distance[this.getVariables()[i].getID()][this.getVariables()[j].getID()]);
			//System.out.println(i + " " + j + " -> " + rigidity[i][j]);
			}
		}
		double sigma = 0;
		for (int i = 0; i < this.getVariables().length; i++) {
			if ( ((TimePoint)this.getVariables()[i]).isUsed() ) {
				sigma += Math.pow(rigidity[i], 2.0);
			}
		}			

		return Math.sqrt(((double)sigma) * ((double)2/(this.getVariables().length * (this.getVariables().length + 1))));
	}

	private void writeObject(ObjectOutputStream out) throws IOException {
		out.defaultWriteObject();
	}

	private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
		in.defaultReadObject();
		logger = MetaCSPLogging.getLogger(this.getClass());
	}

	public int bookmark() {
		logger.fine("Bookmark #"+this.distanceRollback.size()+" MAX_USED="+this.MAX_USED);
		
		long[][] distanceSnapshot = new long[this.distance.length][this.distance[0].length];
		TimePoint[] tPointSnapshot = new TimePoint[this.tPoints.length];

		for ( int i = 0 ; i < tPoints.length ; i++ ) {
			TimePoint clone = tPoints[i].clone();
			tPointSnapshot[i] = clone;
		}		


//		for ( int i = 0 ; i < this.MAX_TPS ; i++ ) {
//			for ( int j = 0 ; j < this.MAX_TPS ; j++ ) {
//				distanceSnapshot[i][j] = distance[i][j];
//			}
//		}
		
		for ( int i = 0 ; i < this.MAX_USED+1 ; i++ ) {
			for ( int j = 0 ; j < this.MAX_USED+1 ; j++ ) {
				distanceSnapshot[i][j] = distance[i][j];
			}
		}

		distanceRollback.add(distanceSnapshot);
		tPointsRollback.add(tPointSnapshot);
		maxUsedRollback.add( new Integer(this.MAX_USED) );

		return distanceRollback.size()-1;
	}

	public void removeBookmark( int i ) {
		this.distanceRollback.remove(i);
		this.tPointsRollback.remove(i);
		this.maxUsedRollback.remove(i);
	}

	public void revert( int i ) {		
		this.distance = this.distanceRollback.get(i);	
		this.tPoints = this.tPointsRollback.get(i);
		this.MAX_USED = this.maxUsedRollback.get(i).intValue();

		for ( int j = this.distanceRollback.size()-1 ; j >= i ; j-- ) {
			this.distanceRollback.remove(j);
			this.tPointsRollback.remove(j);
			this.maxUsedRollback.remove(j);
		}
		logger.fine("Reverting to #"+this.distanceRollback.size()+" MAX_USED="+this.MAX_USED);
	}

	public int numBookmarks() {
		return this.distanceRollback.size();
	}

	public TimePoint getEqualTimePoint( TimePoint queryTp ) {
		for ( TimePoint tp : this.tPoints ) {
			if ( tp.equals(queryTp) ) {
				return tp;
			}
		}
		return null;
	}

	public String printDist() {
		String s = "";
		for ( int i = 0 ; i < this.MAX_USED+1; i++ ) {
			for ( int j = 0 ; j < this.MAX_USED+1; j ++ ) {
				s +=  printLong(distance[i][j]) + " ";
			}
			s += "\n";
		}
		return s;
	}

	private static long sum(long a, long b) {
		if (a == APSPSolver.INF || b == APSPSolver.INF) return APSPSolver.INF;
		return a+b;
	}

	public String printDistHist() {
		String s = "";
		int ci = 0;
		for ( long[][] distance : this.distanceRollback ) {
			s += "=============================\n";
			s += "= " + (ci++) + "\n";
			s += "=============================\n";
			for ( int i = 0 ; i < this.MAX_USED+1 ; i++ ) {
				for ( int j = 0 ; j < this.MAX_USED+1 ; j ++ ) {
					s +=  printLong(distance[i][j]) + " ";
				}
				s += "\n";
			}
		}
		return s;
	}

	@Override
	public void registerValueChoiceFunctions() {
	    ValueChoiceFunction startFunction = new ValueChoiceFunction() {
			@Override
			public Object getValue(Domain dom) {
				return ((Interval)dom).getBounds().min;
			}
	    };
	    ValueChoiceFunction endFunction = new ValueChoiceFunction() {
			@Override
			public Object getValue(Domain dom) {
				return ((Interval)dom).getBounds().max;
			}
	    };
		Domain.registerValueChoiceFunction(Interval.class, startFunction, "ET");
		Domain.registerValueChoiceFunction(Interval.class, endFunction, "LT");
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy