diff --git a/.config/deploy-artifacts.sh b/.config/deploy-artifacts.sh old mode 100644 new mode 100755 index 2db58d2..8bb702a --- a/.config/deploy-artifacts.sh +++ b/.config/deploy-artifacts.sh @@ -1,16 +1,12 @@ #!/bin/bash -echo "Auto-deploying Hipster artifacts..." +echo "Auto-deploying Hipster4j snapshots..." echo "Current branch: $TRAVIS_BRANCH" - -if [ "$TRAVIS_REPO_SLUG" == "citiususc/hipster" ] && [ "$TRAVIS_JDK_VERSION" == "oraclejdk7" ] && [ "$TRAVIS_PULL_REQUEST" == "false" ]; then - echo "Running mvn deploy, current directory: `pwd`" - # Deploy to CITIUS - #mvn --settings .config/maven-settings.xml -P citius-snapshot-deploy deploy -DskipTests=true - # Deploy to Sonatype Nexus OSS - mvn --settings .config/maven-settings.xml -P sonatype-nexus-snapshots deploy -DskipTests=true -else - echo "Skipping deployment for this build..." +if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then + echo "Skipping snapshot deployment for pull request" + exit fi - +echo "Running mvn deploy, current directory: `pwd`" +# Deploy to Sonatype Nexus OSS +mvn --settings .config/maven-settings.xml deploy -DskipTests=true echo "Deployment script finished." diff --git a/.config/deploy-site.sh b/.config/deploy-site.sh deleted file mode 100644 index 107e740..0000000 --- a/.config/deploy-site.sh +++ /dev/null @@ -1,74 +0,0 @@ -#!/bin/bash - -#TRAVIS_REPO_SLUG="citiususc/hipster" -#TRAVIS_JDK_VERSION="oraclejdk7" -#TRAVIS_PULL_REQUEST="false" -#TRAVIS_BRANCH="master" -#HOME=`pwd` - -echo "Preparing Hipster site for auto-deploy" -echo "TRAVIS_REPO_SLUG=$TRAVIS_REPO_SLUG - TRAVIS_JDK_VERSION=$TRAVIS_JDK_VERSION - TRAVIS_PULL_REQUEST=$TRAVIS_PULL_REQUEST" - -if [ "$TRAVIS_REPO_SLUG" == "citiususc/hipster" ] && [ "$TRAVIS_JDK_VERSION" == "oraclejdk7" ] && [ "$TRAVIS_PULL_REQUEST" == "false" ]; then - - # Decide the documentation version folder name depending on the branch and the version in the pom.xml - # wget https://raw.githubusercontent.com/citiususc/hipster/$TRAVIS_BRANCH/pom.xml > /dev/null 2>&1 - # Take the version from the main pom.xml - # grep -m 1 -E '[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9_]+(-[0-9]+)?)?' pom.xml - VERSION=`grep -m 1 "" pom.xml | cut -d ">" -f 2 | cut -d "<" -f 1` - # rm pom.xml - - # Validate if the version is correct (example 1.0.0-SNAPSHOT, or 1.0.0-alpha-1) - VERSION_REGEX='^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9_]+(-[0-9]+)?)?$' - if [[ $VERSION =~ $VERSION_REGEX ]]; then - echo "Current version is $VERSION" - else - echo "Version error. Unrecognized version $VERSION" - exit 1 - fi - - echo "Deploying Hipster [$VERSION] site and documentation to GitHub gh-pages" - echo "Current directory is: `pwd`" - - - echo "Building javadocs..." - mvn javadoc:aggregate - - # Build site only if this is the development branch. - if [ "$TRAVIS_BRANCH" == "master" ]; then - echo "Building site..." - mvn site:site - fi - - # First, copy the generated site to the new folder - mkdir $HOME/site - cp -Rf target/site/* $HOME/site - # Remove the apidocs site and use the aggregated javadoc instead - rm -rf $HOME/site/apidocs - # Copy the apidocs to the site folder - mkdir $HOME/site/documentation - mkdir $HOME/site/documentation/javadoc - mkdir $HOME/site/documentation/javadoc/$VERSION - cp -Rf target/apidocs/* $HOME/site/documentation/javadoc/$VERSION - - # Now prepare for uploading the site to gh-pages - - cd $HOME - git clone --quiet --branch=gh-pages https://github.com/citiususc/hipster.git gh-pages > /dev/null - - # Copy and overwrite the site with the new content - cp -Rf $HOME/site/* gh-pages/ - cd gh-pages - - # Config git user and credentials - git config --global user.email "travis@travis-ci.org" - git config --global user.name "travis-ci" - git config credential.helper "store --file=.git/credentials" - echo "https://${GITHUB_TOKEN}:@github.com" > .git/credentials - - git add -A - git commit -a -m "auto-commit $TRAVIS_BRANCH Hipster site updated (build $TRAVIS_BUILD_NUMBER)" - git push -q origin gh-pages > /dev/null - echo "Published $TRAVIS_BRANCH Hipster site to gh-pages." - -fi diff --git a/.config/maven-settings.xml b/.config/maven-settings.xml index 8ab720f..d0d68a9 100644 --- a/.config/maven-settings.xml +++ b/.config/maven-settings.xml @@ -15,19 +15,48 @@ ~ limitations under the License. --> - + - + - citius-nexus-snapshots - ${env.NEXUS_SNAPSHOT_USERNAME} - ${env.NEXUS_SNAPSHOT_PASSWORD} - - - sonatype-nexus-snapshots ${env.SONATYPE_SNAPSHOT_USERNAME} ${env.SONATYPE_SNAPSHOT_PASSWORD} + + bintray-hipster4j-maven + ${env.BINTRAY_USER} + ${env.BINTRAY_API_KEY} + + + + + + + + false + + bintray-hipster4j-maven + bintray + http://dl.bintray.com/hipster4j/maven + + + + + + false + + bintray-hipster4j-maven + bintray-plugins + http://dl.bintray.com/hipster4j/maven + + + bintray + + + + bintray + diff --git a/.config/publish-javadocs.sh b/.config/publish-javadocs.sh new file mode 100755 index 0000000..8739b2b --- /dev/null +++ b/.config/publish-javadocs.sh @@ -0,0 +1,58 @@ +#!/bin/bash + +if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then + echo "Skipping Javadoc publication for pull request" + exit +fi + +if [ "$TRAVIS_TAG" == "" ]; then + echo "Current version is not a release, skipping Javadoc publication" + exit +fi + +echo "Auto publishing latest javadocs..." +echo "TRAVIS_REPO_SLUG=$TRAVIS_REPO_SLUG - TRAVIS_JDK_VERSION=$TRAVIS_JDK_VERSION - TRAVIS_PULL_REQUEST=$TRAVIS_PULL_REQUEST" + +# Decide the documentation version folder name depending on the branch and the version in the pom.xml +VERSION=`grep -m 1 "" pom.xml | cut -d ">" -f 2 | cut -d "<" -f 1` + +# Validate if the version is correct (example 1.0.0-SNAPSHOT, or 1.0.0-alpha-1) +VERSION_REGEX='^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9_]+(-[0-9]+)?)?$' +if [[ $VERSION =~ $VERSION_REGEX ]]; then + echo "Current version is $VERSION" +else + echo "Version error. Unrecognized version $VERSION" + exit 1 +fi + +echo "Deploying Hipster [$VERSION] javadocs to GitHub gh-pages" +echo "Current directory is: `pwd`" + +echo "Building javadocs..." +# Generate Javadocs in target/apidocs +mvn javadoc:aggregate + +# Clone Hipster4j GitHub gh-pages for Javadocs +git clone --quiet --branch=gh-pages https://github.com/hipster4j/hipster-javadocs.git gh-pages > /dev/null + +# Overwrite the previous version with the new one +cp -Rf target/apidocs/* gh-pages/ + +# Create a new folder with the version number and copy the latest version to it +mkdir gh-pages/$VERSION +cp -Rf target/apidocs/* gh-pages/$VERSION/ + +# Now prepare for uploading the site to gh-pages +cd gh-pages + +# Config git user and credentials +git config --global user.email "travis@travis-ci.org" +git config --global user.name "travis-ci" +git config credential.helper "store --file=.git/credentials" +echo "https://${GITHUB_TOKEN}:@github.com" > .git/credentials + +git add -A +git commit -a -m "auto-commit $TRAVIS_BRANCH Hipster4j Javadocs v$VERSION (build $TRAVIS_BUILD_NUMBER)" +git push -q origin gh-pages > /dev/null +echo "Finished" + diff --git a/.gitignore b/.gitignore index 1d6dfdc..57b8f19 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,7 @@ nb-configuration.xml # Idea specific # *.iml .idea + +# Eclipse specific # +.metadata/ + diff --git a/.travis.yml b/.travis.yml index c20c2e9..7927dfe 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,22 +1,13 @@ language: java jdk: - oraclejdk7 -- oraclejdk8 -- openjdk7 branches: except: - /^(?i:wip).*$/ sudo: false +before_install: + - pip install --user codecov after_success: -#- chmod +x .config/deploy-site.sh -#- .config/deploy-site.sh -- chmod +x .config/deploy-artifacts.sh -- .config/deploy-artifacts.sh -- mvn clean cobertura:cobertura coveralls:report -env: - global: - - secure: ILdOYjPt+8g5rlexXBYAhECtn5Zm26FRf0/nwCxUU303qtzFQyMcxinIC93aun880OnINjA7fzQeBkG4P+LSVOAXbmGTGhtyPBzMOGcnZouJM/RyXHUft6tAXPimXQ7JjDFjyv7EzSeStk/4WEp0mkxheIryZS3X1pbED1TqgUM= - - secure: cwV1rA8+CUHonJg40dRHCcd8KmYl9EzpCUcnS8vm/1qNkXpRmEAvwNKjZct0sZ8ZFKjKEUEzhyG0+cZVRzvEhi108/WZ5pIxp01EdtSFZTlIg73llALb2P8HClHuy4XSBDz0G4nTLES11usUGpCsLckSudNtb7bdUAH356NxFvQ= - - secure: XNFSHaGHiJfj4TvW6Oud1Z1dKnkXYk0njX2xjfljfFlHf4eCSIUxO9giad4xq2sE7vrjk+JpLLj/te7qjug91Ih7KH0i9T56d57q9vfu8juW49NBQxRnfhlvYWyrto5rmnKEa8Mcs/smgER6dllsURz10AqLKM32mkijG4VgdcM= - - secure: VFkAqPydQ9NTxXkp/gNzNUoaYd/xvryKy+ARXJhbvgUkpO2VMyo2D2wU1G6ECVzitqpO+gaUn2mu7fO1M0KjpeKp5Si7HG3fUVb4sXg6Sim563iot2rJUfiXpu6FOqPfUwQHpyUXqUfAPOA6bI9ZNKNBAurGxgLgt4AP2PjObV4= - - secure: Q5UXkBKvrqcC09peZa/U/XilT4q32fm2tWEu/0Pq0JquKS+BmOevEmntW0zMBe8R5K5AzbuDKurAyelCqXDNj3W8Tjg/xreIqR3ccnfo202wkLiIAtko8oUXzyiLgeMjjyge76lHIkaGxWXNGzsDUZmq5qUQW2YEQ5vGiv0vc5A= + - .config/deploy-artifacts.sh + - .config/publish-javadocs.sh + - codecov \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e69de29..516e23f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -0,0 +1,113 @@ +# Contributing to Hipster4j + +First of all, thank you so much for being interested in contributing to Hipster4j!. This document will guide you through this process. You can contribute in different ways: + +- Reporting issues +- Fixing bugs or developing new features +- Creating new examples +- Sending a motivating email telling us how do you like the library (or dislike) :) + +## Issues +Feel free to open new issues or participating in the discussion of the existing ones on +[this repository](https://github.com/citiususc/hipster/issues), but before doing so, please make sure that the issue is not duplicated and/or the discussion is related to the topic of the issue. + +## Pull requests +Code contributions are welcome following a process which guarantees the long-term maintainability of the project. +You can contribute either with bugfixes or new features. Before submitting a new feature, we highly encourage you to first open a new issue describing its motivation and details and discuss it with one of the project mantainers. This will ensure that the feature fits well in the project. + +### Step 1: Open a new issue (if not opened yet) +Before starting to code, it is desirable to first open an issue describing the bug or the new feature. Please be sure the issue is not duplicated. + +### Step 2: Fork the repository +Fork the project https://github.com/citiususc/hipster into your account. Then, check out your copy of the project locally. +``` +git clone git@github.com:username/hipster.git +cd hipster +git remote add upstream https://github.com/citiususc/hipster.git +``` + +### Step 3: Create a new feature branch `contrib/issue-number` +Put your code in a new feature branch. The name of the new branch should start with `contrib/`. This convention will help us to keep track of future changes from pull requests. +``` +git checkout -b contrib/issue-number origin/branch +``` +Note that origin/‘branch’ would correspond with any of the current development branches (for example 1.0.X) but never the origin/master branch. For example, suppose that the latest version of the project is v1.0.0 and you want to fix a new bug that you discovered in this version. If the new reported issue has an id, say, #186, then you would create your feature branch in this way: +``` +git checkout -b contrib/issue-186 origin/1.0.X +``` + +### Step 4: Committing your changes +First of all, make sure that git is configured with your complete name and email address. It is desirable to use the same email of your Github account, this will help to identify the contributions: +``` +git config --global user.name "Your Name" +git config --global user.email "your@email.com" +``` +Write a good commit message. It should describe the changes you made and its motivation. Be sure to reference the issue you are working in the commit that finishes your contribution using one the [keywords to close issues in Github](https://help.github.com/articles/closing-issues-via-commit-messages/). +If your commit message is too long, try to summarize the changes in the header of the message, like this: +``` +fix #xx : summarize your commit in one line + +If needed, explain more in detail the changes introduced in your +commit and the motivation. You could introduce some background +about the issue you worked in. + +This message can contain several paragraphs and be as long as +you need, but try to do a good indentation: the columns should +be shorter than 72 characters and with a proper word-wrap. +The command `git log` will print this complete text in a nice +way if you format it properly. +``` +The header and the body of the commit message must be separated by a line in blank. The header is the message shown when running the command `git shortlog`. + +#### Keep your branch in sync +Remember to keep in sync your version. Use git rebase instead of git merge to bring all the changes from the upstream branch to your feature branch: + +``` +git fetch upstream +git rebase upstream/branch #where branch would be 1.0.X, 1.1.X etc +``` + +#### Test your code +Verify that your changes are actually working by adding the required unit tests. It is desirable to include unit test covering all new features you implement. Also, if you find a bug which is not currently detected by the unit tests you might consider to implement a new one or modify the current implementation. After this, you can verify that everything works fine after your changes with: + +``` +mvn clean test +``` + +### Step 5: Push your changes + +Push your changes to your forked project with: +``` +git push origin my-feature-branch +``` + +### Step 6: Create and submit a pull request +Go to your forked project on GitHub, select your feature branch and click the “Compare, review, create a pull request button”. After that, we will review your pull request in a few days (hopefully!), but if we delay please be patient :). We do our best in our spare time to keep the project updated, but unfortunately there may be some periods of time in which we simply can’t work in the project. + + + +### License Agreement +By contributing your code, you agree to license your contribution under the terms of the [Apache 2.0 license](https://raw.githubusercontent.com/citiususc/hipster/4ca93e681ad7335acbd0bea9e49fe678d56f3519/LICENSE). + +Also, remember to add this header to each new file that you’ve created: + +``` +/* +* Copyright 2015 Centro de Investigación en Tecnoloxías da Información (CITIUS), +* University of Santiago de Compostela (USC). +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +``` + +That’s all! diff --git a/README.md b/README.md index 9042a27..27cfc33 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,9 @@ ![Hipster](src/main/doclava/custom/assets/hipster-template/assets/images/header-logo.png?raw=true) +[![SonarQube Tech Debt](https://img.shields.io/sonar/http/nemo.sonarqube.org/es.usc.citius.hipster:hipster-pom/tech_debt.svg)](http://nemo.sonarqube.org/dashboard/index/es.usc.citius.hipster:hipster-pom) +[![Coverage](https://img.shields.io/sonar/http/nemo.sonarqube.org/es.usc.citius.hipster:hipster-pom/coverage.svg)]() +![Build Status](https://api.travis-ci.org/citiususc/hipster.svg?branch=development) + A powerful and friendly heuristic search library implemented in Java. ## What's Hipster? @@ -11,7 +15,7 @@ You can use Hipster to solve from simple graph search problems to more advanced ## Features -The current version of the library comes with some very well-known and wide used search algorithms. Note that this list may not be exhaustive: +The current version of the library comes with some very well-known and wide used search algorithms. We're working to add more algorithms soon: * Search algorithms: * Uninformed search: @@ -26,8 +30,8 @@ The current version of the library comes with some very well-known and wide used * Local search: * Hill-Climbing. * Enforced-Hill-Climbing. - * Other (experimental implementations) - * Multiobjective LS algorithm. Original paper: Martins, E. D. Q. V., & Santos, J. L. E. (1999). *"The labeling algorithm for the multiobjective shortest path problem"*. Departamento de Matematica, Universidade de Coimbra, Portugal, Tech. Rep. TR-99/005 + * Multiobjective search + * Multiobjective LS algorithm. Original paper: Martins, E. D. Q. V., & Santos, J. L. E. (1999). *"The labeling algorithm for the multiobjective shortest path problem"*. Departamento de Matematica, Universidade de Coimbra, Portugal, Tech. Rep. TR-99/005 ([see an example](https://github.com/citiususc/hipster/wiki/Multiobjective-Search-with-Hipster-and-TinkerPop-Blueprints)) * 3rd party adapters: * [Java Universal/Graph (JUNG)](http://jung.sourceforge.net/) adapter. @@ -38,7 +42,7 @@ If you don't find the algorithm or the feature you are looking for, please consi The easiest way to use Hipster is adding it as a dependency with your favourite dependency manager. Maven users can include the library using the following snippet: -#### Snapshots ![Build Status](https://api.travis-ci.org/citiususc/hipster.svg?branch=development) +#### Snapshots You can use the latest (unstable) version of Hipster under development. Just add the following dependency into your pom.xml: @@ -68,21 +72,21 @@ You can use the latest (unstable) version of Hipster under development. Just add ``` -#### Releases [![Stories in Ready](https://badge.waffle.io/citiususc/hipster.png?label=ready&title=Ready)](http://waffle.io/citiususc/hipster) +#### Releases -Current stable release is v1.0.0-rc1. See the [milestones](https://github.com/citiususc/hipster/milestones) to check the current development status. +Current stable release is v1.0.0-rc2. See the [milestones](https://github.com/citiususc/hipster/milestones) to check the current development status. ```xml es.usc.citius.hipster hipster-all - 1.0.0-rc1 + 1.0.0-rc2 ``` @@ -127,11 +131,11 @@ System.out.println(Hipster.createDijkstra(p).search("F")); Output result: `Total number of iterations: 7. States: [A, B, C, E, D, F], Actions: [2.0, 1.0, 3.0, 2.0, 2.0], Search information: WeightedNode{state=F, cost=10.0, estimation=0.0, score=10.0}` But that's not all. Hipster comes with different problem examples -that illustrate how Hipster can be used to solve a [wide variety of problems](https://github.com/citiususc/hipster/tree/development/hipster-examples/src/main/java/es/usc/citius/hipster/examples) (not only graph search). +that illustrate how Hipster can be used to solve a [wide variety of problems](https://github.com/citiususc/hipster/tree/0c0ec9cb5087fede9930a6efbd5126afd69896ac/hipster-examples/src/main/java/es/usc/citius/hipster/examples) (not only graph search). ## What's next? -If you want to learn how to solve a problem by searching with Hipster, check the wiki to [learn the basics](https://github.com/citiususc/hipster/wiki/Getting-Started) and the [JavaDoc documentation](http://www.hipster4j.org/javadocs/latest). +If you want to learn how to solve a problem by searching with Hipster, check the [wiki](https://github.com/citiususc/hipster/wiki) and the [JavaDoc documentation](http://www.hipster4j.org/hipster-javadocs). We also suggest you to check [this presentation](https://speakerdeck.com/pablormier/hipster-an-open-source-java-library-for-heuristic-search) for a quick introduction. ## License & Citation diff --git a/hipster-all/pom.xml b/hipster-all/pom.xml index 0e6902f..dc9eed4 100644 --- a/hipster-all/pom.xml +++ b/hipster-all/pom.xml @@ -21,7 +21,7 @@ es.usc.citius.hipster hipster-pom - 1.0.0-rc2 + 1.0.0-SNAPSHOT 4.0.0 hipster-all diff --git a/hipster-core/pom.xml b/hipster-core/pom.xml index 5d17218..dc0d5bf 100644 --- a/hipster-core/pom.xml +++ b/hipster-core/pom.xml @@ -5,7 +5,7 @@ es.usc.citius.hipster hipster-pom - 1.0.0-rc2 + 1.0.0-SNAPSHOT 4.0.0 hipster-core diff --git a/hipster-core/src/main/java/es/usc/citius/hipster/algorithm/Algorithm.java b/hipster-core/src/main/java/es/usc/citius/hipster/algorithm/Algorithm.java index bd3667c..fefdc5e 100644 --- a/hipster-core/src/main/java/es/usc/citius/hipster/algorithm/Algorithm.java +++ b/hipster-core/src/main/java/es/usc/citius/hipster/algorithm/Algorithm.java @@ -97,7 +97,7 @@ public String toString() { final String ls = System.getProperty("line.separator"); StringBuilder builder = new StringBuilder(); builder.append("Total solutions: ").append(goalNodes.size()).append(ls); - builder.append("Total time: ").append(getElapsed()).append(ls); + builder.append("Total time: ").append(getElapsed()).append(" ms").append(ls); builder.append("Total number of iterations: ").append(getIterations()).append(ls); // Take solutions int solution=1; @@ -216,7 +216,6 @@ public static > List recoverActionPath(N node){ for(N n : node.path()){ if (n.action() != null) actions.add(n.action()); } - Collections.reverse(actions); return actions; } } diff --git a/hipster-core/src/main/java/es/usc/citius/hipster/algorithm/BellmanFord.java b/hipster-core/src/main/java/es/usc/citius/hipster/algorithm/BellmanFord.java old mode 100644 new mode 100755 index ac520a2..9fd2d74 --- a/hipster-core/src/main/java/es/usc/citius/hipster/algorithm/BellmanFord.java +++ b/hipster-core/src/main/java/es/usc/citius/hipster/algorithm/BellmanFord.java @@ -47,6 +47,7 @@ public class BellmanFord,N extends CostNode> extends Algorithm { protected N initialNode; protected NodeExpander nodeExpander; + protected boolean checkNegativeCycles = true; public BellmanFord(N initialNode, NodeExpander nodeExpander) { this.initialNode = initialNode; @@ -104,6 +105,9 @@ public boolean hasNext() { public N next() { // Take the next node N currentNode = dequeue(); + if (checkNegativeCycles && currentNode.pathSize() > explored.size()){ + throw new NegativeCycleException(); + } for (N successor : nodeExpander.expand(currentNode)) { // Check if there is any improvement in the old cost N previousNode = this.explored.get(successor.state()); @@ -138,7 +142,7 @@ public SearchResult search(Predicate condition){ while(it.hasNext()){ iteration++; currentNode = it.next(); - if (condition.apply(currentNode)) { + if (goalNode == null && condition.apply(currentNode)) { goalNode = currentNode; } } @@ -155,4 +159,28 @@ public SearchResult search(Predicate condition){ public Iterator iterator() { return new Iterator(); } + + public N getInitialNode() { + return initialNode; + } + + public void setInitialNode(N initialNode) { + this.initialNode = initialNode; + } + + public NodeExpander getNodeExpander() { + return nodeExpander; + } + + public void setNodeExpander(NodeExpander nodeExpander) { + this.nodeExpander = nodeExpander; + } + + public boolean isCheckNegativeCycles() { + return checkNegativeCycles; + } + + public void setCheckNegativeCycles(boolean checkNegativeCycles) { + this.checkNegativeCycles = checkNegativeCycles; + } } diff --git a/hipster-core/src/main/java/es/usc/citius/hipster/algorithm/BreadthFirstSearch.java b/hipster-core/src/main/java/es/usc/citius/hipster/algorithm/BreadthFirstSearch.java old mode 100644 new mode 100755 diff --git a/hipster-core/src/main/java/es/usc/citius/hipster/algorithm/DepthFirstSearch.java b/hipster-core/src/main/java/es/usc/citius/hipster/algorithm/DepthFirstSearch.java old mode 100644 new mode 100755 index 238dc3e..aeb1462 --- a/hipster-core/src/main/java/es/usc/citius/hipster/algorithm/DepthFirstSearch.java +++ b/hipster-core/src/main/java/es/usc/citius/hipster/algorithm/DepthFirstSearch.java @@ -19,10 +19,7 @@ import es.usc.citius.hipster.model.Node; import es.usc.citius.hipster.model.function.NodeExpander; -import java.util.HashSet; -import java.util.Iterator; -import java.util.Set; -import java.util.Stack; +import java.util.*; /** *

