diff --git a/README.markdown b/README.markdown new file mode 100755 index 0000000..f907e32 --- /dev/null +++ b/README.markdown @@ -0,0 +1,53 @@ +# Industrial Benchmark + +Requires: Java 8 and Apache Maven 3.x + +Documentation: The documentation is available online at: https://arxiv.org/abs/1709.09480 + Source: D. Hein, S. Depeweg, M. Tokic, S. Udluft, A. Hentschel, T.A. Runkler, and V. Sterzing. + "A Benchmark Environment Motivated by Industrial Control Problems". arXiv preprint arXiv:1709.09480, 2017. + +## Citing Industrial Benchmark + +To cite Industrial Benchmark, please reference: + D. Hein, S. Depeweg, M. Tokic, S. Udluft, A. Hentschel, T.A. Runkler, and V. Sterzing. "A Benchmark Environment + Motivated by Industrial Control Problems". arXiv preprint arXiv:1709.09480, 2017. + + D. Hein, S. Udluft, M. Tokic, A. Hentschel, T.A. Runkler, and V. Sterzing. "Batch Reinforcement + Learning on the Industrial Benchmark: First Experiences," in 2017 International Joint Conference on Neural + Networks (IJCNN), 2017, pp. 4214–4221. + + S. Depeweg, J. M. Hernández-Lobato, F. Doshi-Velez, and S. Udluft. "Learning and + policy search in stochastic dynamical systems with bayesian neural networks." arXiv + preprint arXiv:1605.07127, 2016. + +## Inclusion as a dependency to your Java/Maven project + + + com.siemens.oss.industrialbenchmark + industrialbenchmark + 1.1.1 + + + +## Compilation + Run + +NOTE: It is important to run the maven clean phase (`mvn clean`, like below) +when working with this project for the first time. +This will install the `RLGlue:JavaRLGlueCodec:1.0` dependency into the local repo. +Without it, compilation will fail. + + mvn clean package + java -jar target/industrialbenchmark-*-jar-with-dependencies.jar + # or + java -jar target/industrialbenchmark-*-jar-with-dependencies.jar src/main/resources/sim.properties + +* => a random trajectory is generated +* => all observable state variables are written to file dyn-observable.csv. +* => all markov state variables are written to file dyn-markov.csv + +# Sample usage in code + +An example usage of the industrial benchmark can be found in the class `com.siemens.industrialbenchmark.ExampleMain`. +It is intented to be a template for data generation. + + diff --git a/README.txt b/README.txt deleted file mode 100755 index 3490f4e..0000000 --- a/README.txt +++ /dev/null @@ -1,51 +0,0 @@ -Industrial Benchmark: -===================== - -Requires: Java 8 and Apache Maven 3.x - -Documentation: The documentation is available online at: https://arxiv.org/abs/1610.03793 - Source: D. Hein, A. Hentschel, V. Sterzing, M. Tokic and S. Udluft. "Introduction to the - Industrial Benchmark". CoRR, arXiv:1610.03793 [cs.LG], pages 1-11. 2016. - -Citing Industrial Benchmark: -============================ -To cite Industrial Benchmark, please reference: - D. Hein, A. Hentschel, V. Sterzing, M. Tokic and S. Udluft. "Introduction to the - Industrial Benchmark". CoRR, arXiv:1610.03793 [cs.LG], pages 1-11. 2016. - - D. Hein, S. Udluft, M. Tokic, A. Hentschel, T.A. Runkler, and V. Sterzing. "Batch Reinforcement - Learning on the Industrial Benchmark: First Experiences." Neural Networks (IJCNN), 2017 - International Joint Conference on. IEEE, 2017. (accepted) (to be published) - - S. Depeweg, J. M. Hernández-Lobato, F. Doshi-Velez, and S. Udluft. "Learning and - policy search in stochastic dynamical systems with bayesian neural networks." arXiv - preprint arXiv:1605.07127, 2016. - -Inclusion as a dependency to your Java/Maven project: -===================================================== - - - com.siemens.oss.industrialbenchmark - industrialbenchmark - 1.1.1 - - - -Compilation + Run: -================== - mvn clean package - java -jar industrialbenchmark-.jar - - E.g.: java -jar target/industrialbenchmark--SNAPSHOT-jar-with-dependencies.jar src/main/resources/sim.properties - - => a random trajectory is generated - => all observable state variables are written to file dyn-observable.csv. - => all markov state variables are written to file dyn-markov.csv - -Example main()-Function: -======================== - - An example usage of the industrial benchmark can be found in the class com.siemens.industrialbenchmark.ExampleMain. - This class is intented to be a template for data generation. - - diff --git a/pom.xml b/pom.xml index eeb29b1..d800747 100755 --- a/pom.xml +++ b/pom.xml @@ -1,15 +1,24 @@ 4.0.0 + com.siemens.oss.industrialbenchmark industrialbenchmark 1.1.2-SNAPSHOT - 1.8 - 1.8 - UTF-8 + UTF-8 + ${project.build.encoding} + ${project.build.encoding} + 8 + + com.siemens.industrialbenchmark + ${project.mainPackage}.TrialGuiMain + + Siemens + http://ct.siemens.com + Industrial Benchmark http://github.com/siemens/industrialbenchmark A novel reinforcement learning benchmark, called Industrial Benchmark, is introduced. @@ -17,6 +26,7 @@ The Industrial Benchmark aims at being be realistic in the sense, that it includ of aspects that we found to be vital in industrial applications. It is not designed to be an approximation of any real system, but to pose the same hardness and complexity. + 20?? @@ -27,6 +37,12 @@ approximation of any real system, but to pose the same hardness and complexity. + jar + + + 2.2.1 + + Michel Tokic @@ -67,6 +83,57 @@ approximation of any real system, but to pose the same hardness and complexity. + + + + + + org.apache.maven.plugins + maven-checkstyle-plugin + 2.17 + + ${project.build.sourceEncoding} + true + src/main/resources/checkstyle.xml + java.header.regex.template.file=${basedir}/src/main/resources/java_header_regex_template.txt + + + + + org.apache.maven.plugins + maven-pmd-plugin + 3.7 + + true + true + ${project.build.sourceEncoding} + 1.${java.min.version} + + ${basedir}/src/main/resources/pmd.xml + + + + + + org.codehaus.mojo + findbugs-maven-plugin + 3.0.4 + + + + org.apache.maven.plugins + maven-surefire-report-plugin + 2.19.1 + + + + org.apache.maven.plugins + maven-project-info-reports-plugin + 2.9 + + + + org.slf4j @@ -144,9 +211,11 @@ approximation of any real system, but to pose the same hardness and complexity. maven-compiler-plugin 2.3.2 - ${javaSource} - ${javaTarget} + 1.${java.min.version} + 1.${java.min.version} ${project.build.sourceEncoding} + true + -Xlint:unchecked @@ -162,8 +231,15 @@ approximation of any real system, but to pose the same hardness and complexity. used to specify that all needed libraries are found under lib/ directory. --> lib/ - com.siemens.industrialbenchmark.ExampleMain + ${project.mainClass} + + + + @@ -218,14 +294,11 @@ approximation of any real system, but to pose the same hardness and complexity. - - - - maven-assembly-plugin + - com.siemens.industrialbenchmark.ExampleMain + ${project.mainClass} @@ -233,6 +306,74 @@ approximation of any real system, but to pose the same hardness and complexity. + + + org.apache.maven.plugins + maven-javadoc-plugin + 2.8 + + ${project.build.sourceEncoding} + ${project.build.sourceEncoding} + + + + + org.apache.maven.plugins + maven-resources-plugin + 3.0.2 + + ${project.build.resourceEncoding} + + + + + org.codehaus.mojo + exec-maven-plugin + 1.5.0 + + + + java + + + + + ${project.mainClass} + + + + + org.apache.maven.plugins + maven-checkstyle-plugin + 2.17 + + ${project.build.sourceEncoding} + true + src/main/resources/checkstyle.xml + java.header.regex.template.file=${basedir}/src/main/resources/java_header_regex_template.txt + + + + + org.apache.maven.plugins + maven-pmd-plugin + 3.7 + + true + true + ${project.build.sourceEncoding} + 1.${java.min.version} + + ${basedir}/src/main/resources/pmd.xml + + + + + + org.codehaus.mojo + findbugs-maven-plugin + 3.0.4 + @@ -255,7 +396,8 @@ approximation of any real system, but to pose the same hardness and complexity. - com.siemens.industrialbenchmark.* + ${project.build.sourceEncoding} + ${project.build.sourceEncoding} @@ -307,5 +449,49 @@ approximation of any real system, but to pose the same hardness and complexity. + + + Extensive-Reports + + + + + org.apache.maven.plugins + maven-jxr-plugin + 2.5 + + ${project.build.sourceEncoding} + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 2.8 + + ${project.build.sourceEncoding} + ${project.build.sourceEncoding} + + + + + org.apache.maven.plugins + maven-changelog-plugin + 2.3 + + + + org.codehaus.mojo + sonar-maven-plugin + 3.2 + + + + diff --git a/src/main/java/com/siemens/industrialbenchmark/ExampleExperiment.java b/src/main/java/com/siemens/industrialbenchmark/ExampleExperiment.java new file mode 100644 index 0000000..176ca6a --- /dev/null +++ b/src/main/java/com/siemens/industrialbenchmark/ExampleExperiment.java @@ -0,0 +1,107 @@ +/* +Copyright 2017 Siemens AG. + +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 com.siemens.industrialbenchmark; + +import java.io.File; +import java.io.IOException; +import java.util.Properties; +import com.siemens.industrialbenchmark.properties.PropertiesException; +import com.siemens.industrialbenchmark.properties.PropertiesUtil; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +/** + * Runs a series of simulations. + */ +public class ExampleExperiment { + + private ExampleExperiment() {} + + private static class SimulationWrapper implements Callable { + + private final Callable simulation; + private final int simulationIndex; + private final int numSimulations; + private final long startTimeMillis; + + SimulationWrapper(final Callable simulation, final int simulationIndex, final int numSimulations, final long startTimeMillis) { + + this.simulation = simulation; + this.simulationIndex = simulationIndex; + this.numSimulations = numSimulations; + this.startTimeMillis = startTimeMillis; + } + + @Override + public R call() throws Exception { + + final R result = simulation.call(); + + final long currentMillis = System.currentTimeMillis(); + System.out.println("Compleeted simulation " + (simulationIndex + 1) + "/" + numSimulations + " after " + (currentMillis - startTimeMillis) + "ms"); + + return result; + } + + public Callable getSimulation() { + return simulation; + } + + public int getSimulationIndex() { + return simulationIndex; + } + + public int getNumSimulations() { + return numSimulations; + } + + public long getStartTimeMillis() { + return startTimeMillis; + } + } + + public static void main(final String[] args) throws IOException, PropertiesException { + + final int numThreads = 2; + final int simulationSteps = 1500; + final String simPropsFilePath = "src/main/resources/sim.properties"; + final String outputFileNameTemplate = System.getProperty("user.home") + + "/indBenchSimRes_" + + "SetPoint${STATIONARY_SETPOINT}_" + + "Seed${SEED}_" + + "Time${time:yyyy-MM-dd_HH:mm:ss:SSS}.csv"; // for time format documentation, see: http://docs.oracle.com/javase/7/docs/api/java/text/SimpleDateFormat.html + + final Properties props = PropertiesUtil.loadSetPointProperties(new File(simPropsFilePath)); + + final ExecutorService simulationThreadPool = Executors.newFixedThreadPool(numThreads); + int experimentIndex = 0; + final long startTimeMillis = System.currentTimeMillis(); + + final int numExperiments = 100 * 100; + for (int setPoint = 0; setPoint < 100; setPoint++) { + for (int seed = 0; seed < 100; seed++) { + props.setProperty("STATIONARY_SETPOINT", String.valueOf(setPoint)); + props.setProperty("SEED", String.valueOf(seed)); + + final String outputFileName = TrialGuiMain.formatSaveFileName(outputFileNameTemplate, props); + final RandomSimulation randomSimulation = new RandomSimulation(simulationSteps, props, null, new File(outputFileName)); + simulationThreadPool.submit(new SimulationWrapper<>(randomSimulation, experimentIndex, numExperiments, startTimeMillis)); + experimentIndex++; + } + } + } +} diff --git a/src/main/java/com/siemens/industrialbenchmark/ExampleMain.java b/src/main/java/com/siemens/industrialbenchmark/ExampleMain.java index d6a75ac..b7e6e5c 100755 --- a/src/main/java/com/siemens/industrialbenchmark/ExampleMain.java +++ b/src/main/java/com/siemens/industrialbenchmark/ExampleMain.java @@ -1,130 +1,66 @@ -/** -Copyright 2016 Siemens AG. - -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 com.siemens.industrialbenchmark; - -import java.io.File; -import java.io.FileWriter; -import java.io.IOException; -import java.util.Properties; -import java.util.Random; - -import com.siemens.industrialbenchmark.datavector.action.ActionDelta; -import com.siemens.industrialbenchmark.datavector.state.MarkovianStateDescription; -import com.siemens.industrialbenchmark.dynamics.IndustrialBenchmarkDynamics; -import com.siemens.industrialbenchmark.properties.PropertiesException; -import com.siemens.industrialbenchmark.properties.PropertiesUtil; -import com.siemens.industrialbenchmark.util.PlotCurve; -import com.siemens.rl.interfaces.DataVector; -import com.siemens.rl.interfaces.Environment; - -public class ExampleMain { - - /** - * Run example benchmark with random actions for data generation purposes. - * - * @param args - * @throws IOException - * @throws PropertiesException - */ - public static void main(String[] args) throws IOException, PropertiesException { - - // configuration of the properties file - String filename = "src/main/resources/sim.properties"; // default filepath - if (args.length >= 1) { // if filepath was given to main() - filename = args[0]; - System.out.println("Using config file: '" + filename + "'"); - } else { - System.out.println("Using default config file: '" + filename + "'. A custom config file can be passed as an additional parameter."); - } - - /** - * Instantiate benchmark - */ - // setpoint configuration parameters - Properties props = PropertiesUtil.setpointProperties( new File (filename)); - - // instantiate industrial benchmark - Environment db = new IndustrialBenchmarkDynamics(props); - - // seed PRNG from configured seed in configuration file - long seed = PropertiesUtil.getLong(props, "SEED", System.currentTimeMillis()); - System.out.println("main seed: " + seed); - Random rand = new Random(seed); - - DataVector markovState = db.getInternalMarkovState(); - DataVector observableState = db.getState(); - - // apply constant action (gain and velocity transitions from 0 => 100) - final ActionDelta deltaAction = new ActionDelta(0.1f, 0.1f, 0.1f); - - // write column headers - FileWriter fwm = new FileWriter("dyn-markov.csv"); - fwm.write("time "); - for (String key : db.getInternalMarkovState().getKeys()) { - fwm.write(key + " "); - } - fwm.write("\n"); - - FileWriter fw = new FileWriter("dyn-observable.csv"); - fw.write("time "); - for (String key : db.getState().getKeys()) { - fw.write(key + " "); - } - fw.write("\n"); - - - // data array for memorizing the reward - final int steps = PropertiesUtil.getInt(props, "SIM_STEPS", 1500); - double data[] = new double[steps]; - - /************************************************************* - * Perform random actions and write markov state to text file - *************************************************************/ - for (int i = 0; i < steps; i++) { - - // set random action from the interval [-1, 1] - deltaAction.setDeltaGain(2.f * (rand.nextFloat() - 0.5f)); - deltaAction.setDeltaVelocity(2.f * (rand.nextFloat() - 0.5f)); - deltaAction.setDeltaShift(2.f * (rand.nextFloat() - 0.5f)); - - db.step(deltaAction); - markovState = db.getInternalMarkovState(); - observableState = db.getState(); - - // write data - fw.write(Integer.toString(i+1) + " "); - for (String key : observableState.getKeys()) { - fw.write(observableState.getValue(key) + " "); - } - fw.write("\n"); - - fwm.write(Integer.toString(i+1) + " "); - for (String key : markovState.getKeys()) { - fwm.write(markovState.getValue(key) + " "); - } - fwm.write("\n"); - - data[i] = db.getState().getValue(MarkovianStateDescription.RewardTotal); - } - - fw.close(); - fwm.close(); - - // plot reward - PlotCurve.plot("RewardTotal", "t", "reward", data); - } - -} +/* +Copyright 2016 Siemens AG. + +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 com.siemens.industrialbenchmark; + +import java.io.File; +import java.io.IOException; +import java.util.Properties; +import com.siemens.industrialbenchmark.datavector.state.MarkovianStateDescription; +import com.siemens.industrialbenchmark.properties.PropertiesException; +import com.siemens.industrialbenchmark.properties.PropertiesUtil; +import com.siemens.industrialbenchmark.util.PlotCurve; +import java.util.List; +import java.util.Map; + +public final class ExampleMain { + + public static final String DEFALT_SIM_PROPS_FILE_PATH = "src/main/resources/sim.properties"; + + private ExampleMain() {} + + /** + * Run example benchmark with random actions for data generation purposes. + * + * @param args command-line arguments + * @throws IOException when there is an error reading the configuration file + * @throws PropertiesException if the configuration file is badly formatted + */ + public static void main(final String[] args) throws IOException, PropertiesException { + + final int nSteps = 1500; + final String outputVar = MarkovianStateDescription.REWARD_TOTAL; + + // configuration of the properties file + final String simPropsFilePath; + if (args.length >= 1) { // if filepath was given to main() + simPropsFilePath = args[0]; + System.out.println("Using config file: '" + simPropsFilePath + "'"); + } else { + simPropsFilePath = DEFALT_SIM_PROPS_FILE_PATH; + System.out.println("Using default config file: '" + simPropsFilePath + "'. A custom config file can be passed as an additional parameter."); + } + + // setpoint configuration parameters + final Properties props = PropertiesUtil.loadSetPointProperties(new File(simPropsFilePath)); + + final RandomSimulation randomSimulation = new RandomSimulation(nSteps, props, null, new File("dyn-markov.csv")); + final Map> states = randomSimulation.call(); + final List data = states.get(outputVar); + + // plot the data + PlotCurve.plot(outputVar, "t", outputVar, data); + } +} diff --git a/src/main/java/com/siemens/industrialbenchmark/RandomSimulation.java b/src/main/java/com/siemens/industrialbenchmark/RandomSimulation.java new file mode 100644 index 0000000..bca90e6 --- /dev/null +++ b/src/main/java/com/siemens/industrialbenchmark/RandomSimulation.java @@ -0,0 +1,141 @@ +/* +Copyright 2017 Siemens AG. + +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 com.siemens.industrialbenchmark; + +import com.siemens.industrialbenchmark.datavector.action.ActionDelta; +import com.siemens.industrialbenchmark.dynamics.IndustrialBenchmarkDynamics; +import com.siemens.industrialbenchmark.properties.PropertiesException; +import com.siemens.industrialbenchmark.properties.PropertiesUtil; +import com.siemens.rl.interfaces.DataVector; +import com.siemens.rl.interfaces.Environment; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.util.Properties; +import java.util.Random; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.Callable; +import javax.swing.BoundedRangeModel; + +public class RandomSimulation implements Callable>> { + + private final int nSteps; + private final Properties props; + private final BoundedRangeModel progressListener; + private final File output; + + public RandomSimulation( + final int nSteps, + final Properties props, + final BoundedRangeModel progressListener, + final File output) + throws IOException, PropertiesException + { + this.nSteps = nSteps; + this.props = props; + this.progressListener = progressListener; + this.output = output; + } + + public int getNSteps() { + return nSteps; + } + + public Properties getProps() { + return props; + } + + public BoundedRangeModel getProgressListener() { + return progressListener; + } + + public File getOutput() { + return output; + } + + @Override + public Map> call() throws IOException, PropertiesException { + + /* + * Instantiate benchmark + */ + // instantiate industrial benchmark + final Environment db = new IndustrialBenchmarkDynamics(props); + + // seed PRNG from configured seed in configuration file + final long randomSeed = PropertiesUtil.getLong(props, "SEED", System.currentTimeMillis()); + System.out.println("main seed: " + randomSeed); + final Random rand = new Random(randomSeed); + + // apply constant action (gain and velocity transitions from 0 => 100) + final ActionDelta deltaAction = new ActionDelta(0.1f, 0.1f, 0.1f); + + final DataVector internalMarkovState = db.getMarkovState(); + final LinkedHashMap> internalStates = new LinkedHashMap<>(internalMarkovState.getKeys().size()); + for (final String key : internalMarkovState.getKeys()) { + internalStates.put(key, new ArrayList<>(nSteps)); + } + + try (final FileWriter outputFW = (output == null) ? null : new FileWriter(output)) { + // write column headers + if (outputFW != null) { + for (final String key : db.getMarkovState().getKeys()) { + outputFW.write(key); + outputFW.write(','); + outputFW.write(' '); + } + outputFW.write('\n'); + } + + /* + * Perform random actions and write markov state to text file + */ + // data array that stores the reward + for (int si = 0; si < nSteps; si++) { + // set random action from the interval [-1, 1] + deltaAction.setDeltaGain(2.f * (rand.nextFloat() - 0.5f)); + deltaAction.setDeltaVelocity(2.f * (rand.nextFloat() - 0.5f)); + + db.step(deltaAction); + final DataVector markovState = db.getMarkovState(); + final double[] markovStateValues = markovState.getValuesArray(); + final Iterator> stateValueLists = internalStates.values().iterator(); + for (int msvi = 0; msvi < markovStateValues.length; msvi++) { + stateValueLists.next().add(markovStateValues[msvi]); + } + + // write data + if (outputFW != null) { + for (final String key : markovState.getKeys()) { + outputFW.write(String.valueOf(markovState.getValue(key))); + outputFW.write(' '); + } + outputFW.write('\n'); + } + + if (progressListener != null) { + progressListener.setValue(si); + } + } + } + + return internalStates; + } +} diff --git a/src/main/java/com/siemens/industrialbenchmark/TrialGuiMain.form b/src/main/java/com/siemens/industrialbenchmark/TrialGuiMain.form new file mode 100644 index 0000000..e738573 --- /dev/null +++ b/src/main/java/com/siemens/industrialbenchmark/TrialGuiMain.form @@ -0,0 +1,308 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/java/com/siemens/industrialbenchmark/TrialGuiMain.java b/src/main/java/com/siemens/industrialbenchmark/TrialGuiMain.java new file mode 100644 index 0000000..7547468 --- /dev/null +++ b/src/main/java/com/siemens/industrialbenchmark/TrialGuiMain.java @@ -0,0 +1,706 @@ +/* +Copyright 2017 Siemens AG. + +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 com.siemens.industrialbenchmark; + +import com.siemens.industrialbenchmark.util.PropertiesTable; +import com.siemens.industrialbenchmark.datavector.state.MarkovianStateDescription; +import com.siemens.industrialbenchmark.properties.PropertiesException; +import com.siemens.industrialbenchmark.properties.PropertiesUtil; +import com.siemens.industrialbenchmark.util.PlotCurve; +import info.monitorenter.gui.chart.Chart2D; +import java.awt.event.ActionEvent; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; +import java.beans.PropertyChangeEvent; +import java.io.File; +import java.io.IOException; +import java.text.SimpleDateFormat; +import java.util.Collections; +import java.util.Date; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Properties; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.prefs.Preferences; +import javax.swing.AbstractAction; +import javax.swing.Action; +import javax.swing.BoundedRangeModel; +import javax.swing.DefaultBoundedRangeModel; +import javax.swing.DefaultComboBoxModel; +import javax.swing.JCheckBox; +import javax.swing.JComponent; +import javax.swing.JFileChooser; +import javax.swing.JOptionPane; +import javax.swing.SwingUtilities; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; +import javax.swing.filechooser.FileNameExtensionFilter; +import javax.swing.text.Document; +import javax.swing.text.JTextComponent; + +/** + * A comfortable GUI to play around with a random simulation. + */ +public class TrialGuiMain extends javax.swing.JFrame { + + private static final String PRIMARY_INPUT_VAR = "STATIONARY_SETPOINT"; + private static final String PRIMARY_OUTPUT_VAR = "RewardTotal"; + + private static final String PREF_SAVE_FILE_TEMPLATE = "saveFileTemplate"; + private static final String SAVE_FILE_TEMPLATE_DEFAULT + = System.getProperty("user.home") + + "/industrialBenchmarkSimResults_" + + "SetPoint${STATIONARY_SETPOINT}_" + + "Seed${SEED}_" + + "Time${time:yyyy-MM-dd_HH:mm:ss:SSS}.csv"; // see http://docs.oracle.com/javase/7/docs/api/java/text/SimpleDateFormat.html + + private static final String PREF_AUTO_SAVE = "autoSave"; + private static final boolean AUTO_SAVE_DEFAULT = false; + + private static final String PREF_SIMULATION_STEPS = "simulationSteps"; + private static final Integer SIMULATION_STEPS_DEFAULT = 1500; + + private static final String PREF_SHOWN_OUTPUT = "shownOutput"; + private static final String SHOWN_OUTPUT_DEFAULT = PRIMARY_OUTPUT_VAR; + + private Map> states; + private final PropertiesTable simPropsTable; + private final Preferences preferences; + + public TrialGuiMain() { + + this.states = null; + this.preferences = Preferences.userNodeForPackage(TrialGuiMain.class); + + initComponents(); + + this.setSize(1024, 600); + + final Properties simulationProps = loadDefaultSimulationProperties(); + this.simPropsTable = new PropertiesTable(simulationProps); + this.simPropsSP.getViewport().add(simPropsTable); + + final DefaultComboBoxModel inputVarsKeysModel = new DefaultComboBoxModel<>(); + inputVarsKeysModel.removeAllElements(); + final Enumeration simulationPropKeys = simulationProps.keys(); + while (simulationPropKeys.hasMoreElements()) { + inputVarsKeysModel.addElement((String) simulationPropKeys.nextElement()); + } + this.propNumberSliderCB.setModel(inputVarsKeysModel); + final InputVarKeyChangeListener inputVarKeyChangeListener = new InputVarKeyChangeListener(); + inputVarKeyChangeListener.setFromTo(PRIMARY_INPUT_VAR, 0, 100); // HACK + this.propNumberSliderCB.addItemListener(inputVarKeyChangeListener); + this.propNumberSliderCB.setSelectedItem(PRIMARY_INPUT_VAR); + + final SimulationRunAction simulationRunAction = new SimulationRunAction(); + + this.propNumberSliderS.addChangeListener(new InputVarSliderChangeListener(simulationRunAction)); + this.propNumberSliderS.setMajorTickSpacing(10); + this.propNumberSliderS.setMinorTickSpacing(5); + + final FromToChangeListener fromToChangeListener = new FromToChangeListener(); + addChangeListener(this.propNumberSliderFrom, fromToChangeListener); + addChangeListener(this.propNumberSliderTo, fromToChangeListener); + + this.runB.setAction(simulationRunAction); + + final DefaultComboBoxModel outputStateKeysModel = new DefaultComboBoxModel<>(); + outputStateKeysModel.removeAllElements(); + for (final String key : MarkovianStateDescription.getNonConvolutedInternalVariables()) { + outputStateKeysModel.addElement(key); + } + this.shownStateKeyCB.setModel(outputStateKeysModel); + this.shownStateKeyCB.setSelectedItem(PRIMARY_OUTPUT_VAR); + this.shownStateKeyCB.addItemListener(new OutputVarKeyChangeListener()); + + final String safeFileTemplate = this.preferences.get(PREF_SAVE_FILE_TEMPLATE, SAVE_FILE_TEMPLATE_DEFAULT); + final StringBuilder tt = new StringBuilder(1024 + (simulationProps.size() * 64)); + tt + .append("\n" + + "

File-Name template for the results in CSV format

\n" + + "The file-name has to end in \".csv\",
\n" + + " and may use any of the following variables,
\n" + + " which will be substituted to create the actual file name:
\n" + + "
    \n" + + "
  • ${time:format} (special), gets replaced with the time at the end of the simulation,
    \n" + + " according to the given format
    \n" + + " (see the Javadoc for formatting details).
    \n" + + " example: \"${time:yyyy-MM-dd_HH:mm:ss:SSS}\"\n" + + "
  • \n"); + for (final String key : simulationProps.stringPropertyNames()) { + tt.append("
  • ${").append(key).append("}
  • \n"); + } + tt + .append("
