/* ************************************************************************* * * * * Copyright (c) 2004 Peter Cappello * * * * 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. * * * * ************************************************************************* */ /** * Extend the traveling sales-critter problem (TSP) beyond Java. * * @version 1.0 * @author Andy Pippin */ /* * TSP.java */ package edu.ucsb.cs.jicos.examples.external.tsp; import edu.ucsb.cs.jicos.applications.branchandbound.BranchAndBound; import edu.ucsb.cs.jicos.applications.branchandbound.Solution; import edu.ucsb.cs.jicos.applications.utilities.graph.*; import edu.ucsb.cs.jicos.examples.tsp.TspSolution; import edu.ucsb.cs.jicos.services.Shared; import edu.ucsb.cs.jicos.services.external.ExternalData; import edu.ucsb.cs.jicos.services.external.ExternalRequest; import edu.ucsb.cs.jicos.services.external.XmlConverter; import edu.ucsb.cs.jicos.services.external.XmlDocument; import edu.ucsb.cs.jicos.services.external.services.CollectorHttp; import edu.ucsb.cs.jicos.services.external.services.ExternalRequestProcessor; import edu.ucsb.cs.jicos.services.shared.IntUpperBound; import java.util.List; import java.util.Random; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; public class TSP extends BranchAndBound implements XmlConverter, java.io.Serializable { // //-- Constants ----------------------------------------------------------- public static final int DEFAULT_CITIES = 5; public static final int MAX_EDGES = edu.ucsb.cs.jicos.examples.tsp.TSP.MAX_EDGE; public static final int MAX_DISTANCE = Integer.MAX_VALUE; // //-- Variables ----------------------------------------------------------- private int cities; private int[][] distance; // //-- Constructors -------------------------------------------------------- public TSP() { super( (Solution) null ); this.cities = 0; this.distance = null; } // Mostly for testing. public TSP(int[][] distance) throws Exception { super( (Solution) null ); if (distance.length != distance[0].length) { throw new Exception( "Distance matrix is not square ( " + distance.length + " x " + distance[0].length + " )" ); } else if (distance.length > MAX_EDGES) { throw new Exception( distance.length + " is too many edges (max = " + MAX_EDGES + ")" ); } this.cities = distance.length; this.distance = distance; return; } public void setSolution( Solution solution ) { super.setSolution( solution ); } public Solution getSolution() { return (super.getSolution()); } // //== Implements XmlConverter ============================================= public String toXml( String prefix ) throws Exception { prefix = (null == prefix) ? "" : prefix; final String CRLF = "\r\n"; String msg = prefix + "" + CRLF + prefix + "" + CRLF + prefix + " " + CRLF; for (int src = 0; src < this.cities; ++src) { msg += prefix + " "; for (int dst = 0; dst < this.cities; ++dst) { if (0 != dst) { msg += " "; } msg += String.valueOf( this.distance[src][dst] ); } msg += "" + CRLF; } msg += prefix + " " + CRLF + prefix + "" + CRLF + ""; return (msg); } public boolean fromXml( ExternalData externalData ) throws Exception { boolean success = false; final String topLevel = ExternalRequest.TOP_LEVEL; String numCities = externalData.getValue( "/TSP/@cities" ); if (null != numCities) { try { this.cities = Integer.parseInt( numCities ); // Create the solution and stuff it into the BranchAndBound. Solution emptySolution = new TspSolution( this.cities ); super.setSolution( emptySolution ); success = true; } catch (NumberFormatException numberFormatException) { throw new Exception( " is not a number." ); } } else { throw new Exception( " does not specify \"cities\"!" ); } // See if there is a new number of cities. boolean abortWithFalse = false; try { int prevCities = Integer.parseInt( externalData.getValue( "/TSP/@prevcities" ) ); abortWithFalse = ( prevCities != this.cities ); } catch( Exception exception ) {} if( abortWithFalse ) { // ************************* return( false ); // *** DOES NOT CONTINUE *** // } // ************************* // Make sure it is an XmlDocument. XmlDocument xml = null; if (externalData instanceof List) { xml = new XmlDocument( (List) externalData ); } else if (externalData instanceof XmlDocument) { xml = (XmlDocument) externalData; } else { throw new ClassCastException( "Unknown type of ExternalData!" ); } // This should be part of the createInput, but it is useful to do here, // since after this is created, the instance can be displayed. // // If we are good so far, then get the rest of the data. if (success) { this.distance = new int[this.cities][this.cities]; // Get the data. Element[] rowData = xml.getElement( topLevel + "/TSP/Distance" ); if (null == rowData) { throw new Exception( "There is no data" ); } if (rowData.length != (this.cities * this.cities)) { throw new Exception( "Number of cities (" + this.cities + ") does not match number of rows of data (" + (int) Math.sqrt( rowData.length ) + ")" ); } else if (this.cities > (MAX_EDGES * MAX_EDGES)) { throw new Exception( "Number of nodes (" + this.cities + ") exceeds the maximum (" + MAX_EDGES + ")" ); } String distStr = null; NodeList nodeList = null; Node textNode = null; int ndx = 0; // For each row of data, break it up on the whitespace. // for (int src = 0; src < this.cities; ++src) { for (int dst = 0; dst < this.cities; ++dst) { if (null == (nodeList = rowData[ndx].getChildNodes())) { int err = 1; } else if (null == (textNode = nodeList.item( 0 ))) { int err = 2; } else if (Node.TEXT_NODE == textNode.getNodeType()) { if (null != (distStr = textNode.getNodeValue())) { this.distance[src][dst] = (int) Double .parseDouble( distStr ); } else { throw new NullPointerException( "Distance[" + src + "][" + dst + "] was null" ); } } ++ndx; } } } return (success); } /** * Create the Graph representing the cities. * * @param externalData * The data from the External client. * @return The distance graph. * @throws A * variety of errors. */ public Object createInput( ExternalData externalData ) throws Exception { Object inputObject = null; if (null == this.distance) { throw new Exception( "there are no distances" ); } inputObject = (Object) new edu.ucsb.cs.jicos.examples.tsp.TSP( new GraphImpl( distance ) ); return ((Object) inputObject); } /** * The bounding value: the best tour found to date. * * @return An integer upper bound. * @throws Exception * Not thrown, required from interface. */ public Shared createShared( ExternalData externalData ) throws Exception { return (new IntUpperBound( Integer.MAX_VALUE )); } /** * Convert the result to XML. * * @param result * The answer to the computation. * @return The XML representation of the result object. * @throws Exception * Hiding implementation details from the user. */ public XmlDocument createResult( Object result ) throws Exception { String txt; if (null == result) { txt = "\r\n" + " Result is null\r\n" + "\r\n"; } else if (!(result instanceof Solution)) { txt = "\r\n" + " Result is not a BranchAndBound Solution (" + result.getClass().getName() + ")\r\n" + "\r\n"; } else { TspSolution solution = (TspSolution) result; int cost = solution.getCost(); int[] tour = solution.getTour(); txt = "\r\n" + "\r\n" + " \r\n" + " "; for (int node = 0; node < tour.length; ++node) { if (0 != node) { txt += " "; } txt += String.valueOf( tour[node] ); } txt += "\r\n" + " \r\n" + "\r\n"; } return (new XmlDocument( txt )); } /** * Get the (non-existant) stylesheet. * * @param stylesheetType * The type of stylesheet. * @return The stylesheet if it exists, or null if not. */ public Document getStyleSheet( int styleSheetType ) { Document xsltStyleSheet = null; switch (styleSheetType) { case XmlConverter.STYLESHEET_Unknown: case XmlConverter.STYLESHEET_Xml: case XmlConverter.STYLESHEET_Html: break; } return ((org.w3c.dom.Document) null); } /** * Convert to an HTML string. * * @param result The result object. * @param hostPort The "hostname:port" of the CollectorHttp. */ public String toHtmlString( XmlDocument result, String hostPort ) { final String topLevel = ExternalRequest.TOP_LEVEL; String html = "\r\n" + "\r\n" + " TSP Solver\r\n" + "\r\n" + "\r\n" + CollectorHttp.jicosHtmlHeader() + "

TSP Solver

\r\n" + "\r\n" ; if( null != result ) { String previousTour = result.getValue( "/ExternalResponse/TSP/tour" ); String tourCost = result.getValue( "/ExternalResponse/TSP/tour/@cost" ); if( (null != previousTour) && (null != tourCost) ) { html += "
" + "Best tour

\r\n" ; String[] city = previousTour.split( " " ); if( 0 < city.length ) { html += city[0]; for( int c=1; c < city.length; ++c ) { html += " → " + city[c]; } } html += "
\r\nCost: " + tourCost + "

\r\n"; // Dump the previous distance graph. html += "\r\n" + " \r\n" + " \r\n" ; for( int dst=0; dst < this.cities; ++dst ) { html += " \r\n"; for( int src=0; src < this.cities; ++src ) { html += " "; } html += " \r\n"; } html += "\r\n" + "
Distances
" + this.distance[dst][src] + "
\r\n" ; } } if( 0 == this.cities ) { this.cities = DEFAULT_CITIES; } html += "" + "\r\n" + "\r\n" + "\r\n" + "

\r\n" + "\r\n" + "\r\n" + "\r\n" + "
\r\n" + "
\r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + "
\r\n" + " \r\n" + " \r\n" + " \r\n" ; Random rng = new Random(); for( int fr=0; fr < this.cities; ++fr ) { html += " \r\n"; for( int to=0; to < this.cities; ++to ) { int value = rng.nextInt( 100 ); html += " \r\n"; } html += " \r\n"; } html += "" + "
\r\n" + "
\r\n" + "

\r\n" + "\r\n" + "
\r\n" + "

\r\n" + "\r\n" + CollectorHttp.createResponseSelect( null ) + "\r\n" + "\r\n" + "\r\n" + "\r\n" + CollectorHttp.jicosHtmlFooter() + "\r\n" + "\r\n" ; return ( html ); } }