Question Summary
Is there any way of updating the probabilities within an existing instance of the class EnumeratedIntegerDistribution without creating an entirely new instance?
Background
I'm trying to implement a simplified Q-learning style demonstration using an android phone. I need to update the probabilities for each item with each loop through the learning process. Currently I am unable to find any method accessible from my instance of enumeratedIntegerDistribution that will let me reset|update|modify these probabilities. Therefore, the only way I can see to do this is to create a new instance of EnumeratedIntegerDistribution within each loop. Keeping in mind that each of these loops is only 20ms long, it is my understanding that this would be terribly memory inefficient compared to creating one instance and updating the values within the existing instance. Is there no standard set-style methods to update these probabilities? If not, is there a recommended workaround (i.e. using a different class, making my own class, overriding something to make it accessible, etc.?)
A follow up would be whether or not this question is a moot effort. Would the compiled code actually be any more/less efficient by trying to avoid this new instance every loop? (I'm not knowledgeable enough to know how compilers would handle such things).
Code
A minimal example below:
package com.example.mypackage.learning;
import android.app.Activity;
import android.os.Bundle;
import org.apache.commons.math3.distribution.EnumeratedIntegerDistribution;
public class Qlearning extends Activity {
private int selectedAction;
private int[] actions = {0, 1, 2};
private double[] weights = {1.0, 1.0, 1.0};
private double[] qValues = {1.0, 1.0, 1.0};
private double qValuesSum;
EnumeratedIntegerDistribution enumeratedIntegerDistribution = new EnumeratedIntegerDistribution(actions, weights);
private final double alpha = 0.001;
int action;
double reward;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
while(true){
action = determineAction();
reward = determineReward();
learn(action, reward);
}
}
public void learn(int action, double reward) {
qValues[selectedAction] = (alpha * reward) + ((1.0 - alpha) * qValues[selectedAction]);
qValuesSum = 0;
for (int i = 0; i < qValues.length; i++){
qValuesSum += Math.exp(qValues[i]);
}
weights[selectedAction] = Math.exp(qValues[selectedAction]) / qValuesSum;
// *** This seems inefficient ***
EnumeratedIntegerDistribution enumeratedIntegerDistribution = new EnumeratedIntegerDistribution(actions, weights);
}
}
Please don't focus on the absence of the determineAction() or determineReward() methods, as this is simply a minimal example. You could easily just sub in fixed values there (e.g. 1, and 1.5) if you wanted a working example.
Also, I'm well aware of the infinite while loop that would be troublesome for a GUI, but again, just trying to reduce the code I have to show here to get the point across.
Edit:
In response to a comment I'm posting what I had for a similar class below. Note I haven't used this in over a year and things may be broken. Just posting for reference:
public class ActionDistribution{
private double reward = 0;
private double[] weights = {0.34, 0.34, 0.34};
private double[] qValues = {0.1, 0.1, 0.1};
private double learningRate = 0.1;
private double temperature = 1.0;
private int selectedAction;
public ActionDistribution(){}
public ActionDistribution(double[] weights, double[] qValues, double learningRate, double temperature){
this.weights = weights;
this.qValues = qValues;
this.learningRate = learningRate;
this.temperature = temperature;
}
public int actionSelect(){
double sumOfWeights = 0;
for (double weight: weights){
sumOfWeights = sumOfWeights + weight;
}
double randNum = Math.random() * sumOfWeights;
double selector = 0;
int iterator = -1;
while (selector < randNum){
try {
iterator++;
selector = selector + weights[iterator];
}catch (ArrayIndexOutOfBoundsException e){
Log.e("abcvlib", "weight index bound exceeded. randNum was greater than the sum of all weights. This can happen if the sum of all weights is less than 1.");
}
}
// Assigning this as a read-only value to pass between threads.
this.selectedAction = iterator;
// represents the action to be selected
return iterator;
}
public double[] getWeights(){
return weights;
}
public double[] getqValues(){
return qValues;
}
public double getQValue(int action){
return qValues[action];
}
public double getTemperature(){
return temperature;
}
public int getSelectedAction() {
return selectedAction;
}
public void setWeights(double[] weights) {
this.weights = weights;
}
public void setQValue(int action, double qValue) {
this.qValues[action] = qValue;
}
public void updateValues(double reward, int action){
double qValuePrev = getQValue(action);
// update qValues due to current reward
setQValue(action,(learningRate * reward) + ((1.0 - learningRate) * qValuePrev));
// update weights from new qValues
double qValuesSum = 0;
for (double qValue : getqValues()) {
qValuesSum += Math.exp(temperature * qValue);
}
// update weights
for (int i = 0; i < getWeights().length; i++){
getWeights()[i] = Math.exp(temperature * getqValues()[i]) / qValuesSum;
}
}
public double getReward() {
return reward;
}
public void setReward(double reward) {
this.reward = reward;
}
}
Unfortunately it is not possible to update the existing EnumeratedIntegerDistribution. I have had similar issue in the past and I ended up re-creating the instance everytime I need to update the chances.
I won't worry too much about the memory allocations as those will be short-lived objects. These are micro-optimisations you should not worry about.
In my project I did implement a cleaner way with interfaces to create instances of these EnumeratedDistribution class.
This is not the direct answer but might guide you in the right direction.
public class DistributedProbabilityGeneratorBuilder<T extends DistributedProbabilityGeneratorBuilder.ProbableItem> {
private static final DistributedProbabilityGenerator EMPTY = () -> {
throw new UnsupportedOperationException("Not supported");
};
private final Map<Integer, T> distribution = new HashMap<>();
private DistributedProbabilityGeneratorBuilder() {
}
public static <T extends ProbableItem> DistributedProbabilityGeneratorBuilder<T> newBuilder() {
return new DistributedProbabilityGeneratorBuilder<>();
}
public DistributedProbabilityGenerator build() {
return build(ProbableItem::getChances);
}
/**
* Returns a new instance of probability generator at every call.
* #param chanceChangeFunction - Function to modify existing chances
*/
public DistributedProbabilityGenerator build(Function<T, Double> chanceChangeFunction) {
if (distribution.isEmpty()) {
return EMPTY;
} else {
return new NonEmptyProbabilityGenerator(createPairList(chanceChangeFunction));
}
}
private List<Pair<Integer, Double>> createPairList(Function<T, Double> chanceChangeFunction) {
return distribution.entrySet().stream()
.map(entry -> Pair.create(entry.getKey(), chanceChangeFunction.apply(entry.getValue())))
.collect(Collectors.toList());
}
public DistributedProbabilityGeneratorBuilder<T> add(int id, T item) {
if (distribution.containsKey(id)) {
throw new IllegalArgumentException("Id " + id + " already present.");
}
this.distribution.put(id, item);
return this;
}
public interface ProbableItem {
double getChances();
}
public interface DistributedProbabilityGenerator {
int generateId();
}
public static class NonEmptyProbabilityGenerator implements DistributedProbabilityGenerator {
private final EnumeratedDistribution<Integer> enumeratedDistribution;
NonEmptyProbabilityGenerator(List<Pair<Integer, Double>> pairs) {
this.enumeratedDistribution = new EnumeratedDistribution<>(pairs);
}
#Override
public int generateId() {
return enumeratedDistribution.sample();
}
}
public static ProbableItem ofDouble(double chances) {
return () -> chances;
}
}
Note - I am using EnumeratedDistribution<Integer>. You can easily change it to be EnumuratedIntegerDistribution.
The way I use the above class is as follows.
DistributedProbabilityGenerator distributedProbabilityGenerator = DistributedProbabilityGeneratorBuilder.newBuilder()
.add(0, ofDouble(10))
.add(1, ofDouble(45))
.add(2, ofDouble(45))
.build();
int generatedObjectId = distributedProbabilityGenerator.generateId();
Again, this is not a direct answer to your question but more of a pointer towards how you can use these classes in a better way.
Related
I am attempting to add up
a sequence of double precision floating point numbers which are given as Strings in an
array. The addition of these numbers is carried out in a separate background thread.
Running the code calculates the sum of the array of numbers and also gives the time that
the system took to calculate this in seconds.
I am not sure how to implement synchronization and conditional synchronization to this class:
public class SerialAdder implements Adder {
private String[] values;
private double sum;
private boolean ready = false;
public void run() {
synchronized (this) {
sum = 0.0;
for (int i = 0; i < values.length; i++) {
sum = sum + Double.valueOf(values[i]);
}
ready = true;
}
}
public synchronized void setValues(String[] values) {
this.values = values;
}
public synchronized void setThreads(int threads) {
// This does nothing since this is the single-threaded version.
}
public synchronized double getSum() {
return sum;
}
}
This should not be changed but is here for reference.
public interface Adder extends Runnable {
void setValues(String[] values);
void setThreads(int threads);
double getSum();
}
This is the main
import java.io.*;
public class Main {
/**
* All this data is "statistically initialized" and hence visibility to all threads in the running application.
*/
private static final String[] DATA1 = {"1.0", "2.0", "3.0", "4.0"};
private static final String[] DATA2 = {"100000000000000000000.0", "-100000000000000000000.0", "1.0", "2.0"};
private static final String[] DATA3 = {"1.0", "2.0", "100000000000000000000.0", "-100000000000000000000.0"};
/**
* This is an Example of more complex "static initialization" that guarantees data visibility to all threads.
*/
private static final String[] DATA4;
static {
/*** TASK3: CHANGE THIS VALUE SO THAT YOUR COMPUTER TAKES SEVERAL SECONDS FOR THE SERIAL CASE ***/
final int POWER = 10;
final int N = (int)Math.pow(2, POWER);
DATA4 = new String[N];
for (int i = 0; i < N; i++) {
DATA4[i] = String.valueOf(1.0/N);
}
}
public static void main(String[] args) throws InterruptedException, IOException {
// Start the timer ...
long startTime = System.currentTimeMillis();
/*** TASK 2 - CHANGE THIS LINE TO SEE HOW THE CODE BEHAVES WITH DIFFERENT DATA INPUTS. ***/
String[] values = DATA1;
/*** TASK 3 - CHANGE THE FOLLOWING SINGLE LINE TO CHANGE TO USING A MULTITHREADED VERSION OF THE ADDER. ***/
// This is an example of "programming to an interface" ... so only a single line
// needs to be changed to change the implementation used in the rest of the code.
Adder adder = new SerialAdder(); // = MultithreaderAdder();
adder.setValues(values);
new Thread(adder).start();
System.out.println("Answer = " + adder.getSum());
// Printed answer ... stop the timer.
long endTime = System.currentTimeMillis();
// Nanoseconds to seconds ...
System.out.println("Time = " + (endTime - startTime)/1000.0 + " seconds.") ;
}
}
And the multithreaded adder:
public class MultithreadedAdder implements Adder {
public void run() {};
public void setValues(String[] values) {};
public void setThreads(int threads) {};
public double getSum() {
return 0.0;
}
}
I am using the current data {“1.0”, “2.0”, “3.0”, “4.0”} and so expect answer of 10.0 however I am getting 0.
I would suggest some simplifications:
Drop the Adder interface. Implement the Callable interface instead. It allows you to return a value.
I'd advise against the setThreads() method. Give your Callable instance to a pooled Executor.
If one of those Strings in the array does not parse as a Double your sum will fail. What do you plan to do about it? I'd have a try/catch block.
import java.util.Arrays;
import java.util.concurrent.Callable;
public class DoubleStreamAdder implements Callable<Double> {
private final String [] values;
public DoubleStreamAdder(final String [] v) {
this.values = new String[v.length];
System.arraycopy(v, 0, this.values, 0, v.length);
}
#Override
public Double call() throws Exception {
return Arrays.stream(this.values).mapToDouble(Double::valueOf).sum();
}
}
You could do all of this using Java functional programming without the classes: see the single line in my call() method. That is what you're trying to do. The less code you write, the fewer bugs you'll have. You can eliminate more than 17 lines of code by deleting your interface and the class and writing a single line of code. Much better.
I have a series of small arrays (consisting of two doubles), many of which are
the same. E.g.
{5.0, 15.0}
{5.0, 15.0}
{5.0, 15.0}
{12.0, 8.0}
{10.0, 8.0}
{10.0, 8.0}
I want to be able to count the number of each arrays, i.e.
3 of {5.0, 15.0}
1 of {12.0, 8.0}
2 of {10.0, 8.0}
To do this, I tried making use of a LinkedHashMap (linked, because the order
might come into use later on):
import java.util.Map;
import java.util.LinkedHashMap;
public class MapArrayInt {
Map<double[], Integer> arrays = new LinkedHashMap<double[], Integer>();
public static void main(String[] args) {
MapArrayInt mapArrayInt = new MapArrayInt();
mapArrayInt.addArray(5.0, 15.0);
mapArrayInt.addArray(5.0, 15.0);
mapArrayInt.addArray(5.0, 15.0);
mapArrayInt.addArray(12.0, 8.0);
mapArrayInt.addArray(10.0, 8.0);
mapArrayInt.addArray(10.0, 8.0);
System.out.println(String.valueOf(mapArrayInt.arrays.get(new double[]{5.0, 15.0})));
System.out.println(String.valueOf(mapArrayInt.arrays.get(new double[]{12.0, 8.0})));
System.out.println(String.valueOf(mapArrayInt.arrays.get(new double[]{10.0, 8.0})));
}
void addArray(double val1, double val2) {
double[] newArray = new double[]{val1, val2};
if (!arrays.containsKey(newArray)) {
arrays.put(newArray, 1);
} else {
arrays.put(newArray, arrays.get(newArray) + 1);
}
}
}
I expected this output,
3
1
2
but got,
null
null
null
I'm quite new to Java, but I suspect this might be because each double[] counts as a unique because they are different instances, even though they contain the same two doubles.
How can I fix this, if I should at all (is there a better way)? I just need a data structure that allows me to
Add doubles[]
Preserves order of doubles[]
Easily iterate through to get doubles[] and number of said doubles[]
As I stated in my comment, with new you're creating a new instance of an object. Which means that the arrays you added with mapArrayInt.addArray(5.0, 15.0); and the arrays in mapArrayInt.arrays.get(new double[]{5.0, 15.0}) reference different objects. That's why you get null, because for the map those are different keys.
In order to circumvent this, you could create a custom wrapper class
import java.util.Arrays;
public class Exercise {
private final double[] array;
public Exercise(double first, double second) {
this.array = new double[]{first, second};
}
public boolean equals(Object obj) {
if(!(obj instanceof Exercise)) {
return false;
}
Exercise other = (Exercise)obj;
return Arrays.equals(this.array, other.array);
}
public int hashCode() {
return Arrays.hashCode(array);
}
}
The equals and hashCode methods are important, when you want to use this class in collections like Map, otherwise the hashcode of Object is used for checking equality and you'd have the same problem as you have now.
Then, in your main class you can use it like so:
void addArray(double val1, double val2) {
Exercise exercise = new Exercise(val1, val2);
if (!arrays.containsKey(exercise)) {
arrays.put(exercise, 1);
} else {
arrays.put(exercise, arrays.get(exercise) + 1);
}
}
And System.out.println(String.valueOf(mapArrayInt.arrays.get(new Exercise(5.0, 15.0))));
EDIT:
I changed one of the doubles to int (you said you're representing reps and weight ... and reps can only be a natural number right?)
You could build create an Exercise-Class like below and use the static method "of" to create the instances:
package somepackage;
import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.Map;
public class Exercise
{
private static final Map<Integer, Map<Double, WeakReference<Exercise>>> instances = new HashMap<>();
private final int reps;
private final double weight;
private Exercise(int reps, double weight)
{
this.reps = reps;
this.weight = weight;
}
public static Exercise of(int reps, double weight)
{
if (!instances.containsKey(reps))
{
instances.put(reps, new HashMap<>());
}
Map<Double, WeakReference<Exercise>> innerMap = instances.get(reps);
WeakReference<Exercise> weakRef = innerMap.get(weight);
Exercise instance = null;
if (weakRef != null)
{
instance = weakRef.get();
}
if (weakRef == null || instance == null || weakRef.isEnqueued())
{
instance = new Exercise(reps, weight);
innerMap.put(weight, new WeakReference<>(instance));
}
return instance;
}
public int getReps()
{
return this.reps;
}
public double getWeight()
{
return this.weight;
}
}
And then you could put those exercises in a map like below:
public void addArray(int reps, double weight)
{
Exercise exercise = Exercise.of(reps, weight);
if (!arrays.containsKey(exercise))
{
arrays.put(exercise, 1);
}
else
{
arrays.put(exercise, arrays.get(exercise) + 1);
}
}
OR:
Instead of an double[] as key you can use the a Map<Double, Integer> as your value for 2 values:
package somepackage;
import java.util.HashMap;
import java.util.Map;
public class MapArrayInt
{
private final Map<Double, Map<Double, Integer>> values;
public MapArrayInt()
{
this.values = new HashMap<>();
}
public void addArray(double val1, double val2)
{
if (!this.values.containsKey(val1))
{
this.values.put(val1, new HashMap<>());
}
Map<Double, Integer> innerValues = this.values.get(val1);
if (innerValues.containsKey(val2))
{
innerValues.put(val2, innerValues.get(val2) + 1);
}
else
{
innerValues.put(val2, 1);
}
}
public int getArrayValue(double val1, double val2)
{
Map<Double, Integer> innerValues = this.values.get(val1);
if (innerValues == null)
{
// you may also throw an Exception here
return 0;
}
Integer value = innerValues.get(val2);
if (value == null)
{
// also here you may throw an Exception
return 0;
}
return value;
}
public int getArrayValue(double[] values)
{
return getArrayValue(values[0], values[1]);
}
}
How do you mirror a function on x-Axis in Apache commons math, respectively set f() = -f()?
I found out so far, that you can add functions with FunctionUtils class and i guess i could
do a workaround by taking some points, set y-values negativ and interpolationg new Function,
but that seems a little cumbersome to me. Is there a simpler way?
As all functions are interfaces in org.apache.commons.math3.analysis you can wrap every function you want to invert into an anonymous object implementing that interface.
Here are three examples which should get you started:
/**
* Created for http://stackoverflow.com/q/22929746/1266906
*/
public class MinusFunction {
public static BivariateFunction invert(final BivariateFunction function) {
return new BivariateFunction() {
#Override
public double value(double x, double y) {
return - function.value(x,y);
}
};
}
public static MultivariateFunction invert(final MultivariateFunction function) {
return new MultivariateFunction() {
#Override
public double value(double[] point) {
return -function.value(point);
}
};
}
public static MultivariateMatrixFunction invert(final MultivariateMatrixFunction function) {
return new MultivariateMatrixFunction() {
#Override
public double[][] value(double[] point) {
final double[][] value = function.value(point);
for (int i = 0; i < value.length; i++) {
for (int j = 0; j < value[i].length; j++) {
value[i][j] = -value[i][j];
}
}
return value;
}
};
}
}
This is how I add a line to my chart at the moment. This is the abstract class for an arbitrry funciton I want to display:
public abstract class ArbitraryFunction implements
ValueProvider<ArbitraryFunctionData, Double> {
private String field;
public abstract Double f(Double x);
/**
* Constructor
*/
public ArbitraryFunction(String field) {
this.field = field;
}
#Override
public Double getValue(ArbitraryFunctionData object) {
return object.get(field);
}
#Override
public void setValue(ArbitraryFunctionData object, Double value) {
object.put(field, value);
}
#Override
public String getPath() {
return field;
}
}
This is how the chart is created:
ArbitraryFunction f1 = new ArbitraryFunction("f1") {
#Override
public Double f(Double x) {
return Math.sin(x);
}
};
functionMap.put(f1.getPath(), f1);
// collects the data of the functions and adds them to the store
for (Double x = 0.0; x <= 2 * Math.PI; x = x + 0.1) {
ArbitraryFunctionData d = new ArbitraryFunctionData();
d.setName("" + x);
for (Map.Entry<String, ArbitraryFunction> entry : functionMap.entrySet()) {
ArbitraryFunction tmp = entry.getValue();
d.put(tmp.getPath(), tmp.f(x));
}
store.add(d);
}
chart.setStore(store);
verticalAxis.setPosition(Position.LEFT);
verticalAxis.addField(f1);
verticalAxis.setSteps(2);
verticalAxis.setMinorTickSteps(5);
chart.addAxis(verticalAxis);
This works so far as intended. The graph shows my lines as it should do it and the vertical axis is correct too. But I have problems drawing the horizontal axis since I don't know what I need to give horizontalAxis.addField( ??? ). I've tried a few things, but nothing worked.
Does anyone know how I need to set up the horizontal axis?
What do you want the horizontal axis value to be? Is it another NumericAxis - does each data point have a x value that it should be drawn on? Each d in your loop has a String name and some value - perhaps you want a CategoryAxis<ArbitraryFunctionData, String> that just draws those name values?
Looks like I misunderstood earlier - your Function objects are just used in setup, not in changing how you plot data
I'm still not sure what you are after, but it sounds like you mostly want to plot some lines. Each data point (ArbitraryFunctionData?) seems to have Y values for each function being used, and a title, but no X values, so there is no way to plot each point as (X,Y) with two numeric axes, just as (name, Y) using a CategoryAxis and a NumericAxis. This would end up more or less like this sample: http://www.sencha.com/examples/#ExamplePlace:linechart - strings along the bottom, and numbers along the side.
Here's take one, build mostly off of the idea/structure you already have:
public class FunctionPlotter implements EntryPoint {
public static class ArbitraryFunctionData {
private double xValue;
private Map<String, Double> yValues = new HashMap<String, Double>();
public double get(String key) {
return yValues.get(key);
}
public void put(String key, double yValue) {
yValues.put(key, yValue);
}
public double getXValue() {
return xValue;
}
public void setxValue(double xValue) {
this.xValue = xValue;
}
}
public interface AFDProperties extends PropertyAccess<ArbitraryFunctionData> {
//xvalue is unique, key off of that
#Path("xValue")
ModelKeyProvider<ArbitraryFunctionData> key();
//automatic ValueProvider generation for the get/setXValue methods
ValueProvider<ArbitraryFunctionData, Double> xValue();
}
/**
* This is really doing two different jobs at once - wasn't quite was I was trying to suggest in
* that other question. See the second version of this for clarification...
*/
public static abstract class ArbitraryFunction implements ValueProvider<ArbitraryFunctionData, Double> {
private final String field;
public ArbitraryFunction(String field) {
this.field = field;
}
public abstract Double f(Double x);
#Override
public Double getValue(ArbitraryFunctionData object) {
return object.get(field);
}
#Override
public void setValue(ArbitraryFunctionData object, Double value) {
object.put(field, value);
}
#Override
public String getPath() {
return field;
}
}
#Override
public void onModuleLoad() {
Viewport vp = new Viewport();
Set<ArbitraryFunction> functions = new HashSet<ArbitraryFunction>();
ArbitraryFunction f1 = new ArbitraryFunction("f1") {
#Override
public Double f(Double x) {
return Math.sin(x);
}
};
functions.add(f1);
AFDProperties props = GWT.create(AFDProperties.class);
ListStore<ArbitraryFunctionData> store = new ListStore<ArbitraryFunctionData>(props.key());
// collects the data of the functions and adds them to the store
for (Double x = 0.0; x <= 2 * Math.PI; x = x + 0.1) {
// Create one data object, and set the X value, since that is the same for all Y values
ArbitraryFunctionData d = new ArbitraryFunctionData();
d.setxValue(x);
// For each function, set the corresponding Y value
for (ArbitraryFunction func : functions) {
d.put(func.getPath(), func.f(x));
}
store.add(d);
}
Chart<ArbitraryFunctionData> chart = new Chart<ArbitraryFunctionData>();
chart.setStore(store);
//Y-axis
NumericAxis<ArbitraryFunctionData> verticalAxis = new NumericAxis<ArbitraryFunctionData>();
verticalAxis.setPosition(Position.LEFT);
verticalAxis.addField(f1);//needs to know this field to properly set the range of values
//f2, f3, etc
verticalAxis.setSteps(2);
verticalAxis.setMinorTickSteps(5);
chart.addAxis(verticalAxis);
// X-Axis, this time reading from the xValue, not the series of ValueProviders
NumericAxis<ArbitraryFunctionData> horizAxis = new NumericAxis<ArbitraryFunctionData>();
horizAxis.setPosition(Position.BOTTOM);
horizAxis.addField(props.xValue());//same value for all
horizAxis.setSteps(2);
horizAxis.setMinorTickSteps(5);
chart.addAxis(horizAxis);
for (ArbitraryFunction func : functions) {
LineSeries<ArbitraryFunctionData> line = new LineSeries<ArbitraryFunctionData>();
// configure x axis
line.setXAxisPosition(Position.BOTTOM);//where is it
line.setXField(props.xValue());//what value do i use
// configure y axis
line.setYAxisPosition(Position.LEFT);//where is it
line.setYField(func);//what value do i use
//probably want to customized per func
line.setStroke(RGB.GRAY);
line.setStrokeWidth(2);
chart.addSeries(line);
}
vp.setWidget(chart);
RootPanel.get().add(vp);
}
}
And here's take two, this time with much simpler data and actually making the Function its own ValueProvider, and keeping the data dirt simple - just a double! Note that the ValueProvider is the function, and we never call getValue ourselves, we let the axis/series do it for us! Added a second function here to demonstrate that it does actually work.
public class FunctionPlotter implements EntryPoint {
/**
* Where did everything go? We're just making a ValueProvider now that can handle
* each number as a value, and working out the details from there
*
* For fun, added per-function coloring too
*/
public abstract static class Function implements ValueProvider<Double, Double> {
private final String name;
private final Color color;
public Function(String name, Color color) {
this.name = name;
this.color = color;
}
#Override
public abstract Double getValue(Double object);
#Override
public String getPath() {
return name;
}
#Override
public void setValue(Double object, Double value) {
//no-op
}
public Color getColor() {
return color;
}
}
#Override
public void onModuleLoad() {
Viewport vp = new Viewport();
Set<Function> functions = new HashSet<Function>();
Function f1 = new Function("f1", RGB.RED) {
#Override
public Double getValue(Double x) {
return Math.sin(x);
}
};
functions.add(f1);
Function f2 = new Function("f2", RGB.BLACK) {
#Override
public Double getValue(Double x) {
return Math.cos(x);
}
};
functions.add(f2);
//Turns out Stores can hold any objects - should probably factor out this key provider for reuse...
ListStore<Double> store = new ListStore<Double>(new ModelKeyProvider<Double>() {
#Override
public String getKey(Double item) {
return item.toString();
}
});
// collects the data of the functions and adds them to the store
for (Double x = 0.0; x <= 2 * Math.PI; x = x + 0.1) {
store.add(x);
}
Chart<Double> chart = new Chart<Double>();
chart.setStore(store);
//Y-axis
NumericAxis<Double> verticalAxis = new NumericAxis<Double>();
verticalAxis.setPosition(Position.LEFT);
for (Function func : functions) {
verticalAxis.addField(func);//needs to know this field to properly set the range of values
}
verticalAxis.setSteps(2);
verticalAxis.setMinorTickSteps(5);
chart.addAxis(verticalAxis);
// X-Axis, this time reading from the xValue, not the series of ValueProviders
NumericAxis<Double> horizAxis = new NumericAxis<Double>();
horizAxis.setPosition(Position.BOTTOM);
horizAxis.addField(new IdentityValueProvider<Double>());//magic value provider that returns the same string
horizAxis.setSteps(2);
horizAxis.setMinorTickSteps(5);
chart.addAxis(horizAxis);
for (Function func : functions) {
LineSeries<Double> line = new LineSeries<Double>();
// configure x axis
line.setXAxisPosition(Position.BOTTOM);//where is it
line.setXField(new IdentityValueProvider<Double>());//what value do i use
// configure y axis
line.setYAxisPosition(Position.LEFT);//where is it
line.setYField(func);//what value do i use
//probably want to customized per func
line.setStroke(func.getColor());
line.setStrokeWidth(2);
chart.addSeries(line);
}
vp.setWidget(chart);
RootPanel.get().add(vp);
}
}
I am trying to understand extending inner classes in Java. I have read around but nothing I found quite answers my question. So here goes...
I have...
public class Pie{
protected Slice[] slices;
// Pie constructor
public Pie(int n){
sliceGenerator(n)
}
private void sliceGenerator(int n){
slices = new Slice[n];
final float sweepAngle = 360.0f/(float)n;
float startAngle = 0;
for (int i=0;i<n;i++){
slices[i] = new Slice(startAngle);
startAngle += sweepAngle;
}
}
#Override
public String toString(){
for (Slice s:slices){
s.toString();
}
}
// Inner class...
public class Slice{
public Slice(float startAngle){
//set some private fields based on startAngle and generic pie
}
#Override
public String toString(){
return **string based on private fields**
}
}
}
Then I extend this...
public class ApplePie extends Pie{
protected Slice[] slices;
// Apple Pie constructor
public ApplePie(int n){
super(n);
}
// Inner class...
public class Slice extends Pie.Slice{
public Slice(float startAngle){
super(startAngle);
//set some **additional** private fields based on startAngle **specific to apple pie** appleness or something
}
#Override
public String toString(){
return **string based on apple pie specific private fields**
}
}
}
Now, when I make an Apple pie and call its toString method, like so...
ApplePie ap = new ApplePie(8);
System.out.println(ap.toString());
I do not get information about the apple pie slices, but information about the pie slices. It ignores my toString override, or more likely ignores my apple pie Slice. How can I arrange it such that apple pie slices refer to ApplePie?
Any help much appreciated! Sorry for pie references - it is the actual class I am working with...
I've changed your code to meet your requirements.
Your super class Pie is about to create a new instance of Slice, but the child class ApplePie's Slice does not override the Slice method of its super class'.
I added the functions below to enable the child class to create its own Slice.
protected void newSliceArray(int n) {
slices = new Slice[n];
}
protected Slice newSlice(float startAngle) {
return new Slice(startAngle);
}
Pie.java:
public class Pie {
private int a = 1;
protected Slice[] slices;
// Pie constructor
public Pie(int n) {
sliceGenerator(n);
}
private void sliceGenerator(int n) {
newSliceArray(n);
final float sweepAngle = 360.0f / n;
float startAngle = 0;
for (int i = 0; i < n; i++) {
slices[i] = newSlice(startAngle);
startAngle += sweepAngle;
}
}
protected void newSliceArray(int n) {
slices = new Slice[n];
}
protected Slice newSlice(float startAngle) {
return new Slice(startAngle);
}
#Override
public String toString() {
String t = "";
for (Slice s : slices) {
t += s.toString();
}
return t;
}
// Inner class...
public class Slice {
public Slice(float startAngle) {
// set some private fields based on startAngle and generic pie
}
#Override
public String toString() {
return "" + a;
}
}
}
ApplePie.java:
public class ApplePie extends Pie {
private int b = 2;
// protected Slice[] slices;
// Apple Pie constructor
public ApplePie(int n) {
super(n);
}
protected void newSliceArray(int n) {
slices = new Slice[n];
}
protected Slice newSlice(float startAngle) {
return new Slice(startAngle);
}
// Inner class...
public class Slice extends Pie.Slice {
public Slice(float startAngle) {
super(startAngle);
// set some **additional** private fields based on startAngle **specific to apple pie**
// appleness or something
}
#Override
public String toString() {
return b + "";
}
}
}
Test:
public static void main(String[] args) {
ApplePie ap = new ApplePie(8);
System.out.println(ap.toString());
}
The code will print 22222222
In your superclass, you are creating and storing Pie.Slice objects:
private void sliceGenerator(int n){
slices = new Slice[n];
final float sweepAngle = 360.0f/(float)n;
float startAngle = 0;
for (int i=0;i<n;i++){
slices[i] = new Slice(startAngle);
startAngle += sweepAngle;
}
}
These are the same objects being used by Pie.toString (which ApplePie doesn't override by the way).
Extending Pie with ApplePie and extending Pie.Slice with ApplePie.Slice doesn't change this. The new Slice(startAngle) in the above code does not magically switch to instantiating something different.
Aside from that, your Pie.toString() isn't returning anything - it shouldn't even compile:
#Override
public String toString(){
for (Slice s:slices){
s.toString();
}
}
I'm guessing you want to return a String representing all the slices. This would be a quick solution for example:
#Override
public String toString() {
return Arrays.toString(slices);
}
(Arrays.toString is just a utility method to get a String representing of an array.)
The answer lies within your program. When you instantiate Slice class, it gives call to the super class and invokes sliceGenerator. This method internally creates instances of Pie.Slice and not ApplePie.Slice. To get around this, make sliceGenerator method protected and override it in Apple.Slice class. Create the instances of Apple.Slice and it should work.