@@ -90,13 +87,13 @@ public boolean isProcessed() { * DFS iterator used to expand always the deepest non-visited node. */ public class Iterator implements java.util.Iterator { - protected Stack stack = new Stack(); + protected Deque stack = new ArrayDeque(); protected StackFrameNode next; protected Set closed = new HashSet(); protected boolean graphSupport = true; protected Iterator(){ - this.stack.add(new StackFrameNode(initialNode)); + this.stack.addLast(new StackFrameNode(initialNode)); } @@ -155,14 +152,14 @@ protected StackFrameNode processNextNode(){ if (stack.isEmpty()) return null; - // Take current node in the stack but do not remove - StackFrameNode current = stack.peek(); + // Take last node in the stack but do not remove + StackFrameNode current = stack.peekLast(); // Find a successor if (current.successors.hasNext()){ N successor = current.successors.next(); // push the node (if not explored) if (!graphSupport || !closed.contains(successor.state())) { - stack.add(new StackFrameNode(successor)); + stack.addLast(new StackFrameNode(successor)); } return current; } else { @@ -170,15 +167,15 @@ protected StackFrameNode processNextNode(){ if (current.visited){ current.processed = true; } - return stack.pop(); + return stack.removeLast(); } } - public Stack getStack() { + public Deque getStack() { return stack; } - public void setStack(Stack stack) { + public void setStack(Deque stack) { this.stack = stack; } diff --git a/hipster-core/src/main/java/es/usc/citius/hipster/algorithm/DepthLimitedSearch.java b/hipster-core/src/main/java/es/usc/citius/hipster/algorithm/DepthLimitedSearch.java new file mode 100644 index 0000000..5e5fc15 --- /dev/null +++ b/hipster-core/src/main/java/es/usc/citius/hipster/algorithm/DepthLimitedSearch.java @@ -0,0 +1,131 @@ +package es.usc.citius.hipster.algorithm; + +import es.usc.citius.hipster.model.Node; +import es.usc.citius.hipster.model.function.NodeExpander; +import es.usc.citius.hipster.model.impl.UnweightedNode; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Stack; +/** + * Copyright 2015 Centro de Investigación en Tecnoloxías da Información (CITIUS), + * University of Santiago de Compostela (USC). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + *

+ * In computer science maximumDepth-limited search is an algorithm to explore the vertices of a graph. + * It is a modification of maximumDepth-first search and is used for example in the iterative deepening + * maximumDepth-first search algorithm. + *

+ * + * For more information see
this article of the Wikipedia about DLS. + * + * @author Gabriella Zekany + */ +public class DepthLimitedSearch > extends Algorithm { + protected N initialNode; + protected N finalNode; + protected NodeExpander nodeExpander; + protected int maximumDepth; + protected int currentDepth; + protected ArrayList path; + + public DepthLimitedSearch(N initialNode, N finalNode, NodeExpander nodeExpander, int maximumDepth) { + this.initialNode = initialNode; + this.finalNode = finalNode; + this.nodeExpander = nodeExpander; + this.maximumDepth = maximumDepth; + this.currentDepth = 0; + this.path = new ArrayList<>(); + } + + public int getMaximumDepth() { + return this.maximumDepth; + } + + public int getCurrentDepth() { + return this.currentDepth; + } + + public ArrayList getPath() { + return path; + } + + public void incrementCurrentDepth() { + this.currentDepth ++; + } + + public boolean execute() { + Stack nodeStack = new Stack(); + StackNode tempStackNode = new StackNode(this.initialNode); + nodeStack.add(tempStackNode); + + while(!nodeStack.isEmpty()) { + if(this.currentDepth <= this.maximumDepth) { + StackNode temp = nodeStack.pop(); + if(!path.contains(temp.getNode()) && ((UnweightedNode) temp.getNode()).state().equals(((UnweightedNode)this.finalNode).state())){ + this.path.add((S) temp.getNode().state()); + return true; + } else { + this.path.add((S) temp.getNode().state()); + for(StackNode child : temp.getChildren()) { + if(!this.path.contains(child.getNode().state())) { + nodeStack.add(child); + } + } + this.incrementCurrentDepth(); + } + } else { + return false; + } + } + return false; + } + + private class StackNode { + private N node; + private java.util.Iterator children; + + public StackNode(N node) { + this.node = node; + this.children = nodeExpander.expand(node).iterator(); + } + + public N getNode() { + return node; + } + + public void setNode(N node) { + this.node = node; + } + + public List getChildren() { + ArrayList result = new ArrayList<>(); + while(this.children.hasNext()) { + StackNode temp = new StackNode(this.children.next()); + result.add(temp); + } + return result; + } + + public void setChildren(java.util.Iterator children) { + this.children = children; + } + } + + @Override + public Iterator iterator() { + return null; + } +} diff --git a/hipster-core/src/main/java/es/usc/citius/hipster/algorithm/Hipster.java b/hipster-core/src/main/java/es/usc/citius/hipster/algorithm/Hipster.java index 912d438..d2fe0a5 100644 --- a/hipster-core/src/main/java/es/usc/citius/hipster/algorithm/Hipster.java +++ b/hipster-core/src/main/java/es/usc/citius/hipster/algorithm/Hipster.java @@ -1,182 +1,305 @@ -/* - * Copyright 2014 CITIUS , University of Santiago de Compostela. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package es.usc.citius.hipster.algorithm; - - -import es.usc.citius.hipster.algorithm.localsearch.HillClimbing; -import es.usc.citius.hipster.model.CostNode; -import es.usc.citius.hipster.model.HeuristicNode; -import es.usc.citius.hipster.model.Node; -import es.usc.citius.hipster.model.function.impl.ADStarNodeExpander; -import es.usc.citius.hipster.model.function.impl.ADStarNodeFactory; -import es.usc.citius.hipster.model.impl.ADStarNodeImpl; -import es.usc.citius.hipster.model.problem.SearchComponents; -import es.usc.citius.hipster.model.problem.SearchProblem; - -import java.util.Collections; - -/** - * Util class to create algorithms easily. Each method expects a {@link es.usc.citius.hipster.model.problem.SearchProblem} - * with the components of the algorithm and returns an iterable algorithm that can be used to search a goal or iterate over - * the state space. A SearchProblem can be easily defined with the {@link es.usc.citius.hipster.model.problem.ProblemBuilder} class. - * - * @see es.usc.citius.hipster.model.problem.ProblemBuilder - * - * @author Pablo Rodríguez Mier <pablo.rodriguez.mier@usc.es> - * @author Adrián González Sieira <adrian.gonzalez@usc.es> - */ -public final class Hipster { - - private Hipster(){ - - } - - /** - * Instantiates a A* algorithm given a problem definition. - * - * @param components search problem definition with the components of the algorithm - * @param type of the actions - * @param type of the states - * @param type of the cost - * @param type of the nodes - * @return instance of {@link es.usc.citius.hipster.algorithm.AStar} for the problem definition - */ - public static , N extends HeuristicNode> AStar createAStar(SearchProblem components){ - return new AStar(components.getInitialNode(), components.getExpander()); - } - - /** - * Instantiates a Dijkstra algorithm (A* algorithm with no heuristic function) given a problem definition. - * - * @param components search problem definition with the components of the algorithm - * @param type of the actions - * @param type of the states - * @param type of the cost - * @param type of the nodes - * @return instance of {@link es.usc.citius.hipster.algorithm.AStar} for the problem definition, using no heuristic. - */ - public static , N extends HeuristicNode> AStar createDijkstra(SearchProblem components){ - //TODO: There is no difference with AStar. Actually if the NodeExpander uses heuristics, this "Dijkstra" impl works as the AStar. This should be changed! - return new AStar(components.getInitialNode(), components.getExpander()); - } - - /** - * Instantiates a Bellman Ford algorithm for a problem definition. - * - * @param components search problem definition with the components of the algorithm - * @param type of the actions - * @param type of the states - * @param type of the cost - * @param type of the nodes - * @return instance of {@link es.usc.citius.hipster.algorithm.BellmanFord} for the problem definition - */ - public static , N extends CostNode> BellmanFord createBellmanFord(SearchProblem components){ - return new BellmanFord(components.getInitialNode(), components.getExpander()); - } - - /** - * Instantiates Breadth First Search algorithm for a problem definition. - * - * @param components search problem definition with the components of the algorithm - * @param type of the actions - * @param type of the states - * @param type of the nodes - * @return instance of {@link es.usc.citius.hipster.algorithm.BreadthFirstSearch} for the problem definition - */ - public static > BreadthFirstSearch createBreadthFirstSearch(SearchProblem components){ - return new BreadthFirstSearch(components.getInitialNode(), components.getExpander()); - } - - /** - * Instantiates Depth First Search algorithm for a problem definition. - * - * @param components search problem definition with the components of the algorithm - * @param type of the actions - * @param type of the states - * @param type of the nodes - * @return instance of {@link es.usc.citius.hipster.algorithm.DepthFirstSearch} for the problem definition - */ - public static > DepthFirstSearch createDepthFirstSearch(SearchProblem components){ - return new DepthFirstSearch(components.getInitialNode(), components.getExpander()); - } - - /** - * Instantiates a IDA* algorithm given a problem definition. - * - * @param components search problem definition with the components of the algorithm - * @param type of the actions - * @param type of the states - * @param type of the cost - * @param type of the nodes - * @return instance of {@link es.usc.citius.hipster.algorithm.IDAStar} for the problem definition - */ - public static , N extends HeuristicNode> IDAStar createIDAStar(SearchProblem components){ - return new IDAStar(components.getInitialNode(), components.getExpander()); - } - - /** - * Instantiates a Hill Climbing algorithm given a problem definition. - * - * @param components search problem definition with the components of the algorithm - * @param enforced flag to use Enforced Hill Climbing instead of classic Hill Climbing algorithm - * @param type of the actions - * @param type of the states - * @param type of the cost - * @param type of the nodes - * @return instance of {@link es.usc.citius.hipster.algorithm.localsearch.HillClimbing} for the problem definition - */ - public static , N extends HeuristicNode> HillClimbing createHillClimbing(SearchProblem components, boolean enforced){ - return new HillClimbing(components.getInitialNode(), components.getExpander(), enforced); - } - - /** - * Instantiates a Multi-objective Label Setting algorithm given a problem definition. - * - * @param components search problem definition with the components of the algorithm - * @param type of the actions - * @param type of the states - * @param type of the cost - * @param type of the nodes - * @return instance of {@link es.usc.citius.hipster.algorithm.MultiobjectiveLS} for the problem definition - */ - public static , N extends HeuristicNode> MultiobjectiveLS createMultiobjectiveLS(SearchProblem components){ - return new MultiobjectiveLS(components.getInitialNode(), components.getExpander()); - } - - /** - * Instantiates a Anytime Dynamic A* algorithm given the search components. Search components can be obtained - * easily for graph-based problems using {@link es.usc.citius.hipster.util.graph.GraphSearchProblem}. - * - * @param components search components to be used by the algorithm - * @param type of the actions - * @param type of the states - * @param type of the cost - * @return instance of {@link es.usc.citius.hipster.algorithm.ADStarForward} for the search components - */ - public static > ADStarForward> createADStar(SearchComponents components){ - //node factory instantiation - ADStarNodeFactory factory = new ADStarNodeFactory(components); - //node expander instantiation - ADStarNodeExpander> expander = - new ADStarNodeExpander>(components, factory, 1.0); - //instantiate algorithm - return new ADStarForward( - components.getBegin(), - Collections.singleton(components.getGoal()), - expander); - } -} +/* + * Copyright 2014 CITIUS , University of Santiago de Compostela. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package es.usc.citius.hipster.algorithm; + +import es.usc.citius.hipster.algorithm.localsearch.AnnealingSearch; +import es.usc.citius.hipster.algorithm.localsearch.HillClimbing; +import es.usc.citius.hipster.algorithm.localsearch.AnnealingSearch.AcceptanceProbability; +import es.usc.citius.hipster.algorithm.localsearch.AnnealingSearch.SuccessorFinder; +import es.usc.citius.hipster.model.CostNode; +import es.usc.citius.hipster.model.HeuristicNode; +import es.usc.citius.hipster.model.Node; +import es.usc.citius.hipster.model.function.NodeExpander; +import es.usc.citius.hipster.model.function.impl.ADStarNodeExpander; +import es.usc.citius.hipster.model.function.impl.ADStarNodeFactory; +import es.usc.citius.hipster.model.impl.ADStarNodeImpl; +import es.usc.citius.hipster.model.problem.SearchComponents; +import es.usc.citius.hipster.model.problem.SearchProblem; + +import java.util.Collections; + +/** + * Util class to create algorithms easily. Each method expects a + * {@link es.usc.citius.hipster.model.problem.SearchProblem} with the components + * of the algorithm and returns an iterable algorithm that can be used to search + * a goal or iterate over the state space. A SearchProblem can be easily defined + * with the {@link es.usc.citius.hipster.model.problem.ProblemBuilder} class. + * + * @see es.usc.citius.hipster.model.problem.ProblemBuilder + * + * @author Pablo Rodríguez Mier < + * pablo.rodriguez.mier@usc + * .es> + * @author Adrián González Sieira < + * adrian.gonzalez@usc.es> + */ +public final class Hipster { + + private Hipster() { + + } + + /** + * Instantiates a A* algorithm given a problem definition. + * + * @param components + * search problem definition with the components of the algorithm + * @param + * type of the actions + * @param + * type of the states + * @param + * type of the cost + * @param + * type of the nodes + * @return instance of {@link es.usc.citius.hipster.algorithm.AStar} for the + * problem definition + */ + public static , N extends HeuristicNode> AStar createAStar( + SearchProblem components) { + return new AStar(components.getInitialNode(), components.getExpander()); + } + + /** + * Instantiates a Dijkstra algorithm (A* algorithm with no heuristic + * function) given a problem definition. + * + * @param components + * search problem definition with the components of the algorithm + * @param + * type of the actions + * @param + * type of the states + * @param + * type of the cost + * @param + * type of the nodes + * @return instance of {@link es.usc.citius.hipster.algorithm.AStar} for the + * problem definition, using no heuristic. + */ + public static , N extends HeuristicNode> AStar createDijkstra( + SearchProblem components) { + // TODO: There is no difference with AStar. Actually if the NodeExpander + // uses heuristics, this "Dijkstra" impl works as the AStar. This should + // be changed! + return new AStar(components.getInitialNode(), components.getExpander()); + } + + /** + * Instantiates a Bellman Ford algorithm for a problem definition. + * + * @param components + * search problem definition with the components of the algorithm + * @param + * type of the actions + * @param + * type of the states + * @param + * type of the cost + * @param + * type of the nodes + * @return instance of {@link es.usc.citius.hipster.algorithm.BellmanFord} + * for the problem definition + */ + public static , N extends CostNode> BellmanFord createBellmanFord( + SearchProblem components) { + return new BellmanFord(components.getInitialNode(), components.getExpander()); + } + + /** + * Instantiates Breadth First Search algorithm for a problem definition. + * + * @param components + * search problem definition with the components of the algorithm + * @param + * type of the actions + * @param + * type of the states + * @param + * type of the nodes + * @return instance of + * {@link es.usc.citius.hipster.algorithm.BreadthFirstSearch} for + * the problem definition + */ + public static > BreadthFirstSearch createBreadthFirstSearch( + SearchProblem components) { + return new BreadthFirstSearch(components.getInitialNode(), components.getExpander()); + } + + /** + * Instantiates Depth First Search algorithm for a problem definition. + * + * @param components + * search problem definition with the components of the algorithm + * @param + * type of the actions + * @param + * type of the states + * @param + * type of the nodes + * @return instance of + * {@link es.usc.citius.hipster.algorithm.DepthFirstSearch} for the + * problem definition + */ + public static > DepthFirstSearch createDepthFirstSearch( + SearchProblem components) { + return new DepthFirstSearch(components.getInitialNode(), components.getExpander()); + } + + /** + * Instantiates Depth Limited Search algorithm for a problem definition. + * + * @param components + * search problem definition with the components of the algorithm + * @param + * type of the actions + * @param + * type of the states + * @param + * type of the nodes + * @return instance of + * {@link es.usc.citius.hipster.algorithm.DepthFirstSearch} for the + * problem definition + */ + public static > DepthLimitedSearch createDepthLimitedSearch( + SearchProblem components, int depth) { + return new DepthLimitedSearch(components.getInitialNode(), components.getFinalNode(), + components.getExpander(), depth); + } + + /** + * Instantiates a IDA* algorithm given a problem definition. + * + * @param components + * search problem definition with the components of the algorithm + * @param + * type of the actions + * @param + * type of the states + * @param + * type of the cost + * @param + * type of the nodes + * @return instance of {@link es.usc.citius.hipster.algorithm.IDAStar} for + * the problem definition + */ + public static , N extends HeuristicNode> IDAStar createIDAStar( + SearchProblem components) { + return new IDAStar(components.getInitialNode(), components.getExpander()); + } + + /** + * Instantiates a Hill Climbing algorithm given a problem definition. + * + * @param components + * search problem definition with the components of the algorithm + * @param enforced + * flag to use Enforced Hill Climbing instead of classic Hill + * Climbing algorithm + * @param + * type of the actions + * @param + * type of the states + * @param + * type of the cost + * @param + * type of the nodes + * @return instance of + * {@link es.usc.citius.hipster.algorithm.localsearch.HillClimbing} + * for the problem definition + */ + public static , N extends HeuristicNode> HillClimbing createHillClimbing( + SearchProblem components, boolean enforced) { + return new HillClimbing(components.getInitialNode(), components.getExpander(), enforced); + } + + /** + * Instantiates an AnnealingSearch algorithm given a problem definition. + * + * @param components + * search problem definition with the components of the algorithm + * @param alpha + * coefficient of the geometric cooling schedule + * @param + * type of the actions + * @param + * type of the states + * @param + * type of the cost + * @param + * type of the nodes + * @return instance of + * {@link es.usc.citius.hipster.algorithm.localsearch.HillClimbing} + * for the problem definition + */ + public static > AnnealingSearch createAnnealingSearch( + SearchProblem components, Double alpha, Double minTemp, + AcceptanceProbability acceptanceProbability, SuccessorFinder successorFinder) { + return new AnnealingSearch(components.getInitialNode(), components.getExpander(), alpha, + minTemp, acceptanceProbability, successorFinder); + } + + /** + * Instantiates a Multi-objective Label Setting algorithm given a problem + * definition. + * + * @param components + * search problem definition with the components of the algorithm + * @param + * type of the actions + * @param + * type of the states + * @param + * type of the cost + * @param + * type of the nodes + * @return instance of + * {@link es.usc.citius.hipster.algorithm.MultiobjectiveLS} for the + * problem definition + */ + public static , N extends HeuristicNode> MultiobjectiveLS createMultiobjectiveLS( + SearchProblem components) { + return new MultiobjectiveLS(components.getInitialNode(), components.getExpander()); + } + + /** + * Instantiates a Anytime Dynamic A* algorithm given the search components. + * Search components can be obtained easily for graph-based problems using + * {@link es.usc.citius.hipster.util.graph.GraphSearchProblem}. + * + * @param components + * search components to be used by the algorithm + * @param + * type of the actions + * @param + * type of the states + * @param + * type of the cost + * @return instance of {@link es.usc.citius.hipster.algorithm.ADStarForward} + * for the search components + */ + public static > ADStarForward> createADStar( + SearchComponents components) { + // node factory instantiation + ADStarNodeFactory factory = new ADStarNodeFactory(components); + // node expander instantiation + ADStarNodeExpander> expander = new ADStarNodeExpander>( + components, factory, 1.0); + // instantiate algorithm + return new ADStarForward(components.getBegin(), Collections.singleton(components.getGoal()), expander); + } +} diff --git a/hipster-core/src/main/java/es/usc/citius/hipster/algorithm/IDAStar.java b/hipster-core/src/main/java/es/usc/citius/hipster/algorithm/IDAStar.java old mode 100644 new mode 100755 index 22f61a7..24f1483 --- a/hipster-core/src/main/java/es/usc/citius/hipster/algorithm/IDAStar.java +++ b/hipster-core/src/main/java/es/usc/citius/hipster/algorithm/IDAStar.java @@ -22,8 +22,6 @@ import es.usc.citius.hipster.model.HeuristicNode; import es.usc.citius.hipster.model.function.NodeExpander; -import java.util.Stack; - /** *

* Implementation of the IDA* algorithm. Similar to Iterative DFS but using heuristics to limit @@ -40,38 +38,18 @@ * @param type of the heuristic search node. * * @author Pablo Rodríguez Mier <pablo.rodriguez.mier@usc.es> + * @author Jennnnyz + * */ -public class IDAStar,N extends HeuristicNode> extends Algorithm { - - protected final N initialNode; - protected final NodeExpander expander; - +public class IDAStar,N extends HeuristicNode> extends DepthFirstSearch { + /** + * + * @param initialNode + * @param expander + */ public IDAStar(N initialNode, NodeExpander expander) { - this.initialNode = initialNode; - this.expander = expander; - } - - private class StackFrameNode { - // Iterable used to compute neighbors of the current node - java.util.Iterator successors; - // Current search node - N node; - // Boolean value to check if the node is still unvisited - // in the stack or not - boolean visited = false; - // Boolean to indicate that this node is fully processed - boolean processed = false; - - StackFrameNode(java.util.Iterator successors, N node) { - this.successors = successors; - this.node = node; - } - - StackFrameNode(N node) { - this.node = node; - this.successors = expander.expand(node).iterator(); - } + super(initialNode, expander); } /** @@ -80,51 +58,17 @@ private class StackFrameNode { * call to {@code iterator.next()} returns the next state after performing * backtracking. */ - public class Iterator implements java.util.Iterator { - protected Stack stack = new Stack(); + + public class Iterator extends DepthFirstSearch.Iterator { protected C fLimit; protected C minfLimit; protected int reinitialization = 0; - protected StackFrameNode next; protected Iterator(){ // Set initial bound + super(); fLimit = initialNode.getEstimation(); minfLimit = null; - this.stack.add(new StackFrameNode(initialNode)); - } - - @Override - public boolean hasNext() { - if (next == null){ - // Compute next - next = nextUnvisited(); - if (next == null) return false; - } - return true; - } - - @Override - public N next(){ - if (next != null){ - StackFrameNode e = next; - // Compute the next one - next = null; - // Return current node - return e.node; - } - // Compute next - StackFrameNode nextUnvisited = nextUnvisited(); - if (nextUnvisited!=null){ - return nextUnvisited.node; - } - return null; - - } - - @Override - public void remove() { - throw new UnsupportedOperationException(); } protected void updateMinFLimit(C currentFLimit){ @@ -137,6 +81,7 @@ protected void updateMinFLimit(C currentFLimit){ } } + @Override protected StackFrameNode nextUnvisited(){ StackFrameNode nextNode; do { @@ -148,7 +93,7 @@ protected StackFrameNode nextUnvisited(){ fLimit = minfLimit; reinitialization++; minfLimit = null; - stack.add(new StackFrameNode(initialNode)); + super.getStack().addLast(new StackFrameNode(initialNode)); nextNode = processNextNode(); } } @@ -160,7 +105,7 @@ protected StackFrameNode nextUnvisited(){ return nextNode; } - + @Override protected StackFrameNode processNextNode(){ // Get and process the current node. Cases: // 1 - empty stack, return null @@ -172,27 +117,27 @@ protected StackFrameNode processNextNode(){ // mark as visited, pop and return. // 1- If the stack is empty, change fLimit and reinitialize the search - if (stack.isEmpty()) return null; + if (super.getStack().isEmpty()) return null; // Take current node in the stack but do not remove - StackFrameNode current = stack.peek(); + StackFrameNode current = (StackFrameNode) super.stack.peekLast(); // 2 - Check if the current node exceeds the limit bound - C fCurrent = current.node.getScore(); + C fCurrent = current.getNode().getScore(); if (fCurrent.compareTo(fLimit)>0){ // Current node exceeds the limit bound, update minfLimit, pop and skip. updateMinFLimit(fCurrent); // Remove from stack current.processed = true; - return stack.pop(); + return (StackFrameNode) super.getStack().removeLast(); } // Find a successor - if (current.successors.hasNext()){ + if (current.getSuccessors().hasNext()){ // 3 - Node has at least one neighbor - N successor = current.successors.next(); + N successor = current.getSuccessors().next(); // push the node - stack.add(new StackFrameNode(successor)); + super.getStack().addLast(new StackFrameNode(successor)); return current; } else { @@ -200,18 +145,10 @@ protected StackFrameNode processNextNode(){ if (current.visited){ current.processed = true; } - return stack.pop(); + return (StackFrameNode) super.getStack().removeLast(); } } - public Stack getStack() { - return stack; - } - - public void setStack(Stack stack) { - this.stack = stack; - } - public C getfLimit() { return fLimit; } @@ -235,14 +172,6 @@ public int getReinitialization() { public void setReinitialization(int reinitialization) { this.reinitialization = reinitialization; } - - public StackFrameNode getNext() { - return next; - } - - public void setNext(StackFrameNode next) { - this.next = next; - } } @Override diff --git a/hipster-core/src/main/java/es/usc/citius/hipster/algorithm/MultiobjectiveLS.java b/hipster-core/src/main/java/es/usc/citius/hipster/algorithm/MultiobjectiveLS.java old mode 100644 new mode 100755 index a7d5247..bd36c7b --- a/hipster-core/src/main/java/es/usc/citius/hipster/algorithm/MultiobjectiveLS.java +++ b/hipster-core/src/main/java/es/usc/citius/hipster/algorithm/MultiobjectiveLS.java @@ -56,7 +56,7 @@ public MultiobjectiveLS(N initialNode, NodeExpander nodeExpander) { public class Iterator implements java.util.Iterator { protected Queue queue = new LinkedList(); public Map> nonDominated; - //auxiliar variable which stores an empty list to avoid nullable values in code + //auxiliary variable which stores an empty list to avoid nullable values in code private final Collection EMPTYLIST = new ArrayList(); protected Iterator(){ diff --git a/hipster-core/src/main/java/es/usc/citius/hipster/algorithm/NegativeCycleException.java b/hipster-core/src/main/java/es/usc/citius/hipster/algorithm/NegativeCycleException.java new file mode 100644 index 0000000..b012eb5 --- /dev/null +++ b/hipster-core/src/main/java/es/usc/citius/hipster/algorithm/NegativeCycleException.java @@ -0,0 +1,10 @@ +package es.usc.citius.hipster.algorithm; + + +public class NegativeCycleException extends RuntimeException { + private static final String message = "Existence of a negative cycle detected"; + + public NegativeCycleException() { + super(message); + } +} diff --git a/hipster-core/src/main/java/es/usc/citius/hipster/algorithm/localsearch/AnnealingSearch.java b/hipster-core/src/main/java/es/usc/citius/hipster/algorithm/localsearch/AnnealingSearch.java new file mode 100644 index 0000000..cb805ea --- /dev/null +++ b/hipster-core/src/main/java/es/usc/citius/hipster/algorithm/localsearch/AnnealingSearch.java @@ -0,0 +1,203 @@ +package es.usc.citius.hipster.algorithm.localsearch; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Queue; +import java.util.Random; + +import es.usc.citius.hipster.algorithm.Algorithm; +import es.usc.citius.hipster.model.HeuristicNode; +import es.usc.citius.hipster.model.Node; +import es.usc.citius.hipster.model.function.NodeExpander; + +/** + * Implementation of the simulated annealing search that is a probabilistic + * technique for approximating the global optimum of a given function. It starts + * the exploration from a random point as a global optimum and selects one of + * its neighbors with a neighboring function. The neighbor will become the new + * optimum if its associated cost is lower or if the acceptance probability + * function returns a probability greater than a random number. The probability + * function takes as an input the cost of the current selected node, the cost of + * its randomly selected neighbour and the current temperature. The higher the + * cost of the neighbour is or the lower the temperature is, the more unlikely + * it is that the neighbour becomes the new optimum. The process continues until + * the temperature is below a given threshold. The temperature decreases at each + * iteration according to a geometric cooling schedule that has two parameters + * alpha and temperature min. The main idea of this algorithm is to avoid to be + * "trapped" in a bad local optimum by exploring more deeply the state space by + * looking at states whose cost is not optimum but that may have interesting + * neighbours. A user can adjusted the algorithm by tuning the alpha coefficient + * (default 0.9) or the min temperature (0.00001) or by providing his own + * implementation of the acceptance probability function (default: exp((old + * score - new score) / current temperature)) or the neighbouring function + * (random selection by default). Note: costs are Double in this implementation + * and have no type parameters. + * + * see in + * Wikipedia and in + * annealing search for more details. + * + * @param + * class defining the action + * @param + * class defining the state + * @param + * class defining the cost, must implement + * {@link java.lang.Comparable} + * @param + * type of the nodes + * + * @author Christophe Moins < + * christophe.moins@yahoo.fr + * > + */ +public class AnnealingSearch> extends Algorithm { + + static final private Double DEFAULT_ALPHA = 0.9; + static final private Double DEFAULT_MIN_TEMP = 0.00001; + static final private Double START_TEMP = 1.; + + private N initialNode; + private Double alpha; + private Double minTemp; + private AcceptanceProbability acceptanceProbability; + private SuccessorFinder successorFinder; + // expander to find all the successors of a given node. + private NodeExpander nodeExpander; + + public AnnealingSearch(N initialNode, NodeExpander nodeExpander, Double alpha, Double minTemp, + AcceptanceProbability acceptanceProbability, SuccessorFinder successorFinder) { + if (initialNode == null) { + throw new IllegalArgumentException("Provide a valid initial node"); + } + this.initialNode = initialNode; + if (nodeExpander == null) { + throw new IllegalArgumentException("Provide a valid node expander"); + } + this.nodeExpander = nodeExpander; + if (alpha != null) { + if ((alpha <= 0.) || (alpha >= 1.0)) { + throw new IllegalArgumentException("alpha must be between 0. and 1."); + } + this.alpha = alpha; + } else { + this.alpha = DEFAULT_ALPHA; + } + if (minTemp != null) { + if ((minTemp < 0.) || (minTemp > 1.)) { + throw new IllegalArgumentException("Minimum temperature must be between 0. and 1."); + } + this.minTemp = minTemp; + } else { + this.minTemp = DEFAULT_MIN_TEMP; + } + if (acceptanceProbability != null) { + this.acceptanceProbability = acceptanceProbability; + } else { + this.acceptanceProbability = new AcceptanceProbability() { + @Override + public Double compute(Double oldScore, Double newScore, Double temp) { + return (newScore < oldScore ? 1 : Math.exp((oldScore - newScore) / temp)); + } + }; + } + if (successorFinder != null) { + this.successorFinder = successorFinder; + } else { + // default implementation of the successor: picks up a successor + // randomly + this.successorFinder = new SuccessorFinder() { + @Override + public N estimate(N node, NodeExpander nodeExpander) { + List successors = new ArrayList<>(); + // find a random successor + for (N successor : nodeExpander.expand(node)) { + successors.add(successor); + } + Random randIndGen = new Random(); + return successors.get(Math.abs(randIndGen.nextInt()) % successors.size()); + } + }; + } + } + + @Override + public ASIterator iterator() { + // TODO Auto-generated method stub + return new ASIterator(); + } + + public class ASIterator implements Iterator { + + private Queue queue = new LinkedList(); + private Double bestScore = null; + private Double curTemp = START_TEMP; + + private ASIterator() { + bestScore = initialNode.getEstimation(); + queue.add(initialNode); + } + + @Override + public boolean hasNext() { + return !queue.isEmpty(); + } + + @Override + public N next() { + N currentNode = this.queue.poll(); + if (curTemp > minTemp) { + N newNode = null; + // we add a loop to increase the effect of a change of alpha. + for (int i = 0; i < 100; i++) { + N randSuccessor = successorFinder.estimate(currentNode, nodeExpander); + Double score = randSuccessor.getScore(); + if (acceptanceProbability.compute(bestScore, score, curTemp) > Math.random()) { + newNode = randSuccessor; + bestScore = score; + } + } + if (newNode != null) { + queue.add(newNode); + } else { + queue.add(currentNode); + } + curTemp *= alpha; + } + return currentNode; + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + + } + } + + /** + * Interface to compute the acceptance probability. If the new score is less + * than the old score, 1 will be returned so that the node is selected. + * Otherwise, we compute a probability that will decrease when the newScore + * or the temperature increase. + * + */ + + public interface AcceptanceProbability { + Double compute(Double oldScore, Double newScore, Double temp); + } + + /** + * Interface to find the successor of a node. + * + * @param + */ + public interface SuccessorFinder> { + /** + * @param Node + * @return the successor of a node. + */ + N estimate(N node, NodeExpander nodeExpander); + } +} diff --git a/hipster-core/src/main/java/es/usc/citius/hipster/graph/GraphBuilder.java b/hipster-core/src/main/java/es/usc/citius/hipster/graph/GraphBuilder.java index 9a9476e..540246c 100644 --- a/hipster-core/src/main/java/es/usc/citius/hipster/graph/GraphBuilder.java +++ b/hipster-core/src/main/java/es/usc/citius/hipster/graph/GraphBuilder.java @@ -44,6 +44,48 @@ private Connection(V vertex1, V vertex2, E edge) { this.vertex2 = vertex2; this.edge = edge; } + + private Connection(V vertex1, V vertex2) { + this.vertex1 = vertex1; + this.vertex2 = vertex2; + this.edge = (E) new Object(); + } + + public V getVertex1() { + return vertex1; + } + + public void setVertex1(V vertex1) { + this.vertex1 = vertex1; + } + + public V getVertex2() { + return vertex2; + } + + public void setVertex2(V vertex2) { + this.vertex2 = vertex2; + } + + public E getEdge() { + return edge; + } + + public void setEdge(E edge) { + this.edge = edge; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + Connection that = (Connection) o; + + if (!vertex1.equals(that.vertex1)) return false; + return vertex2.equals(that.vertex2); + + } } private List connections = new LinkedList(); @@ -54,11 +96,16 @@ public static GraphBuilder create() { return new GraphBuilder(); } - public Vertex1 connect(V vertex) { return new Vertex1(vertex); } + public GraphBuilder connect(V vertex1, V vertex2) { + Vertex1 vertex = new Vertex1(vertex1); + vertex.to(vertex2); + return this; + } + public HipsterDirectedGraph createDirectedGraph() { HashBasedHipsterDirectedGraph graph = HashBasedHipsterDirectedGraph.create(); for (Connection c : connections) { @@ -114,10 +161,18 @@ public class Vertex2 { private Vertex2(V vertex) { this.vertex2 = vertex; + connections.add(new Connection(vertex1, vertex2)); } public GraphBuilder withEdge(E edge) { - connections.add(new Connection(vertex1, vertex2, edge)); + Connection connection = new Connection(vertex1, vertex2); + int connectionIndex = connections.indexOf(connection); + if(connectionIndex != -1 ) { + connections.get(connectionIndex).setEdge(edge); + } else { + connection.setEdge(edge); + connections.add(connection); + } return GraphBuilder.this; } } diff --git a/hipster-core/src/main/java/es/usc/citius/hipster/graph/GraphSearchProblem.java b/hipster-core/src/main/java/es/usc/citius/hipster/graph/GraphSearchProblem.java index 8cf7c6a..a7dfa50 100644 --- a/hipster-core/src/main/java/es/usc/citius/hipster/graph/GraphSearchProblem.java +++ b/hipster-core/src/main/java/es/usc/citius/hipster/graph/GraphSearchProblem.java @@ -150,7 +150,7 @@ public C evaluate(Transition transition) { public SearchProblem> build() { return ProblemBuilder.create() - .initialState(fromVertex) + .initialState(fromVertex, toVertex) .defineProblemWithExplicitActions() .useTransitionFunction(tf) .build(); diff --git a/hipster-core/src/main/java/es/usc/citius/hipster/graph/HashBasedHipsterDirectedGraph.java b/hipster-core/src/main/java/es/usc/citius/hipster/graph/HashBasedHipsterDirectedGraph.java index 75bbce6..6e641e9 100644 --- a/hipster-core/src/main/java/es/usc/citius/hipster/graph/HashBasedHipsterDirectedGraph.java +++ b/hipster-core/src/main/java/es/usc/citius/hipster/graph/HashBasedHipsterDirectedGraph.java @@ -30,7 +30,7 @@ public class HashBasedHipsterDirectedGraph extends HashBasedHipsterGraph buildEdge(V v1, V v2, E value) { + public GraphEdge buildEdge(V v1, V v2, E value) { return new DirectedEdge(v1, v2, value); } diff --git a/hipster-core/src/main/java/es/usc/citius/hipster/graph/HashBasedHipsterGraph.java b/hipster-core/src/main/java/es/usc/citius/hipster/graph/HashBasedHipsterGraph.java index 102925e..e39b57f 100644 --- a/hipster-core/src/main/java/es/usc/citius/hipster/graph/HashBasedHipsterGraph.java +++ b/hipster-core/src/main/java/es/usc/citius/hipster/graph/HashBasedHipsterGraph.java @@ -98,7 +98,7 @@ public GraphEdge connect(V v1, V v2, E value){ return edge; } - protected GraphEdge buildEdge(V v1, V v2, E value){ + public GraphEdge buildEdge(V v1, V v2, E value){ return new UndirectedEdge(v1, v2, value); } diff --git a/hipster-core/src/main/java/es/usc/citius/hipster/model/AbstractNode.java b/hipster-core/src/main/java/es/usc/citius/hipster/model/AbstractNode.java index fa60d3d..404e843 100644 --- a/hipster-core/src/main/java/es/usc/citius/hipster/model/AbstractNode.java +++ b/hipster-core/src/main/java/es/usc/citius/hipster/model/AbstractNode.java @@ -36,6 +36,7 @@ public class AbstractNode> implements Node path() { return path; } + @Override + public int pathSize() { + return pathSize; + } + @Override public N previousNode() { return this.previousNode; diff --git a/hipster-core/src/main/java/es/usc/citius/hipster/model/Node.java b/hipster-core/src/main/java/es/usc/citius/hipster/model/Node.java index 61ad7d4..a2cc519 100644 --- a/hipster-core/src/main/java/es/usc/citius/hipster/model/Node.java +++ b/hipster-core/src/main/java/es/usc/citius/hipster/model/Node.java @@ -41,6 +41,13 @@ public interface Node> { */ List path(); + /** + * Returns the length of the path from the start to the current node. Note that pathSize() == path().size() + * but this method provides a fast way to obtain the length of the path without tracking back the nodes of the path. + * @return length of the path from the initial node to this node. + */ + int pathSize(); + /** * Returns the previous node to the current. * diff --git a/hipster-core/src/main/java/es/usc/citius/hipster/model/problem/ProblemBuilder.java b/hipster-core/src/main/java/es/usc/citius/hipster/model/problem/ProblemBuilder.java index c62bc31..bf05034 100644 --- a/hipster-core/src/main/java/es/usc/citius/hipster/model/problem/ProblemBuilder.java +++ b/hipster-core/src/main/java/es/usc/citius/hipster/model/problem/ProblemBuilder.java @@ -45,9 +45,16 @@ private Wizard(){} */ public static final class ActionState { private final S initialState; + private final S finalState; public ActionState(S initialState) { this.initialState = initialState; + this.finalState = null; + } + + public ActionState(S initialState, S finalState) { + this.initialState = initialState; + this.finalState = finalState; } /** @@ -175,8 +182,9 @@ public UnweightedNode makeNode(UnweightedNode fromNode, Transition initialNode = factory.makeNode(null, Transition.create(null, null, initialState)); + UnweightedNode finalNode = factory.makeNode(null, Transition.create(null, null, finalState)); NodeExpander> nodeExpander = new LazyNodeExpander>(tf, factory); - return new SearchProblem>(initialNode, nodeExpander); + return new SearchProblem>(initialNode, finalNode, nodeExpander); } /** @@ -255,6 +263,10 @@ public ActionState initialState(S initialState){ return new ActionState(initialState); } + public ActionState initialState(S initialState, S finalState){ + return new ActionState(initialState, finalState); + } + } /** diff --git a/hipster-core/src/main/java/es/usc/citius/hipster/model/problem/SearchProblem.java b/hipster-core/src/main/java/es/usc/citius/hipster/model/problem/SearchProblem.java index 07c1995..d6f23ce 100644 --- a/hipster-core/src/main/java/es/usc/citius/hipster/model/problem/SearchProblem.java +++ b/hipster-core/src/main/java/es/usc/citius/hipster/model/problem/SearchProblem.java @@ -12,10 +12,18 @@ */ public class SearchProblem> { private N initialNode; + private N finalNode; private NodeExpander expander; public SearchProblem(N initialNode, NodeExpander expander) { this.initialNode = initialNode; + this.finalNode = null; + this.expander = expander; + } + + public SearchProblem(N initialNode, N finalNode, NodeExpander expander) { + this.initialNode = initialNode; + this.finalNode = finalNode; this.expander = expander; } @@ -26,4 +34,12 @@ public N getInitialNode() { public NodeExpander getExpander() { return expander; } + + public N getFinalNode() { + return finalNode; + } + + public void setFinalNode(N finalNode) { + this.finalNode = finalNode; + } } diff --git a/hipster-core/src/main/java/es/usc/citius/hipster/util/F.java b/hipster-core/src/main/java/es/usc/citius/hipster/util/F.java index ccc550e..c1556c0 100644 --- a/hipster-core/src/main/java/es/usc/citius/hipster/util/F.java +++ b/hipster-core/src/main/java/es/usc/citius/hipster/util/F.java @@ -16,6 +16,8 @@ */ public final class F { + private F() {} + public static Iterable map(final Iterable it, final Function mapf){ return new Iterable() { @Override diff --git a/hipster-core/src/main/java/es/usc/citius/hipster/util/Iterators.java b/hipster-core/src/main/java/es/usc/citius/hipster/util/Iterators.java index a489792..35f2195 100644 --- a/hipster-core/src/main/java/es/usc/citius/hipster/util/Iterators.java +++ b/hipster-core/src/main/java/es/usc/citius/hipster/util/Iterators.java @@ -23,12 +23,16 @@ public boolean hasNext() { @Override public E next() { + E next; if (current != null) { - E next = current; + next = current; current = null; return next; + } else { + next = computeNext(); + if (next == null) throw new NoSuchElementException("next"); + return next; } - return computeNext(); } @Override diff --git a/hipster-core/src/main/java/es/usc/citius/hipster/util/examples/maze/Maze2D.java b/hipster-core/src/main/java/es/usc/citius/hipster/util/examples/maze/Maze2D.java index 4ea198a..68a8117 100644 --- a/hipster-core/src/main/java/es/usc/citius/hipster/util/examples/maze/Maze2D.java +++ b/hipster-core/src/main/java/es/usc/citius/hipster/util/examples/maze/Maze2D.java @@ -371,7 +371,7 @@ public boolean validLocation(Point loc) { * @return true if the point is in the maze. */ public boolean pointInBounds(Point loc) { - return loc.x < this.columns && loc.y < this.rows; + return loc.x >= 0 && loc.x < this.columns && loc.y >= 0 && loc.y < this.rows; } /** diff --git a/hipster-core/src/test/java/es/usc/citius/hipster/util/graph/GraphBuilderTest.java b/hipster-core/src/test/java/es/usc/citius/hipster/util/graph/GraphBuilderTest.java index 7b37737..fdf622e 100644 --- a/hipster-core/src/test/java/es/usc/citius/hipster/util/graph/GraphBuilderTest.java +++ b/hipster-core/src/test/java/es/usc/citius/hipster/util/graph/GraphBuilderTest.java @@ -19,6 +19,7 @@ import es.usc.citius.hipster.graph.GraphBuilder; import es.usc.citius.hipster.graph.GraphEdge; +import es.usc.citius.hipster.graph.HashBasedHipsterDirectedGraph; import es.usc.citius.hipster.graph.HipsterDirectedGraph; import org.junit.BeforeClass; import org.junit.Test; @@ -31,6 +32,7 @@ public class GraphBuilderTest { private static HipsterDirectedGraph testGraph; + private static HipsterDirectedGraph testGraph2; @BeforeClass public static void setUp() throws Exception { @@ -44,55 +46,113 @@ public static void setUp() throws Exception { .connect("D").to("F").withEdge(11d) .connect("E").to("D").withEdge(4d) .createDirectedGraph(); + + testGraph2 = GraphBuilder.create() + .connect("A","B") + .connect("A","C") + .connect("B","C") + .connect("B","D") + .connect("C","E") + .connect("D","F") + .connect("E","D") + .createDirectedGraph(); } @Test public void testVertices() { Set vertices = new HashSet(Arrays.asList("A","B","C","D","E","F")); assertEquals(vertices, testGraph.vertices()); + assertEquals(vertices, testGraph2.vertices()); } @Test public void testEdges() { Set expectedValues = new HashSet(Arrays.asList(4d, 2d, 5d, 10d, 3d, 11d, 4d)); - Set values = new HashSet(); - int count = 0; + Set valuesGraph1 = new HashSet(); + Set valuesGraph2 = new HashSet(); + int countEdges1 = 0; + int countEdges2 = 0; for(GraphEdge edge : testGraph.edges()){ - values.add(edge.getEdgeValue()); - count++; + valuesGraph1.add(edge.getEdgeValue()); + countEdges1++; + } + + for(GraphEdge edge : testGraph2.edges()){ + valuesGraph2.add(edge.getEdgeValue()); + countEdges2++; } - assertEquals(7, count); - assertEquals(expectedValues, values); + assertEquals(7, countEdges1); + assertEquals(expectedValues, valuesGraph1); + assertEquals(7, countEdges2); } @Test public void testIncomingEdges(){ - Set> edges = new HashSet>(); + Set> edgesGraph1 = new HashSet>(); + Set> edgesGraph2 = new HashSet>(); for(GraphEdge current : testGraph.incomingEdgesOf("D")){ - edges.add(current); + edgesGraph1.add(current); + } + for(GraphEdge current : testGraph2.incomingEdgesOf("D")){ + edgesGraph2.add(current); } Set values = new HashSet(); - for(GraphEdge e : edges){ + for(GraphEdge e : edgesGraph1){ values.add(e.getEdgeValue()); } - assertEquals(2, edges.size()); + assertEquals(2, edgesGraph1.size()); assertEquals(values, new HashSet(Arrays.asList(4.0d, 10.0d))); + assertEquals(2, edgesGraph2.size()); } @Test public void testOutgoingEdges(){ - Set> edges = new HashSet>(); + Set> edgesGraph1 = new HashSet>(); + Set> edgesGraph2 = new HashSet>(); for(GraphEdge current : testGraph.outgoingEdgesOf("B")){ - edges.add(current); + edgesGraph1.add(current); + } + for(GraphEdge current : testGraph2.outgoingEdgesOf("B")){ + edgesGraph2.add(current); } Set values = new HashSet(); - for(GraphEdge e : edges){ + for(GraphEdge e : edgesGraph1){ values.add(e.getEdgeValue()); } - assertEquals(2, edges.size()); + assertEquals(2, edgesGraph1.size()); assertEquals(values, new HashSet(Arrays.asList(5.0d, 10.0d))); + assertEquals(2, edgesGraph2.size()); } + @Test + public void testEdgeSetter() { + Set> edgesGraph = new HashSet>(); + Set expectedValues = new HashSet(Arrays.asList(4d, 2d, 5d, 10d, 3d, 11d, 4d)); + Set valuesGraph = new HashSet(); + Set values = new HashSet(); + for(GraphEdge current : testGraph2.outgoingEdgesOf("B")){ + edgesGraph.add(current); + } + for(GraphEdge e : edgesGraph){ + values.add(e.getEdgeValue()); + } + assertEquals(2, edgesGraph.size()); + + ((HashBasedHipsterDirectedGraph) testGraph2).buildEdge("A", "B", 4d); + ((HashBasedHipsterDirectedGraph) testGraph2).buildEdge("A", "C", 2d); + ((HashBasedHipsterDirectedGraph) testGraph2).buildEdge("B", "C", 5d); + ((HashBasedHipsterDirectedGraph) testGraph2).buildEdge("B", "D", 10d); + ((HashBasedHipsterDirectedGraph) testGraph2).buildEdge("C", "E", 3d); + ((HashBasedHipsterDirectedGraph) testGraph2).buildEdge("D", "F", 11d); + ((HashBasedHipsterDirectedGraph) testGraph2).buildEdge("E", "D", 4d); + + for(GraphEdge edge : testGraph.edges()){ + valuesGraph.add(edge.getEdgeValue()); + } + assertEquals(expectedValues, valuesGraph); + + + } } diff --git a/hipster-core/src/test/java/es/usc/citius/lab/hipster/algorithm/BellmanFordTest.java b/hipster-core/src/test/java/es/usc/citius/lab/hipster/algorithm/BellmanFordTest.java index 6dc4dfb..bc8f19f 100644 --- a/hipster-core/src/test/java/es/usc/citius/lab/hipster/algorithm/BellmanFordTest.java +++ b/hipster-core/src/test/java/es/usc/citius/lab/hipster/algorithm/BellmanFordTest.java @@ -16,26 +16,79 @@ package es.usc.citius.lab.hipster.algorithm; +import es.usc.citius.hipster.algorithm.Algorithm; +import es.usc.citius.hipster.algorithm.Hipster; +import es.usc.citius.hipster.algorithm.NegativeCycleException; import es.usc.citius.hipster.graph.GraphBuilder; +import es.usc.citius.hipster.graph.GraphSearchProblem; +import es.usc.citius.hipster.graph.HashBasedHipsterGraph; import es.usc.citius.hipster.graph.HipsterDirectedGraph; +import es.usc.citius.hipster.model.impl.WeightedNode; import org.junit.Test; +import java.util.Arrays; +import java.util.List; + +import static org.junit.Assert.assertEquals; + /** * @author Pablo Rodríguez Mier <pablo.rodriguez.mier@usc.es> */ public class BellmanFordTest { - @Test + @Test(expected = NegativeCycleException.class) public void negativeCycleTest(){ - // Create a graph with negative cycles - HipsterDirectedGraph graph = - GraphBuilder.create() - .connect("A").to("B").withEdge(1d) - .connect("B").to("C").withEdge(1d) - .connect("C").to("B").withEdge(-2d) - .connect("C").to("D").withEdge(1d) - .buildDirectedGraph(); - //TODO; Complete test - //System.out.println(Hipster.createBellmanFord(GraphSearchProblem.from("A").to("D").in(graph)).search()); + // Create a simple graph with negative cycles + HipsterDirectedGraph g = + GraphBuilder.create() + .connect(1).to(2).withEdge(1) + .connect(2).to(3).withEdge(1) + .connect(3).to(1).withEdge(-3) + .connect(3).to(4).withEdge(2) + .createDirectedGraph(); + + // Test search + Hipster.createBellmanFord(GraphSearchProblem.startingFrom(1).in(g).takeCostsFromEdges().build()).search(4); + } + + @Test + public void negativeWeightedGraphTest(){ + HipsterDirectedGraph g = + GraphBuilder.create() + .connect("s").to("A").withEdge(5) + .connect("s").to("C").withEdge(-2) + .connect("A").to("B").withEdge(1) + .connect("B").to("C").withEdge(2) + .connect("B").to("t").withEdge(3) + .connect("B").to("D").withEdge(7) + .connect("C").to("A").withEdge(2) + .connect("D").to("C").withEdge(3) + .connect("D").to("t").withEdge(10) + .createDirectedGraph(); + // Test search + Algorithm>.SearchResult result = + Hipster.createBellmanFord(GraphSearchProblem.startingFrom("s").in(g).takeCostsFromEdges().build()) + .search("t"); + + List path = result.getOptimalPaths().get(0); + int cost = result.getGoalNode().getCost().intValue(); + + assertEquals(Arrays.asList("s", "C", "A", "B", "t"), path); + assertEquals(4, cost); } + + + + public static HashBasedHipsterGraph completeRandomGraph(int vertices){ + HashBasedHipsterGraph graph = new HashBasedHipsterGraph<>(); + for(int i=0; i tree = + GraphBuilder.create() + .connect("A").to("B").withEdge("0") + .connect("A").to("C").withEdge("0") + .connect("A").to("D").withEdge("0") + .connect("A").to("E").withEdge("0") + .connect("C").to("F").withEdge("0") + .connect("C").to("G").withEdge("0") + .connect("G").to("L").withEdge("0") + .connect("L").to("M").withEdge("0") + .connect("L").to("N").withEdge("0") + .connect("L").to("O").withEdge("0") + .connect("N").to("P").withEdge("0") + .connect("D").to("H").withEdge("0") + .connect("E").to("I").withEdge("0") + .connect("E").to("J").withEdge("0") + .connect("E").to("K").withEdge("0") + .createDirectedGraph(); + + DepthLimitedSearch dps = + Hipster.createDepthLimitedSearch(GraphSearchProblem.startingFrom("A").goalAt("O").in(tree).build(), 3); + boolean result = dps.execute(); + assertEquals(3, dps.getMaximumDepth()); + assertEquals(false, result); + } + + @Test + public void testTreeStructureFound(){ + HipsterDirectedGraph tree = + GraphBuilder.create() + .connect("A").to("B").withEdge("0") + .connect("A").to("C").withEdge("0") + .connect("B").to("D").withEdge("0") + .connect("B").to("E").withEdge("0") + .connect("B").to("F").withEdge("0") + .connect("D").to("G").withEdge("0") + .connect("D").to("H").withEdge("0") + .connect("H").to("I").withEdge("0") + .connect("H").to("J").withEdge("0") + .connect("E").to("K").withEdge("0") + .connect("F").to("L").withEdge("0") + .connect("F").to("M").withEdge("0") + .connect("L").to("N").withEdge("0") + .connect("C").to("O").withEdge("0") + .connect("C").to("P").withEdge("0") + .connect("C").to("Q").withEdge("0") + .connect("P").to("S").withEdge("0") + .connect("P").to("T").withEdge("0") + .connect("P").to("U").withEdge("0") + .connect("S").to("V").withEdge("0") + .connect("S").to("W").withEdge("0") + .connect("S").to("X").withEdge("0") + .connect("Q").to("Y").withEdge("0") + .createDirectedGraph(); + + List pathDps1 = Arrays.asList("A", "C", "Q", "Y", "P", "U", "T", "S", "X", "W", "V", "O", "B", "F", "M", "L", "N"); + DepthLimitedSearch dps1 = + Hipster.createDepthLimitedSearch(GraphSearchProblem.startingFrom("A").goalAt("N").in(tree).build(), 20); + boolean result1 = dps1.execute(); + assertEquals(true, result1); + assertEquals(16, dps1.getCurrentDepth()); + assertEquals(pathDps1, dps1.getPath()); + + List pathDps2 = Arrays.asList("F", "M", "L", "N"); + DepthLimitedSearch dps2 = + Hipster.createDepthLimitedSearch(GraphSearchProblem.startingFrom("F").goalAt("N").in(tree).build(), 5); + boolean result2 = dps2.execute(); + assertEquals(true, result2); + assertEquals(3, dps2.getCurrentDepth()); + assertEquals(pathDps2, dps2.getPath()); + + List pathDps3 = Arrays.asList("S", "X", "W"); + DepthLimitedSearch dps3 = + Hipster.createDepthLimitedSearch(GraphSearchProblem.startingFrom("S").goalAt("W").in(tree).build(), 2); + boolean result3 = dps3.execute(); + assertEquals(true, result3); + assertEquals(2, dps3.getCurrentDepth()); + assertEquals(pathDps3, dps3.getPath()); + } + + @Test + public void testGraphFound() { + HipsterDirectedGraph graph = + GraphBuilder.create() + .connect("A").to("B").withEdge("1") + .connect("A").to("C").withEdge("2") + .connect("B").to("D").withEdge("3") + .connect("B").to("E").withEdge("4") + .connect("E").to("C").withEdge("5") + .connect("C").to("A").withEdge("6") + .createDirectedGraph(); + List path = Arrays.asList("A","C","B","E"); + DepthLimitedSearch dps1 = + Hipster.createDepthLimitedSearch(GraphSearchProblem.startingFrom("A").goalAt("E").in(graph).build(), 6); + boolean result1 = dps1.execute(); + assertEquals(true, result1); + assertEquals(3, dps1.getCurrentDepth()); + assertEquals(path, dps1.getPath()); + } +} diff --git a/hipster-core/src/test/java/es/usc/citius/lab/hipster/collection/HashQueueTest.java b/hipster-core/src/test/java/es/usc/citius/lab/hipster/collection/HashQueueTest.java index b117033..fa59a60 100644 --- a/hipster-core/src/test/java/es/usc/citius/lab/hipster/collection/HashQueueTest.java +++ b/hipster-core/src/test/java/es/usc/citius/lab/hipster/collection/HashQueueTest.java @@ -21,7 +21,7 @@ import org.junit.Test; import java.util.Iterator; -;import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; diff --git a/hipster-core/src/test/java/es/usc/citius/lab/hipster/maze/Maze2DTest.java b/hipster-core/src/test/java/es/usc/citius/lab/hipster/maze/Maze2DTest.java index 022117f..1d1ba09 100644 --- a/hipster-core/src/test/java/es/usc/citius/lab/hipster/maze/Maze2DTest.java +++ b/hipster-core/src/test/java/es/usc/citius/lab/hipster/maze/Maze2DTest.java @@ -26,6 +26,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertFalse; public class Maze2DTest { @@ -133,4 +134,21 @@ public void testObstacles() { assertTrue(maze.diff(new Maze2D(refMaze)).isEmpty()); assertTrue(!maze.diff(goal).isEmpty()); } + + @Test + public void testPointInBounds() { + Maze2D maze = new Maze2D(testMaze); + assertFalse(maze.pointInBounds(new Point(-1,0))); + assertFalse(maze.pointInBounds(new Point(0,-1))); + + int colLength = maze.getMaze()[0].length; + int rowLength = maze.getMaze().length; + assertFalse(maze.pointInBounds(new Point(colLength,0))); + assertFalse(maze.pointInBounds(new Point(0,rowLength))); + + assertTrue(maze.pointInBounds(new Point(0,0))); + assertTrue(maze.pointInBounds(new Point(0,0))); + assertTrue(maze.pointInBounds(new Point(colLength-1,0))); + assertTrue(maze.pointInBounds(new Point(0,rowLength-1))); + } } diff --git a/hipster-examples/pom.xml b/hipster-examples/pom.xml index 188f85f..7208490 100644 --- a/hipster-examples/pom.xml +++ b/hipster-examples/pom.xml @@ -21,7 +21,7 @@ es.usc.citius.hipster hipster-pom - 1.0.0-rc2 + 1.0.0-SNAPSHOT 4.0.0 hipster-examples diff --git a/hipster-examples/src/main/java/es/usc/citius/hipster/examples/EightQueensProblemExampleWithAnnealingSearch.java b/hipster-examples/src/main/java/es/usc/citius/hipster/examples/EightQueensProblemExampleWithAnnealingSearch.java new file mode 100644 index 0000000..a8c1fcd --- /dev/null +++ b/hipster-examples/src/main/java/es/usc/citius/hipster/examples/EightQueensProblemExampleWithAnnealingSearch.java @@ -0,0 +1,112 @@ +/* + * Copyright 2014 CITIUS , University of Santiago de Compostela. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package es.usc.citius.hipster.examples; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +import es.usc.citius.hipster.algorithm.Hipster; +import es.usc.citius.hipster.examples.problem.NQueens; +import es.usc.citius.hipster.model.Transition; +import es.usc.citius.hipster.model.function.CostFunction; +import es.usc.citius.hipster.model.function.HeuristicFunction; +import es.usc.citius.hipster.model.function.impl.StateTransitionFunction; +import es.usc.citius.hipster.model.impl.WeightedNode; +import es.usc.citius.hipster.model.problem.ProblemBuilder; +import es.usc.citius.hipster.model.problem.SearchProblem; +import es.usc.citius.hipster.util.Predicate; + +/** + * Example using the N-Queens problem (size 8x8) solved using the Annealing search. + * + * This example is a search problem with no explicit actions, only using a + * transition function which generates from a state a set of + * successor states. The cost function in this case is constant, and + * we assume it has no cost to move a queen. As heuristic for this problem + * we use the number of attacked queens. These components are defined + * as the same time the problem is being built (using a + * {@link es.usc.citius.hipster.model.problem.ProblemBuilder}. + * + * + * @see {@link es.usc.citius.hipster.examples.problem.NQueens} + * + * @author Pablo Rodríguez Mier <pablo.rodriguez.mier@usc.es> + * @author Adrián González Sieira <adrian.gonzalez@usc.es> + */ +public class EightQueensProblemExampleWithAnnealingSearch { + + public static void main(String[] args) { + // Solve the 8-Queen problem with Hill Climbing and Enforced Hill Climbing + final int size = 8; + //search problem definition, here we define also + //the transition function between states + //and the cost (always 0) + //and heuristic function (number of attacked queens) + SearchProblem> p = ProblemBuilder.create() + .initialState(new NQueens(size)) + //problem without explicit actions, only a transition function is needed + .defineProblemWithoutActions() + .useTransitionFunction(new StateTransitionFunction() { + @Override + public Iterable successorsOf(NQueens state) { + // Generate all possible movements of one queen + // There are size*(size-1) available movements + Set states = new HashSet(); + for (int i = 0; i < size; i++) { + for (int j = 0; j < size; j++) { + // Change the queen at row i to column j + // If i is already in j, then do not add the state + if (state.getQueens()[i] != j) { + int[] queens = Arrays.copyOf(state.getQueens(), size); + queens[i] = j; + states.add(new NQueens(queens)); + } + } + } + return states; + } + }) + .useCostFunction(new CostFunction() { + @Override + public Double evaluate(Transition transition) { + // We assume that the cost of moving a queen is 0 + return 0d; + } + }) + .useHeuristicFunction(new HeuristicFunction() { + @Override + public Double estimate(NQueens state) { + return (double) state.attackedQueens(); + } + }).build(); + + //print some information about the search that will be executed + System.out.println("Random initial state (" + p.getInitialNode().state().attackedQueens() + " attacked queens):"); + System.out.println(p.getInitialNode().state()); + + System.out.println("Running 8-Queens problem with Annealing search and a custom goal test predicate"); + //To execute the algorithm we have two options: + // Option 1 - Run the algorithm until the predicate is satisfied (until we find a state with score 0, that is, no attacked queens) + System.out.println(Hipster.createAnnealingSearch(p, null,null,null,null).search(new Predicate>() { + @Override + public boolean apply(WeightedNode node) { + return node.getScore().equals(0d); + } + })); + } +} diff --git a/hipster-examples/src/main/java/es/usc/citius/hipster/examples/MazeShortestPathExample.java b/hipster-examples/src/main/java/es/usc/citius/hipster/examples/MazeShortestPathExample.java index 69009e1..d681555 100644 --- a/hipster-examples/src/main/java/es/usc/citius/hipster/examples/MazeShortestPathExample.java +++ b/hipster-examples/src/main/java/es/usc/citius/hipster/examples/MazeShortestPathExample.java @@ -20,6 +20,7 @@ import es.usc.citius.hipster.algorithm.Hipster; import es.usc.citius.hipster.model.Transition; import es.usc.citius.hipster.model.function.CostFunction; +import es.usc.citius.hipster.model.function.HeuristicFunction; import es.usc.citius.hipster.model.function.impl.StateTransitionFunction; import es.usc.citius.hipster.model.problem.ProblemBuilder; import es.usc.citius.hipster.model.problem.SearchProblem; @@ -60,8 +61,8 @@ public static void main(String[] args) throws InterruptedException { final Maze2D maze = example.getMaze(); // In order to search, we need at least the origin and the goal destination. // We can take these two points from the default maze: - Point origin = maze.getInitialLoc(); - Point goal = maze.getGoalLoc(); + final Point origin = maze.getInitialLoc(); + final Point goal = maze.getGoalLoc(); // The maze is a 2D map, where each tile defined by 2D coordinates x and y // can be empty or occupied by an obstacle. We have to define de transition // function that tells the algorithm which are the available movements from @@ -99,6 +100,16 @@ public Double evaluate(Transition transition) { return source.distance(destination); } }) + .useHeuristicFunction(new HeuristicFunction() { + // Give A* an estimate for the remaining distance to goal. Estimate need not be exact as long as + // it doesn't overestimate the remaining distance. + @Override + public Double estimate(Point state) { + // Provide Euclidean distance as simple estimate. + // Manhattan distance is not applicable as it doesn't support diagonal transitions. + return state.distance(goal); + } + }) .build(); //MazeSearch.printSearch(Hipster.createAStar(p).iterator(), maze); diff --git a/hipster-examples/src/main/java/es/usc/citius/hipster/examples/UndirectedGraphSearchExample.java b/hipster-examples/src/main/java/es/usc/citius/hipster/examples/UndirectedGraphSearchExample.java new file mode 100644 index 0000000..4040c5e --- /dev/null +++ b/hipster-examples/src/main/java/es/usc/citius/hipster/examples/UndirectedGraphSearchExample.java @@ -0,0 +1,63 @@ +package es.usc.citius.hipster.examples; + +import es.usc.citius.hipster.algorithm.Hipster; +import es.usc.citius.hipster.graph.GraphBuilder; +import es.usc.citius.hipster.graph.GraphSearchProblem; +import es.usc.citius.hipster.graph.HipsterDirectedGraph; +import es.usc.citius.hipster.graph.HipsterGraph; +import es.usc.citius.hipster.model.problem.SearchProblem; + +/** + * This example creates an undirected graph + * {@link es.usc.citius.hipster.graph.HipsterGraph} and performs a simple search + * using DFS. + * + * We use the GraphSearchProblem util + * {@link es.usc.citius.hipster.model.problem.SearchProblem} to create the + * problem. The initial state (since we are dealing with graphs, this means + * initial node) is set using this util, as well as other aspects of the search + * problem such as the cost associated with each action (since we are dealing + * with graphs the cost is simply the cost of the edge). + * + * We finally set the goal state (aka the node to find) and print. + */ + +public class UndirectedGraphSearchExample { + + public static void main(String args[]) { + + // Here we create a simple, undirected graph using Hipster. + // Note this graph is essentially a tree. + HipsterGraph graph = + GraphBuilder.create() + .connect("A").to("B").withEdge(2d) + .connect("A").to("C").withEdge(2d) + .connect("A").to("D").withEdge(2d) + .connect("B").to("E").withEdge(5d) + .connect("B").to("F").withEdge(10d) + .connect("B").to("G").withEdge(5d) + .connect("B").to("H").withEdge(10d) + .connect("C").to("I").withEdge(5d) + .connect("C").to("J").withEdge(10d) + .connect("C").to("K").withEdge(5d) + .connect("K").to("L").withEdge(5d) + .createUndirectedGraph(); + + // Here we create the search problem using the GraphSearchProblem util. + // We use {@link GraphSearchProblem#takeCostsFromEdges}to give edges a + // cost - however + // {@link GraphSearchProblem#useGenericCosts} can also be used to give + // each edge a unitary cost + + SearchProblem p = GraphSearchProblem + .startingFrom("A") + .in(graph) + .takeCostsFromEdges() + .build(); + + // Search the shortest path from "A" to "L". The search will stop when + // the goal state + // is reached - aka when L is reached. + System.out.println(Hipster.createDepthFirstSearch(p).search("L")); + } +} diff --git a/hipster-extensions/pom.xml b/hipster-extensions/pom.xml index eefaefd..3686369 100644 --- a/hipster-extensions/pom.xml +++ b/hipster-extensions/pom.xml @@ -5,7 +5,7 @@ hipster-pom es.usc.citius.hipster - 1.0.0-rc2 + 1.0.0-SNAPSHOT 4.0.0 diff --git a/hipster-test/pom.xml b/hipster-test/pom.xml index 9ee9433..6f5efb9 100644 --- a/hipster-test/pom.xml +++ b/hipster-test/pom.xml @@ -21,7 +21,7 @@ es.usc.citius.hipster hipster-pom - 1.0.0-rc2 + 1.0.0-SNAPSHOT 4.0.0 hipster-test diff --git a/hipster-third-party-graphs/pom.xml b/hipster-third-party-graphs/pom.xml index 614d071..028ad65 100644 --- a/hipster-third-party-graphs/pom.xml +++ b/hipster-third-party-graphs/pom.xml @@ -21,7 +21,7 @@ es.usc.citius.hipster hipster-pom - 1.0.0-rc2 + 1.0.0-SNAPSHOT 4.0.0 diff --git a/pom.xml b/pom.xml index 7daf610..84b945f 100644 --- a/pom.xml +++ b/pom.xml @@ -15,13 +15,13 @@ http://hipster4j.org - 1.0.0-rc2 + 1.0.0-SNAPSHOT es.usc.citius.hipster hipster-pom pom - 1.0.0-rc2 + 1.0.0-SNAPSHOT hipster-pom ${site.url} @@ -151,38 +151,10 @@ org.apache.maven.plugins maven-site-plugin 3.3 - - - lt.velykis.maven.skins - reflow-velocity-tools - 1.1.0 - - - - org.apache.velocity - velocity - 1.7 - - - - net.ju-n.maven.doxia - doxia-module-markdown - 1.0.0 - - - - true UTF-8 UTF-8 - true @@ -276,49 +248,26 @@ - org.codehaus.mojo - cobertura-maven-plugin - 2.6 - - xml - 256m - - true - - + org.jacoco + jacoco-maven-plugin + 0.7.5.201505241946 + + + + prepare-agent + + + + report + test + + report + + + - - - - org.codehaus.mojo - cobertura-maven-plugin - 2.5.2 - - - - html - xml - - - - - - - - - citius-snapshot-deploy - - - - citius-nexus-snapshots - Internal Snapshots - http://tec.citius.usc.es/nexus/content/repositories/snapshots/ - - - - \ No newline at end of file