\n" + + "\n"); + this.saveFileNameTF.setToolTipText(tt.toString()); + this.saveFileNameTF.setText(safeFileTemplate); + this.saveFileNameTF.getDocument().addDocumentListener(new DocumentListener() { + @Override + public void changedUpdate(final DocumentEvent evt) { + textChanged(); + } + + @Override + public void removeUpdate(final DocumentEvent evt) { + textChanged(); + } + + @Override + public void insertUpdate(final DocumentEvent evt) { + textChanged(); + } + + private void textChanged() { + preferences.put(PREF_SAVE_FILE_TEMPLATE, saveFileNameTF.getText()); + } + }); + + this.saveFileChooserB.setAction(new ChooseSaveFileAction()); + this.saveAutoCB.setAction(new AutoSaveAction(new JComponent[] {saveFileNameTF, saveFileChooserB})); + + final boolean autoSave = this.preferences.getBoolean(PREF_AUTO_SAVE, AUTO_SAVE_DEFAULT); + this.saveAutoCB.setSelected(autoSave); + this.saveFileNameTF.setEnabled(autoSave); + this.saveFileChooserB.setEnabled(autoSave); + + final int nSimSteps = this.preferences.getInt(PREF_SIMULATION_STEPS, SIMULATION_STEPS_DEFAULT); + this.nSimStepsS.setValue(nSimSteps); + + final String shownOutputVar = this.preferences.get(PREF_SHOWN_OUTPUT, SHOWN_OUTPUT_DEFAULT); + this.shownStateKeyCB.setSelectedItem(shownOutputVar); + + simulationRunAction.actionPerformed(null); + } + + protected Map> getStates() { + return states; + } + + protected PropertiesTable getSimPropsTable() { + return simPropsTable; + } + + protected Preferences getPreferences() { + return preferences; + } + + public static String formatSaveFileName(final String saveFileNameTemplate, final Properties inputParams) { + + final Date now = new Date(); + String saveFileName = saveFileNameTemplate; + while (saveFileName.contains("${")) { + final int startIndex = saveFileName.indexOf("${"); + final int endIndex = saveFileName.indexOf('}', startIndex) + 1; + final String replaceVarRaw = saveFileName.substring(startIndex, endIndex); + final String replaceVar = replaceVarRaw.substring(2, replaceVarRaw.length() - 1); + final String replaceValue; + if (replaceVar.startsWith("time:")) { + final String timeFormat = replaceVar.substring(replaceVar.indexOf(':') + 1); + final String formattedTime = new SimpleDateFormat(timeFormat).format(now); + replaceValue = formattedTime; + } else if (inputParams.containsKey(replaceVar)) { + replaceValue = inputParams.getProperty(replaceVar); + } else { + // do nothing + replaceValue = replaceVarRaw; + break; + } + saveFileName = saveFileName.replace(replaceVarRaw, replaceValue); + } + return saveFileName; + } + + /** + * Installs a listener to receive notification when the text of any + * {@code JTextComponent} is changed. Internally, it installs a + * {@link DocumentListener} on the text component's {@link Document}, and a + * {@link java.beans.PropertyChangeListener} on the text component to detect + * if the {@code Document} itself is replaced. + * + * @param text any text component, such as a {@link javax.swing.JTextField} + * or {@link javax.swing.JTextArea} + * @param changeListener a listener to receive {@link ChangeEvent}s when + * the text is changed; the source object for the events will be the text + * component + * @throws NullPointerException if either parameter is null + */ + public static void addChangeListener(final JTextComponent text, final ChangeListener changeListener) { + Objects.requireNonNull(text); + Objects.requireNonNull(changeListener); + final DocumentListener docLst = new DocumentListener() { + private int lastChange = 0, lastNotifiedChange = 0; + + @Override + public void insertUpdate(final DocumentEvent evt) { + changedUpdate(evt); + } + + @Override + public void removeUpdate(final DocumentEvent evt) { + changedUpdate(evt); + } + + @Override + public void changedUpdate(final DocumentEvent evt) { + lastChange++; + SwingUtilities.invokeLater(() -> { + if (lastNotifiedChange != lastChange) { + lastNotifiedChange = lastChange; + changeListener.stateChanged(new ChangeEvent(text)); + } + }); + } + }; + text.addPropertyChangeListener("document", (final PropertyChangeEvent evt) -> { + final Document doc1 = (Document) evt.getOldValue(); + final Document doc2 = (Document) evt.getNewValue(); + if (doc1 != null) { + doc1.removeDocumentListener(docLst); + } + if (doc2 != null) { + doc2.addDocumentListener(docLst); + } + docLst.changedUpdate(null); + }); + final Document doc = text.getDocument(); + if (doc != null) { + doc.addDocumentListener(docLst); + } + } + + private static Properties loadDefaultSimulationProperties() { + + // configuration of the properties file + final String simPropsFilePath = ExampleMain.DEFALT_SIM_PROPS_FILE_PATH; // default filepath + System.out.println("Using config file: '" + simPropsFilePath + "'"); + + // set-point configuration parameters + try { + return PropertiesUtil.loadSetPointProperties(new File(simPropsFilePath)); + } catch (final IOException ex) { + throw new RuntimeException(ex); + } + } + + private Properties getSimulationProperties() { + return simPropsTable.getProperties(); + } + + private int getSimulationSteps() { + return (Integer) nSimStepsS.getValue(); + } + + private class InputVarKeyChangeListener implements ItemListener { + + private final Map> propKeyFromTo; + + InputVarKeyChangeListener() { + + this.propKeyFromTo = new HashMap<>(); + } + + public void setFromTo(final String propName, final Object from, final Object to) { + + propKeyFromTo.put(propName, Collections.singletonMap(from, to)); + } + + @Override + public void itemStateChanged(final ItemEvent event) { + + if (event.getStateChange() != ItemEvent.SELECTED) { + return; + } + + final String previouslySelectedKey = (String) propNumberSliderCB.getSelectedItem(); + setFromTo(previouslySelectedKey, propNumberSliderFrom.getText(), propNumberSliderTo.getText()); + + final String newlySelectedKey = (String) event.getItem(); + final Map fromTo + = propKeyFromTo.containsKey(newlySelectedKey) + ? propKeyFromTo.get(newlySelectedKey) + : Collections.singletonMap(0, 100); + final Map.Entry fromToEntry = fromTo.entrySet().iterator().next(); + propNumberSliderFrom.setText(fromToEntry.getKey().toString()); + propNumberSliderTo.setText(fromToEntry.getValue().toString()); +// propNumberSliderS.getModel().setRangeProperties(vale, 1, from, to, false); + final String valueRaw = simPropsTable.getProperties().getProperty(newlySelectedKey); + boolean sliderSuport; + try { + propNumberSliderS.setValue(Double.valueOf(valueRaw).intValue()); + sliderSuport = true; + } catch (final NumberFormatException ex) { + ex.printStackTrace(); + sliderSuport = false; + } + propNumberSliderFrom.setEnabled(sliderSuport); + propNumberSliderS.setEnabled(sliderSuport); + propNumberSliderTo.setEnabled(sliderSuport); + } + } + + private class InputVarSliderChangeListener implements ChangeListener { + + private final Action action; + + InputVarSliderChangeListener(final Action action) { + + this.action = action; + } + + @Override + public void stateChanged(final ChangeEvent event) { + + if (propNumberSliderS.getValueIsAdjusting()) { + return; + } + + final int newValue = propNumberSliderS.getValue(); + final String selectedProp = (String) propNumberSliderCB.getSelectedItem(); + simPropsTable.setProperty(selectedProp, String.valueOf(newValue)); + + action.actionPerformed(null); + } + } + + private class FromToChangeListener implements ChangeListener { + + @Override + public void stateChanged(final ChangeEvent event) { + + try { + final Double from = Double.valueOf(propNumberSliderFrom.getText()); + final Double to = Double.valueOf(propNumberSliderTo.getText()); + propNumberSliderS.setMinimum((int) Math.ceil(from)); + propNumberSliderS.setMaximum((int) Math.floor(to)); + + propNumberSliderS.setMajorTickSpacing((int) ((to - from) / 10)); + propNumberSliderS.setMinorTickSpacing((int) ((to - from) / 20)); + } catch (final NumberFormatException ex) { + JOptionPane.showMessageDialog(null, ex.getMessage(), "Failed to set from & to", JOptionPane.WARNING_MESSAGE); + } + } + } + + private class OutputVarKeyChangeListener implements ItemListener { + + @Override + public void itemStateChanged(final ItemEvent event) { + + if (states == null) { + return; + } + + if (event.getStateChange() != ItemEvent.SELECTED) { + return; + } + + final String newlySelectedKey = (String) event.getItem(); + preferences.put(PREF_SHOWN_OUTPUT, newlySelectedKey); + final List shownValues = states.get(newlySelectedKey); + final Chart2D chart = PlotCurve.plotChart("t", newlySelectedKey, shownValues); + chartSP.getViewport().removeAll(); + chartSP.getViewport().add(chart); + } + } + + private class SimulationRunAction extends AbstractAction { + + private final ExecutorService simulationThreadPool; + + SimulationRunAction() { + + this.simulationThreadPool = Executors.newSingleThreadExecutor(); + putValue(NAME, "Run Simulation"); + } + + @Override + public void actionPerformed(final ActionEvent evt) { + + chartSP.getViewport().removeAll(); + states = null; + + final Properties simulationProperties = getSimulationProperties(); + final int simulationSteps = getSimulationSteps(); + final BoundedRangeModel progessModel = new DefaultBoundedRangeModel(0, 0, 0, simulationSteps); + simulationProgressPB.setModel(progessModel); + states = null; + File csvSaveFile = null; + if (saveAutoCB.isSelected()) { + final String saveFileNameTemplate = saveFileNameTF.getText(); + final String saveFileNameFormatted = formatSaveFileName(saveFileNameTemplate, simPropsTable.getProperties()); + csvSaveFile = new File(saveFileNameFormatted); + } + + preferences.putInt(PREF_SIMULATION_STEPS, getSimulationSteps()); + + try { + final RandomSimulation randomSimulation = new RandomSimulation(simulationSteps, simulationProperties, progessModel, csvSaveFile); + final Callable>> simulationWrapper = new Callable>>() { + @Override + public Map> call() throws Exception { + + final Map> result = randomSimulation.call(); + states = result; + + final String shownKey = (String) shownStateKeyCB.getSelectedItem(); + shownStateKeyCB.setSelectedItem(null); + shownStateKeyCB.setSelectedItem(shownKey); + + return result; + } + }; + simulationThreadPool.submit(simulationWrapper); + } catch (final IOException | PropertiesException ex) { + throw new RuntimeException(ex); + } + } + } + + private class ChooseSaveFileAction extends AbstractAction { + + private final JFileChooser fileChooser; + + ChooseSaveFileAction() { + + this.fileChooser = new JFileChooser(); + this.fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY); + this.fileChooser.setAcceptAllFileFilterUsed(false); + this.fileChooser.setFileFilter(new FileNameExtensionFilter("CSV Files", ".csv")); + putValue(NAME, "..."); + } + + @Override + public void actionPerformed(final ActionEvent evt) { + + fileChooser.setSelectedFile(new File(saveFileNameTF.getText())); + + final int usersChoice = fileChooser.showOpenDialog(TrialGuiMain.this); + + if (usersChoice == JFileChooser.APPROVE_OPTION) { + final File file = fileChooser.getSelectedFile(); + saveFileNameTF.setText(file.getAbsolutePath()); + } + } + } + + private class AutoSaveAction extends AbstractAction { + + private final JComponent[] relatedComponents; + + AutoSaveAction(final JComponent... relatedComponents) { + + this.relatedComponents = relatedComponents; + putValue(NAME, "Auto Save"); + } + + @Override + public void actionPerformed(final ActionEvent evt) { + + final boolean activated = ((JCheckBox) evt.getSource()).isSelected(); + preferences.putBoolean(PREF_AUTO_SAVE, activated); + for (final JComponent relatedComponent : relatedComponents) { + relatedComponent.setEnabled(activated); + } + } + } + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + + mainSP = new javax.swing.JSplitPane(); + westP = new javax.swing.JPanel(); + simPropsP = new javax.swing.JPanel(); + simPropsSP = new javax.swing.JScrollPane(); + propNumberSliderP = new javax.swing.JPanel(); + propNumberSliderCB = new javax.swing.JComboBox<>(); + propNumberSliderFrom = new javax.swing.JTextField(); + propNumberSliderS = new javax.swing.JSlider(); + propNumberSliderTo = new javax.swing.JTextField(); + outputP = new javax.swing.JPanel(); + chartSP = new javax.swing.JScrollPane(); + shownStateKeyP = new javax.swing.JPanel(); + shownStateKeyCB = new javax.swing.JComboBox<>(); + bottomP = new javax.swing.JPanel(); + simulationProgressPB = new javax.swing.JProgressBar(); + controllsP = new javax.swing.JPanel(); + nSimStepsS = new javax.swing.JSpinner(); + runB = new javax.swing.JButton(); + saveP = new javax.swing.JPanel(); + saveActionsP = new javax.swing.JPanel(); + saveAutoCB = new javax.swing.JCheckBox(); + saveFileP = new javax.swing.JPanel(); + saveFileNameTF = new javax.swing.JFormattedTextField(); + saveFileChooserB = new javax.swing.JButton(); + + setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE); + setTitle("Industrial Benchmark - Random Simulation"); + + westP.setLayout(new java.awt.BorderLayout()); + + simPropsP.setBorder(javax.swing.BorderFactory.createTitledBorder("Simulation Properties")); + simPropsP.setLayout(new java.awt.BorderLayout()); + simPropsP.add(simPropsSP, java.awt.BorderLayout.CENTER); + + propNumberSliderP.setLayout(new java.awt.BorderLayout()); + + propNumberSliderCB.setModel(new javax.swing.DefaultComboBoxModel<>(new String[] { "Item 1", "Item 2", "Item 3", "Item 4" })); + propNumberSliderP.add(propNumberSliderCB, java.awt.BorderLayout.NORTH); + + propNumberSliderFrom.setText("0"); + propNumberSliderFrom.setPreferredSize(new java.awt.Dimension(60, 23)); + propNumberSliderP.add(propNumberSliderFrom, java.awt.BorderLayout.WEST); + + propNumberSliderS.setPaintLabels(true); + propNumberSliderS.setPaintTicks(true); + propNumberSliderP.add(propNumberSliderS, java.awt.BorderLayout.CENTER); + + propNumberSliderTo.setText("100"); + propNumberSliderTo.setPreferredSize(new java.awt.Dimension(60, 23)); + propNumberSliderP.add(propNumberSliderTo, java.awt.BorderLayout.EAST); + + simPropsP.add(propNumberSliderP, java.awt.BorderLayout.SOUTH); + + westP.add(simPropsP, java.awt.BorderLayout.CENTER); + + mainSP.setLeftComponent(westP); + + outputP.setLayout(new java.awt.BorderLayout()); + outputP.add(chartSP, java.awt.BorderLayout.CENTER); + + shownStateKeyCB.setModel(new javax.swing.DefaultComboBoxModel<>(new String[] { "Item 1", "Item 2", "Item 3", "Item 4" })); + shownStateKeyCB.setToolTipText("Shown Output Value"); + shownStateKeyP.add(shownStateKeyCB); + + outputP.add(shownStateKeyP, java.awt.BorderLayout.SOUTH); + + mainSP.setRightComponent(outputP); + + getContentPane().add(mainSP, java.awt.BorderLayout.CENTER); + + bottomP.setLayout(new java.awt.BorderLayout()); + + simulationProgressPB.setToolTipText("Simulation Progress"); + bottomP.add(simulationProgressPB, java.awt.BorderLayout.NORTH); + + nSimStepsS.setModel(new javax.swing.SpinnerNumberModel(1500, 1, null, 500)); + nSimStepsS.setToolTipText("Simulation Steps"); + nSimStepsS.setPreferredSize(new java.awt.Dimension(100, 24)); + controllsP.add(nSimStepsS); + + runB.setText("Run"); + controllsP.add(runB); + + bottomP.add(controllsP, java.awt.BorderLayout.CENTER); + + saveP.setBorder(javax.swing.BorderFactory.createTitledBorder("Save results to CSV")); + saveP.setLayout(new java.awt.BorderLayout()); + + saveAutoCB.setText("Auto Save"); + saveAutoCB.setToolTipText("save the results automatically after every simulation run"); + saveActionsP.add(saveAutoCB); + + saveP.add(saveActionsP, java.awt.BorderLayout.CENTER); + + saveFileP.setLayout(new java.awt.BorderLayout()); + + saveFileNameTF.setText("jFormattedTextField1"); + saveFileNameTF.setEnabled(false); + saveFileP.add(saveFileNameTF, java.awt.BorderLayout.CENTER); + + saveFileChooserB.setText("..."); + saveFileChooserB.setEnabled(false); + saveFileP.add(saveFileChooserB, java.awt.BorderLayout.EAST); + + saveP.add(saveFileP, java.awt.BorderLayout.NORTH); + + bottomP.add(saveP, java.awt.BorderLayout.SOUTH); + + getContentPane().add(bottomP, java.awt.BorderLayout.SOUTH); + + pack(); + }// //GEN-END:initComponents + + /** + * @param args the command line arguments + */ + public static void main(final String[] args) { + /* Set the Nimbus look and feel */ + // + /* If Nimbus (introduced in Java SE 6) is not available, stay with the default look and feel. + * For details see http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html + */ + try { + for (final javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) { + if ("Nimbus".equals(info.getName())) { + javax.swing.UIManager.setLookAndFeel(info.getClassName()); + break; + } + } + } catch (final ClassNotFoundException | InstantiationException | IllegalAccessException | javax.swing.UnsupportedLookAndFeelException ex) { + java.util.logging.Logger.getLogger(TrialGuiMain.class.getName()).log(java.util.logging.Level.SEVERE, null, ex); + } + // + + /* Create and display the form */ + java.awt.EventQueue.invokeLater(new Runnable() { + @Override + public void run() { + new TrialGuiMain().setVisible(true); + } + }); + } + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JPanel bottomP; + private javax.swing.JScrollPane chartSP; + private javax.swing.JPanel controllsP; + private javax.swing.JSplitPane mainSP; + private javax.swing.JSpinner nSimStepsS; + private javax.swing.JPanel outputP; + private javax.swing.JComboBox propNumberSliderCB; + private javax.swing.JTextField propNumberSliderFrom; + private javax.swing.JPanel propNumberSliderP; + private javax.swing.JSlider propNumberSliderS; + private javax.swing.JTextField propNumberSliderTo; + private javax.swing.JButton runB; + private javax.swing.JPanel saveActionsP; + private javax.swing.JCheckBox saveAutoCB; + private javax.swing.JButton saveFileChooserB; + private javax.swing.JFormattedTextField saveFileNameTF; + private javax.swing.JPanel saveFileP; + private javax.swing.JPanel saveP; + private javax.swing.JComboBox shownStateKeyCB; + private javax.swing.JPanel shownStateKeyP; + private javax.swing.JPanel simPropsP; + private javax.swing.JScrollPane simPropsSP; + private javax.swing.JProgressBar simulationProgressPB; + private javax.swing.JPanel westP; + // End of variables declaration//GEN-END:variables +} diff --git a/src/main/java/com/siemens/industrialbenchmark/datavector/DataVectorDescription.java b/src/main/java/com/siemens/industrialbenchmark/datavector/DataVectorDescription.java deleted file mode 100755 index 99c583a..0000000 --- a/src/main/java/com/siemens/industrialbenchmark/datavector/DataVectorDescription.java +++ /dev/null @@ -1,89 +0,0 @@ -/** -Copyright 2016 Siemens AG. - -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 com.siemens.industrialbenchmark.datavector; - -import java.util.List; - -import com.google.common.base.Preconditions; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList.Builder; - - -/** - * state/action description for the industrial benchmark - */ -abstract public class DataVectorDescription -{ - protected List names; - - /** - * Constructor with a given List of state/action dimension names - * @param names - */ - public DataVectorDescription (List names) { - Preconditions.checkNotNull(names, "name list must not be null."); - Preconditions.checkArgument(names.size() > 0, "name list has size 0"); - - Builder lb = ImmutableList.builder(); - for (String key : names) { - lb.add(key); - } - this.names = lb.build(); - } - - /** - * Constructor with a given set of state/action description names - * @param names - */ - public DataVectorDescription (String names[]) { - Preconditions.checkNotNull(names, "name list must not be null."); - Preconditions.checkArgument(names.length > 0, "name list has size 0"); - - Builder lb = ImmutableList.builder(); - for (String key : names) { - lb.add(key); - } - this.names = lb.build(); - } - - /** - * Returns the number of variables - * @return the number of variables - */ - public int getNumberVariables() - { - return names.size(); - } - - /** - * returns a List containing the variable names - * @return A List containing the variable names - */ - public List getVarNames() { - return names; - } - - @Override - public boolean equals (Object o) { - DataVectorDescription os = (DataVectorDescription)o; - return names.equals(os.getVarNames()); - } - - @Override - public String toString() { - return names.toString(); - } -} \ No newline at end of file diff --git a/src/main/java/com/siemens/industrialbenchmark/datavector/DataVectorDescriptionImpl.java b/src/main/java/com/siemens/industrialbenchmark/datavector/DataVectorDescriptionImpl.java new file mode 100755 index 0000000..7dfe9fe --- /dev/null +++ b/src/main/java/com/siemens/industrialbenchmark/datavector/DataVectorDescriptionImpl.java @@ -0,0 +1,96 @@ +/* +Copyright 2016 Siemens AG. + +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 com.siemens.industrialbenchmark.datavector; + +import java.util.List; + +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableList.Builder; +import com.siemens.rl.interfaces.DataVectorDescription; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Objects; + + +/** + * state/action description for the industrial benchmark + */ +public abstract class DataVectorDescriptionImpl implements DataVectorDescription { + + private final List varNames; + + /** + * Constructs a new description based on names in a List. + * @param names state/action description names + */ + public DataVectorDescriptionImpl(final List names) { + Preconditions.checkNotNull(names, "name list must not be null."); + Preconditions.checkArgument(!names.isEmpty(), "name list has size 0"); + Preconditions.checkArgument(new HashSet<>(names).size() == names.size(), "name list contains duplicates"); + + final Builder lb = ImmutableList.builder(); + for (final String key : names) { + lb.add(key); + } + this.varNames = lb.build(); + } + + /** + * Constructs a new description based on names in a String array. + * @param names state/action description names + */ + public DataVectorDescriptionImpl(final String... names) { + this(Arrays.asList(names)); + } + + @Override + public int getNumberVariables() { + return varNames.size(); + } + + @Override + public List getVarNames() { + return varNames; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final DataVectorDescriptionImpl other = (DataVectorDescriptionImpl) obj; + return Objects.equals(this.varNames, other.varNames); + } + + @Override + public int hashCode() { + int hash = 7; + hash = 19 * hash + Objects.hashCode(this.varNames); + return hash; + } + + @Override + public String toString() { + return varNames.toString(); + } +} diff --git a/src/main/java/com/siemens/industrialbenchmark/datavector/DataVectorImpl.java b/src/main/java/com/siemens/industrialbenchmark/datavector/DataVectorImpl.java index 447c876..458a31d 100755 --- a/src/main/java/com/siemens/industrialbenchmark/datavector/DataVectorImpl.java +++ b/src/main/java/com/siemens/industrialbenchmark/datavector/DataVectorImpl.java @@ -1,4 +1,4 @@ -/** +/* Copyright 2016 Siemens AG. Licensed under the Apache License, Version 2.0 (the "License"); @@ -22,135 +22,152 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList.Builder; import com.siemens.rl.interfaces.DataVector; +import com.siemens.rl.interfaces.DataVectorDescription; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.util.Arrays; +import java.util.Collections; +import java.util.Map; /** - * This class holds a HashMap of state-dimension/action-dimension -value pairs. - * - * @author Michel Tokic + * This holds a map of state-dimension/action-dimension -value pairs. * + * @author Michel Tokic */ -public class DataVectorImpl implements Cloneable, DataVector { - +public class DataVectorImpl implements DataVector { + + private static final long serialVersionUID = 2L; + + private transient Map indexMap; + private final double[] values; + private final DataVectorDescription description; + private final ImmutableList keys; + + private DataVectorImpl(final DataVectorDescription description, final List keys) { + Preconditions.checkNotNull(keys, "Keys must not be null."); + this.description = description; + this.keys = ImmutableList.copyOf(keys); + createIndexMap(); + this.values = new double[this.keys.size()]; + for (int i = 0; i < this.keys.size(); i++) { + this.values[i] = Double.NaN; + } + } + /** - * + * Initializes the state with a given StateVectorDescription. + * All associated values are set to NaN initially. + * @param description The StateVectorDescription */ - private static final long serialVersionUID = 4956886314253943518L; - - private HashMap indexMap = new HashMap(); - private double values[]; - - DataVectorDescription description = null; - ImmutableList keys; - + public DataVectorImpl(final DataVectorDescription description) { + this(description, description == null ? Collections.emptyList() : description.getVarNames()); + Preconditions.checkNotNull(description, "Description must not be null."); + } + /** - * Initializes the state with a given StateVectorDescription. All associated values are set to NaN initially. - * @param desc The StateVectorDescription + * Initializes the state with a given list of state dimension names. + * All associated values are set to NaN initially. + * @param keys A list of keys. */ - public DataVectorImpl (DataVectorDescription desc) { - Preconditions.checkNotNull(desc, "Description must not be null."); - this.description = desc; - this.keys = ImmutableList.copyOf(desc.getVarNames()); - values = new double[this.keys.size()]; - for (int i=0; i keys) { + this(null, keys); } /** - * Initializes the state with a given list of state dimension names. All associated values are set to NaN initially. - * @param keys A list of keys. + * Initializes the state with the description, keys and values + * of the given state. + * @param otherDataVector the vector to copy */ - public DataVectorImpl (List keys) { - Preconditions.checkNotNull(keys, "Description must not be null."); - this.keys = ImmutableList.copyOf(keys); - values = new double[this.keys.size()]; - for (int i=0; i(keys.size()); + for (int i = 0; i < keys.size(); i++) { + indexMap.put(keys.get(i), i); } } - - /** - * Returns the value for a given state/action dimension - * @param key The state dimension - * @return The value - */ - public Double getValue(String key) { - Preconditions.checkArgument(this.getKeys().contains(key), "%s is not a valid variable", key); - - if (!indexMap.containsKey(key)) { - return Double.NaN; - } else { + + @Override + public DataVectorDescription getDescription() { + return description; + } + + @Override + public Double getValue(final String key) { + Preconditions.checkArgument(this.getKeys().contains(key), + "%s is not a valid variable", key); + + if (indexMap.containsKey(key)) { return values[indexMap.get(key)]; + } else { + return Double.NaN; } } - - /** - * Sets the current value of a given state/action dimension - * @param key The state/action dimension - * @param value The value - */ - public void setValue (String key, double value) { - Preconditions.checkNotNull(this.getKeys(), "keySet is null!!"); - Preconditions.checkArgument(this.getKeys().contains(key), "%s is not a valid variable. Available names are: %s", key, this.getKeys()); + + @Override + public void setValue(final String key, final double value) { + Preconditions.checkNotNull(getKeys(), "keySet is null!!"); + Preconditions.checkArgument(getKeys().contains(key), + "%s is not a valid variable. Available names are: %s", key, getKeys()); values[indexMap.get(key)] = value; } - - /** - * returns a list containing the state/action dimension names - * @return a list containing the state/action dimension names - */ + + @Override public List getKeys() { return this.keys; } - + /** - * returns a list of current values * @return a list of current values */ - public List getValues() { - Builder valueBuilder = new ImmutableList.Builder(); - for (String key : keys) { + public List getValues() { + final Builder valueBuilder = new ImmutableList.Builder<>(); + for (final String key : keys) { valueBuilder.add(values[indexMap.get(key)]); - } + } return valueBuilder.build(); } - /** - * returns a double[] array containing the values - * @return a double[] array containing the values - */ + @Override public double[] getValuesArray() { - double values[] = new double[keys.size()]; - for (int i=0; i= gainMin && gainCandidate <= gainMax, + "gain=%f must be in range [%f, %f].", + gainCandidate, gainMin, gainMax); } /** * @param gain the gain to set */ - public void setGain(double gain) { - double delta = Math.abs(gain- absGain); - Preconditions.checkArgument(gain>= gainMin && gain <= gainMax, "gain=%s must be in range [%s, %s].", gain, gainMin, gainMax); - Preconditions.checkArgument(delta <= maxDelta, "delta_gain=%s out of range. 'gain' must be in range [%s, %s].", absGain-delta, absGain+delta); - this.setValue(ActionDeltaDescription.DeltaGain, gain - this.absGain); - //this.deltaGain = gain - this.absGain; // update delta - this.absGain = gain; + public void setGain(final double gain) { + checkGain(gain); + setDeltaGain(gain - this.gain); + this.gain = gain; } - + + private void checkShift(final double shiftCandidate) { + Preconditions.checkArgument( + shiftCandidate >= shiftMin && shiftCandidate <= shiftMax, + "shift=%f must be in range [%f, %f].", + shiftCandidate, shiftMin, shiftMax); + } + /** * @param shift the shift to set */ - public void setShift(float shift) { - double delta = Math.abs(shift- absShift); - Preconditions.checkArgument(shift>= shiftMin && shift <= shiftMax, "=%s must be in range [%s, %s].", shift, shiftMin, shiftMax); - Preconditions.checkArgument(delta <= maxDelta, "delta_shift=%s out of range. 'C' must be in range [%s, %s].", absShift-delta, absShift+delta); - this.setValue(ActionDeltaDescription.DeltaShift, shift - this.absShift); - //this.deltaGS = gs - this.absGS; - this.absShift = shift; + public void setShift(final float shift) { + checkShift(shift); + setDeltaShift(shift - this.shift); + this.shift = shift; } } + diff --git a/src/main/java/com/siemens/industrialbenchmark/datavector/action/ActionDelta.java b/src/main/java/com/siemens/industrialbenchmark/datavector/action/ActionDelta.java index a08a53b..b81b553 100755 --- a/src/main/java/com/siemens/industrialbenchmark/datavector/action/ActionDelta.java +++ b/src/main/java/com/siemens/industrialbenchmark/datavector/action/ActionDelta.java @@ -1,4 +1,4 @@ -/** +/* Copyright 2016 Siemens AG. Licensed under the Apache License, Version 2.0 (the "License"); @@ -17,82 +17,89 @@ import com.google.common.base.Preconditions; import com.siemens.industrialbenchmark.datavector.DataVectorImpl; -import com.siemens.industrialbenchmark.properties.PropertiesException; /** * This class keeps and checks the deltaA, deltaB and deltaC. - * + * * @author Michel Tokic */ public class ActionDelta extends DataVectorImpl { - /** - * - */ private static final long serialVersionUID = 1159603096632053185L; - protected double maxDelta = 10.0; - + private static final double MAX_DELTA = 100.0; // HACK This was 10.0 before, but the default range is 100.0, so it produced runtime exceptions by default. why use this arbitrary value here anyway? + /** - * Constructor with deltas and properties file + * Constructs an action based on the three delta values. * @param deltaVelocity The delta velocity to apply * @param deltaGain The delta gain to apply * @param deltaShift The delta shift to apply - * @throws PropertiesException */ - public ActionDelta (double deltaVelocity, double deltaGain, double deltaShift) throws PropertiesException { + public ActionDelta(final double deltaVelocity, final double deltaGain, final double deltaShift) { + super(new ActionDeltaDescription()); - super (new ActionDeltaDescription()); - - Preconditions.checkArgument(Math.abs(deltaVelocity) <= maxDelta, "Math.abs(deltaA=%s) must be <= %s", deltaVelocity, maxDelta); - Preconditions.checkArgument(Math.abs(deltaGain) <= maxDelta, "Math.abs(deltaB=%s) must be <= %s", deltaGain, maxDelta); + checkDeltaVelocity(deltaVelocity); + checkDeltaGain(deltaGain); + // XXX Should we really not check delta-shift? - this.setValue(ActionDeltaDescription.DeltaVelocity, deltaVelocity); - this.setValue(ActionDeltaDescription.DeltaGain, deltaGain); - this.setValue(ActionDeltaDescription.DeltaShift, deltaShift); + setValue(ActionDeltaDescription.DELTA_VELOCITY, deltaVelocity); + setValue(ActionDeltaDescription.DELTA_GAIN, deltaGain); + setValue(ActionDeltaDescription.DELTA_SHIFT, deltaShift); } /** * @return the deltaA */ - public double getDeltaVelocity() { - return getValue(ActionDeltaDescription.DeltaVelocity); + public double getDeltaVelocity() { + return getValue(ActionDeltaDescription.DELTA_VELOCITY); } /** * @return the deltaB */ - public double getDeltaGain() { - return getValue(ActionDeltaDescription.DeltaGain); + public double getDeltaGain() { + return getValue(ActionDeltaDescription.DELTA_GAIN); } - + /** * @return the deltaC */ public double getDeltaShift() { - return getValue(ActionDeltaDescription.DeltaShift); + return getValue(ActionDeltaDescription.DELTA_SHIFT); + } + + private void checkDeltaVelocity(final double deltaVelocity) { + Preconditions.checkArgument(Math.abs(deltaVelocity) <= MAX_DELTA, + "abs(delta_velocity=%f) must be <= %f", deltaVelocity, MAX_DELTA); } /** * @param deltaVelocity the delta velocity to set */ - public void setDeltaVelocity(double deltaVelocity) { - Preconditions.checkArgument(Math.abs(deltaVelocity) <= maxDelta, "Math.abs(deltaVelocity=%s) must be <= %s", deltaVelocity, maxDelta); - this.setValue(ActionDeltaDescription.DeltaVelocity, deltaVelocity); + public void setDeltaVelocity(final double deltaVelocity) { + checkDeltaVelocity(deltaVelocity); + setValue(ActionDeltaDescription.DELTA_VELOCITY, deltaVelocity); + } + + private void checkDeltaGain(final double deltaGain) { + Preconditions.checkArgument(Math.abs(deltaGain) <= MAX_DELTA, + "abs(delta_gain=%f) must be <= %f", deltaGain, MAX_DELTA); } /** * @param deltaGain the delta gain to set */ - public void setDeltaGain(double deltaGain) { - Preconditions.checkArgument(Math.abs(deltaGain) <= maxDelta, "Math.abs(deltaGain=%s) must be <= %s", deltaGain, maxDelta); - this.setValue(ActionDeltaDescription.DeltaGain, deltaGain); + public void setDeltaGain(final double deltaGain) { + checkDeltaGain(deltaGain); + setValue(ActionDeltaDescription.DELTA_GAIN, deltaGain); } - + /** * @param deltaShift The delta shift to set. */ - public void setDeltaShift(double deltaShift) { - this.setValue(ActionDeltaDescription.DeltaShift, deltaShift); + public void setDeltaShift(final double deltaShift) { + // XXX Should we really not check delta-shift? + setValue(ActionDeltaDescription.DELTA_SHIFT, deltaShift); } } + diff --git a/src/main/java/com/siemens/industrialbenchmark/datavector/action/ActionDeltaDescription.java b/src/main/java/com/siemens/industrialbenchmark/datavector/action/ActionDeltaDescription.java index 02e59db..a191973 100755 --- a/src/main/java/com/siemens/industrialbenchmark/datavector/action/ActionDeltaDescription.java +++ b/src/main/java/com/siemens/industrialbenchmark/datavector/action/ActionDeltaDescription.java @@ -1,4 +1,4 @@ -/** +/* Copyright 2016 Siemens AG. Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,28 +18,29 @@ import java.util.ArrayList; import java.util.List; -import com.siemens.industrialbenchmark.datavector.DataVectorDescription; +import com.siemens.industrialbenchmark.datavector.DataVectorDescriptionImpl; -/** - * Action description for the ActionDelta - * +/** + * Action description for the ActionDelta. + * * @author Michel Tokic */ -public class ActionDeltaDescription extends DataVectorDescription { - - public static final String DeltaVelocity = "DeltaVelocity"; // Velocity - public static final String DeltaGain = "DeltaGain"; // Gain - public static final String DeltaShift = "DeltaShift"; // GS - - private static List actionVars = new ArrayList(); - - static{ - actionVars.add(DeltaVelocity); - actionVars.add(DeltaGain); - actionVars.add(DeltaShift); - } - - public ActionDeltaDescription() { - super(actionVars); +public class ActionDeltaDescription extends DataVectorDescriptionImpl { + + public static final String DELTA_VELOCITY = "DeltaVelocity"; + public static final String DELTA_GAIN = "DeltaGain"; + public static final String DELTA_SHIFT = "DeltaShift"; + + private static final List ACTION_VARS = new ArrayList(); + + static { + ACTION_VARS.add(DELTA_VELOCITY); + ACTION_VARS.add(DELTA_GAIN); + ACTION_VARS.add(DELTA_SHIFT); + } + + public ActionDeltaDescription() { + super(ACTION_VARS); } } + diff --git a/src/main/java/com/siemens/industrialbenchmark/datavector/action/EffectiveAction.java b/src/main/java/com/siemens/industrialbenchmark/datavector/action/EffectiveAction.java index 93f0298..dd38765 100755 --- a/src/main/java/com/siemens/industrialbenchmark/datavector/action/EffectiveAction.java +++ b/src/main/java/com/siemens/industrialbenchmark/datavector/action/EffectiveAction.java @@ -1,4 +1,4 @@ -/** +/* Copyright 2016 Siemens AG. Licensed under the Apache License, Version 2.0 (the "License"); @@ -24,102 +24,97 @@ import com.siemens.industrialbenchmark.properties.PropertiesUtil; public class EffectiveAction { - - ActionAbsolute action; - - final double alpha; - final double effectiveA; - final double effectiveB; - final double beta; - final double setpoint; - - public EffectiveAction(ActionAbsolute action, double setpoint) { - this.action = action; - this.setpoint = setpoint; - - effectiveA = calcEffectiveA(action.getVelocity(), setpoint); - effectiveB = calcEffectiveB(action.getGain(), setpoint); - alpha = calcAlphaScaled(action.getVelocity(), action.getGain(), setpoint); - beta = calcBetaScaled(action.getGain(), setpoint); + + private final double alpha; + private final double effectiveA; + private final double effectiveB; + private final double beta; + + public EffectiveAction(final ActionAbsolute action, final double setpoint) { + + this.effectiveA = calcEffectiveA(action.getVelocity(), setpoint); + this.effectiveB = calcEffectiveB(action.getGain(), setpoint); + this.alpha = calcAlphaScaled(action.getVelocity(), action.getGain(), setpoint); + this.beta = calcBetaScaled(action.getGain(), setpoint); + } + + public double getEffectiveA() { + return effectiveA; + } + + public double getEffectiveB() { + return effectiveB; } - - public double getEffectiveA() { - return this.effectiveA; + + public double getAlpha() { + return alpha; } - public double getEffectiveB() { - return this.effectiveB; + + public double getBeta() { + return beta; } - - private double calcAlphaScaled(double a, double b, double setpoint) { - final double minAlphaUnscaled = calcAlphaUnscaled(calcEffectiveA(100, setpoint), calcEffectiveB(0, setpoint)); - final double maxAlphaUnscaled = calcAlphaUnscaled(calcEffectiveA(0, setpoint), calcEffectiveB(100, setpoint)); - final double alphaUnscaled = calcAlphaUnscaled(calcEffectiveA(a, setpoint), calcEffectiveB(b, setpoint)); - + + private static double calcAlphaScaled(final double a, final double b, final double setpoint) { + final double minAlphaUnscaled = calcAlphaUnscaled(calcEffectiveA(100, setpoint), calcEffectiveB(0, setpoint)); + final double maxAlphaUnscaled = calcAlphaUnscaled(calcEffectiveA(0, setpoint), calcEffectiveB(100, setpoint)); + final double alphaUnscaled = calcAlphaUnscaled(calcEffectiveA(a, setpoint), calcEffectiveB(b, setpoint)); + return (alphaUnscaled - minAlphaUnscaled) / (maxAlphaUnscaled - minAlphaUnscaled); } - - private double calcBetaScaled(double b, double setpoint) { - final double minBetaUnscaled = calcBetaUnscaled(calcEffectiveB(100, setpoint)); - final double maxBetaUnscaled = calcBetaUnscaled(calcEffectiveB(0, setpoint)); - final double betaUnscaled = calcBetaUnscaled(calcEffectiveB(b, setpoint)); - - return (betaUnscaled - minBetaUnscaled) / (maxBetaUnscaled - minBetaUnscaled); + + private static double calcBetaScaled(final double b, final double setpoint) { + final double minBetaUnscaled = calcBetaUnscaled(calcEffectiveB(100, setpoint)); + final double maxBetaUnscaled = calcBetaUnscaled(calcEffectiveB(0, setpoint)); + final double betaUnscaled = calcBetaUnscaled(calcEffectiveB(b, setpoint)); + + return (betaUnscaled - minBetaUnscaled) / (maxBetaUnscaled - minBetaUnscaled); } - - private double calcEffectiveA (double a, double setpoint) { + + private static double calcEffectiveA(final double a, final double setpoint) { return a + 101.f - setpoint; } - - private double calcEffectiveB (double b, double setpoint) { + + private static double calcEffectiveB(final double b, final double setpoint) { return b + 1.f + setpoint; } - - private double calcAlphaUnscaled (double effectiveA, double effectiveB) { + + private static double calcAlphaUnscaled(final double effectiveA, final double effectiveB) { return (effectiveB + 1.0f) / effectiveA; } - - private double calcBetaUnscaled (double effectiveB) { + + private static double calcBetaUnscaled(final double effectiveB) { return 1.0f / effectiveB; } - - public double getVelocityAlpha () { - return this.alpha; + + public double getVelocityAlpha() { + return alpha; } - - public double getGainBeta() { - return this.beta; + + public double getGainBeta() { + return beta; } - - public static void main(String[] args) { - + + public static void main(final String[] args) { + final float steps = 50; - final int min=0; - final int max=100; - final float step = (max-min) / steps; - - try { - File f = new File ("output.txt"); - PrintWriter writer = null; - writer = new PrintWriter (f); - + final int min = 0; + final int max = 100; + final float step = (max - min) / steps; + + final File file = new File("output.txt"); + try (final PrintWriter writer = new PrintWriter(file)) { // evaluate EffectiveAction class final int setpoint = 100; - Properties props; - props = PropertiesUtil.setpointProperties(new File ("src/main/resources/sim.properties")); - writer.println ("x,y,alpha,beta"); - - for (float x=min; x<=max; x+=step) { - for (float y=min; y<=max; y+=step) { - EffectiveAction action = new EffectiveAction (new ActionAbsolute (x, y, 0.0f, props), setpoint); - writer.println (x + "," + y + "," + action.getVelocityAlpha() + "," + action.getGainBeta()); + final Properties props = PropertiesUtil.loadSetPointProperties(new File("src/main/resources/sim.properties")); + writer.println("x,y,alpha,beta"); + + for (float x = min; x <= max; x += step) { + for (float y = min; y <= max; y += step) { + final EffectiveAction action = new EffectiveAction(new ActionAbsolute(x, y, 0.0f, props), setpoint); + writer.println(x + "," + y + "," + action.getVelocityAlpha() + "," + action.getGainBeta()); } } - - writer.close(); - - } catch (IOException e) { - e.printStackTrace(); - } catch (PropertiesException e) { + } catch (final IOException | PropertiesException e) { e.printStackTrace(); } } diff --git a/src/main/java/com/siemens/industrialbenchmark/datavector/state/MarkovianState.java b/src/main/java/com/siemens/industrialbenchmark/datavector/state/MarkovianState.java index df4101f..1fbcde4 100755 --- a/src/main/java/com/siemens/industrialbenchmark/datavector/state/MarkovianState.java +++ b/src/main/java/com/siemens/industrialbenchmark/datavector/state/MarkovianState.java @@ -1,4 +1,4 @@ -/** +/* Copyright 2016 Siemens AG. Licensed under the Apache License, Version 2.0 (the "License"); @@ -21,17 +21,22 @@ public class MarkovianState extends DataVectorImpl { + private static final long serialVersionUID = -4104133495199919343L; /** - * + * Constructs a new description. + * @param operationalCostVars names of convoluted operational cost variables */ - private static final long serialVersionUID = -4104133495199919343L; + public MarkovianState(final List operationalCostVars) { + super(new MarkovianStateDescription(operationalCostVars)); + } /** - * Constructor with the names of convoluted operationalcost variables - * @param operationalcostVars + * Copy-constructor. + * @param otherMarkovianState the state to copy */ - public MarkovianState(List operationalcostVars) { - super(new MarkovianStateDescription(operationalcostVars)); + public MarkovianState(final MarkovianState otherMarkovianState) { + super(otherMarkovianState); } } + diff --git a/src/main/java/com/siemens/industrialbenchmark/datavector/state/MarkovianStateDescription.java b/src/main/java/com/siemens/industrialbenchmark/datavector/state/MarkovianStateDescription.java index dc232be..6d85bc6 100755 --- a/src/main/java/com/siemens/industrialbenchmark/datavector/state/MarkovianStateDescription.java +++ b/src/main/java/com/siemens/industrialbenchmark/datavector/state/MarkovianStateDescription.java @@ -1,4 +1,4 @@ -/** +/* Copyright 2016 Siemens AG. Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,94 +18,92 @@ import java.util.ArrayList; import java.util.List; -import com.siemens.industrialbenchmark.datavector.DataVectorDescription; +import com.siemens.industrialbenchmark.datavector.DataVectorDescriptionImpl; -/** - * Markovian state description for the industrial benchmark - * +/** + * Markovian state description for the industrial benchmark. + * * @author Alexander Hentschel, Michel Tokic */ -public class MarkovianStateDescription extends DataVectorDescription { - - public static final String CurrentOperationalCost = "CurrentOperationalCost"; - public static final String FatigueLatent2 = "FatigueLatent2"; - public static final String FatigueLatent1 = "FatigueLatent1"; - - // gold stone - public static final String EffectiveShift = "EffectiveShift"; - public static final String MisCalibrationDomain = "MisCalibrationDomain"; - public static final String MisCalibrationSystemResponse = "MisCalibrationSystemResponse"; - public static final String MisCalibrationPhiIdx = "MisCalibrationPhiIdx"; - - public static final String EffectiveActionGainBeta = "EffectiveActionGainBeta"; - public static final String EffectiveActionVelocityAlpha = "EffectiveActionVelocityAlpha"; - - public static final String RewardFatigue="RewardFatigue"; - public static final String RewardFatigueWeighted="RewardFatigueWeighted"; - public static final String RewardConsumption="RewardConsumption"; - public static final String RewardConsumptionWeighted="RewardConsumptionWeighted"; - public static final String MisCalibration ="MisCalibration"; - public static final String RandomSeed = "RandomSeed"; - - /* observables */ - public static final String SetPoint = "SetPoint"; - public static final String Action_Velocity = "Velocity"; - public static final String Action_Gain = "Gain"; - public static final String Action_Shift = "Shift"; - public static final String Fatigue = "Fatigue"; - public static final String FatigueBase = "FatigueBase"; // without bifurcation aspects - public static final String OperationalCostsConv = "OperationalCostsConv"; - public static final String Consumption = "Consumption"; - public static final String RewardTotal = "RewardTotal"; - - - private static List mStateVars = new ArrayList(); - - static{ - // hidden state variables: - mStateVars.add(CurrentOperationalCost); - mStateVars.add(FatigueLatent2); - mStateVars.add(FatigueLatent1); - mStateVars.add(EffectiveShift); - mStateVars.add(MisCalibrationDomain); - mStateVars.add(MisCalibrationSystemResponse); - mStateVars.add(MisCalibrationPhiIdx); - mStateVars.add(EffectiveActionGainBeta); - mStateVars.add(EffectiveActionVelocityAlpha); - mStateVars.add(RewardConsumption); - mStateVars.add(RewardFatigue); - mStateVars.add(MisCalibration); - mStateVars.add(RewardConsumptionWeighted); - mStateVars.add(RewardFatigueWeighted); - mStateVars.add(RandomSeed); - mStateVars.add(Consumption); - - // observables: - mStateVars.add(SetPoint); - mStateVars.add(Action_Velocity); - mStateVars.add(Action_Gain); - mStateVars.add(Action_Shift); - mStateVars.add(Fatigue); - mStateVars.add(FatigueBase); - mStateVars.add(OperationalCostsConv); - mStateVars.add(RewardTotal); - } - - /** - * constructor with operationalcost_XXX - * @param names - */ - public MarkovianStateDescription (List names) { - super (names); - //System.out.println ("MarkovianState names: " + names.toString()); - } - - /** - * returns the list of non-convoluted internal variables - * @return the list of non-convoluted internal variables - * - */ - public static List getNonConvolutedInternalVariables() { - return mStateVars; - } +public class MarkovianStateDescription extends DataVectorDescriptionImpl { + + public static final String CURRENT_OPERATIONAL_COST = "CurrentOperationalCost"; + public static final String FATIGUE_LATENT_2 = "FatigueLatent2"; + public static final String FATIGUE_LATENT_1 = "FatigueLatent1"; + + // gold stone + public static final String EFFECTIVE_SHIFT = "EffectiveShift"; + public static final String MIS_CALIBRATION_DOMAIN = "MisCalibrationDomain"; + public static final String MIS_CALIBRATION_SYSTEM_RESPONSE = "MisCalibrationSystemResponse"; + public static final String MIS_CALIBRATION_PHI_IDX = "MisCalibrationPhiIdx"; + + public static final String EFFECTIVE_ACTION_GAIN_BETA = "EffectiveActionGainBeta"; + public static final String EFFECTIVE_ACTION_VELOCITY_ALPHA = "EffectiveActionVelocityAlpha"; + + public static final String REWARD_FATIGUE = "RewardFatigue"; + public static final String REWARD_FATIGUE_WEIGHTED = "RewardFatigueWeighted"; + public static final String REWARD_CONSUMPTION = "RewardConsumption"; + public static final String REWARD_CONSUMPTION_WEIGHTED = "RewardConsumptionWeighted"; + public static final String MIS_CALIBRATION = "MisCalibration"; + public static final String RANDOM_SEED = "RandomSeed"; + + /* observables */ + public static final String SET_POINT = "SetPoint"; + public static final String ACTION_VELOCITY = "Velocity"; + public static final String ACTION_GAIN = "Gain"; + public static final String ACTION_SHIFT = "Shift"; + public static final String FATIGUE = "Fatigue"; + public static final String FATIGUE_BASE = "FatigueBase"; // without bifurcation aspects + public static final String OPERATIONAL_COSTS_CONV = "OperationalCostsConv"; + public static final String CONSUMPTION = "Consumption"; + public static final String REWARD_TOTAL = "RewardTotal"; + + private static final List STATE_VARS = new ArrayList(); + + static { + // hidden state variables + STATE_VARS.add(CURRENT_OPERATIONAL_COST); + STATE_VARS.add(FATIGUE_LATENT_2); + STATE_VARS.add(FATIGUE_LATENT_1); + STATE_VARS.add(EFFECTIVE_SHIFT); + STATE_VARS.add(MIS_CALIBRATION_DOMAIN); + STATE_VARS.add(MIS_CALIBRATION_SYSTEM_RESPONSE); + STATE_VARS.add(MIS_CALIBRATION_PHI_IDX); + STATE_VARS.add(EFFECTIVE_ACTION_GAIN_BETA); + STATE_VARS.add(EFFECTIVE_ACTION_VELOCITY_ALPHA); + STATE_VARS.add(REWARD_CONSUMPTION); + STATE_VARS.add(REWARD_FATIGUE); + STATE_VARS.add(MIS_CALIBRATION); + STATE_VARS.add(REWARD_CONSUMPTION_WEIGHTED); + STATE_VARS.add(REWARD_FATIGUE_WEIGHTED); + STATE_VARS.add(RANDOM_SEED); + STATE_VARS.add(CONSUMPTION); + + // observables: + STATE_VARS.add(SET_POINT); + STATE_VARS.add(ACTION_VELOCITY); + STATE_VARS.add(ACTION_GAIN); + STATE_VARS.add(ACTION_SHIFT); + STATE_VARS.add(FATIGUE); + STATE_VARS.add(FATIGUE_BASE); + STATE_VARS.add(OPERATIONAL_COSTS_CONV); + STATE_VARS.add(REWARD_TOTAL); + } + + /** + * Constructs a new description. + * @param names state/action description names + */ + public MarkovianStateDescription(final List names) { + super(names); + //System.out.println("MarkovianState names: " + names.toString()); + } + + /** + * @return the list of non-convoluted internal variables + */ + public static List getNonConvolutedInternalVariables() { + return STATE_VARS; + } } + diff --git a/src/main/java/com/siemens/industrialbenchmark/datavector/state/ObservableState.java b/src/main/java/com/siemens/industrialbenchmark/datavector/state/ObservableState.java index 7ffe7d7..2050c1b 100755 --- a/src/main/java/com/siemens/industrialbenchmark/datavector/state/ObservableState.java +++ b/src/main/java/com/siemens/industrialbenchmark/datavector/state/ObservableState.java @@ -1,4 +1,4 @@ -/** +/* Copyright 2016 Siemens AG. Licensed under the Apache License, Version 2.0 (the "License"); @@ -17,11 +17,10 @@ import com.siemens.industrialbenchmark.datavector.DataVectorImpl; - public class ObservableState extends DataVectorImpl { /** - * + * */ private static final long serialVersionUID = 6835795950225418933L; @@ -29,3 +28,4 @@ public ObservableState() { super(new ObservableStateDescription()); } } + diff --git a/src/main/java/com/siemens/industrialbenchmark/datavector/state/ObservableStateDescription.java b/src/main/java/com/siemens/industrialbenchmark/datavector/state/ObservableStateDescription.java index db2c856..0fe91f7 100755 --- a/src/main/java/com/siemens/industrialbenchmark/datavector/state/ObservableStateDescription.java +++ b/src/main/java/com/siemens/industrialbenchmark/datavector/state/ObservableStateDescription.java @@ -1,4 +1,4 @@ -/** +/* Copyright 2016 Siemens AG. Licensed under the Apache License, Version 2.0 (the "License"); @@ -15,27 +15,33 @@ */ package com.siemens.industrialbenchmark.datavector.state; -import com.siemens.industrialbenchmark.datavector.DataVectorDescription; +import com.siemens.industrialbenchmark.datavector.DataVectorDescriptionImpl; /** * State description for the industrial benchmark. */ -public class ObservableStateDescription extends DataVectorDescription { - - // Definition of the observable variables from the markov state - public static final String SetPoint = MarkovianStateDescription.SetPoint; - public static final String Action_Velocity = MarkovianStateDescription.Action_Velocity; - public static final String Action_Gain = MarkovianStateDescription.Action_Gain; - public static final String Action_Shift = MarkovianStateDescription.Action_Shift; - public static final String Fatigue = MarkovianStateDescription.Fatigue; - public static final String Consumption = MarkovianStateDescription.Consumption; - public static final String RewardTotal = MarkovianStateDescription.RewardTotal; - - private final static String[] mStateVars = new String[] { - SetPoint, Action_Velocity, Action_Gain, Action_Shift, Fatigue, RewardTotal, Consumption - }; - - public ObservableStateDescription () { - super (mStateVars); - } -} \ No newline at end of file +public class ObservableStateDescription extends DataVectorDescriptionImpl { + + // Definition of the observable variables from the markov state ... + public static final String SET_POINT = MarkovianStateDescription.SET_POINT; + public static final String ACTION_VELOCITY = MarkovianStateDescription.ACTION_VELOCITY; + public static final String ACTION_GAIN = MarkovianStateDescription.ACTION_GAIN; + public static final String ACTION_SHIFT = MarkovianStateDescription.ACTION_SHIFT; + public static final String FATIGUE = MarkovianStateDescription.FATIGUE; + public static final String CONSUMPTION = MarkovianStateDescription.CONSUMPTION; + public static final String REWARD_TOTAL = MarkovianStateDescription.REWARD_TOTAL; + + private static final String[] STATE_VARS = new String[] { + SET_POINT, + ACTION_VELOCITY, + ACTION_GAIN, + ACTION_SHIFT, + FATIGUE, + REWARD_TOTAL, + CONSUMPTION + }; + + public ObservableStateDescription() { + super(STATE_VARS); + } +} diff --git a/src/main/java/com/siemens/industrialbenchmark/dynamics/IndustrialBenchmarkDynamics.java b/src/main/java/com/siemens/industrialbenchmark/dynamics/IndustrialBenchmarkDynamics.java index 404cb78..7772d93 100755 --- a/src/main/java/com/siemens/industrialbenchmark/dynamics/IndustrialBenchmarkDynamics.java +++ b/src/main/java/com/siemens/industrialbenchmark/dynamics/IndustrialBenchmarkDynamics.java @@ -1,4 +1,4 @@ -/** +/* Copyright 2016 Siemens AG. Licensed under the Apache License, Version 2.0 (the "License"); @@ -22,7 +22,6 @@ import org.apache.commons.collections.buffer.CircularFifoBuffer; import org.apache.commons.math3.random.RandomDataGenerator; -import org.apache.log4j.Logger; import com.google.common.base.Preconditions; import com.siemens.industrialbenchmark.datavector.action.ActionAbsolute; @@ -40,154 +39,165 @@ import com.siemens.rl.interfaces.Environment; import com.siemens.rl.interfaces.ExternalDriver; -/** Basic dynamics of the industrial benchmark. The following steps are contained; - *
    - *
  • update setpoint: the setpoint influences the state and changes according to a random walk, generated by a {@link SetPointGenerator}
  • - *
  • the influence of the actions is added to the state
  • - *
  • operationalcosts are computed
  • - *
  • operationalcosts are delayed and a convoluted operationalcost level is calculated
  • - *
- * +/** + * Basic dynamics of the industrial benchmark. + * The following steps are contained; + *
    + *
  • update setpoint: the setpoint influences the state and changes according to a random walk, + * generated by a {@link SetPointGenerator}
  • + *
  • the influence of the actions is added to the state
  • + *
  • operational costs are computed
  • + *
  • operational costs are delayed, and a convoluted operational cost level is calculated
  • + *
+ * * @author Siegmund Duell, Alexander Hentschel, Michel Tokic */ -public class IndustrialBenchmarkDynamics implements Environment -{ - /** Variable Names */ - //protected Logger mLogger = Logger.getLogger(IndustrialBenchmarkDynamics.class); - protected final float STEP_SIZE_VELOCITY; - protected final float STEP_SIZE_GAIN; +public class IndustrialBenchmarkDynamics implements Environment { + + //private Logger mLogger = Logger.getLogger(IndustrialBenchmarkDynamics.class); + private final transient float stepSizeVelocity; + private final transient float stepSizeGain; /** Ring Buffer of fixed size implementing a FIFO queue */ - protected CircularFifoBuffer mOperationalCostsBuffer; + private CircularFifoBuffer operationalCostsBuffer; - private float[] mEmConvWeights; - private boolean convToInit = true; - - private GoldstoneEnvironment gsEnvironment; - private final float maxRequiredStep = (float) Math.sin(15.0f/180.0f*Math.PI); - private final float gsBound = 1.5f; - private final float gsSetPointDependency = 0.02f; + private transient float[] emConvWeights; + private boolean convToInit; + + private final GoldstoneEnvironment gsEnvironment; + private static final float MAX_REQUIRED_STEP = (float) Math.sin(15.0f / 180.0f * Math.PI); + private static final float GS_BOUND = 1.5f; + private static final float GS_SET_POINT_DEPENDENCY = 0.02f; private enum C { - DGain, DVelocity, DSetPoint, + DGain, DVelocity, DSetPoint, CostSetPoint, CostGain, CostVelocity, DBase, STEP_SIZE_GAIN, STEP_SIZE_VELOCITY } - protected MarkovianState markovState; - protected MarkovianState mMax; - protected MarkovianState mMin; - protected final Properties mProperties; - - private IndustrialBenchmarkRewardFunction mRewardCore; - private RandomDataGenerator rda = new RandomDataGenerator(); - private long randomSeed = 0; - private float CRGS; - - private List markovStateAdditionalNames; - private List externalDrivers = new ArrayList(); - private final ActionDelta zeroAction = new ActionDelta(0, 0, 0); - - - /** - * Constructor with configuration Properties - * @param aProperties The properties objects - * @throws PropertiesException - */ - public IndustrialBenchmarkDynamics(Properties aProperties) throws PropertiesException { - mProperties = aProperties; - mRewardCore = new IndustrialBenchmarkRewardFunction(aProperties); - STEP_SIZE_GAIN = PropertiesUtil.getFloat(mProperties, C.STEP_SIZE_GAIN.name(), true); - STEP_SIZE_VELOCITY = PropertiesUtil.getFloat(mProperties, C.STEP_SIZE_VELOCITY.name(), true); - - externalDrivers.add(new SetPointGenerator(mProperties)); - - init(); - step(zeroAction); - } - - /** - * Constructor with configuration Properties and external driver list - * @param aProperties The properties objects - * @param externalDrivers The list containing external drivers - * @throws PropertiesException - */ - public IndustrialBenchmarkDynamics(Properties aProperties, List externalDrivers) throws PropertiesException { - this(aProperties); - - this.externalDrivers = externalDrivers; - - init(); - step(zeroAction); - } + private MarkovianState markovState; + private transient MarkovianState max; + private transient MarkovianState min; + private final Properties properties; + + private transient IndustrialBenchmarkRewardFunction rewardCore; + private final RandomDataGenerator rda; + private transient long randomSeed; + private transient float crgs; + + private final List externalDrivers; + private static final ActionDelta ZERO_ACTION = new ActionDelta(0.0, 0.0, 0.0); /** - * initialize the industrial benchmark - * @throws PropertiesException + * Constructor with configuration Properties and external driver list. + * @param aProperties The properties objects + * @param externalDrivers The list containing external drivers + * @throws PropertiesException if property values are badly formatted + */ + public IndustrialBenchmarkDynamics( + final Properties aProperties, + final List externalDrivers) + throws PropertiesException + { + this.properties = aProperties; + this.rewardCore = new IndustrialBenchmarkRewardFunction(aProperties); + this.stepSizeGain = PropertiesUtil.getFloat(properties, C.STEP_SIZE_GAIN.name(), true); + this.stepSizeVelocity = PropertiesUtil.getFloat(properties, C.STEP_SIZE_VELOCITY.name(), true); + this.convToInit = true; + + if (externalDrivers == null) { + this.externalDrivers = new ArrayList<>(1); + this.externalDrivers.add(new SetPointGenerator(properties)); + } else { + this.externalDrivers = new ArrayList<>(externalDrivers); + } + this.gsEnvironment = new GoldstoneEnvironment(24, MAX_REQUIRED_STEP, MAX_REQUIRED_STEP / 2.0); + this.rda = new RandomDataGenerator(); + + init(); + step(ZERO_ACTION); + } + + /** + * Constructor with configuration Properties. + * @param aProperties The properties objects + * @throws PropertiesException if property values are badly formatted + */ + public IndustrialBenchmarkDynamics(final Properties aProperties) throws PropertiesException { + this(aProperties, null); + } + + /** + * Initializes the industrial benchmark. + * @throws PropertiesException if property values are badly formatted */ - protected void init() throws PropertiesException { - - // configure convolution variables - CRGS = PropertiesUtil.getFloat(mProperties, "CRGS", true); - mEmConvWeights = getFloatArray(mProperties.getProperty("ConvArray")); - markovStateAdditionalNames = new ArrayList (); - mOperationalCostsBuffer = new CircularFifoBuffer(mEmConvWeights.length); - for (int i = 0; i < mEmConvWeights.length; i++) { - mOperationalCostsBuffer.add(0.0d); // initialize all operationalcosts with zero - markovStateAdditionalNames.add("OPERATIONALCOST_" + i); // add operationalcost_lag to list of convoluted markov variables - } - markovStateAdditionalNames.addAll(MarkovianStateDescription.getNonConvolutedInternalVariables()); - - // add variables from external driver - List extNames = new ArrayList(); - for (ExternalDriver d : this.externalDrivers) { - for (String n : d.getState().getKeys()) { - if (!extNames.contains(n)) { - extNames.add(n); - } - } - } - markovStateAdditionalNames.addAll(extNames); - //markovStateAdditionalNames.addAll(extDriver.getState().getKeys()); - - // instantiate markov state with additional convolution variable names - markovState = new MarkovianState(markovStateAdditionalNames); - mMin = new MarkovianState(markovStateAdditionalNames); // lower variable boundaries - mMax = new MarkovianState(markovStateAdditionalNames); // upper variable boundaries - - // extract variable boundings + initial values from Properties - for (String v : this.markovState.getKeys()) { - float init = PropertiesUtil.getFloat(mProperties, v + "_INIT", 0); - float max = PropertiesUtil.getFloat(mProperties, v + "_MAX", Float.MAX_VALUE); - float min = PropertiesUtil.getFloat(mProperties, v + "_MIN", -Float.MAX_VALUE); - Preconditions.checkArgument(max > min, "variable=%s: max=%s must be > than min=%s", v, max, min); - Preconditions.checkArgument(init >= min && init <= max, "variable=%s: init=%s must be between min=%s and max=%s", v, init, min, max); - mMax.setValue(v, max); - mMin.setValue(v, min); - markovState.setValue(v, init); - } - - // seed all random number generators for allowing to re-conduct the experiment - randomSeed = PropertiesUtil.getLong(mProperties, "SEED", System.currentTimeMillis()); - //mLogger.debug("init seed: " + randomSeed); - rda.reSeed(randomSeed); - - //extDriver.setSeed(rda.nextLong(0, Long.MAX_VALUE)); - for (ExternalDriver d : this.externalDrivers) { - d.setSeed(rda.nextLong(0, Long.MAX_VALUE)); - d.filter(markovState); - } - - this.gsEnvironment = new GoldstoneEnvironment(24, maxRequiredStep, maxRequiredStep/2.0); + protected final void init() throws PropertiesException { + + // configure convolution variables + crgs = PropertiesUtil.getFloat(properties, "CRGS", true); + emConvWeights = getFloatArray(properties.getProperty("ConvArray")); + final List markovStateAdditionalNames = new ArrayList<>(); + operationalCostsBuffer = new CircularFifoBuffer(emConvWeights.length); + for (int cwi = 0; cwi < emConvWeights.length; cwi++) { + // initialize all operational costs with zero + operationalCostsBuffer.add(0.0); + // add operational cost lag to list of convoluted markov variables + markovStateAdditionalNames.add("OPERATIONALCOST_" + cwi); + } + markovStateAdditionalNames.addAll(MarkovianStateDescription.getNonConvolutedInternalVariables()); + + // add variables from external driver + final List extNames = new ArrayList<>(); + for (final ExternalDriver drv : externalDrivers) { + for (final String name : drv.getState().getKeys()) { + if (!extNames.contains(name) && !markovStateAdditionalNames.contains(name)) { + extNames.add(name); + } + } + } + markovStateAdditionalNames.addAll(extNames); + //markovStateAdditionalNames.addAll(extDriver.getState().getKeys()); + + // instantiate markov state with additional convolution variable names + markovState = new MarkovianState(markovStateAdditionalNames); + // lower variable boundaries + min = new MarkovianState(markovStateAdditionalNames); + // upper variable boundaries + max = new MarkovianState(markovStateAdditionalNames); + + // extract variable boundings + initial values from Properties + for (final String var : markovState.getKeys()) { + final float init = PropertiesUtil.getFloat(properties, var + "_INIT", 0.0f); + final float mMax = PropertiesUtil.getFloat(properties, var + "_MAX", Float.MAX_VALUE); + final float mMin = PropertiesUtil.getFloat(properties, var + "_MIN", -Float.MAX_VALUE); + Preconditions.checkArgument(mMax > mMin, "variable=%s: max=%s must be > than min=%s", + var, mMax, mMin); + Preconditions.checkArgument(init >= mMin && init <= mMax, + "variable=%s: init=%s must be between min=%s and max=%s", var, init, mMin, mMax); + max.setValue(var, mMax); + min.setValue(var, mMin); + markovState.setValue(var, init); + } + + // seed all random number generators for allowing to re-conduct the experiment + randomSeed = PropertiesUtil.getLong(properties, "SEED", System.currentTimeMillis()); + //mLogger.debug("init seed: " + randomSeed); + rda.reSeed(randomSeed); + + //extDriver.setSeed(rda.nextLong(0L, Long.MAX_VALUE)); + for (final ExternalDriver drv : externalDrivers) { + drv.setSeed(rda.nextLong(0L, Long.MAX_VALUE)); + drv.filter(markovState); + } // set all NaN values to 0.0 - for (String key : markovState.getKeys()) { + for (final String key : markovState.getKeys()) { if (Double.isNaN(markovState.getValue(key))) { - markovState.setValue(key, 0.0); + markovState.setValue(key, 0.0); } } - - //for (String key : markovState.getKeys()) { + + //for (final String key : markovState.getKeys()) { // mLogger.debug(key + "=" + markovState.getValue(key)); //} //System.exit(-1); @@ -195,349 +205,383 @@ protected void init() throws PropertiesException { } /** - * converts a float array represented as a string (e.g. "0.01, 0.2, 0.9") to a Java float[] + * converts a float array represented as a string (e.g. "0.01, 0.2, 0.9") to a Java float[] * @param aFloatArrayAsString The float array represented as a String * @return The float[] array */ - private float[] getFloatArray(String aFloatArrayAsString) { + private static float[] getFloatArray(final String aFloatArrayAsString) { // remove all whitespace - String components = aFloatArrayAsString.replaceAll("( |\t|\n)", ""); - String[] split = components.split(","); - float[] result = new float[split.length]; - for (int i = 0; i < result.length; i++) { - result[i] = Float.parseFloat(split[i]); + final String components = aFloatArrayAsString.replaceAll("( |\t|\n)", ""); + final String[] split = components.split(","); + final float[] result = new float[split.length]; + for (int ri = 0; ri < result.length; ri++) { + result[ri] = Float.parseFloat(split[ri]); } return result; } - /** - * Returns the observable components from the markovian state. - * - * @return current state of the industrial benchmark - */ - public ObservableState getState() { - ObservableState s = new ObservableState(); - for (String key : s.getKeys()) { - s.setValue(key, this.markovState.getValue(key)); - } - return s; - } + /** + * Returns the observable components from the markovian state. + * + * @return current state of the industrial benchmark + */ + @Override + public ObservableState getState() { + final ObservableState state = new ObservableState(); + for (final String key : state.getKeys()) { + state.setValue(key, markovState.getValue(key)); + } + return state; + } /** * This function applies an action to the industrial benchmark * @param aAction The industrial benchmark action * @return The successor state - * @throws PropertiesException */ - @Override - public double step(DataVector aAction) { + @Override + public double step(final DataVector aAction) { - // apply randomSeed to PRNGs and external drivers + filter (e.g. setpoint) - this.rda.reSeed(randomSeed); - for (ExternalDriver d : externalDrivers) { - d.setSeed(rda.nextLong(0, Long.MAX_VALUE)); - d.filter(this.markovState); - } + // apply randomSeed to PRNGs and external drivers + filter (e.g. setpoint) + rda.reSeed(randomSeed); + for (final ExternalDriver drv : externalDrivers) { + drv.setSeed(rda.nextLong(0L, Long.MAX_VALUE)); + drv.filter(markovState); + } // add actions to state: addAction((ActionDelta) aAction); try { - // update spiking dynamics - updateFatigue(); + // update spiking dynamics + updateFatigue(); // updated current operationalcost updateCurrentOperationalCost(); - - } catch (PropertiesException e) { - e.printStackTrace(); + } catch (final PropertiesException ex) { + ex.printStackTrace(); } // update convoluted operationalcosts updateOperationalCostCovolution(); - + // update gs updateGS(); - + updateOperationalCosts(); - - - // update reward - mRewardCore.calcReward(markovState); - - // set random seed for next iteration - this.randomSeed = rda.nextLong(0, Long.MAX_VALUE); - this.markovState.setValue(MarkovianStateDescription.RandomSeed, Double.longBitsToDouble(this.randomSeed)); - - //return observableState; - return this.markovState.getValue(ObservableStateDescription.RewardTotal); + + // update reward + rewardCore.calcReward(markovState); + + // set random seed for next iteration + randomSeed = rda.nextLong(0L, Long.MAX_VALUE); + markovState.setValue(MarkovianStateDescription.RANDOM_SEED, Double.longBitsToDouble(randomSeed)); + + //return observableState; + return markovState.getValue(ObservableStateDescription.REWARD_TOTAL); } - + private void updateOperationalCosts() { - - - double rGS = markovState.getValue(MarkovianStateDescription.MisCalibration); - - // set new OperationalCosts - double eNewHidden = (markovState.getValue(MarkovianStateDescription.OperationalCostsConv) - (CRGS * (rGS - 1.0))); - double operationalcosts = eNewHidden - rda.nextGaussian(0, 1) * (1+0.005*eNewHidden); - - markovState.setValue(MarkovianStateDescription.Consumption, operationalcosts); + + final double rGS = markovState.getValue(MarkovianStateDescription.MIS_CALIBRATION); + + // set new OperationalCosts + final double eNewHidden + = markovState.getValue(MarkovianStateDescription.OPERATIONAL_COSTS_CONV) + - (crgs * (rGS - 1.0)); + final double operationalcosts = eNewHidden + - rda.nextGaussian(0.0, 1.0) * (1.0 + 0.005 * eNewHidden); + + markovState.setValue(MarkovianStateDescription.CONSUMPTION, operationalcosts); } - private void addAction(ActionDelta aAction) { - - double velocityMax = mMax.getValue(MarkovianStateDescription.Action_Velocity); - double velocityMin = mMin.getValue(MarkovianStateDescription.Action_Velocity); - double velocity = Math.min(velocityMax, Math.max(velocityMin, markovState.getValue(MarkovianStateDescription.Action_Velocity) + aAction.getDeltaVelocity() * STEP_SIZE_VELOCITY)); - if(aAction instanceof ActionAbsolute){ - double velocityToSet = ((ActionAbsolute)aAction).getVelocity(); - double diff = velocityToSet - markovState.getValue(MarkovianStateDescription.Action_Velocity); - if(diff>STEP_SIZE_VELOCITY){ - diff = STEP_SIZE_VELOCITY; - }else if(diff<-STEP_SIZE_VELOCITY){ - diff = -STEP_SIZE_VELOCITY; - } - velocity = Math.min(velocityMax, Math.max(velocityMin, markovState.getValue(MarkovianStateDescription.Action_Velocity) + diff)); - } - - double gainMax = mMax.getValue(MarkovianStateDescription.Action_Gain); - double gainMin = mMin.getValue(MarkovianStateDescription.Action_Gain); - double gain = Math.min(gainMax, Math.max(gainMin, markovState.getValue(MarkovianStateDescription.Action_Gain) + aAction.getDeltaGain() * STEP_SIZE_GAIN)); - if(aAction instanceof ActionAbsolute){ - double gainToSet = ((ActionAbsolute)aAction).getGain(); - double diff = gainToSet - markovState.getValue(MarkovianStateDescription.Action_Gain); - if(diff>STEP_SIZE_GAIN){ - diff = STEP_SIZE_GAIN; - }else if(diff<-STEP_SIZE_GAIN){ - diff = -STEP_SIZE_GAIN; - } - gain = Math.min(gainMax, Math.max(gainMin, markovState.getValue(MarkovianStateDescription.Action_Gain) + diff)); - } - - // beide: 10 = 2*1.5 + 0.07*100 - final double gsScale = 2.0f*gsBound + 100.0f*gsSetPointDependency; - double shift = (float) Math.min(100.0f, Math.max(0.0f, markovState.getValue(MarkovianStateDescription.Action_Shift) + aAction.getDeltaShift()*(maxRequiredStep/0.9f)*100.0f/gsScale)); - if(aAction instanceof ActionAbsolute){ - double shiftToSet = ((ActionAbsolute)aAction).getShift(); - double diff = shiftToSet - markovState.getValue(MarkovianStateDescription.Action_Shift); - if(diff>((maxRequiredStep/0.9f)*100.0f/gsScale)){ - diff = ((maxRequiredStep/0.9f)*100.0f/gsScale); - }else if(diff<-((maxRequiredStep/0.9f)*100.0f/gsScale)){ - diff = -((maxRequiredStep/0.9f)*100.0f/gsScale); - } - shift = (float) Math.min(100.0f, Math.max(0.0f, markovState.getValue(MarkovianStateDescription.Action_Shift) + diff)); - } - double hiddenShift = (float) Math.min(gsBound, Math.max(-gsBound, (gsScale*shift/100.0f - gsSetPointDependency*markovState.getValue(MarkovianStateDescription.SetPoint) - gsBound))); - - markovState.setValue(MarkovianStateDescription.Action_Velocity, velocity); - markovState.setValue(MarkovianStateDescription.Action_Gain, gain); - markovState.setValue(MarkovianStateDescription.Action_Shift, shift); - markovState.setValue(MarkovianStateDescription.EffectiveShift, hiddenShift); + private void addAction(final ActionDelta aAction) { + + final double velocityMax = max.getValue(MarkovianStateDescription.ACTION_VELOCITY); + final double velocityMin = min.getValue(MarkovianStateDescription.ACTION_VELOCITY); + double velocity = Math.min(velocityMax, Math.max(velocityMin, + markovState.getValue(MarkovianStateDescription.ACTION_VELOCITY) + + aAction.getDeltaVelocity() * stepSizeVelocity)); + if (aAction instanceof ActionAbsolute) { + final double velocityToSet = ((ActionAbsolute)aAction).getVelocity(); + double diff = velocityToSet - markovState.getValue(MarkovianStateDescription.ACTION_VELOCITY); + if (diff > stepSizeVelocity) { + diff = stepSizeVelocity; + } else if (diff < -stepSizeVelocity) { + diff = -stepSizeVelocity; + } + velocity = Math.min(velocityMax, Math.max(velocityMin, + markovState.getValue(MarkovianStateDescription.ACTION_VELOCITY) + diff)); + } + final double gainMax = max.getValue(MarkovianStateDescription.ACTION_GAIN); + final double gainMin = min.getValue(MarkovianStateDescription.ACTION_GAIN); + double gain = Math.min(gainMax, Math.max(gainMin, + markovState.getValue(MarkovianStateDescription.ACTION_GAIN) + + aAction.getDeltaGain() * stepSizeGain)); + if (aAction instanceof ActionAbsolute) { + final double gainToSet = ((ActionAbsolute)aAction).getGain(); + double diff = gainToSet - markovState.getValue(MarkovianStateDescription.ACTION_GAIN); + if (diff > stepSizeGain){ + diff = stepSizeGain; + } else if (diff < -stepSizeGain) { + diff = -stepSizeGain; + } + gain = Math.min(gainMax, Math.max(gainMin, + markovState.getValue(MarkovianStateDescription.ACTION_GAIN) + diff)); + } + // both: 10 = 2*1.5 + 0.07*100 + final double gsScale = (2.0f * GS_BOUND) + (100.0f * GS_SET_POINT_DEPENDENCY); + double shift = (float) Math.min(100.0f, Math.max(0.0f, + markovState.getValue(MarkovianStateDescription.ACTION_SHIFT) + + aAction.getDeltaShift() * (MAX_REQUIRED_STEP / 0.9f) * 100.0f / gsScale)); + if (aAction instanceof ActionAbsolute) { + final double shiftToSet = ((ActionAbsolute)aAction).getShift(); + double diff = shiftToSet - markovState.getValue(MarkovianStateDescription.ACTION_SHIFT); + if (diff > ((MAX_REQUIRED_STEP / 0.9f) * 100.0f / gsScale)) { + diff = ((MAX_REQUIRED_STEP / 0.9f) * 100.0f / gsScale); + } else if (diff < -((MAX_REQUIRED_STEP / 0.9f) * 100.0f / gsScale)) { + diff = -((MAX_REQUIRED_STEP / 0.9f) * 100.0f / gsScale); + } + shift = (float) Math.min(100.0f, Math.max(0.0f, + markovState.getValue(MarkovianStateDescription.ACTION_SHIFT) + diff)); + } + final double hiddenShift = (float) Math.min(GS_BOUND, Math.max(-GS_BOUND, + ((gsScale * shift / 100.0f) + - (GS_SET_POINT_DEPENDENCY * markovState.getValue(MarkovianStateDescription.SET_POINT)) + - GS_BOUND))); + + markovState.setValue(MarkovianStateDescription.ACTION_VELOCITY, velocity); + markovState.setValue(MarkovianStateDescription.ACTION_GAIN, gain); + markovState.setValue(MarkovianStateDescription.ACTION_SHIFT, shift); + markovState.setValue(MarkovianStateDescription.EFFECTIVE_SHIFT, hiddenShift); } /** - * updates the spiking fatigue dynamics - * @throws PropertiesException + * Updates the spiking fatigue dynamics. + * @throws PropertiesException */ private void updateFatigue() throws PropertiesException { - final float expLambda = 0.1f; // => scale = 1/lambda + // scale = 1/lambda + final float expLambda = 0.1f; final float actionTolerance = 0.05f; - final float fatigueAmplification = 1.1f; + final float fatigueAmplification = 1.1f; final float fatigueAmplificationMax = 5.0f; - final float fatigueAmplificationStart = 1.2f; + final float fatigueAmplificationStart = 1.2f; // action - double velocity = markovState.getValue(MarkovianStateDescription.Action_Velocity); - double gain = markovState.getValue(MarkovianStateDescription.Action_Gain); - double setpoint = markovState.getValue(MarkovianStateDescription.SetPoint); + final double velocity = markovState.getValue(MarkovianStateDescription.ACTION_VELOCITY); + final double gain = markovState.getValue(MarkovianStateDescription.ACTION_GAIN); + final double setpoint = markovState.getValue(MarkovianStateDescription.SET_POINT); // hidden state variables for fatigue - double hiddenStateVelocity = markovState.getValue(MarkovianStateDescription.FatigueLatent1); - double hiddenStateGain = markovState.getValue(MarkovianStateDescription.FatigueLatent2); + double hiddenStateVelocity = markovState.getValue(MarkovianStateDescription.FATIGUE_LATENT_1); + double hiddenStateGain = markovState.getValue(MarkovianStateDescription.FATIGUE_LATENT_2); // dyn variables - double dyn = 0.0f; - - EffectiveAction effAction = new EffectiveAction (new ActionAbsolute(velocity, gain, 0.0, this.mProperties), setpoint); - double effActionVelocityAlpha = effAction.getVelocityAlpha(); // => gain - double effActionGainBeta = effAction.getGainBeta(); // => velocity - - // base noise - double noiseGain = 2.0 * (1.0/(1.0+Math.exp(-rda.nextExponential(expLambda))) - 0.5); - double noiseVelocity = 2.0 * (1.0/(1.0+Math.exp(-rda.nextExponential(expLambda))) - 0.5); - - // add spikes - // keep error within range of [0.001, 0.999] because otherwise Binomial.staticNextInt() will fail. - noiseGain += (1-noiseGain) * rda.nextUniform(0,1) * rda.nextBinomial(1, Math.min(Math.max(0.001, effActionGainBeta), 0.999)) * effActionGainBeta; - noiseVelocity += (1-noiseVelocity) * rda.nextUniform(0,1) * rda.nextBinomial(1, Math.min(Math.max(0.001, effActionVelocityAlpha), 0.999)) * effActionVelocityAlpha; - - // compute internal dynamics - if (hiddenStateGain >= fatigueAmplificationStart) { - hiddenStateGain = Math.min(fatigueAmplificationMax, hiddenStateGain*fatigueAmplification); - } else if (effActionGainBeta > actionTolerance) { - hiddenStateGain = (hiddenStateGain*0.9f) + ((float)noiseGain/3.0f); - } - - if (hiddenStateVelocity >= fatigueAmplificationStart) { - hiddenStateVelocity = Math.min(fatigueAmplificationMax, hiddenStateVelocity*fatigueAmplification); - } else if (effActionVelocityAlpha > actionTolerance) { - hiddenStateVelocity = (hiddenStateVelocity*0.9f) + ((float)noiseVelocity/3.0f); - } - - // reset hiddenState in case actionError is within actionTolerance - if (effActionVelocityAlpha <= actionTolerance) { - hiddenStateVelocity = effActionVelocityAlpha; - } - if (effActionGainBeta <= actionTolerance) { - hiddenStateGain = effActionGainBeta; - } - - // compute observation variables - if (Math.max(hiddenStateVelocity, hiddenStateGain) == fatigueAmplificationMax) { - // bad noise in case fatigueAmplificationMax is reached - dyn = 1.0 / (1.0+Math.exp(-4.0 * rda.nextGaussian(0.6, 0.1))); - } else { - dyn = Math.max(noiseGain, noiseVelocity); - } - - final float cDGain = getConst(C.DGain); - final float cDVelocity = getConst(C.DVelocity); - final float cDSetPoint = getConst(C.DSetPoint); - final float cDynBase = getConst(C.DBase); - - double dynOld = ((cDynBase / ((cDVelocity * velocity) + cDSetPoint)) - cDGain * gain*gain); - if(dynOld<0) dynOld=0; - dyn = ((2.f*dyn+1.0) * dynOld )/ 3.f; - - markovState.setValue(MarkovianStateDescription.Fatigue, dyn); - markovState.setValue(MarkovianStateDescription.FatigueBase, dynOld); - - // hidden state variables for fatigue - markovState.setValue(MarkovianStateDescription.FatigueLatent1, hiddenStateVelocity); - markovState.setValue(MarkovianStateDescription.FatigueLatent2, hiddenStateGain); - markovState.setValue(MarkovianStateDescription.EffectiveActionVelocityAlpha, effActionVelocityAlpha); - markovState.setValue(MarkovianStateDescription.EffectiveActionGainBeta, effActionGainBeta); + final EffectiveAction effAction = new EffectiveAction(new ActionAbsolute(velocity, gain, 0.0, this.properties), setpoint); + final double effActionVelocityAlpha = effAction.getVelocityAlpha(); + final double effActionGainBeta = effAction.getGainBeta(); + + // base noise + double noiseGain = 2.0 * (1.0 / (1.0 + Math.exp(-rda.nextExponential(expLambda))) - 0.5); + double noiseVelocity = 2.0 * (1.0 / (1.0 + Math.exp(-rda.nextExponential(expLambda))) - 0.5); + + // add spikes + // keep error within range of [0.001, 0.999] because otherwise Binomial.staticNextInt() will fail. + noiseGain += (1 - noiseGain) + * rda.nextUniform(0, 1) + * rda.nextBinomial(1, Math.min(Math.max(0.001, effActionGainBeta), 0.999)) + * effActionGainBeta; + noiseVelocity += (1 - noiseVelocity) + * rda.nextUniform(0, 1) + * rda.nextBinomial(1, Math.min(Math.max(0.001, effActionVelocityAlpha), 0.999)) + * effActionVelocityAlpha; + + // compute internal dynamics + if (hiddenStateGain >= fatigueAmplificationStart) { + hiddenStateGain = Math.min(fatigueAmplificationMax, hiddenStateGain * fatigueAmplification); + } else if (effActionGainBeta > actionTolerance) { + hiddenStateGain = (hiddenStateGain * 0.9f) + ((float)noiseGain / 3.0f); + } + + if (hiddenStateVelocity >= fatigueAmplificationStart) { + hiddenStateVelocity = Math.min(fatigueAmplificationMax, hiddenStateVelocity * fatigueAmplification); + } else if (effActionVelocityAlpha > actionTolerance) { + hiddenStateVelocity = (hiddenStateVelocity * 0.9f) + ((float)noiseVelocity / 3.0f); + } + + // reset hiddenState in case actionError is within actionTolerance + if (effActionVelocityAlpha <= actionTolerance) { + hiddenStateVelocity = effActionVelocityAlpha; + } + if (effActionGainBeta <= actionTolerance) { + hiddenStateGain = effActionGainBeta; + } + + // compute observation variables + double dyn; + if (Math.max(hiddenStateVelocity, hiddenStateGain) == fatigueAmplificationMax) { + // bad noise in case fatigueAmplificationMax is reached + dyn = 1.0 / (1.0 + Math.exp(-4.0 * rda.nextGaussian(0.6, 0.1))); + } else { + dyn = Math.max(noiseGain, noiseVelocity); + } + + final float cDGain = getConst(C.DGain); + final float cDVelocity = getConst(C.DVelocity); + final float cDSetPoint = getConst(C.DSetPoint); + final float cDynBase = getConst(C.DBase); + + double dynOld = ((cDynBase / ((cDVelocity * velocity) + cDSetPoint)) - cDGain * gain*gain); + if (dynOld < 0.0) { + dynOld = 0.0; + } + dyn = ((2.0f * dyn + 1.0) * dynOld) / 3.0f; + + markovState.setValue(MarkovianStateDescription.FATIGUE, dyn); + markovState.setValue(MarkovianStateDescription.FATIGUE_BASE, dynOld); + + // hidden state variables for fatigue + markovState.setValue(MarkovianStateDescription.FATIGUE_LATENT_1, hiddenStateVelocity); + markovState.setValue(MarkovianStateDescription.FATIGUE_LATENT_2, hiddenStateGain); + markovState.setValue(MarkovianStateDescription.EFFECTIVE_ACTION_VELOCITY_ALPHA, effActionVelocityAlpha); + markovState.setValue(MarkovianStateDescription.EFFECTIVE_ACTION_GAIN_BETA, effActionGainBeta); } - private void updateCurrentOperationalCost() throws PropertiesException { - final float cCostSetPoint = getConst(C.CostSetPoint); - final float cCostGain = getConst(C.CostGain); - final float cCostVelocity = getConst(C.CostVelocity); - double setpoint = markovState.getValue(MarkovianStateDescription.SetPoint); - double gain = markovState.getValue(MarkovianStateDescription.Action_Gain); - double velocity = markovState.getValue(MarkovianStateDescription.Action_Velocity); - double costs = cCostSetPoint * setpoint + cCostGain * gain + cCostVelocity * velocity; - - double operationalcosts = (float) Math.exp(costs / 100.); - markovState.setValue(MarkovianStateDescription.CurrentOperationalCost, operationalcosts); - mOperationalCostsBuffer.add(operationalcosts); - - if(convToInit){ - for(int i=1; i iterator = mOperationalCostsBuffer.iterator(); - while(iterator.hasNext()) { - double operationalcost = (Double)iterator.next(); - aggregatedOperationalCosts += mEmConvWeights[i] * operationalcost; - markovState.setValue("OPERATIONALCOST_"+i, operationalcost); - i += 1; - } - markovState.setValue(MarkovianStateDescription.OperationalCostsConv, aggregatedOperationalCosts); + int oci = 0; + final Iterator iterator = operationalCostsBuffer.iterator(); + while (iterator.hasNext()) { + final double operationalCost = (Double)iterator.next(); + aggregatedOperationalCosts += emConvWeights[oci] * operationalCost; + markovState.setValue("OPERATIONALCOST_" + oci, operationalCost); + oci += 1; + } + markovState.setValue(MarkovianStateDescription.OPERATIONAL_COSTS_CONV, aggregatedOperationalCosts); } - + private void updateGS() { - gsEnvironment.setControlPosition(markovState.getValue(MarkovianStateDescription.EffectiveShift)); - markovState.setValue(MarkovianStateDescription.MisCalibration, (float) gsEnvironment.reward()); - markovState.setValue(MarkovianStateDescription.MisCalibrationDomain, gsEnvironment.getDomain()); - markovState.setValue(MarkovianStateDescription.MisCalibrationSystemResponse, gsEnvironment.getSystemResponse()); - markovState.setValue(MarkovianStateDescription.MisCalibrationPhiIdx, gsEnvironment.getPhiIdx()); + gsEnvironment.setControlPosition(markovState.getValue(MarkovianStateDescription.EFFECTIVE_SHIFT)); + markovState.setValue(MarkovianStateDescription.MIS_CALIBRATION, (float) gsEnvironment.reward()); + markovState.setValue(MarkovianStateDescription.MIS_CALIBRATION_DOMAIN, gsEnvironment.getDomain()); + markovState.setValue(MarkovianStateDescription.MIS_CALIBRATION_SYSTEM_RESPONSE, gsEnvironment.getSystemResponse()); + markovState.setValue(MarkovianStateDescription.MIS_CALIBRATION_PHI_IDX, gsEnvironment.getPhiIdx()); } - private float getConst(C aConst) throws PropertiesException { - return PropertiesUtil.getFloat(mProperties, aConst.name()); + private float getConst(final C aConst) throws PropertiesException { + return PropertiesUtil.getFloat(properties, aConst.name()); } - /** Returns the operationalcosts history length. The current operationalcosts value is part of the history. - * @return length of the operationalcosts history (including current value) + /** + * Returns the operational costs history length. + * The current operational costs value is part of the history. + * @return length of the operational-costs history (including current value) */ public int getOperationalCostsHistoryLength() { - return mOperationalCostsBuffer.size(); + return operationalCostsBuffer.size(); } - /** - * Returns a copy of the the current markovian state of the dynamics. - * - * @return current internal markovian state of the industrial benchmark - */ - public DataVector getInternalMarkovState() { - return this.markovState.clone(); - } - - /** - * Sets the current markovian state of the dynamics. Also the - * setpoint generator is set and the operationalcosts are convoluted (+reward recomputed). - * - * @param markovState The markovian state from which the values are copied - */ - public void setInternalMarkovState(DataVector markovState) { - - // 1) import all key/value pairs - for (String key : markovState.getKeys()) { - this.markovState.setValue(key, markovState.getValue(key)); - } - - // 2) set random number generator states - this.randomSeed = Double.doubleToLongBits(this.markovState.getValue(MarkovianStateDescription.RandomSeed)); - - this.gsEnvironment.setControlPosition(markovState.getValue(MarkovianStateDescription.EffectiveShift)); - this.gsEnvironment.setDomain(markovState.getValue(MarkovianStateDescription.MisCalibrationDomain)); - this.gsEnvironment.setSystemResponse(markovState.getValue(MarkovianStateDescription.MisCalibrationSystemResponse)); - this.gsEnvironment.setPhiIdx(markovState.getValue(MarkovianStateDescription.MisCalibrationPhiIdx)); - - // 3) reconstruct operationalcost convolution + reward computation - double aggregatedOperationalCosts = 0; - double operationalcost = 0; - for (int i=0; imarkovian state of the dynamics. + * + * @return current internal markovian state of the industrial benchmark + */ + @Override + public DataVector getMarkovState() { + return new MarkovianState(markovState); } + /** + * Sets the current markovian state of the dynamics. + * Also, the setpoint generator is set and the operational costs + * are convoluted (+reward recomputed). + * + * @param markovState The markovian state from which the values are copied + */ + public void setMarkovState(final DataVector markovState) { + + // 1) import all key/value pairs + for (final String key : markovState.getKeys()) { + this.markovState.setValue(key, markovState.getValue(key)); + } + + // 2) set random number generator states + randomSeed = Double.doubleToLongBits(this.markovState.getValue(MarkovianStateDescription.RANDOM_SEED)); + + gsEnvironment.setControlPosition(this.markovState.getValue(MarkovianStateDescription.EFFECTIVE_SHIFT)); + gsEnvironment.setDomain(this.markovState.getValue(MarkovianStateDescription.MIS_CALIBRATION_DOMAIN)); + gsEnvironment.setSystemResponse(this.markovState.getValue(MarkovianStateDescription.MIS_CALIBRATION_SYSTEM_RESPONSE)); + gsEnvironment.setPhiIdx(this.markovState.getValue(MarkovianStateDescription.MIS_CALIBRATION_PHI_IDX)); + + // 3) reconstruct operationalcost convolution + reward computation + double aggregatedOperationalCosts = 0; + for (int cwi = 0; cwi < emConvWeights.length; cwi++) { + final String key = "OPERATIONALCOST_" + cwi; + final double operationalcost = this.markovState.getValue(key); + aggregatedOperationalCosts += this.markovState.getValue(key) * emConvWeights[cwi]; + operationalCostsBuffer.add(operationalcost); + } + this.markovState.setValue(MarkovianStateDescription.OPERATIONAL_COSTS_CONV, aggregatedOperationalCosts); + //mRewardCore.setNormal(rda); + rewardCore.calcReward(this.markovState); + + // 4) set state variables to external driver (e.g. SetPointGenerator parameters) + for (final ExternalDriver drv : externalDrivers) { + drv.setConfiguration(this.markovState); + } + } @Override public void reset() { try { - this.init(); - } catch (PropertiesException e) { - e.printStackTrace(); - } + init(); + } catch (final PropertiesException ex) { + ex.printStackTrace(); + } } @Override public double getReward() { - return this.markovState.getValue(ObservableStateDescription.RewardTotal); + return markovState.getValue(ObservableStateDescription.REWARD_TOTAL); + } + + protected final DataVector getMarkovStateInternal() { + return markovState; + } + + protected final MarkovianState getMax() { + return max; + } + + protected final MarkovianState getMin() { + return min; + } + + protected final Properties getProperties() { + return properties; } - } diff --git a/src/main/java/com/siemens/industrialbenchmark/dynamics/IndustrialBenchmarkRewardFunction.java b/src/main/java/com/siemens/industrialbenchmark/dynamics/IndustrialBenchmarkRewardFunction.java index f76406d..459b1b4 100755 --- a/src/main/java/com/siemens/industrialbenchmark/dynamics/IndustrialBenchmarkRewardFunction.java +++ b/src/main/java/com/siemens/industrialbenchmark/dynamics/IndustrialBenchmarkRewardFunction.java @@ -1,4 +1,4 @@ -/** +/* Copyright 2016 Siemens AG. Licensed under the Apache License, Version 2.0 (the "License"); @@ -17,47 +17,48 @@ import java.util.Properties; -import org.apache.commons.math3.random.RandomDataGenerator; - import com.siemens.industrialbenchmark.datavector.state.MarkovianStateDescription; import com.siemens.industrialbenchmark.properties.PropertiesException; import com.siemens.industrialbenchmark.properties.PropertiesUtil; import com.siemens.rl.interfaces.DataVector; /** - * Reward function for the industrial benchmark - * + * Reward function for the industrial benchmark. + * * @author Siegmund Duell, Michel Tokic */ public class IndustrialBenchmarkRewardFunction { - private final double CRD; - private final double CRE; - - public IndustrialBenchmarkRewardFunction (Properties aProperties) throws PropertiesException{ - CRD = PropertiesUtil.getFloat(aProperties, "CRD", true); - CRE = PropertiesUtil.getFloat(aProperties, "CRE", true); + private final transient double crd; + private final transient double cre; + + public IndustrialBenchmarkRewardFunction(final Properties aProperties) + throws PropertiesException + { + crd = PropertiesUtil.getFloat(aProperties, "CRD", true); + cre = PropertiesUtil.getFloat(aProperties, "CRE", true); } - + /** - * Calculates the reward for a given state. Values are updated in the State object. + * Calculates the reward for a given state. Values are updated in the State object. * @param mState The state to calculate the reward for */ - public void calcReward (DataVector mState) { - + public void calcReward(final DataVector mState) { + // Dynamics - double rD = -mState.getValue(MarkovianStateDescription.Fatigue); - + final double rD = -mState.getValue(MarkovianStateDescription.FATIGUE); + // Goldstone reward //double rGS = mState.getValue(MarkovianStateDescription.RewardGS); - + // OperationalCost - double rE = -mState.getValue(MarkovianStateDescription.Consumption); - - mState.setValue(MarkovianStateDescription.RewardConsumptionWeighted, CRE * rE); - mState.setValue(MarkovianStateDescription.RewardFatigueWeighted, CRD * rD); - mState.setValue(MarkovianStateDescription.RewardConsumption, rE); - mState.setValue(MarkovianStateDescription.RewardFatigue, rD); - mState.setValue(MarkovianStateDescription.RewardTotal, CRD * rD + CRE * rE); + final double rE = -mState.getValue(MarkovianStateDescription.CONSUMPTION); + + mState.setValue(MarkovianStateDescription.REWARD_CONSUMPTION_WEIGHTED, cre * rE); + mState.setValue(MarkovianStateDescription.REWARD_FATIGUE_WEIGHTED, crd * rD); + mState.setValue(MarkovianStateDescription.REWARD_CONSUMPTION, rE); + mState.setValue(MarkovianStateDescription.REWARD_FATIGUE, rD); + mState.setValue(MarkovianStateDescription.REWARD_TOTAL, crd * rD + cre * rE); } } + diff --git a/src/main/java/com/siemens/industrialbenchmark/dynamics/goldstone/DoubleFunction.java b/src/main/java/com/siemens/industrialbenchmark/dynamics/goldstone/DoubleFunction.java index 8ea10b1..29e5999 100755 --- a/src/main/java/com/siemens/industrialbenchmark/dynamics/goldstone/DoubleFunction.java +++ b/src/main/java/com/siemens/industrialbenchmark/dynamics/goldstone/DoubleFunction.java @@ -1,4 +1,4 @@ -/** +/* Copyright 2016 Siemens AG. Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,5 +16,6 @@ package com.siemens.industrialbenchmark.dynamics.goldstone; public interface DoubleFunction { - double apply (double value); + double apply(double value); } + diff --git a/src/main/java/com/siemens/industrialbenchmark/dynamics/goldstone/GoldStoneEnvironmentDynamics.java b/src/main/java/com/siemens/industrialbenchmark/dynamics/goldstone/GoldStoneEnvironmentDynamics.java index 9c2ac40..4b1921c 100755 --- a/src/main/java/com/siemens/industrialbenchmark/dynamics/goldstone/GoldStoneEnvironmentDynamics.java +++ b/src/main/java/com/siemens/industrialbenchmark/dynamics/goldstone/GoldStoneEnvironmentDynamics.java @@ -1,4 +1,4 @@ -/** +/* Copyright 2016 Siemens AG. Licensed under the Apache License, Version 2.0 (the "License"); @@ -12,7 +12,7 @@ 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 com.siemens.industrialbenchmark.dynamics.goldstone; import org.slf4j.Logger; @@ -23,294 +23,247 @@ public class GoldStoneEnvironmentDynamics { private final int strongestPenaltyAbsIdx; - private Domain domain = Domain.INITIAL; - private SystemResponse systemResponse = SystemResponse.ADVANTAGEOUS; - private PenaltyFunction currentPenaltyFunction = null; - private int phiIdx = 0; + private Domain domain; + private SystemResponse systemResponse; + private PenaltyFunction currentPenaltyFunction; + private int phiIdx; private final double safeZone; private final PenaltyFunction[] penaltyFunctionsArray; - final static Logger LOGGER = LoggerFactory.getLogger(GoldStoneEnvironmentDynamics.class); - + private static final Logger LOGGER = LoggerFactory.getLogger(GoldStoneEnvironmentDynamics.class); + public enum Domain { POSITIVE(+1), - INITIAL(0), NEGATIVE(-1); - + private final int id; - Domain(int id) { this.id = id; } + Domain(final int id) { this.id = id; } public int getValue() { return id; } - public static Domain fromDouble (double id) { - if (id == -1.0) return NEGATIVE; - if (id == 0.0) return INITIAL; - if (id == 1.0) return POSITIVE; - throw new IllegalArgumentException("id must be either [-1, 0, 1], but is " + id); + public static Domain fromDouble(final double id) { + if (id < 0.0) return NEGATIVE; + if (id >= 0.0) return POSITIVE; + throw new IllegalArgumentException("id must be either [-1, 1], but is " + id); } } - + public enum SystemResponse { ADVANTAGEOUS(+1), - DISADVANTAGEOUS(-1), - NEUTRAL(0); + DISADVANTAGEOUS(-1); private final int id; - SystemResponse(int id) { this.id = id; } + SystemResponse(final int id) { this.id = id; } public int getValue() { return id; } - public static SystemResponse fromDouble (double id) { - if (id == -1.0) return DISADVANTAGEOUS; - if (id == 0.0) return NEUTRAL; - if (id == 1.0) return ADVANTAGEOUS; - throw new IllegalArgumentException("id must be either [-1, 0, 1], but is " + id); + public static SystemResponse fromDouble(final double id) { + if (id < 0.0) return DISADVANTAGEOUS; + if (id >= 0.0) return ADVANTAGEOUS; + throw new IllegalArgumentException("id must be either [-1, 1], but is " + id); } } - - public GoldStoneEnvironmentDynamics(int numberSteps, double maxRequiredStep, double safeZone) { + + public GoldStoneEnvironmentDynamics(final int numberSteps, final double maxRequiredStep, final double safeZone) { Preconditions.checkArgument(safeZone >= 0, "safeZone must be non-negative, but is %s.", safeZone); - + this.safeZone = safeZone; this.strongestPenaltyAbsIdx = computeStrongestPenaltyAbsIdx(numberSteps); - this.penaltyFunctionsArray = this.defineRewardFunctions(numberSteps, maxRequiredStep); - this.reset(); + this.penaltyFunctionsArray = defineRewardFunctions(numberSteps, maxRequiredStep); + reset(); } - - public void reset() { - this.domain = Domain.INITIAL; + + private void reset() { + domain = Domain.POSITIVE; phiIdx = 0; systemResponse = SystemResponse.ADVANTAGEOUS; - currentPenaltyFunction = getPenaltyFunction(); } - - public double rewardAt (double pos) { + + public double rewardAt(final double pos) { return -currentPenaltyFunction.reward(pos); } public double optimalPosition() { return currentPenaltyFunction.getOptimumRadius(); } - + public double optimalReward() { return -currentPenaltyFunction.getOptimumValue(); } - - public void stateTransition(double newControlValue) { - final Domain oldDomain = this.domain; - + public void stateTransition(final double newControlValue) { + + Domain oldDomain = domain; + // (0) compute new domain - this.domain = this.computeDomain(newControlValue); - + domain = computeDomain(newControlValue); + // (1) if domain change: system response <- advantageous - if (this.domain != oldDomain) { - this.systemResponse = SystemResponse.ADVANTAGEOUS; + if (domain != oldDomain) { // FIXME use equals instead, and implement it for Domain (also hashCode()) + systemResponse = SystemResponse.ADVANTAGEOUS; LOGGER.trace(" turning sys behavior -> advantageous"); } - + + int oldPhiIdx = this.phiIdx; + // (2) compute & apply turn direction - this.phiIdx += computeAngularStep(newControlValue); - + phiIdx += computeAngularStep(newControlValue); + // (3) update system response if necessary - this.systemResponse = updateSystemResponse (this.phiIdx, newControlValue); - - // (4) if Phi_index == 0: reset internal state - if (this.phiIdx == 0 && Math.abs(newControlValue) <= this.safeZone) { - this.reset(); - } - - // (5) apply symmetry + systemResponse = updateSystemResponse(phiIdx); + + oldPhiIdx = this.phiIdx; + + // (4) apply symmetry this.phiIdx = this.applySymmetry(this.phiIdx); - - LOGGER.trace (" phiIdx = " + phiIdx); - this.currentPenaltyFunction = this.getPenaltyFunction(); + + oldDomain = this.domain; + + // (5) if Phi_index == 0: reset internal state + if (phiIdx == 0 && Math.abs(newControlValue) <= safeZone) { + reset(); + } + + LOGGER.trace(" phiIdx = " + phiIdx); + currentPenaltyFunction = getPenaltyFunction(); } /** - * - * Compute the new domain of control action. - * Note: - * if control action is in safe zone, domain remains unchanged - * as 'penalty landscape' turn direction is independent of exact position - * in safe zone, reset to Domain.initial can be applied later. - * - * @param newPosition The new position. - * @return The numerical value of the new domain. + * Computes the new domain of control action. + * @param newPosition The new position. + * @return The numerical value of the new domain. */ - private Domain computeDomain(double newPosition) { - if (Math.abs(newPosition) <= this.safeZone) { - return this.domain; - } else { + private Domain computeDomain(final double newPosition) { + if (Math.abs(newPosition) <= safeZone) { + return domain; + } else { return Domain.fromDouble(Math.signum(newPosition)); } } - - - private double computeAngularStep(double newPosition) { - // cool down: when position close to zero - if (Math.abs(newPosition) <= this.safeZone) { - return -Math.signum(this.phiIdx); - } - - if (this.phiIdx == (-this.domain.getValue() * strongestPenaltyAbsIdx)) { - LOGGER.trace (" no turning"); - return 0; - } - - return this.systemResponse.getValue() * Math.signum(newPosition); + + + private double computeAngularStep(final double newPosition) { + // cool down when position close to zero + if (Math.abs(newPosition) <= safeZone) { + return -Math.signum(phiIdx); + } + + if (phiIdx == (-domain.getValue() * strongestPenaltyAbsIdx)) { + LOGGER.trace(" no turning"); + return 0; + } + + return systemResponse.getValue() * Math.signum(newPosition); } - + /** - * only changes system response to if turn angle hits 90deg - * in domain of current position, i.e.: - * new_position > self.__safe_zone and new_Phi_idx = 90deg - * new_position < -self.__safe_zone and new_Phi_idx = -90deg - * + * Update system response * @param phiIdx - * @param newControlValue - * @return + * @return the resulting system response */ - private SystemResponse updateSystemResponse(int newPhiIdx, double newControlValue) { + private SystemResponse updateSystemResponse(final int newPhiIdx) { if (Math.abs(newPhiIdx) >= strongestPenaltyAbsIdx) { - LOGGER.trace (" turning sys behavior -> disadvantageous"); + LOGGER.trace(" turning sys behavior -> disadvantageous"); return SystemResponse.DISADVANTAGEOUS; } else { return this.systemResponse; } } - /** - * - * By employing reflection symmetry with respect to x-axis: - * o Phi -> pi -Phi - * o turn direction -> turn direction - * o x -> x - * moves 'penalty landscape' rotation angle in domain [-90deg ... +90deg] - * corresponding to Phi_index in - * [ -strongest_penality_abs_idx*angular_speed, ..., -angular_speed, - * 0, - * angular_speed, ..., strongest_penality_abs_idx*angular_speed ] - + * Apply symmetric properties on phiIdx * @param phiIdx * @return */ - private int applySymmetry(int phiIdx) { - - /* - * Do nothing if 'penalty landscape' rotation angle is in [-90deg ... +90deg] - * corresponding to angle indices - * [-self.__strongest_penality_abs_idx, ...-1,0,1, ..., self.__strongest_penality_abs_idx-] - */ - if (Math.abs(phiIdx) <= strongestPenaltyAbsIdx) { - return phiIdx; - } - - /* - * Otherwise: - * Use 2pi symmetry to move angle index p in domain - * [0 ... 360deg) - * corresponding to angle indices - * [0, ..., 4*self.__strongest_penality_abs_idx-1] - * But we are only executing the following code, if the angle is in - * (90deg, ..., 270deg) - * corresponding to angle indices - * [self.__strongest_penality_abs_idx+1, ..., 3*self.__strongest_penality_abs_idx-1] - * Therefore, the reflection-symmetry operation - * p <- 2*self.__strongest_penality_abs_idx - p - * will transform p back into the desired angle indices domain - * [-self.__strongest_penality_abs_idx, ...-1,0,1, ..., self.__strongest_penality_abs_idx-] - * */ - phiIdx = phiIdx % (4*strongestPenaltyAbsIdx); - if (phiIdx < 0) { - phiIdx += (4*strongestPenaltyAbsIdx); - } - phiIdx = 2*strongestPenaltyAbsIdx - phiIdx; - - return phiIdx; + private int applySymmetry(final int otherPhiIdx) { + if (Math.abs(otherPhiIdx) < strongestPenaltyAbsIdx) { + return otherPhiIdx; + } + + int currentPhiIdx = (otherPhiIdx + (4 * strongestPenaltyAbsIdx)) % (4 * strongestPenaltyAbsIdx); + currentPhiIdx = 2 * strongestPenaltyAbsIdx - currentPhiIdx; + return currentPhiIdx; } public PenaltyFunction getPenaltyFunction() { - return getPenaltyFunction(this.phiIdx); + return getPenaltyFunction(phiIdx); } - public PenaltyFunction getPenaltyFunction(int phiIdx) { - int idx = strongestPenaltyAbsIdx + this.applySymmetry(phiIdx); + public PenaltyFunction getPenaltyFunction(final int otherPhiIdx) { + int idx = strongestPenaltyAbsIdx + otherPhiIdx; if (idx < 0) { idx += penaltyFunctionsArray.length; } return penaltyFunctionsArray[idx]; } - /** - """ - Parameters - ---------------- - * Number_steps: - the number of steps required for one full cycle of the optimal policy. - For easy numerics, it is required that number_steps is positive and an integer multiple of 4. - - By employing reflection symmetry with respect to x-axis: - o Phi -> pi -Phi - o turn direction -> turn direction - o x -> x - The required rewards functions can be restricted to turn angles Phi in [-90deg ... +90deg] - of the 'penalty landscape'. One quarter-segment (e.g [0 ... 90deg]) of the entire - 'penalty landscape' is divided into Number_steps / 4 steps. Note that Number_steps / 4 - is an integer per requirement from above. - - - Implementation: - ---------------- - According to the above explanation, the 'penalty landscape' turns in each state transition - angular_speed = 360deg / number_steps - or does not turn at all. Per convention, the 'penalty landscape' positions are confined to a - homogeneously spaced grid of turn angles - [ 0, angular_speed, 2*angular_speed, ... , (number_steps -1)*angular_speed ] - Lets define - strongest_penality_abs_idx = number_steps / 4 - Hence, - strongest_penality_abs_idx*angular_speed = (number_steps / 4) * (360deg / number_steps)= 90deg - It follows that - o Phi = - strongest_penality_abs_idx*angular_speed maximized the control penalties - for the positive domain (i.e. where x > 0) - o Phi = + strongest_penality_abs_idx*angular_speed maximized the control penalties - for the negative domain (i.e. where x < 0) - Exploiting reflection symmetry the required grid of reward functions can be reduced to - [ -strongest_penality_abs_idx*angular_speed, ..., -angular_speed, - 0, - angular_speed, ..., strongest_penality_abs_idx*angular_speed ] - This has the advantage, that either end of the grid represents the worst case points of the - """ - + * Define the reward functions * @param numberSteps + * the number of steps required for one full cycle of the optimal policy. + * For easy numerics, it is required that this is positive and an integer + * multiple of 4. + * By employing reflection symmetry with respect to x-axis: + *
    + *
  • Phi -> pi -Phi
  • + *
  • turn direction -> turn direction
  • + *
  • x -> x
  • + *
+ * The required rewards functions can be restricted to turn angles Phi in + * [-90deg ... +90deg] of the 'penalty landscape'. + * One quarter-segment (e.g [0 ... 90deg]) of the entire + * 'penalty landscape' is divided into numberSteps / 4 steps. + * Note that numberSteps / 4 is an integer per requirement + * from above. * @param maxRequiredStep + * + * Implementation: + * According to the above explanation, the 'penalty landscape' turns + * angular_speed = 360deg / numberSteps + * in each state transition, or does not turn at all. + * Per convention, the 'penalty landscape' positions are confined to a + * homogeneously spaced grid of turn angles + * [ 0, angular_speed, 2*angular_speed, ... , (numberSteps -1)*angular_speed ]. + * Lets define strongest_penality_abs_idx = numberSteps / 4. + * Hence, + * strongest_penality_abs_idx*angular_speed = (numberSteps / 4) * (360deg / numberSteps)= 90deg. + * It follows that: + *
    + *
  • Phi = - strongest_penality_abs_idx*angular_speed + * maximized the control penalties for the positive domain + * (i.e. where x > 0)
  • + *
  • Phi = + strongest_penality_abs_idx*angular_speed + * maximized the control penalties for the negative domain + * (i.e. where x < 0)
  • + *
+ * Exploiting reflection symmetry, + * the required grid of reward functions can be reduced to + * [ -strongest_penality_abs_idx*angular_speed, ..., -angular_speed, 0, angular_speed, ..., strongest_penality_abs_idx*angular_speed ] + * This has the advantage, that either end of the grid represents + * the worst case points of the ???. */ - private PenaltyFunction[] defineRewardFunctions(int numberSteps, double maxRequiredStep) { + private PenaltyFunction[] defineRewardFunctions(final int numberSteps, final double maxRequiredStep) { - final int k = computeStrongestPenaltyAbsIdx(numberSteps); - double angle_gid[] = new double[k*2+1]; - for (int i=-k; i<=k; i++) { - angle_gid[i+k] = i * 2*Math.PI / numberSteps; + final int k = strongestPenaltyAbsIdx; + final double[] angleGid = new double[k * 2 + 1]; + for (int i = -k; i <= k; i++) { + angleGid[i + k] = i * 2 * Math.PI / numberSteps; } - PenaltyFunction[] penaltyFunctionsArray = new PenaltyFunction[angle_gid.length]; - for (int i=0; i= 1 && (numberSteps %4) == 0, + + private static int computeStrongestPenaltyAbsIdx(final int numberSteps) { + Preconditions.checkArgument(numberSteps >= 1 && (numberSteps % 4) == 0, "numberSteps must be positive and an integer multiple of 4, but is %s", numberSteps); - - final int k = numberSteps / 4; - return k; + + return numberSteps / 4; } public Domain getDomain() { return domain; } - public void setDomain(Domain domain) { + public void setDomain(final Domain domain) { this.domain = domain; } @@ -318,7 +271,7 @@ public SystemResponse getSystemResponse() { return systemResponse; } - public void setSystemResponse(SystemResponse systemResponse) { + public void setSystemResponse(final SystemResponse systemResponse) { this.systemResponse = systemResponse; } @@ -326,8 +279,23 @@ public int getPhiIdx() { return phiIdx; } - public void setPhiIdx(int phiIdx) { + public void setPhiIdx(final int phiIdx) { this.phiIdx = phiIdx; } -} + protected int getStrongestPenaltyAbsIdx() { + return strongestPenaltyAbsIdx; + } + + protected PenaltyFunction getCurrentPenaltyFunction() { + return currentPenaltyFunction; + } + + protected double getSafeZone() { + return safeZone; + } + + protected PenaltyFunction[] getPenaltyFunctionsArray() { + return penaltyFunctionsArray; + } +} diff --git a/src/main/java/com/siemens/industrialbenchmark/dynamics/goldstone/GoldstoneEnvironment.java b/src/main/java/com/siemens/industrialbenchmark/dynamics/goldstone/GoldstoneEnvironment.java index 7662656..4faa7d4 100755 --- a/src/main/java/com/siemens/industrialbenchmark/dynamics/goldstone/GoldstoneEnvironment.java +++ b/src/main/java/com/siemens/industrialbenchmark/dynamics/goldstone/GoldstoneEnvironment.java @@ -1,4 +1,4 @@ -/** +/* Copyright 2016 Siemens AG. Licensed under the Apache License, Version 2.0 (the "License"); @@ -20,79 +20,88 @@ public class GoldstoneEnvironment { - final GoldStoneEnvironmentDynamics dynamics; + private static final double DEFAULT_CONTROL_START_VALUE = 0.0; + + private final GoldStoneEnvironmentDynamics dynamics; private double controlPosition; - - public GoldstoneEnvironment(int numberSteps, double maxRequiredStep, double safeZone) { - dynamics = new GoldStoneEnvironmentDynamics(numberSteps, maxRequiredStep, safeZone); - this.reset(); + + public GoldstoneEnvironment(final int numberSteps, final double maxRequiredStep, final double safeZone) { + this.dynamics = new GoldStoneEnvironmentDynamics(numberSteps, maxRequiredStep, safeZone); + this.controlPosition = DEFAULT_CONTROL_START_VALUE; + } + + // for eventual serialization + protected GoldStoneEnvironmentDynamics getDynamics() { + return dynamics; } public void reset() { - this.reset(0); + setControlStartValue(DEFAULT_CONTROL_START_VALUE); } - - public void reset(double controlStartValue) { + + public void setControlStartValue(final double controlStartValue) { this.controlPosition = controlStartValue; } - + public double reward() { - return this.dynamics.rewardAt(controlPosition); + return dynamics.rewardAt(controlPosition); } - + public double optimalPosition() { - return this.dynamics.optimalPosition(); + return dynamics.optimalPosition(); } - + public double optimalReward() { - return this.dynamics.optimalReward(); + return dynamics.optimalReward(); } - + /** - * Applies action and returns reward + * Applies action and returns reward. + * @param controlValueChange the relative change/delta to apply to the control value + * @return the reward */ - public double stateTransition(double controlValueChange) { - // TODO: (comment from Alex) not yet implemented: test if action is allowed - this.controlPosition += controlValueChange; - this.dynamics.stateTransition(controlPosition); - return this.reward(); + public double stateTransition(final double controlValueChange) { + controlPosition += controlValueChange; + dynamics.stateTransition(controlPosition); + return reward(); } - + public PenaltyFunction getRewardFunction() { - return this.dynamics.getPenaltyFunction(); + return dynamics.getPenaltyFunction(); } public double getControlPosition() { return controlPosition; } - public void setControlPosition(double controlPosition) { + public void setControlPosition(final double controlPosition) { this.controlPosition = controlPosition; - this.dynamics.stateTransition(controlPosition); + dynamics.stateTransition(controlPosition); reward(); } - - public float getDomain(){ + + public float getDomain() { return dynamics.getDomain().getValue(); } - - public void setDomain(double double1){ + + public void setDomain(final double double1) { dynamics.setDomain(Domain.fromDouble(double1)); } - - public float getSystemResponse(){ + + public float getSystemResponse() { return dynamics.getSystemResponse().getValue(); } - - public void setSystemResponse(double systemResponse){ + + public void setSystemResponse(final double systemResponse) { dynamics.setSystemResponse(SystemResponse.fromDouble(systemResponse)); } - - public float getPhiIdx(){ + + public float getPhiIdx() { return dynamics.getPhiIdx(); } - - public void setPhiIdx(double phiIdx){ + + public void setPhiIdx(final double phiIdx) { dynamics.setPhiIdx((int) phiIdx); } } + diff --git a/src/main/java/com/siemens/industrialbenchmark/dynamics/goldstone/NLGP.java b/src/main/java/com/siemens/industrialbenchmark/dynamics/goldstone/NLGP.java index 4e75b15..17a59f2 100755 --- a/src/main/java/com/siemens/industrialbenchmark/dynamics/goldstone/NLGP.java +++ b/src/main/java/com/siemens/industrialbenchmark/dynamics/goldstone/NLGP.java @@ -1,4 +1,4 @@ -/** +/* Copyright 2016 Siemens AG. Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,137 +16,53 @@ package com.siemens.industrialbenchmark.dynamics.goldstone; /** - * Normalized, linearly biased Goldstone Potential - - * Normalization: - * global minimum has functional value of -1 - * transition of domain with three extrema to one domain with one extrema - * is at angle phi_b = pi / 4 [45 deg] - * + * Normalized, linearly biased Goldstone Potential. * @author Alexander Hentschel, Michel Tokic - * */ public class NLGP { - private static final double u0 = Math.cbrt(1 + Math.sqrt(2)) / Math.sqrt(3); - private static final double r0 = u0 + 1. / (3.*u0); - private static final double lmbd = 2. * r0*r0 - Math.pow(r0,4) + 8. * Math.sqrt(2./27.) * r0; - - private static final double norm_alpha = 2. / lmbd; - private static final double norm_beta = 1. / lmbd; - private static final double norm_kappa = -8. * Math.sqrt(2./27.) / lmbd; - private static final double phi_b = Math.PI/4.; - private static final double qh_b = -Math.sqrt(1./27.); - - /** - * Returns angle where transition occurs from domain with three extrema to one domain with one extrema - * @return angle where transition occurs from domain with three extrema to one domain with one extrema - */ - public double domain_border_angle() { - return phi_b; - } + private static final double U0 = Math.cbrt(1 + Math.sqrt(2)) / Math.sqrt(3); + private static final double R0 = U0 + 1. / (3.*U0); + private static final double LMBD = 2. * R0 * R0 - Math.pow(R0, 4) + 8. * Math.sqrt(2. / 27.) * R0; + private static final double NORM_ALPHA = 2. / LMBD; + private static final double NORM_BETA = 1. / LMBD; + private static final double NORM_KAPPA = -8. * Math.sqrt(2./27.) / LMBD; + private static final double QH_B = -Math.sqrt(1./27.); - /** - * Function value of normalized, linearly biased Goldstone Potential - * in euclidean coordinates. - * @param x in R - * @param y in R - * @return - */ - public double euclidean_nlgp(final double x, final double y) { - // rsq = np.square(x) + np.square(y) - // return -self.__norm_alpha * rsq + self.__norm_beta * np.square(rsq) + self.__norm_kappa * y - final double rsq = x*x + y*y; - return -norm_alpha * rsq + norm_beta * rsq*rsq + norm_kappa * y; - } - /** * Function value of normalized, linearly biased Goldstone Potential * in polar coordinates. * @param r in R * @param phi angle in Radians - * @return + * @return resulting polar-NLGP function value */ - public double polar_nlgp (final double r, final double phi) { - //rsq = np.square(r) - //return -self.__norm_alpha * rsq + self.__norm_beta * np.square(rsq) + self.__norm_kappa * sin(phi) * r - final double rsq = r*r; - return -norm_alpha * rsq + norm_beta * rsq*rsq + norm_kappa * Math.sin(phi) * r; + public double polarNlgp(final double r, final double phi) { + final double rsq = r*r; + return -NORM_ALPHA * rsq + NORM_BETA * rsq*rsq + NORM_KAPPA * Math.sin(phi) * r; } - + /** - * returns the radius r0 along phi-axis where NLG has minimal function value, i.e. - r0 = argmin_{r} polar_nlgp(r,phi) - * @param phi angle in Radians - * @return returns the radius r0 along phi-axis where NLG has minimal function value + * Returns the radius r0 along the phi-axis where NLG has minimal function value. + * i.e. r0 = argmin_{r} polarNlgp(r, phi). + * @param phi angle in radians + * @return r0 with minimal NLG */ - public double global_minimum_radius(double phi) { - // use 2-pi-symmetry to move phi in domain [0,360°] - phi = phi % (2.*Math.PI); - - // - // if phi >= 180°, use symmetry of LGP: - // * compute r_min in domain: phi - 180° in [0,180°] - // * multiply resulting radius with -1 - double scalar = 1; - if (phi >= Math.PI) { - phi -= Math.PI; - scalar = -1; - } - - final double qh = norm_kappa * Math.sin(phi) / (8. * norm_beta); - - /* - # For numerical stability, we distinguish the domain with 3 extrema from the - # domain with one extremum based on qh. Specifically, the domain-limit - # value qh_b is - # qh_b = norm_kappa * sin(phi_b) / (8*norm_beta) = - sqrt(1/27) - # In comparison, distinguishing domains based on phi_b leads to numerical - # instabilities when phi -> phi_b. For example in Case A, i.e. phi > phi_b, - # tiny numerical errors could result in - # qh^2 < sqrt(1/27) when phi -> phi_b - # even though this would be analytically never possible. Nevertheless, - # with limited precision this instability occurs resulting in - # sqrt(qh*qh - 1 / 27) = NaN - # - # determine, if qh is in domain with one extreme or three - # * if qh <= qh_b = - sqrt(1/27) - # => domain with only one global extremum - # else => domain with three global extrema - */ - - final double r0; - if (qh <= qh_b) { - final double u = Math.cbrt(-qh + Math.sqrt(qh*qh - 1. / 27.)); - r0 = u + 1. / (3.*u); + public double globalMinimumRadius(final double phi) { + final double qh = NORM_KAPPA * Math.abs(Math.sin(phi)) / (8. * NORM_BETA); + + final double r0; + + double signum_phi = Math.signum(Math.sin(phi)); + if (signum_phi == 0.0) { + signum_phi = 1.0; + } + + if (qh < QH_B) { + final double u2 = Math.cbrt(-signum_phi*qh + Math.sqrt(qh*qh - 1. / 27.)); + r0 = u2 + 1. / (3.*u2); } else { - r0 = Math.sqrt(4./3.) * Math.cos(1./3. * Math.acos(-qh*Math.sqrt(27.)) ); + r0 = signum_phi * Math.sqrt(4./3.) * Math.cos(1./3. * Math.acos(-qh*Math.sqrt(27.)) ); } - return scalar*r0; - } - - /** - * vectorized version of global_minimum_radius(double) - * @param phi - * @return - */ - public double[] global_minimum_radius(double[] phi) { - double ret[] = new double [phi.length]; - for (int i=0; i 0, - "max_required_step must be > 0, but was %s", max_required_step); - this.phi = phi; - this.max_required_step = max_required_step; - - reward_function = reward_function_factory(phi, max_required_step); - optimum_radius = compute_optimal_radius(phi, max_required_step); - optimum_value = reward_function.apply(optimum_radius); - } - - public double reward (double r) { - return reward_function.apply(r); - } - - /** - * vectorized version of reward(double) - * @param r - * @return - */ - public double[] reward (double r[]) { - double ret[] = new double [r.length]; - for (int i=0; i=0 && r0>=0 && x1>=0 && r1>=0), - "x0, r0, x1, r1 must be either all positive or all negative, " + - "but was x0=%s, r0=%s, x1=%s, r1=%s", x0, r0, x1, r1); - - x0 = Math.abs(x0); - r0 = Math.abs(r0); - x1 = Math.abs(x1); - r1 = Math.abs(r1); - - Preconditions.checkArgument(x0>0 && r0>0, - "x0 and r0 must be positive, but was x0=%s and r0=%s", x0, r0); - Preconditions.checkArgument(x1>=x0 && r1>=r0, - "required: (x0, r0) < (x1, r1), but was x0=%s, r0=%s x1=%s, r1=%s", x0, r0, x1, r1); - - final double rscaler = r0 / x0; - - final double final_r0 = r0; - final double final_x0 = x0; - final double final_x1 = x1; - final double final_r1 = r1; - - final DoubleFunction seg2_tsf = - transfer_function_factory(final_r0, final_x1-final_x0, final_r1-final_r0); - - return new DoubleFunction() { - @Override - public double apply(double x) { - final double s = Math.signum(x); - x = Math.abs(x); - - if (x<=final_x0) { - return s*rscaler*x; - } - if (x 0 for phi in [0,pi) - opt radius < 0 for phi in [pi,2pi) - Explanation: - * for very small angles, where the sin(phi) is smaller than max_required_step - the opt of the reward landscape should be per definition at - max_required_step if phi in [0,pi) - -max_required_step if phi in [pi,2pi) - (max_required_step is assumed to be positive) - * for all cases phi != 0,pi,2pi - the sign is identical to the sign of the sin function - * but for phi = 0,pi,2pi the sig-function is zero and - provides no indication about the sign of the opt - """ - * + * Computes radius for the optimum (global minimum). * @param phi - * @param max_required_step + * @param maxRequiredStep * @return */ - private double compute_optimal_radius (double phi, double max_required_step) { - phi = phi % (2*Math.PI); - double opt = Math.max(Math.abs(Math.sin(phi)), max_required_step); - if (phi>=Math.PI) { - opt *= -1; - } - return opt; + private static double computeOptimalRadius(final double phi, final double maxRequiredStep) { + double signum_phi = Math.signum(Math.sin(phi)); + if (signum_phi == 0.0) { + signum_phi = 1.0; + } + return signum_phi * Math.max(Math.abs(Math.sin(phi)), maxRequiredStep); } /** - Generates the reward function for fixed phi. Works ONLY WITH SCALAR inputs. + * Generates the reward function for fixed phi. Works ONLY WITH SCALAR inputs. * @param p angle in Radians - * @param max_required_step the max. required step by the optimal policy; must be positive + * @param maxRequiredStep the max. required step by the optimal policy; must be positive * @return */ - private DoubleFunction reward_function_factory(double phi, double max_required_step) { - + private static DoubleFunction rewardFunctionFactory(final double phi, final double maxRequiredStep) { final NLGP l = new NLGP(); - final double pTmp = phi % (2*Math.PI); - final double p = pTmp < 0 ? pTmp+(2*Math.PI) : pTmp; - - double opt_rad = compute_optimal_radius(p, max_required_step); - double r0 = l.global_minimum_radius(p); - - final DoubleFunction tr = rad_transformation_factory(opt_rad, r0, Math.signum(opt_rad)*2, Math.signum(r0)*2); - + + final double optRad = computeOptimalRadius(phi, maxRequiredStep); + final double r0 = l.globalMinimumRadius(phi); + return new DoubleFunction() { @Override - public double apply(double r) { - return l.polar_nlgp(tr.apply(r), p); + public double apply(final double x) { + double result = Double.NaN; + if (Math.abs(x) <= Math.abs(optRad)) { + result = x * Math.abs(r0) / Math.abs(optRad); + } else { + final double exponent = (2.0 - Math.abs(optRad)) / (2.0 - Math.abs(r0)); + final double scaling = (2.0 - Math.abs(r0)) / Math.pow((2.0 - Math.abs(optRad)), exponent); + result = Math.signum(x) * (Math.abs(r0) + scaling * Math.pow(Math.abs(x) - Math.abs(optRad), exponent)); + } + return l.polarNlgp(result, phi); } }; } public double getOptimumRadius() { - return optimum_radius; + return optimumRadius; } + public double getOptimumValue() { - return optimum_value; - } - public double getPhi() { - return this.phi; - } - public double getMaxRequiredStep() { - return this.max_required_step; + return optimumValue; } } + diff --git a/src/main/java/com/siemens/industrialbenchmark/externaldrivers/setpointgen/SetPointGenerator.java b/src/main/java/com/siemens/industrialbenchmark/externaldrivers/setpointgen/SetPointGenerator.java index 3e90136..860d29f 100755 --- a/src/main/java/com/siemens/industrialbenchmark/externaldrivers/setpointgen/SetPointGenerator.java +++ b/src/main/java/com/siemens/industrialbenchmark/externaldrivers/setpointgen/SetPointGenerator.java @@ -1,4 +1,4 @@ -/** +/* Copyright 2016 Siemens AG. Licensed under the Apache License, Version 2.0 (the "License"); @@ -28,119 +28,137 @@ import com.siemens.industrialbenchmark.util.PlotCurve; import com.siemens.rl.interfaces.DataVector; import com.siemens.rl.interfaces.ExternalDriver; +import java.util.ArrayList; +import java.util.List; /** - * The seedable setpoint generator - * - * @author Siegmund Duell, Michel Tokic + * The seedable setpoint generator. * + * @author Siegmund Duell, Michel Tokic */ public class SetPointGenerator implements ExternalDriver { - private final float SETPOINT_STEP_SIZE; - private final float MAX_CHANGE_RATE_PER_STEP_SETPOINT; - private final int MAX_SEQUENCE_LENGTH; - private final float MINSETPOINT; - private final float MAXSETPOINT; - - private int mCurrentSteps; - private int mLastSequenceSteps; - private double mChangeRatePerStep; - private boolean mIsStationary; - private double mSetPoint; - - private RandomDataGenerator mRandom = new RandomDataGenerator(); - + private final float setPointStepSize; + private final float maxChangeRatePerStepSetPoint; + private final int maxSequenceLength; + private final float minSetPoint; + private final float maxSetPoint; + private int currentSteps; + private int lastSequenceSteps; + private double changeRatePerStep; + private boolean stationary; + private double setPoint; + private final RandomDataGenerator random; /** - Constructor with given seed and properties file - * @param seed The seed for the random number generator - * @param aProperties The properties file to parse - * @throws PropertiesException + * Constructor with given seed and properties file + * @param seed The seed for the random number generator + * @param aProperties The properties file to parse + * @throws PropertiesException if property values are badly formatted */ - public SetPointGenerator(long seed, Properties aProperties) - throws PropertiesException { - - mIsStationary = aProperties.containsKey("STATIONARY_SETPOINT"); - if (mIsStationary) { - mSetPoint = PropertiesUtil.getFloat (aProperties, "STATIONARY_SETPOINT", true); - Preconditions.checkArgument(mSetPoint >= 0.0f && mSetPoint <= 100.0f, "setpoint must be in range [0, 100]"); + public SetPointGenerator(final long seed, final Properties aProperties) + throws PropertiesException + { + this.stationary = aProperties.containsKey("STATIONARY_SETPOINT"); + if (stationary) { + this.setPoint = PropertiesUtil.getFloat(aProperties, "STATIONARY_SETPOINT", true); + Preconditions.checkArgument(setPoint >= 0.0f && setPoint <= 100.0f, "setpoint must be in range [0, 100]"); } - MAX_CHANGE_RATE_PER_STEP_SETPOINT = PropertiesUtil.getFloat(aProperties, "MAX_CHANGE_RATE_PER_STEP_SETPOINT", true); - MAX_SEQUENCE_LENGTH = PropertiesUtil.getInt(aProperties, "MAX_SEQUENCE_LENGTH", true); - MINSETPOINT = PropertiesUtil.getFloat(aProperties, "SetPoint_MIN", true); - MAXSETPOINT = PropertiesUtil.getFloat(aProperties, "SetPoint_MAX", true); - SETPOINT_STEP_SIZE = PropertiesUtil.getFloat(aProperties, "SETPOINT_STEP_SIZE", true); - - this.mRandom = new RandomDataGenerator(); - this.mRandom.reSeed(seed); + this.maxChangeRatePerStepSetPoint = PropertiesUtil.getFloat(aProperties, "MAX_CHANGE_RATE_PER_STEP_SETPOINT", true); + this.maxSequenceLength = PropertiesUtil.getInt(aProperties, "MAX_SEQUENCE_LENGTH", true); + this.minSetPoint = PropertiesUtil.getFloat(aProperties, "SetPoint_MIN", true); + this.maxSetPoint = PropertiesUtil.getFloat(aProperties, "SetPoint_MAX", true); + this.setPointStepSize = PropertiesUtil.getFloat(aProperties, "SETPOINT_STEP_SIZE", true); + + this.random = new RandomDataGenerator(); + this.random.reSeed(seed); defineNewSequence(); } + /** + * Constructs a new object using the current time in milliseconds as a seed. + * @param aProperties The properties file to parse + * @throws PropertiesException if property values are badly formatted + */ + public SetPointGenerator(final Properties aProperties) throws PropertiesException { + this(System.currentTimeMillis(), aProperties); + } + + protected float getSetPointStepSize() { + return setPointStepSize; + } + + protected float getMaxChangeRatePerStepSetPoint() { + return maxChangeRatePerStepSetPoint; + } + + protected int getMaxSequenceLength() { + return maxSequenceLength; + } + + protected float getMinSetPoint() { + return minSetPoint; + } + + protected float getMaxSetPoint() { + return maxSetPoint; + } + + protected RandomDataGenerator getRandom() { + return random; + } /** - * returns the current steps * @return the current steps */ - public int getCurrentSteps () { - return this.mCurrentSteps; + public int getCurrentSteps() { + return this.currentSteps; } /** - * returns the change rate per step * @return the change rate per step */ - public double getChangeRatePerStep () { - return this.mChangeRatePerStep; + public double getChangeRatePerStep() { + return this.changeRatePerStep; } - + /** - * sets the current state of the setpoint generation engine - * @param currentSteps - * @param lastSequenceSteps - * @param changeRatePerStep + * Sets the current state of the setpoint generation engine. + * @param setpoint see {@link #getSetPoint()} + * @param currentSteps see {@link #getCurrentSteps()} + * @param lastSequenceSteps see {@link #getLastSequenceSteps()} + * @param changeRatePerStep see {@link #getChangeRatePerStep()} */ - public void setState (double setpoint, int currentSteps, int lastSequenceSteps, double changeRatePerStep) { - this.mSetPoint = setpoint; - this.mCurrentSteps = currentSteps; - this.mLastSequenceSteps = lastSequenceSteps; - this.mChangeRatePerStep = changeRatePerStep; + public void setState(final double setpoint, final int currentSteps, final int lastSequenceSteps, final double changeRatePerStep) { + this.setPoint = setpoint; + this.currentSteps = currentSteps; + this.lastSequenceSteps = lastSequenceSteps; + this.changeRatePerStep = changeRatePerStep; } - + /** * returns the current setpoint * @return the last sequence steps */ - public double getSetPoint() { - return this.mSetPoint; + public double getSetPoint() { + return this.setPoint; } - + /** * returns the last sequence steps * @return the last sequence steps */ - public int getLastSequenceSteps() { - return this.mLastSequenceSteps; - } - - - /** - * Default constructor with seed=System.currentTimeMillis() - * @param aProperties The properties file to parse - * @throws PropertiesException - */ - public SetPointGenerator(Properties aProperties) throws PropertiesException { - this(System.currentTimeMillis(), aProperties); + public int getLastSequenceSteps() { + return this.lastSequenceSteps; } - /** * Returns the next setpoint and on the internal memorized old setpoint * @return the next setpoint */ public double step() { - double newSetPoint = step(mSetPoint); - mSetPoint = newSetPoint; + final double newSetPoint = step(setPoint); + setPoint = newSetPoint; return newSetPoint; } @@ -149,32 +167,32 @@ public double step() { * @param aSetPoint * @return */ - private double step(double aSetPoint) { - if (mIsStationary) + private double step(final double aSetPoint) { + if (stationary) { return aSetPoint; + } - double setpointLevel = aSetPoint; - if (mCurrentSteps >= mLastSequenceSteps) { + if (currentSteps >= lastSequenceSteps) { defineNewSequence(); } - - mCurrentSteps++; - setpointLevel += mChangeRatePerStep * SETPOINT_STEP_SIZE; - - if (setpointLevel > MAXSETPOINT) { - setpointLevel = MAXSETPOINT; - if (mRandom.nextBinomial(1, 0.5) == 1) { - mChangeRatePerStep *= (-1); + + currentSteps++; + double setpointLevel = aSetPoint + changeRatePerStep * setPointStepSize; + + if (setpointLevel > maxSetPoint) { + setpointLevel = maxSetPoint; + if (random.nextBinomial(1, 0.5) == 1) { + changeRatePerStep *= (-1); } } - if (setpointLevel < MINSETPOINT) { - setpointLevel = MINSETPOINT; - if (mRandom.nextBinomial(1, 0.5) == 1) { - mChangeRatePerStep *= (-1); + if (setpointLevel < minSetPoint) { + setpointLevel = minSetPoint; + if (random.nextBinomial(1, 0.5) == 1) { + changeRatePerStep *= (-1); } } - assert setpointLevel <= MAXSETPOINT; + assert setpointLevel <= maxSetPoint; return setpointLevel; } @@ -183,72 +201,71 @@ private double step(double aSetPoint) { */ private void defineNewSequence() { //mLastSequenceSteps = mRandom.nextIntFromTo(0, MAX_SEQUENCE_LENGTH) + 1; - mLastSequenceSteps = mRandom.nextInt(1, MAX_SEQUENCE_LENGTH); - mCurrentSteps = 0; - mChangeRatePerStep = mRandom.nextUniform(0, 1) * MAX_CHANGE_RATE_PER_STEP_SETPOINT; - double r = mRandom.nextUniform(0, 1); + lastSequenceSteps = random.nextInt(1, maxSequenceLength); + currentSteps = 0; + changeRatePerStep = random.nextUniform(0, 1) * maxChangeRatePerStepSetPoint; + final double r = random.nextUniform(0, 1); if (r < 0.45f) { - mChangeRatePerStep *= (-1); + changeRatePerStep *= (-1); } if (r > 0.9f) { - mChangeRatePerStep = 0; + changeRatePerStep = 0; } } - + /** - * plots a setpoint trajectory for 10000 points - * - * @throws IOException - * @throws PropertiesException + * Plots a setpoint trajectory for 10000 points. + * + * @param args command-line arguments + * @throws IOException when there is an error reading the configuration file + * @throws PropertiesException if the configuration file is badly formatted */ - public static void main(String[] args) throws IOException, PropertiesException { + public static void main(final String[] args) throws IOException, PropertiesException { final int episodeLength = 10000; - double data[] = new double[episodeLength]; - - Properties props = null; - props = PropertiesUtil.setpointProperties(new File("src/main/resources/sim.properties")); - - SetPointGenerator lg = new SetPointGenerator(props); - - for (int i=0; i data = new ArrayList<>(episodeLength); + + final Properties props = PropertiesUtil.loadSetPointProperties(new File("src/main/resources/sim.properties")); + + final SetPointGenerator lg = new SetPointGenerator(props); + + for (int si = 0; si < episodeLength; si++) { + data.add(lg.step()); } - PlotCurve.plot ("SetPoint Trajectory", "Time", "SetPoint [%]", data); + PlotCurve.plot("SetPoint Trajectory", "Time", "SetPoint [%]", data); } - @Override - public void setSeed(long seed) { - this.mRandom.reSeed(seed); + public void setSeed(final long seed) { + this.random.reSeed(seed); } @Override - public void filter(DataVector state) { - state.setValue(SetPointGeneratorStateDescription.SetPoint, this.step()); - state.setValue(SetPointGeneratorStateDescription.SetPointChangeRatePerStep, this.mChangeRatePerStep); - state.setValue(SetPointGeneratorStateDescription.SetPointCurrentSteps, this.mCurrentSteps); - state.setValue(SetPointGeneratorStateDescription.SetPointLastSequenceSteps, this.mLastSequenceSteps); + public void filter(final DataVector state) { + state.setValue(SetPointGeneratorStateDescription.SET_POINT, step()); + state.setValue(SetPointGeneratorStateDescription.SET_POINT_CHANGE_RATE_PER_STEP, changeRatePerStep); + state.setValue(SetPointGeneratorStateDescription.SET_POINT_CURRENT_STEPS, currentSteps); + state.setValue(SetPointGeneratorStateDescription.SET_POINT_LAST_SEQUENCE_STEPS, lastSequenceSteps); } @Override - public void setConfiguration(DataVector state) { - this.mSetPoint = state.getValue(SetPointGeneratorStateDescription.SetPoint); - this.mChangeRatePerStep = state.getValue(SetPointGeneratorStateDescription.SetPointChangeRatePerStep); - this.mCurrentSteps = state.getValue(SetPointGeneratorStateDescription.SetPointCurrentSteps).intValue(); - this.mLastSequenceSteps = state.getValue(SetPointGeneratorStateDescription.SetPointLastSequenceSteps).intValue(); + public void setConfiguration(final DataVector state) { + this.setPoint = state.getValue(SetPointGeneratorStateDescription.SET_POINT); + this.changeRatePerStep = state.getValue(SetPointGeneratorStateDescription.SET_POINT_CHANGE_RATE_PER_STEP); + this.currentSteps = state.getValue(SetPointGeneratorStateDescription.SET_POINT_CURRENT_STEPS).intValue(); + this.lastSequenceSteps = state.getValue(SetPointGeneratorStateDescription.SET_POINT_LAST_SEQUENCE_STEPS).intValue(); } - @Override public DataVector getState() { - DataVectorImpl s = new DataVectorImpl (new SetPointGeneratorStateDescription()); - s.setValue(SetPointGeneratorStateDescription.SetPoint, mSetPoint); - s.setValue(SetPointGeneratorStateDescription.SetPointChangeRatePerStep, mChangeRatePerStep); - s.setValue(SetPointGeneratorStateDescription.SetPointCurrentSteps, mCurrentSteps); - s.setValue(SetPointGeneratorStateDescription.SetPointLastSequenceSteps, mLastSequenceSteps); - - return s; + final DataVectorImpl state = new DataVectorImpl(new SetPointGeneratorStateDescription()); + state.setValue(SetPointGeneratorStateDescription.SET_POINT, setPoint); + state.setValue(SetPointGeneratorStateDescription.SET_POINT_CHANGE_RATE_PER_STEP, changeRatePerStep); + state.setValue(SetPointGeneratorStateDescription.SET_POINT_CURRENT_STEPS, currentSteps); + state.setValue(SetPointGeneratorStateDescription.SET_POINT_LAST_SEQUENCE_STEPS, lastSequenceSteps); + + return state; } } + diff --git a/src/main/java/com/siemens/industrialbenchmark/externaldrivers/setpointgen/SetPointGeneratorStateDescription.java b/src/main/java/com/siemens/industrialbenchmark/externaldrivers/setpointgen/SetPointGeneratorStateDescription.java index 92c069f..da90e36 100755 --- a/src/main/java/com/siemens/industrialbenchmark/externaldrivers/setpointgen/SetPointGeneratorStateDescription.java +++ b/src/main/java/com/siemens/industrialbenchmark/externaldrivers/setpointgen/SetPointGeneratorStateDescription.java @@ -1,4 +1,4 @@ -/** +/* Copyright 2016 Siemens AG. Licensed under the Apache License, Version 2.0 (the "License"); @@ -15,28 +15,31 @@ */ package com.siemens.industrialbenchmark.externaldrivers.setpointgen; -import com.siemens.industrialbenchmark.datavector.DataVectorDescription; +import com.siemens.industrialbenchmark.datavector.DataVectorDescriptionImpl; import com.siemens.industrialbenchmark.datavector.state.MarkovianStateDescription; /** - * state description for the setpoint generator + * State description for the setpoint generator. */ -public class SetPointGeneratorStateDescription extends DataVectorDescription { - - // Definition of the observable variables from the markov state - public static final String SetPoint = MarkovianStateDescription.SetPoint; // the variable we drive +public class SetPointGeneratorStateDescription extends DataVectorDescriptionImpl { + + // Definition of the observable variables from the markov state ... + // the variable we drive + public static final String SET_POINT = MarkovianStateDescription.SET_POINT; // internal state variables - public static final String SetPointLastSequenceSteps="SetPointLastSequenceSteps"; - public static final String SetPointCurrentSteps="SetPointCurrentSteps"; - public static final String SetPointChangeRatePerStep="SetPointChangeRatePerStep"; - - - private final static String[] stateVars = new String[] { - SetPoint, SetPointLastSequenceSteps, SetPointCurrentSteps, SetPointChangeRatePerStep - }; - - public SetPointGeneratorStateDescription () { - super (stateVars); - } -} \ No newline at end of file + public static final String SET_POINT_LAST_SEQUENCE_STEPS = "SetPointLastSequenceSteps"; + public static final String SET_POINT_CURRENT_STEPS = "SetPointCurrentSteps"; + public static final String SET_POINT_CHANGE_RATE_PER_STEP = "SetPointChangeRatePerStep"; + + private static final String[] STATE_VARS = new String[] { + SET_POINT, + SET_POINT_LAST_SEQUENCE_STEPS, + SET_POINT_CURRENT_STEPS, + SET_POINT_CHANGE_RATE_PER_STEP + }; + + public SetPointGeneratorStateDescription() { + super(STATE_VARS); + } +} diff --git a/src/main/java/com/siemens/industrialbenchmark/properties/MissingPropertyException.java b/src/main/java/com/siemens/industrialbenchmark/properties/MissingPropertyException.java index e7d817e..c412bb8 100755 --- a/src/main/java/com/siemens/industrialbenchmark/properties/MissingPropertyException.java +++ b/src/main/java/com/siemens/industrialbenchmark/properties/MissingPropertyException.java @@ -1,4 +1,4 @@ -/** +/* Copyright 2016 Siemens AG. Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,17 +18,19 @@ import java.util.Properties; /** - * Exception thrown when a property was expected but not found within a {@link Properties} object + * EIndicates that a property was expected but not found within a + * {@link Properties} object. */ public class MissingPropertyException extends PropertiesException { private static final long serialVersionUID = -2126707120438673909L; - public MissingPropertyException(String aMessage, Throwable aThrowable, Properties aProperties, String aKey) { - super(aMessage, aThrowable, aProperties, aKey); - } - - public MissingPropertyException(Properties aProperties, String aKey) { - super("property '" + aKey + "' missing from configuration.", aProperties, aKey); - } + public MissingPropertyException(final String aMessage, final Throwable aThrowable, final Properties aProperties, final String aKey) { + super(aMessage, aThrowable, aProperties, aKey); + } + + public MissingPropertyException(final Properties aProperties, final String aKey) { + super("property '" + aKey + "' missing from configuration.", aProperties, aKey); + } } + diff --git a/src/main/java/com/siemens/industrialbenchmark/properties/PropertiesException.java b/src/main/java/com/siemens/industrialbenchmark/properties/PropertiesException.java index aec288b..7a67ee2 100755 --- a/src/main/java/com/siemens/industrialbenchmark/properties/PropertiesException.java +++ b/src/main/java/com/siemens/industrialbenchmark/properties/PropertiesException.java @@ -1,4 +1,4 @@ -/** +/* Copyright 2016 Siemens AG. Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,65 +18,59 @@ import java.util.Properties; /** - * Exception used for properties that are not available within a given property object - * + * Indicates properties that are not available within a given property object. + * * @author duell */ public class PropertiesException extends Exception { - /** - * - */ private static final long serialVersionUID = 4372660914816539735L; - private Properties mProperties; - private String mKey; + private final Properties properties; + private final String key; - /** - * @param aProperties the property object searched for a property - * @param aKey key of the missing property - */ - public PropertiesException(Properties aProperties, String aKey) { - super("Error while retrieving property '" + aKey + "' from configuration."); - mProperties = aProperties; - mKey = aKey; - } + /** + * @param properties the property object searched for a property + * @param key key of the missing property + */ + public PropertiesException(final Properties properties, final String key) { + this("Error while retrieving property '" + key + "' from configuration.", properties, key); + } - /** - * @param aMessage error message - * @param aProperties the property object searched for a property - * @param aKey key of the missing property - */ - public PropertiesException(String aMessage, Properties aProperties, String aKey) { - super(aMessage); - mProperties = aProperties; - mKey = aKey; - } + /** + * @param aMessage error message + * @param aProperties the property object searched for a property + * @param aKey key of the missing property + */ + public PropertiesException(final String aMessage, final Properties aProperties, final String aKey) { + super(aMessage); + properties = aProperties; + key = aKey; + } - /** - * @param aMessage error message - * @param aThrowable cause of the exception - * @param aProperties the property object searched for a property - * @param aKey key of the missing property - */ - public PropertiesException(String aMessage, Throwable aThrowable, Properties aProperties, String aKey) { - super(aMessage, aThrowable); - mProperties = aProperties; - mKey = aKey; - } + /** + * @param aMessage error message + * @param aThrowable cause of the exception + * @param aProperties the property object searched for a property + * @param aKey key of the missing property + */ + public PropertiesException(final String aMessage, final Throwable aThrowable, final Properties aProperties, final String aKey) { + super(aMessage, aThrowable); + properties = aProperties; + key = aKey; + } - /** - * @return property object missing a property and therefore causing the exception - */ - public Properties getProperties() { - return mProperties; - } + /** + * @return property object missing a property and therefore causing the exception + */ + public Properties getProperties() { + return properties; + } - /** - * property key causing the exception - * - * @return - */ - public String getKey() { - return mKey; - } + /** + * @return property key causing the exception + */ + public String getKey() { + return key; + } } + diff --git a/src/main/java/com/siemens/industrialbenchmark/properties/PropertiesUtil.java b/src/main/java/com/siemens/industrialbenchmark/properties/PropertiesUtil.java index 39d29a3..0df7d85 100755 --- a/src/main/java/com/siemens/industrialbenchmark/properties/PropertiesUtil.java +++ b/src/main/java/com/siemens/industrialbenchmark/properties/PropertiesUtil.java @@ -1,4 +1,4 @@ -/** +/* Copyright 2016 Siemens AG. Licensed under the Apache License, Version 2.0 (the "License"); @@ -22,8 +22,9 @@ import java.util.Properties; /** - * Properties util to retrieve typed property values from a properties object general pattern of methods: - * + * Properties utility to retrieve typed property values from a properties object. + * + * General pattern of methods: *
  * {@code
  * public type getType(Properties aProperties, String aKey)
@@ -32,222 +33,249 @@
  * public type getType(Properties aProperties, String aKey, booelan aIsRequiered, Type aDefault)
  * }
  * 
- * - * where
- *
  • type is the requested value of type Type
  • aProperties is the properties object that should contain the property
  • aKey is the - * key to the requested property
  • aDefault value is a possible default value. If the property is not available from the properties - * object, the default value is returned instead
  • aIsRequiered if true, the property is expected to be available, a - * {@link PropertiesException} is thrown if unavailable
    + * + * where:
    + *
      + *
    • type is the requested value of type Type
    • + *
    • aProperties is the properties object that should contain the property
    • + *
    • aKey is the key to the requested property
    • + *
    • aDefault value is a possible default value. + * If the property is not available from the properties object, + * the default value is returned instead
    • + *
    • aIsRequiered if true, the property is expected to be available, + * a {@link PropertiesException} is thrown if unavailable
    • + *
    *
    - * Default behavior: the method - * + * + * Default behavior + * + * the method: + * *
      * {@code
      * public type getType(aProperties, aKey)
      * }
      * 
    - * + * * shall be equivalent to: - * + * *
      * {@code
    - * public type getType(aProperties, aKey, false) // false: not required
    + * // false: not required
    + * public type getType(aProperties, aKey, false)
      * }
      * 
    - * - * resulting in - * + * + * resulting in: + * *
      * {@code
    - * public type getType(aProperties, aKey, false, javaDefault)// javaDefault is 0 for number, null for objects
    + * // javaDefault is 0 for number, null for objects
    + * public type getType(aProperties, aKey, false, javaDefault)
      * }
      * 
    - * + * * @author duell */ -public class PropertiesUtil -{ - - public static float getFloat(Properties aProperties, String aTag) throws PropertiesException - { - return getFloat(aProperties, aTag, false); - } - - public static float getFloat(Properties aProperties, String aTag, boolean aIsRequiered) throws PropertiesException - { - return getFloat(aProperties, aTag, aIsRequiered, 0); - } - - public static long getLong(Properties aProperties, String aTag, long aDefault) throws PropertiesException - { - return getLong(aProperties, aTag, false, aDefault); - } - - public static long getLong(Properties aProperties, String aTag) throws PropertiesException - { - return getLong(aProperties, aTag, false, 0L); - } - - public static long getLong(Properties aProperties, String aTag, boolean aIsRequiered) throws PropertiesException - { - return getLong(aProperties, aTag, aIsRequiered, 0L); - } - - public static long getLong(Properties aProperties, String aTag, boolean aIsRequiered, long aDefault) throws PropertiesException - { - String value; - if (aIsRequiered) value = getRequiredProperty(aProperties, aTag); - else value = aProperties.getProperty(aTag, Long.toString(aDefault)); - - try - { - return Long.parseLong(value.trim()); - } - catch (Exception e) - { - throw new PropertiesException("Could not map " + aTag + " to a double value: ", e, aProperties, aTag); - } - } - - public static double getDouble(Properties aProperties, String aTag) throws PropertiesException - { - try - { - return Double.parseDouble(aProperties.getProperty(aTag).trim()); - } - catch (Exception e) - { - throw new PropertiesException("Could not map " + aTag + " to a double value: ", e, aProperties, aTag); - } - } - - public static boolean getBoolean(Properties aProperties, String aTag) throws PropertiesException - { - return getBoolean(aProperties, aTag, false); - } - - public static boolean getBoolean(Properties aProperties, String aTag, boolean aIsRequiered) throws PropertiesException - { - return getBoolean(aProperties, aTag, aIsRequiered, false); - } - - public static boolean getBoolean(Properties aProperties, String aTag, boolean aIsRequiered, boolean aDefault) - throws PropertiesException - { - String value; - if (aIsRequiered) value = getRequiredProperty(aProperties, aTag); - else value = aProperties.getProperty(aTag, Boolean.toString(aDefault)); - try - { - return Boolean.parseBoolean(value.trim()); - } - catch (Exception e) - { - throw new PropertiesException("Could not map " + aTag + " to an integer value: ", e, aProperties, aTag); - } - } - - public static float getFloat(Properties aProperties, String aTag, float aDefault) throws PropertiesException - { - return getFloat(aProperties, aTag, false, aDefault); - } - - public static float getFloat(Properties aProperties, String aTag, boolean aIsRequiered, float aDefault) throws PropertiesException - { - String value; - if (aIsRequiered) value = getRequiredProperty(aProperties, aTag); - else value = aProperties.getProperty(aTag, Float.toString(aDefault)); - try - { - return Float.parseFloat(value.trim()); - } - catch (Exception e) - { - throw new PropertiesException("Could not map " + aTag + " to a float value: ", e, aProperties, aTag); - } - } - - public static int getInt(Properties aProperties, String aTag, int aDefault) throws PropertiesException - { - return getInt(aProperties, aTag, false, aDefault); - } - - public static int getInt(Properties aProperties, String aTag) throws PropertiesException - { - return getInt(aProperties, aTag, false, 0); - } - - public static int getInt(Properties aProperties, String aTag, boolean aIsRequiered) throws PropertiesException - { - return getInt(aProperties, aTag, aIsRequiered, 0); - } - - public static int getInt(Properties aProperties, String aTag, boolean aIsRequiered, int aDefault) throws PropertiesException - { - String value; - if (aIsRequiered) value = getRequiredProperty(aProperties, aTag); - else value = aProperties.getProperty(aTag, Integer.toString(aDefault)); - try - { - return Integer.parseInt(value.trim()); - } - catch (Exception e) - { - throw new PropertiesException("Could not map " + aTag + " to an integer value: ", e, aProperties, aTag); - } - } - - public static Properties getProperties(String aFilename) throws IOException - { - return getProperties(new File(aFilename)); - } - - public static Properties getProperties(File aFile) throws IOException - { - Properties p = new Properties(); - - FileInputStream in = new FileInputStream(aFile); - try - { - p.load(in); - } - finally - { - in.close(); - } - return p; - } - - /** - * @param aProp - * a property object - * @param aKey - * the key of the desired property - * @return the property value - * @throws PropertiesException - * if the property is not contained in the given {@link Properties} object - */ - public static String getRequiredProperty(Properties aProp, String aKey) throws PropertiesException - { - String ret = aProp.getProperty(aKey); - if (ret == null) throw new MissingPropertyException(aProp, aKey); - return ret; - } - - public static Properties setpointProperties(File aFile) throws IOException - { - if (!aFile.exists()) throw new FileNotFoundException("Properties file '" + aFile.getAbsolutePath() + "' does not exist"); - FileInputStream in = null; - Properties p = new Properties(); - try - { - in = new FileInputStream(aFile); - p.load(in); - } - finally - { - in.close(); - } - return p; - } +public final class PropertiesUtil { + + private PropertiesUtil() {} + + private static class MalformedPropertiesException extends PropertiesException { + MalformedPropertiesException( + final Properties properties, + final String tag, + final Exception exception, + final String type) + { + super(String.format("Could not map %s to a %s value: ", tag, type), + exception, properties, tag); + } + } + + public static float getFloat(final Properties aProperties, final String aTag) + throws PropertiesException + { + return getFloat(aProperties, aTag, false); + } + + public static float getFloat(final Properties aProperties, final String aTag, final boolean aIsRequiered) + throws PropertiesException + { + return getFloat(aProperties, aTag, aIsRequiered, 0); + } + + public static long getLong(final Properties aProperties, final String aTag, final long aDefault) + throws PropertiesException + { + return getLong(aProperties, aTag, false, aDefault); + } + + public static long getLong(final Properties aProperties, final String aTag) + throws PropertiesException + { + return getLong(aProperties, aTag, false, 0L); + } + + public static long getLong(final Properties aProperties, final String aTag, final boolean aIsRequiered) + throws PropertiesException + { + return getLong(aProperties, aTag, aIsRequiered, 0L); + } + + public static long getLong(final Properties aProperties, final String aTag, final boolean aIsRequiered, final long aDefault) + throws PropertiesException + { + final String value; + if (aIsRequiered) { + value = getRequiredProperty(aProperties, aTag); + } else { + value = aProperties.getProperty(aTag, Long.toString(aDefault)); + } + + try { + return Long.parseLong(value.trim()); + } catch (final NumberFormatException exc) { + throw new MalformedPropertiesException(aProperties, aTag, exc, "long"); + } + } + + public static double getDouble(final Properties aProperties, final String aTag) + throws PropertiesException + { + try { + return Double.parseDouble(aProperties.getProperty(aTag).trim()); + } catch (final NumberFormatException exc) { + throw new MalformedPropertiesException(aProperties, aTag, exc, "double"); + } + } + + public static boolean getBoolean(final Properties aProperties, final String aTag) + throws PropertiesException + { + return getBoolean(aProperties, aTag, false); + } + + public static boolean getBoolean(final Properties aProperties, final String aTag, final boolean aIsRequiered) + throws PropertiesException + { + return getBoolean(aProperties, aTag, aIsRequiered, false); + } + + public static boolean getBoolean(final Properties aProperties, final String aTag, final boolean aIsRequiered, final boolean aDefault) + throws PropertiesException + { + final String value; + if (aIsRequiered) { + value = getRequiredProperty(aProperties, aTag); + } else { + value = aProperties.getProperty(aTag, Boolean.toString(aDefault)); + } + try { + return Boolean.parseBoolean(value.trim()); + } catch (final Exception exc) { + // FIXME Actually, parseBoolean will nevr throw an exception, no matter the value. Therefore we might want to "manually" throw exceptions if the value does not confirm to something we want to accept as a boolean + throw new MalformedPropertiesException(aProperties, aTag, exc, "boolean"); + } + } + + public static float getFloat(final Properties aProperties, final String aTag, final float aDefault) + throws PropertiesException + { + return getFloat(aProperties, aTag, false, aDefault); + } + + public static float getFloat(final Properties aProperties, final String aTag, final boolean aIsRequiered, final float aDefault) + throws PropertiesException + { + final String value; + if (aIsRequiered) { + value = getRequiredProperty(aProperties, aTag); + } else { + value = aProperties.getProperty(aTag, Float.toString(aDefault)); + } + try { + return Float.parseFloat(value.trim()); + } catch (NumberFormatException exc) { + throw new MalformedPropertiesException(aProperties, aTag, exc, "float"); + } + } + + public static int getInt(final Properties aProperties, final String aTag, final int aDefault) + throws PropertiesException + { + return getInt(aProperties, aTag, false, aDefault); + } + + public static int getInt(final Properties aProperties, final String aTag) + throws PropertiesException + { + return getInt(aProperties, aTag, false, 0); + } + + public static int getInt(final Properties aProperties, final String aTag, final boolean aIsRequiered) + throws PropertiesException + { + return getInt(aProperties, aTag, aIsRequiered, 0); + } + + public static int getInt(final Properties aProperties, final String aTag, final boolean aIsRequiered, final int aDefault) + throws PropertiesException + { + final String value; + if (aIsRequiered) { + value = getRequiredProperty(aProperties, aTag); + } else { + value = aProperties.getProperty(aTag, Integer.toString(aDefault)); + } + try { + return Integer.parseInt(value.trim()); + } catch (final NumberFormatException exc) { + throw new MalformedPropertiesException(aProperties, aTag, exc, "integer"); + } + } + + public static Properties getProperties(final String aFilename) throws IOException { + return getProperties(new File(aFilename)); + } + + public static Properties getProperties(final File aFile) throws IOException { + + final Properties p = new Properties(); + + try (final FileInputStream in = new FileInputStream(aFile)) { + p.load(in); + } + return p; + } + + /** + * @param aProp a property object + * @param aKey the key of the desired property + * @return the property value + * @throws PropertiesException if the property is not contained in the given + * {@link Properties} object + */ + public static String getRequiredProperty(final Properties aProp, final String aKey) + throws PropertiesException + { + final String ret = aProp.getProperty(aKey); + if (ret == null) { + throw new MissingPropertyException(aProp, aKey); + } + return ret; + } + + public static Properties loadSetPointProperties(final File propertiesFile) throws IOException { + + if (!propertiesFile.exists()) { + throw new FileNotFoundException("Properties file '" + propertiesFile.getAbsolutePath() + "' does not exist"); + } + final Properties p = new Properties(); + try (final FileInputStream in = new FileInputStream(propertiesFile)) { + p.load(in); + } + return p; + } } + diff --git a/src/main/java/com/siemens/industrialbenchmark/util/PlotCurve.java b/src/main/java/com/siemens/industrialbenchmark/util/PlotCurve.java index f6edb01..6bbe15e 100755 --- a/src/main/java/com/siemens/industrialbenchmark/util/PlotCurve.java +++ b/src/main/java/com/siemens/industrialbenchmark/util/PlotCurve.java @@ -1,4 +1,4 @@ -/** +/* Copyright 2016 Siemens AG. Licensed under the Apache License, Version 2.0 (the "License"); @@ -19,48 +19,55 @@ import info.monitorenter.gui.chart.ITrace2D; import info.monitorenter.gui.chart.IAxis.AxisTitle; import info.monitorenter.gui.chart.traces.Trace2DSimple; - import java.awt.Toolkit; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; +import java.util.List; import javax.swing.JFrame; /** - * This class is a simple curve plotter. + * This is a simple curve plotter. * @author Michel Tokic - * */ -public class PlotCurve { - - public static void plot (String title, String xlabel, String ylabel, double[] data) { - - ////////////////////////////////// - // Create a chart: - ////////////////////////////////// - Chart2D chart = new Chart2D(); - - // Create an ITrace: - ITrace2D trace = new Trace2DSimple(); - +public final class PlotCurve { + + private PlotCurve() {} + + public static Chart2D plotChart(final String xlabel, final String ylabel, final List data) { + + // Create a chart + final Chart2D chart = new Chart2D(); + + // Create an ITrace + final ITrace2D trace = new Trace2DSimple(); + // Add the trace to the chart. This has to be done before adding points chart.addTrace(trace); - // Add all points, as it is static: - for (int i = 0; i < data.length; i++) { - trace.addPoint(i, data[i]); + // Add all points, as it is static + for (int di = 0; di < data.size(); di++) { + trace.addPoint(di, data.get(di)); } chart.getAxisX().setAxisTitle(new AxisTitle(xlabel)); chart.getAxisY().setAxisTitle(new AxisTitle(ylabel)); - - // Make it visible: + + return chart; + } + + public static void plot(final String title, final String xlabel, final String ylabel, final List data) { + + final Chart2D chart = plotChart(xlabel, ylabel, data); + + // Display the chart ... // Create a frame. - JFrame frame = new JFrame(title); - // add the chart to the frame: - frame.getContentPane().add(chart); + final JFrame frame = new JFrame(title); + // add the chart to the frame + frame.getContentPane().add(chart); frame.setSize(Toolkit.getDefaultToolkit().getScreenSize().width, 400); - - // Enable the termination button [cross on the upper right edge]: + + // Enable the termination button (cross on the upper right edge) frame.addWindowListener(new WindowAdapter() { - public void windowClosing(WindowEvent e) { + @Override + public void windowClosing(final WindowEvent evt) { System.exit(0); } }); diff --git a/src/main/java/com/siemens/industrialbenchmark/util/PropertiesTable.java b/src/main/java/com/siemens/industrialbenchmark/util/PropertiesTable.java new file mode 100644 index 0000000..f05b11c --- /dev/null +++ b/src/main/java/com/siemens/industrialbenchmark/util/PropertiesTable.java @@ -0,0 +1,160 @@ +/* +Copyright 2017 Siemens AG. + +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 com.siemens.industrialbenchmark.util; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import javax.swing.JTable; +import javax.swing.SwingConstants; +import javax.swing.table.AbstractTableModel; +import javax.swing.table.DefaultTableCellRenderer; +import javax.swing.table.TableColumn; +import javax.swing.table.TableColumnModel; + +/** + * This class is a JTable subclass that displays a table of Properties. + */ +public class PropertiesTable extends JTable { + + public PropertiesTable(final Properties properties) { + + setModel(new PropertiesTableModel(properties)); + + // Tweak the appearance of the table by manipulating its column model + final TableColumnModel colmodel = getColumnModel(); + + // Set column widths + colmodel.getColumn(0).setPreferredWidth(200); + colmodel.getColumn(1).setPreferredWidth(200); + + // Right justify the text in the first column + final TableColumn nameCol = colmodel.getColumn(0); + final DefaultTableCellRenderer renderer = new DefaultTableCellRenderer(); + renderer.setHorizontalAlignment(SwingConstants.RIGHT); + nameCol.setCellRenderer(renderer); + } + + public Properties getProperties() { + return ((PropertiesTableModel) getModel()).getProperties(); + } + + public void setProperty(final String key, final Object value) { + + final PropertiesTableModel model = (PropertiesTableModel) getModel(); + model.setValueAt(value, model.getPropertyRow(key), 1); + repaint(); + } + + private static class PropertiesTableModel extends AbstractTableModel { + + // These are the names of the columns represented by this TableModel + private static final String[] COLUMN_NAMES = new String[] {"Name", "Value"}; + + // These are the types of the columns represented by this TableModel + private static final Class[] COLUMN_TYPES = new Class[] {String.class, Object.class}; + + private final Properties properties; + private final transient List propertiesKeys; + private final transient List propertiesValues; + + PropertiesTableModel(final Properties properties) { + + this.properties = properties; + this.propertiesKeys = new ArrayList<>(properties.size()); + this.propertiesValues = new ArrayList<>(properties.size()); + for (final Map.Entry propertyEntry : properties.entrySet()) { + propertiesKeys.add((String) propertyEntry.getKey()); + propertiesValues.add(propertyEntry.getValue()); + } + } + + public Properties getProperties() { + return properties; + } + + public int getPropertyRow(final String key) { + return propertiesKeys.indexOf(key); + } + + // These simple methods return basic information about the table + @Override + public int getColumnCount() { + return COLUMN_NAMES.length; + } + + @Override + public int getRowCount() { + return properties.size(); + } + + @Override + public String getColumnName(final int column) { + return COLUMN_NAMES[column]; + } + + @Override + public Class getColumnClass(final int column) { + return COLUMN_TYPES[column]; + } + + /** + * This method returns the value that appears at the specified row and + * column of the table + */ + @Override + public Object getValueAt(final int row, final int column) { + + switch (column) { + case 0: + return propertiesKeys.get(row); + case 1: + return propertiesValues.get(row); + default: + return null; + } + } + + @Override + public boolean isCellEditable(final int row, final int column) { + + switch (column) { + case 0: + return false; + case 1: + return true; + default: + throw new IllegalStateException(); + } + } + + @Override + public void setValueAt(final Object value, final int row, final int column) { + + switch (column) { + case 0: + throw new IllegalStateException(); + case 1: + propertiesValues.set(row, value); + properties.setProperty(propertiesKeys.get(row), String.valueOf(value)); + break; + default: + throw new IllegalStateException(); + } + } + } +} diff --git a/src/main/java/com/siemens/rl/interfaces/DataVector.java b/src/main/java/com/siemens/rl/interfaces/DataVector.java index 98d3954..6d5b8f3 100755 --- a/src/main/java/com/siemens/rl/interfaces/DataVector.java +++ b/src/main/java/com/siemens/rl/interfaces/DataVector.java @@ -1,4 +1,4 @@ -/** +/* Copyright 2016 Siemens AG. Licensed under the Apache License, Version 2.0 (the "License"); @@ -19,46 +19,42 @@ import java.util.List; /** - * This interface lists all necessary methods to implement a data vector, + * This implements a data vector, * which might be a state- or action-vector. - * - * @author Michel Tokic * + * @author Michel Tokic */ public interface DataVector extends Serializable { + DataVectorDescription getDescription(); + /** * Returns the value for a given data-vector dimension. * @param key The state dimension. * @return The value */ - public Double getValue(String key); - + Double getValue(String key); + /** * Sets the current value of a given data-vector dimension. * @param key The state or action dimension. * @param value The associated value. */ - public void setValue (String key, double value); - + void setValue(String key, double value); + /** - * Returns a list containing the data-vector dimension names. + * Returns a list containing the data-vector dimension names. * The keys are ordered in the way as they are associated when * calling getValuesArray(). - * - * @return A list containing the data-vector dimension names. - */ - public List getKeys(); - - /** - * Returns a double[] array containing the values. - * @return A double[] array containing the values. + * + * @return dimension names */ - public double[] getValuesArray(); + List getKeys(); /** - * Returns a copy of the data vector. - * @return A copy of the data vector. + * Returns the values. + * @return dimension values */ - public DataVector clone(); + double[] getValuesArray(); } + diff --git a/src/main/java/com/siemens/rl/interfaces/DataVectorDescription.java b/src/main/java/com/siemens/rl/interfaces/DataVectorDescription.java new file mode 100644 index 0000000..4b105bc --- /dev/null +++ b/src/main/java/com/siemens/rl/interfaces/DataVectorDescription.java @@ -0,0 +1,36 @@ +/* +Copyright 2017 Siemens AG. + +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 com.siemens.rl.interfaces; + +import java.util.List; + +/** + * State/action description for the industrial benchmark + */ +public interface DataVectorDescription { + + /** + * Returns the number of variables. + * @return number of keys/dimensions + */ + int getNumberVariables(); + + /** + * Returns a list containing the variable names. + * @return keys, in the same order as the dimensions in the data vector + */ + List getVarNames(); +} diff --git a/src/main/java/com/siemens/rl/interfaces/Environment.java b/src/main/java/com/siemens/rl/interfaces/Environment.java index a1c257b..079070e 100755 --- a/src/main/java/com/siemens/rl/interfaces/Environment.java +++ b/src/main/java/com/siemens/rl/interfaces/Environment.java @@ -1,4 +1,4 @@ -/** +/* Copyright 2016 Siemens AG. Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,40 +16,37 @@ package com.siemens.rl.interfaces; /** - * This interface describes all relevant methods for implementing the dynamics of an environment. - * - * @author Michel Tokic + * This describes the dynamics of an environment. * + * @author Michel Tokic */ public interface Environment { - + /** - * Returns the reward. - * @return the reward + * @return the current/last reward */ double getReward(); /** - * Returns the observable state. * @return the observable state */ - DataVector getState(); - + DataVector getState(); + /** - * Returns the internal Markovian state. * @return the internal Markovian state */ - DataVector getInternalMarkovState(); - + DataVector getMarkovState(); + /** - * Performs an action within the environment and returns the reward + * Performs an action within the environment and returns the reward. * @param action The action to perform - * @return The reward + * @return The reward */ - double step (DataVector action); - + double step(DataVector action); + /** - * Function for resetting the environment. + * Resets the environment. */ void reset(); } + diff --git a/src/main/java/com/siemens/rl/interfaces/ExternalDriver.java b/src/main/java/com/siemens/rl/interfaces/ExternalDriver.java index 8427b21..e271d3a 100755 --- a/src/main/java/com/siemens/rl/interfaces/ExternalDriver.java +++ b/src/main/java/com/siemens/rl/interfaces/ExternalDriver.java @@ -1,4 +1,4 @@ -/** +/* Copyright 2016 Siemens AG. Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,35 +16,34 @@ package com.siemens.rl.interfaces; /** - * Abstract interface for attaching external drivers to the - * Environment, that affect/filter certain state dimensions + * Interface for attaching external drivers to the Environment. + * This affects the/filters certain state dimensions. * (e.g. such as setpoint). - * + * * @author Michel Tokic */ public interface ExternalDriver { /** - * Sets the random seed. - * @param seed The random seed to set. + * @param seed the random seed to set */ - public void setSeed (long seed); - + void setSeed(long seed); + /** - * Applies "in-place" the external drivers to the given data vector. + * Applies the external drivers to the given data vector "in-place". * @param state The data vector to apply the external drivers to. */ - public void filter (DataVector state); + void filter(DataVector state); /** - * Sets the external driver configuration from within the given data vector. + * Sets the external driver configuration from within the given data vector. * @param state The data vector containing the configuration variables. */ - public void setConfiguration (DataVector state); - + void setConfiguration(DataVector state); + /** - * Returns the current configuration. - * @return The current configuration. + * @return the current configuration */ - public DataVector getState(); + DataVector getState(); } + diff --git a/src/main/python/IndustrialBenchmarkEnv.py b/src/main/python/IndustrialBenchmarkEnv.py new file mode 100644 index 0000000..74a1088 --- /dev/null +++ b/src/main/python/IndustrialBenchmarkEnv.py @@ -0,0 +1,192 @@ +import jpype +import gym +import os + +# These will be set in load_constants() +DELTA_VELOCITY = None +DELTA_GAIN = None +DELTA_SHIFT = None +RANDOM_SEED = None + +def load_constants(): + '''Set some constants to values from Java. + ''' + global DELTA_VELOCITY + global DELTA_GAIN + global DELTA_SHIFT + global RANDOM_SEED + jpkg_action = jpype.JPackage("com.siemens.industrialbenchmark.datavector.action") + jpkg_state = jpype.JPackage("com.siemens.industrialbenchmark.datavector.state") + DELTA_VELOCITY = jpkg_action.ActionDeltaDescription.DELTA_VELOCITY + DELTA_GAIN = jpkg_action.ActionDeltaDescription.DELTA_GAIN + DELTA_SHIFT = jpkg_action.ActionDeltaDescription.DELTA_SHIFT + RANDOM_SEED = jpkg_state.MarkovianStateDescription.RANDOM_SEED + +def start_jvm(jvm_path = None, ind_bench_jar = None): + '''Starts the Java Virtual Machine (JVM) for use with this class. + Note: The JVM can only be started (and stopped) a single time per process! + So if you want to use it over many simulations in a single process, + only stop it in the very end, after all Java related activities have ended. + + Keyword arguments: + sim_props_file -- file path string to a *.properties; + example: industrialbenchmark/src/main/resources/sim.properties + ''' + if jpype.isJVMStarted(): + raise Exception("The JVM was already started by this process!") + if (jvm_path == None): + jvm_path = jpype.getDefaultJVMPath() + if (ind_bench_jar == None): + # FIXME System and version dependent paths: + this_files_dir = os.path.dirname(os.path.realpath(__file__)) + # We just assume we are in the siemens/industrialbenchmark sources + project_source_root = os.path.join(this_files_dir, '../../..') + ind_bench_jar = os.path.join(project_source_root, 'target/industrialbenchmark-1.1.2-SNAPSHOT-jar-with-dependencies.jar') + + classpath = ind_bench_jar + jpype.startJVM(jvm_path, "-Djava.class.path=%s" % classpath) + load_constants() + +def stop_jvm(): + '''Shuts down the Java Virtual Machine (JVM). + Note: The JVM can only be started (and stopped) a single time per process! + So if you want to use it over many simulations in a single process, + only stop it in the very end, after all Java related activities have ended. + ''' + if jpype.isJVMStarted(): + jpype.shutdownJVM() + +def is_jvm_running(): + '''Indicates whether a Java Virtual Machine (JVM) is running for this process. + ''' + return jpype.isJVMStarted() + + +def to_java_list(py_list): + """Convert from a Python to a Java list. + + Keyword arguments: + py_list -- a python list, for example: [a, b, c] + + Returns: + java_list -- a java.util.List with the same content as py_list + """ + java_list = jpype.java.util.ArrayList() + for item in py_list: + java_list.add(item) + return java_list + +def from_java_list(java_list): + """Convert from a Java to a Python list. + + Keyword arguments: + java_list -- a java.util.List + + Returns: + py_list -- a python list (like [a, b, c]) with the same content as java_list + """ + py_list = [None] * java_list.size() + for it in range(0, java_list.size()): + py_list[it] = java_list.get(it) + return py_list + +def into_java_dictionary(java_dict, py_dict): + '''Copies values from a Python to a Java dicionary. + + Keyword arguments: + java_dict -- the target (an implementation of com.siemens.rl.interfaces.DataVector) + py_dict -- the source (a python dictionary, as in `myDic['key'] = value`) + ''' + for key in py_dict: + java_dict.setValue(key, float(py_dict[key])) + + +class IndustrialBenchmarkEnv(gym.Env): + '''An OpenAI Gym environment implementation/wrapper + of the (Siemens) Industrial Benchmark simulator of gas and wind turbines + (written in Java). + The JPype library is used as the Python <=> Java bridge. + + To install prerequisites: + # Python 2 + sudo pip install Numpy + sudo pip install JPype1 + sudo pip install gym + # Python 3 + sudo pip3 install Numpy + sudo pip3 install JPype1-py3 + sudo pip3 install gym + + See: + * https://github.com/openai/gym/blob/master/gym/core.py + * https://gym.openai.com/docs + * https://github.com/siemens/industrialbenchmark + ''' + + def __init__(self, sim_props_file): + '''Creates a new IndustrialBenchmarkEnv on the basis + of simulation properties from a properties file. + + Keyword arguments: + sim_props_file -- file path string to a *.properties; + example: industrialbenchmark/src/main/resources/sim.properties + ''' + if not is_jvm_running(): + raise Exception("The JVM needs to be running!") + + load_constants() + + self._sim_props_file = sim_props_file + self._jpkg_ind_bench_ = jpype.JPackage("com.siemens.industrialbenchmark") + self._jpkg_ind_bench_dyn = jpype.JPackage("com.siemens.industrialbenchmark.dynamics") + self._jpkg_ind_bench_properties = jpype.JPackage("com.siemens.industrialbenchmark.properties") + self._jpkg_ind_bench_data_vec = jpype.JPackage("com.siemens.industrialbenchmark.datavector") + self._jpkg_ind_bench_data_vec_action = jpype.JPackage("com.siemens.industrialbenchmark.datavector.action") + self._jpkg_ind_bench_data_vec_state = jpype.JPackage("com.siemens.industrialbenchmark.datavector.state") + + self._props = self._jpkg_ind_bench_properties.PropertiesUtil.loadSetPointProperties(jpype.java.io.File(self._sim_props_file)); + self._java_env = self._jpkg_ind_bench_dyn.IndustrialBenchmarkDynamics(self._props) + self._action = self._jpkg_ind_bench_data_vec_action.ActionDelta(0.0, 0.0, 0.0) + self._seed_data_vec = self._jpkg_ind_bench_data_vec.DataVectorImpl(to_java_list([RANDOM_SEED])) + + # Override (from gym.Env) + self.reward_range = ( + int(self._props.getProperty(self._jpkg_ind_bench_data_vec_state.ObservableStateDescription.REWARD_TOTAL + "_MIN")), + int(self._props.getProperty(self._jpkg_ind_bench_data_vec_state.ObservableStateDescription.REWARD_TOTAL + "_MAX"))) + self.action_space = from_java_list(self._action.getDescription().getVarNames()) # returns java.util.List + self.observation_space = from_java_list(self._java_env.getMarkovState().getDescription().getVarNames()) # returns java.util.List + + def _step(self, action): + # XXX what would be better? + # XXX option 1: + #self._action.setDeltaVelocity(action.delta_velocity) + #self._action.setDeltaGain(action.delta_gain) + #self._action.setDeltaShift(action.delta_shift) + # XXX option 2: + self._action.setDeltaVelocity(action[DELTA_VELOCITY]) + self._action.setDeltaGain(action[DELTA_GAIN]) + self._action.setDeltaShift(action[DELTA_SHIFT]) + # XXX option 3: + # action being a jpype.com.siemens.industrialbenchmark.datavector.action.ActionDelta: + #into_java_dictionary(self._action, action) + + reward = self._java_env.step(self._action) + + observation = self._java_env.getMarkovState().getValuesArray() # returns double[] + done = False # never finnished + info = None # XXX Anything useful we could put here? + return [observation, reward, done, info] + + def _reset(self): + self._java_env.reset() + + def _render(self, mode='human', close=False): + '''We do not support any visualization. + ''' + return + + def _seed(self, seed=None): + self._seed_data_vec.setValue(RANDOM_SEED, float(seed)) + print(from_java_list(self._seed_data_vec.getKeys())) + self._java_env.setMarkovState(self._seed_data_vec) + return [seed] diff --git a/src/main/python/test_OpenAIGym_wrapper.py b/src/main/python/test_OpenAIGym_wrapper.py new file mode 100644 index 0000000..2671ad8 --- /dev/null +++ b/src/main/python/test_OpenAIGym_wrapper.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python + +import IndustrialBenchmarkEnv +import os + +# FIXME System dependent paths: +this_files_dir = os.path.dirname(os.path.realpath(__file__)) +# We just assume we are in the siemens/industrialbenchmark sources +project_source_root = os.path.join(this_files_dir, '../../..') + +# Start the JVM +# NOTE Make sure you execute `mvn package` (once) before this script +# FIXME System and version dependent path: +ind_bench_jar = os.path.join(project_source_root, 'target/industrialbenchmark-1.1.2-SNAPSHOT-jar-with-dependencies.jar') +IndustrialBenchmarkEnv.start_jvm(None, ind_bench_jar) + +# Create the environment +# FIXME System dependent path: +sim_props_file = os.path.join(project_source_root, 'src/main/resources/sim.properties') +indBenEnv = IndustrialBenchmarkEnv.IndustrialBenchmarkEnv(sim_props_file) + +print("\nindBenEnv.reward_range:") +print(indBenEnv.reward_range) +print("\nindBenEnv.action_space (len: %d):" % len(indBenEnv.action_space)) +print(indBenEnv.action_space) +print("\nindBenEnv.observation_space (len: %d):" % len(indBenEnv.observation_space)) +print(indBenEnv.observation_space) + +# Execute a single step +#jpkg_ind_bench_DatavecAction = jpype.JPackage("com.siemens.industrialbenchmark.datavector.action") +#action = { +# jpkg_ind_bench_DatavecAction.ActionDeltaDescription.DELTA_VELOCITY: 0.1, +# jpkg_ind_bench_DatavecAction.ActionDeltaDescription.DELTA_GAIN: 0.1, +# jpkg_ind_bench_DatavecAction.ActionDeltaDescription.DELTA_SHIFT: 0.1} +#action = object() +#action.delta_velocity = 0.1 +#action.delta_gain = 0.1 +#action.delta_shift = 0.1 +action = { + IndustrialBenchmarkEnv.DELTA_VELOCITY: 0.1, + IndustrialBenchmarkEnv.DELTA_GAIN: 0.1, + IndustrialBenchmarkEnv.DELTA_SHIFT: 0.1} +[observation, reward, done, info] = indBenEnv.step(action) + +print("\nobservation (len: %d):" % len(observation)) +print(observation) +print("\nreward:") +print(reward) +print("\ndone:") +print(done) +print("\ninfo:") +print(info) +print("") + +# Some more testing +indBenEnv.step(action) +indBenEnv.seed(12345) +indBenEnv.step(action) +indBenEnv.seed(123456) +indBenEnv.reset() +indBenEnv.step(action) + +# Stop the JVM +IndustrialBenchmarkEnv.stop_jvm() diff --git a/src/main/resources/checkstyle.xml b/src/main/resources/checkstyle.xml new file mode 100644 index 0000000..2566e34 --- /dev/null +++ b/src/main/resources/checkstyle.xml @@ -0,0 +1,224 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/java_header_regex_template.txt b/src/main/resources/java_header_regex_template.txt new file mode 100644 index 0000000..e79a521 --- /dev/null +++ b/src/main/resources/java_header_regex_template.txt @@ -0,0 +1,15 @@ +^/\*$ +^Copyright \d\d\d\d(-\d\d\d\d)? Siemens AG\.$ +^$ +^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\.$ +^\*/$ \ No newline at end of file diff --git a/src/main/resources/log4j.properties b/src/main/resources/log4j.properties index 561e3f0..250d377 100755 --- a/src/main/resources/log4j.properties +++ b/src/main/resources/log4j.properties @@ -1,4 +1,4 @@ -log4j.rootLogger=INFO, stdout, file +log4j.rootLogger=INFO, stdout, file log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout=org.apache.log4j.PatternLayout @@ -13,12 +13,12 @@ log4j.appender.file.layout=org.apache.log4j.PatternLayout log4j.appender.file.layout.ConversionPattern=%d{ISO8601} %-5p [%t] %c: %m%n -# logger level for core packages +# logger level for core packages log4j.category.com.siemens.ct.fenrir.project.alice.poweropt = DEBUG log4j.category.com.siemens.ct.fenrir.comproc.xmlgenerator.core.GeneratorParsingContext = WARN -# reducing logger level for service packages +# reducing logger level for service packages log4j.category.com.siemens.ct.seneca = WARN log4j.category.com.siemens.ct.fenrir.comproc.generator.core = WARN -log4j.category.com.siemens.sompc.util = WARN +log4j.category.com.siemens.sompc.util = WARN diff --git a/src/main/resources/pmd.xml b/src/main/resources/pmd.xml new file mode 100644 index 0000000..1d02e37 --- /dev/null +++ b/src/main/resources/pmd.xml @@ -0,0 +1,43 @@ + + + + PMD rules selection suitable for Industrial Benchmark + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/sim.properties b/src/main/resources/sim.properties index abec45a..0e7bfe6 100755 --- a/src/main/resources/sim.properties +++ b/src/main/resources/sim.properties @@ -1,11 +1,11 @@ -# Properties for IndustrialBenchmark +# Properties for IndustrialBenchmark # =================================== # benchmark params: SEED=1 SIM_STEPS=1000 -# weighting dynamics in reward +# weighting dynamics in reward CRD=3 # weighting operationalcosts in reward CRE=1 diff --git a/src/main/resources/simTest.properties b/src/main/resources/simTest.properties index b30f467..bc3c8fc 100755 --- a/src/main/resources/simTest.properties +++ b/src/main/resources/simTest.properties @@ -1,11 +1,11 @@ -# Properties for IndustrialBenchmark +# Properties for IndustrialBenchmark # =================================== # benchmark params: SEED=1 SIM_STEPS=10000 -# weighting dynamics in reward +# weighting dynamics in reward CRD=3 # weighting operationalcosts in reward CRE=1 diff --git a/src/test/java/com/siemens/industialbenchmark/dynamics/DynamicsRegressionTest.java b/src/test/java/com/siemens/industialbenchmark/dynamics/DynamicsRegressionTest.java index fb6dff2..328a3e0 100755 --- a/src/test/java/com/siemens/industialbenchmark/dynamics/DynamicsRegressionTest.java +++ b/src/test/java/com/siemens/industialbenchmark/dynamics/DynamicsRegressionTest.java @@ -1,4 +1,4 @@ -/** +/* Copyright 2016 Siemens AG. Licensed under the Apache License, Version 2.0 (the "License"); @@ -49,22 +49,22 @@ public void test() { /* // parse regression data file ClassLoader classLoader = getClass().getClassLoader(); - File f = new File (classLoader.getResource("dynamics/dyn-markov-old.csv").getFile()); - HashMap keyMap = new HashMap(); - + File f = new File(classLoader.getResource("dynamics/dyn-markov-old.csv").getFile()); + HashMap keyMap = new HashMap(); + try { - + BufferedReader reader = new BufferedReader(new FileReader(f)); // parse CSV keys if (reader.ready()) { - String[] fields = reader.readLine().split(" "); - + String[] fields = reader.readLine().split(" "); + for (int i=0; i 100) final ActionDelta action = new ActionDelta(0.1f, 0.1f, 0.1f); - - //parse data rows and compare current dynamics to the test data + + //parse data rows and compare current dynamics to the test data while (reader.ready()) { String values[] = reader.readLine().split(" "); - + // apply random action and retrieve markov state action.setDeltaGain(2.f * (rand.nextFloat() - 0.5f)); action.setDeltaVelocity(2.f * (rand.nextFloat() - 0.5f)); db.step(action); DataVector markovState = db.getInternalMarkovState(); - // compare values + // compare values assertEquals(Double.parseDouble(values[keyMap.get("Velocity")]), markovState.getValue(MarkovianStateDescription.Action_Velocity), 0.01); assertEquals(Double.parseDouble(values[keyMap.get("Gain")]), markovState.getValue(MarkovianStateDescription.Action_Gain), 0.01); assertEquals(Double.parseDouble(values[keyMap.get("HiddenDynVelocity")]), markovState.getValue(MarkovianStateDescription.FatigueLatent1), 0.01); assertEquals(Double.parseDouble(values[keyMap.get("HiddenDynGain")]), markovState.getValue(MarkovianStateDescription.FatigueLatent2), 0.01); assertEquals(Double.parseDouble(values[keyMap.get("Dynamics")]), markovState.getValue(MarkovianStateDescription.Fatigue), 0.01); - + // check goldstone variables assertEquals(Double.parseDouble(values[keyMap.get("HiddenGSDomain")]), markovState.getValue(MarkovianStateDescription.MisCalibrationDomain), 0.01); assertEquals(Double.parseDouble(values[keyMap.get("HiddenGSSystemResponse")]), markovState.getValue(MarkovianStateDescription.MisCalibrationSystemResponse), 0.01); assertEquals(Double.parseDouble(values[keyMap.get("HiddenGSPhiIdx")]), markovState.getValue(MarkovianStateDescription.MisCalibrationPhiIdx), 0.01); - + // check operationalcosts assertEquals(Double.parseDouble(values[keyMap.get("OperationalCostsConv")]), markovState.getValue(MarkovianStateDescription.OperationalCostsConv), 0.01); - assertEquals(Double.parseDouble(values[keyMap.get("OperationalCosts")]), markovState.getValue(MarkovianStateDescription.CurrentOperationalCost), 0.01); + assertEquals(Double.parseDouble(values[keyMap.get("OperationalCosts")]), markovState.getValue(MarkovianStateDescription.CurrentOperationalCost), 0.01); for (int i=0; i<10; i++) { String e = "OPERATIONALCOST_" + i; assertEquals(Double.parseDouble(values[keyMap.get(e)]), markovState.getValue(e), 0.01); } - assertEquals(Double.parseDouble(values[keyMap.get("RewardOperationalCosts")]), markovState.getValue(MarkovianStateDescription.RewardConsumption), 0.01); - assertEquals(Double.parseDouble(values[keyMap.get("RewardTotal")]), markovState.getValue(MarkovianStateDescription.RewardTotal), 0.01); + assertEquals(Double.parseDouble(values[keyMap.get("RewardOperationalCosts")]), markovState.getValue(MarkovianStateDescription.RewardConsumption), 0.01); + assertEquals(Double.parseDouble(values[keyMap.get("RewardTotal")]), markovState.getValue(MarkovianStateDescription.RewardTotal), 0.01); } - + reader.close(); - + } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { @@ -129,3 +129,4 @@ public void test() { */ } } + diff --git a/src/test/java/com/siemens/industialbenchmark/dynamics/TestAction.java b/src/test/java/com/siemens/industialbenchmark/dynamics/TestAction.java index 736aee0..4e4fbda 100755 --- a/src/test/java/com/siemens/industialbenchmark/dynamics/TestAction.java +++ b/src/test/java/com/siemens/industialbenchmark/dynamics/TestAction.java @@ -1,4 +1,4 @@ -/** +/* Copyright 2016 Siemens AG. Licensed under the Apache License, Version 2.0 (the "License"); @@ -32,40 +32,39 @@ public class TestAction { /** - * This function tests whether the absolute action would produce the deltas as expected. - * @throws IOException - * @throws PropertiesException + * This function tests whether the absolute action would produce the deltas as expected. + * @throws IOException when there is an error reading the configuration file + * @throws PropertiesException if the configuration file is badly formatted */ - @Test public void testActionAbsolute() throws IOException, PropertiesException { - + float b = 0; float a = 0; float c = 0; - Properties props = PropertiesUtil.setpointProperties(new File ("src/main/resources/sim.properties")); - ActionAbsolute aa = new ActionAbsolute (b, a, c, props); - ActionDelta ad = new ActionDelta (0, 0, 0); + Properties props = PropertiesUtil.loadSetPointProperties(new File("src/main/resources/sim.properties")); + ActionAbsolute aa = new ActionAbsolute(b, a, c, props); + ActionDelta ad = new ActionDelta(0, 0, 0); Random actionRand = new Random(System.currentTimeMillis()); float deltaB = 0; float deltaA = 0; float deltaC = 0; - + for (int i=0; i<100000; i++) { - - // search for valid gain delta + + // search for valid gain delta do { deltaB = 2.f*(actionRand.nextFloat()-0.5f); } while ((b+deltaB) < 0 || (b+deltaB) > 100 ); - + // search for valid velocity delta do { deltaA= 2.f*(actionRand.nextFloat()-0.5f); } while ((a+deltaA) < 0 || (a+deltaA) > 100 ); - + // search for valid velocity delta do { deltaC = 2.f*(actionRand.nextFloat()-0.5f); @@ -73,26 +72,27 @@ public void testActionAbsolute() throws IOException, PropertiesException { //System.out.println("gain=" + gain + ", deltaGain=" + deltaGain + ", newGain=" + (gain+deltaGain)); //System.out.println("velocity=" + velocity + ", deltaVelocity=" + deltaVelocity + ", newVelocity=" + (velocity+deltaVelocity)); - + aa.setGain(b + deltaB); aa.setVelocity(a + deltaA); aa.setShift(c + deltaC); - + ad.setDeltaGain(deltaB); ad.setDeltaVelocity(deltaA); ad.setDeltaShift(deltaC); - - assertEquals (aa.getDeltaGain(), ad.getDeltaGain(), 0.0001); - assertEquals (aa.getDeltaVelocity(), ad.getDeltaVelocity(), 0.0001); - assertEquals (aa.getDeltaShift(), ad.getDeltaShift(), 0.0001); - + + assertEquals(aa.getDeltaGain(), ad.getDeltaGain(), 0.0001); + assertEquals(aa.getDeltaVelocity(), ad.getDeltaVelocity(), 0.0001); + assertEquals(aa.getDeltaShift(), ad.getDeltaShift(), 0.0001); + b += deltaB; a += deltaA; c += deltaC; - - assertEquals (aa.getGain(), b, 0.0001); - assertEquals (aa.getVelocity(), a, 0.0001); - assertEquals (aa.getShift(), c, 0.0001); + + assertEquals(aa.getGain(), b, 0.0001); + assertEquals(aa.getVelocity(), a, 0.0001); + assertEquals(aa.getShift(), c, 0.0001); } } } + diff --git a/src/test/java/com/siemens/industialbenchmark/dynamics/TestDynamics.java b/src/test/java/com/siemens/industialbenchmark/dynamics/TestDynamics.java index da0d186..f7667d5 100755 --- a/src/test/java/com/siemens/industialbenchmark/dynamics/TestDynamics.java +++ b/src/test/java/com/siemens/industialbenchmark/dynamics/TestDynamics.java @@ -1,4 +1,4 @@ -/** +/* Copyright 2016 Siemens AG. Licensed under the Apache License, Version 2.0 (the "License"); @@ -41,7 +41,7 @@ /** * This is a test class for the Dynamics. - * + * * @author Michel Tokic */ public class TestDynamics { @@ -49,65 +49,67 @@ public class TestDynamics { final int INIT_STEPS = 10000; final int MEM_STEPS = 10000; final long ACTION_SEED = 12345; - + /** - * This class tests that dynamics are repeatable, which is required by Particle-Swarm-Optimization. - * - * 1) 100000 steps are taken => initializes dynamics with a random trajectory + * This class tests that dynamics are repeatable, + * which is required by Particle-Swarm-Optimization. + * + * 1) 100000 steps are taken + * (this initializes dynamics with a random trajectory) * 2) observable and markov state are memorized - * 3) a random trajectory is performed and memorized - * 4) reset of states from (2) and call Environment.reset() + * 3) a random trajectory is performed and memorized + * 4) reset of states from (2) and call Environment.reset() * 5) test if replay produces the same dynamics - * - * @throws IOException - * @throws PropertiesException + * + * @throws IOException when there is an error reading the configuration file + * @throws PropertiesException if the configuration file is badly formatted */ @Test public void testRepeatibleDynamics() throws IOException, PropertiesException { - - // INSTANTIATE benchmark - Properties props = PropertiesUtil.setpointProperties(new File ("src/main/resources/sim.properties")); - SetPointGenerator lg = new SetPointGenerator (props); + + // INSTANTIATE benchmark + Properties props = PropertiesUtil.loadSetPointProperties(new File("src/main/resources/sim.properties")); + SetPointGenerator lg = new SetPointGenerator(props); List externalDrivers = new ArrayList(); externalDrivers.add(lg); - IndustrialBenchmarkDynamics d = new IndustrialBenchmarkDynamics (props, externalDrivers); + IndustrialBenchmarkDynamics d = new IndustrialBenchmarkDynamics(props, externalDrivers); Random actionRand = new Random(System.currentTimeMillis()); - + // 1) do 100000 random steps, in order to initialize dynamics - final ActionDelta action = new ActionDelta(0.001f, 0.001f, 0.001f); + final ActionDelta action = new ActionDelta(0.001f, 0.001f, 0.001f); for (int i=0; i externalDrivers = new ArrayList(); externalDrivers.add(lg); - IndustrialBenchmarkDynamics d = new IndustrialBenchmarkDynamics (props, externalDrivers); - + IndustrialBenchmarkDynamics d = new IndustrialBenchmarkDynamics(props, externalDrivers); + int expHistSize = 0; - for (String key : d.getInternalMarkovState().getKeys()) { + for (String key : d.getMarkovState().getKeys()) { if (key.startsWith("OPERATIONALCOST_")) { expHistSize++; } } - - assertEquals (expHistSize, d.getOperationalCostsHistoryLength()); + + assertEquals(expHistSize, d.getOperationalCostsHistoryLength()); } } + diff --git a/src/test/java/com/siemens/industialbenchmark/dynamics/goldstone/TestGoldstoneDynamics.java b/src/test/java/com/siemens/industialbenchmark/dynamics/goldstone/TestGoldstoneDynamics.java index b09f2ea..a6c5e31 100755 --- a/src/test/java/com/siemens/industialbenchmark/dynamics/goldstone/TestGoldstoneDynamics.java +++ b/src/test/java/com/siemens/industialbenchmark/dynamics/goldstone/TestGoldstoneDynamics.java @@ -1,4 +1,4 @@ -/** +/* Copyright 2016 Siemens AG. Licensed under the Apache License, Version 2.0 (the "License"); @@ -12,7 +12,7 @@ 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 com.siemens.industialbenchmark.dynamics.goldstone; import static org.junit.Assert.*; @@ -36,39 +36,39 @@ public void test() { // parse regression data file ClassLoader classLoader = getClass().getClassLoader(); - File f = new File (classLoader.getResource("dynamics_class_regression_data.xa").getFile()); + File f = new File(classLoader.getResource("dynamics_class_regression_data.xa").getFile()); - ArrayList position = new ArrayList(); + ArrayList position = new ArrayList(); ArrayList penalty = new ArrayList(); - + try { - + BufferedReader reader = new BufferedReader(new FileReader(f)); while (reader.ready()) { String line = reader.readLine(); - + // only catch non-comment lines if (!line.startsWith("#")){ String[] fields = line.split(" "); - - Preconditions.checkArgument(fields.length == 3, + + Preconditions.checkArgument(fields.length == 3, "three fields (step, action, penalty) are expected, but " + fields.length + " fields were parsed."); position.add(Double.parseDouble(fields[1])); - penalty.add(Double.parseDouble(fields[2])); + penalty.add(Double.parseDouble(fields[2])); } } - + reader.close(); - System.out.println ("Goldstone dynamics class regression test: read " + position.size() + " rows."); - + System.out.println("Goldstone dynamics class regression test: read " + position.size() + " rows."); + } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } - + // TODO: implement Test int numberOfSteps = 24; double maxRequiredStep = 0.25; @@ -78,8 +78,11 @@ public void test() { dyn.stateTransition(position.get(step)); double reward = dyn.rewardAt(position.get(step)); - //System.out.println ("step: " + step + ", pos=" + position.get(step) + ", reward=" + reward); - assertEquals (-penalty.get(step), reward, 1e-8); +// System.out.println("step: " + step + ", pos=" + position.get(step) + ", reward=" + reward); + if (Math.abs(position.get(step)) <= 1.5) { + assertEquals(-penalty.get(step), reward, 1e-8); + } } } } + diff --git a/src/test/java/com/siemens/industialbenchmark/dynamics/goldstone/TestGoldstoneEnvironment.java b/src/test/java/com/siemens/industialbenchmark/dynamics/goldstone/TestGoldstoneEnvironment.java index ff1b4d7..d9d0d96 100755 --- a/src/test/java/com/siemens/industialbenchmark/dynamics/goldstone/TestGoldstoneEnvironment.java +++ b/src/test/java/com/siemens/industialbenchmark/dynamics/goldstone/TestGoldstoneEnvironment.java @@ -1,4 +1,4 @@ -/** +/* Copyright 2016 Siemens AG. Licensed under the Apache License, Version 2.0 (the "License"); @@ -12,7 +12,7 @@ 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 com.siemens.industialbenchmark.dynamics.goldstone; import static org.junit.Assert.*; @@ -36,39 +36,39 @@ public void test() { // parse regression data file ClassLoader classLoader = getClass().getClassLoader(); - File f = new File (classLoader.getResource("environment_class_regression_data.xa").getFile()); + File f = new File(classLoader.getResource("environment_class_regression_data.xa").getFile()); - ArrayList action = new ArrayList(); + ArrayList action = new ArrayList(); ArrayList penalty = new ArrayList(); - + try { - + BufferedReader reader = new BufferedReader(new FileReader(f)); while (reader.ready()) { String line = reader.readLine(); - + // only catch non-comment lines if (!line.startsWith("#")){ String[] fields = line.split(" "); - Preconditions.checkArgument(fields.length == 3, + Preconditions.checkArgument(fields.length == 3, "three fields (step, action, penalty) are expected, but " + fields.length + " fields were parsed."); - + action.add(Double.parseDouble(fields[1])); - penalty.add(Double.parseDouble(fields[2])); + penalty.add(Double.parseDouble(fields[2])); } } - + reader.close(); - System.out.println ("Goldstone environment class regression test: read " + action.size() + " rows."); - + System.out.println("Goldstone environment class regression test: read " + action.size() + " rows."); + } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } - + int numberOfSteps = 24; double maxRequiredStep = 0.25; double safeZone = 0.5 * maxRequiredStep; @@ -76,10 +76,13 @@ public void test() { for (int step=0; step< action.size(); step++) { double reward = env.stateTransition(action.get(step)); - - //System.out.println ("step: " + step + ", pos=" + position.get(step) + ", reward=" + reward); - assertEquals (-penalty.get(step), reward, 1e-8); + +// System.out.println("step: " + step + ", pos=" + env.getControlPosition() + ", reward=" + reward); + if (Math.abs(env.getControlPosition()) <= 1.5) { + assertEquals(-penalty.get(step), reward, 1e-8); + } } } } + diff --git a/src/test/java/com/siemens/industialbenchmark/dynamics/goldstone/TestGoldstoneReward.java b/src/test/java/com/siemens/industialbenchmark/dynamics/goldstone/TestGoldstoneReward.java index 2d99256..56b9b9c 100755 --- a/src/test/java/com/siemens/industialbenchmark/dynamics/goldstone/TestGoldstoneReward.java +++ b/src/test/java/com/siemens/industialbenchmark/dynamics/goldstone/TestGoldstoneReward.java @@ -1,4 +1,4 @@ -/** +/* Copyright 2016 Siemens AG. Licensed under the Apache License, Version 2.0 (the "License"); @@ -28,91 +28,91 @@ import com.siemens.industrialbenchmark.dynamics.goldstone.PenaltyFunction; public class TestGoldstoneReward { - + @Test public void rewardRegressionTest() { // Open File ClassLoader classLoader = getClass().getClassLoader(); - File f = new File (classLoader.getResource("reward_function_regression_data.xa").getFile()); + File f = new File(classLoader.getResource("reward_function_regression_data.xa").getFile()); ArrayList> data = new ArrayList>(); - ArrayList posIdx = new ArrayList(); + ArrayList posIdx = new ArrayList(); ArrayList header = new ArrayList(); - + int columns=0; int rows=0; try { - + BufferedReader reader = new BufferedReader(new FileReader(f)); while (reader.ready()) { String line = reader.readLine(); - + // catch comment lines if (line.startsWith("#\t") || line.startsWith("# ")) { - + // catch column headers } else if (line.startsWith("#!")) { - + line = line.replaceAll("#!\\t", ""); - + String[] fields = line.split(" "); for (int i=1; i()); columns++; } - + // catch data } else { String[] fields = line.split(" "); posIdx.add(Double.parseDouble(fields[0])); - + for (int i=1; i expected_penalties = data.get(i); - PenaltyFunction r = new PenaltyFunction (h, 0.25); - + PenaltyFunction r = new PenaltyFunction(h, 0.25); + for (int row=0; row initialized several parameters) + * + * For this the following procedure is performed: + * + * 1) a random walk of 100000 random steps is taken + * (this initializes several parameters) * 2) the internal parameters at t=100000 are memorized - * 3) a setpoint trajectory is captured for M steps + * 3) a setpoint trajectory is captured for M steps * 4) the parameters are reset to the parameters from (2) - * 5) a test is performed that compares if the regenerated setpoint is - * identical to the setpoint at same timestep in (3) + * 5) a test is performed that compares if the regenerated setpoint is + * identical to the setpoint at same timestep in (3) * * @author Michel Tokic */ public class TestSetPointRepeat { - + @Test public void testSetPointRepeat() throws IOException, PropertiesException, InstantiationException, IllegalAccessException { - + double setpointTrajectory[] = new double[100000]; - + // instantiate objects Properties props = null; - props = PropertiesUtil.setpointProperties(new File("src/main/resources/sim.properties")); + props = PropertiesUtil.loadSetPointProperties(new File("src/main/resources/sim.properties")); SetPointGenerator setpointgen = new SetPointGenerator(props); - + // 1) do a few random steps for (int i=0; i<100000; i++) { setpointgen.step(); } - - // 2) memorize setpointgen parameters before first step + + // 2) memorize setpointgen parameters before first step final double changeRate = setpointgen.getChangeRatePerStep(); final double startSetPoint = setpointgen.getSetPoint(); final int steps = setpointgen.getCurrentSteps(); final int sequenceSteps = setpointgen.getLastSequenceSteps(); - + // 3) perform M steps of a random walk setpointgen.setSeed(1234); for (int i=0; i mNames = new ArrayList(); mNames.add("TestVar"); - + MarkovianStateDescription m1 = new MarkovianStateDescription(mNames); - MarkovianStateDescription m2 = new MarkovianStateDescription(mNames); + MarkovianStateDescription m2 = new MarkovianStateDescription(mNames); - assertTrue (m1.toString().equals(m2.toString())); + assertTrue(m1.toString().equals(m2.toString())); } - + @Test public void testSizes() { - + ObservableStateDescription o1 = new ObservableStateDescription(); ObservableStateDescription o2 = new ObservableStateDescription(); - - assertTrue (o1.getNumberVariables() == o2.getVarNames().size()); - + + assertTrue(o1.getNumberVariables() == o2.getVarNames().size()); + } - + } + diff --git a/src/test/java/com/siemens/industrialbenchmark/util/RandomNumberExpectedValueTest.java b/src/test/java/com/siemens/industrialbenchmark/util/RandomNumberExpectedValueTest.java index a35fa2a..cb41cef 100755 --- a/src/test/java/com/siemens/industrialbenchmark/util/RandomNumberExpectedValueTest.java +++ b/src/test/java/com/siemens/industrialbenchmark/util/RandomNumberExpectedValueTest.java @@ -1,4 +1,4 @@ -/** +/* Copyright 2016 Siemens AG. Licensed under the Apache License, Version 2.0 (the "License"); @@ -23,21 +23,21 @@ import org.junit.Test; public class RandomNumberExpectedValueTest { - + @Test public void testExpectedValues() { - + long seed = 0; - Random rand = new Random (seed); - RandomDataGenerator randomData = new RandomDataGenerator(); + Random rand = new Random(seed); + RandomDataGenerator randomData = new RandomDataGenerator(); double uniformAverage = 0.0; double binomialAverage = 0.0; double normalAverage = 0.0; double exponentialAverage = 0.0; - + for(int i=0; i<1e6; i++){ - + // set current seed randomData.reSeed(seed); @@ -46,21 +46,21 @@ public void testExpectedValues() { double u = randomData.nextUniform(0, 1); double b = randomData.nextBinomial(1, 0.5); double e = randomData.nextExponential(0.25); - + // average mean random number uniformAverage += (1. / (1.+i))*(u - uniformAverage); binomialAverage += (1. / (1.+i))*(b - binomialAverage); normalAverage += (1. / (1.+i))*(n - normalAverage); exponentialAverage += (1. / (1.+i))*(e - exponentialAverage); - + // draw new seed from global random generator seed = rand.nextLong(); } - - assertEquals (0.5, uniformAverage, 0.001); - assertEquals (0.5, binomialAverage, 0.001); - assertEquals (0.0, normalAverage, 0.001); - assertEquals (0.25, exponentialAverage, 0.001); + + assertEquals(0.5, uniformAverage, 0.001); + assertEquals(0.5, binomialAverage, 0.001); + assertEquals(0.0, normalAverage, 0.001); + assertEquals(0.25, exponentialAverage, 0.001); } }