Suggestions for non-working Radial Basis Function Neural Network - c#
I'm creating a small C# application to help investigate different designs of Multilayer Perceptron and Radial Basis Function Neural Networks. The MLP is working adequately, but I can't manage to get the RBF Net to work at all.
I've checked and double checked and triple checked the algorithms to see if they match the algorithms that are available online and in papers and books and it seems like they do. I've also checked a colleagues (working) code and compared it to mine to see if there is anything I'd done wrong or left out and found nothing.
So I was hoping some extra eyes and opinions might help me root out the problem, as I've run out of ideas of what to try or where to look. If you want to have a look at or download the entire project you can find it at https://bitbucket.org/floofykh/gameai-coursework
I'll also post here the RBF specific code.
This is the Windows Form which allows the user to input the design of the RBF net and train it.
public partial class RBFForm : Form
{
private const double X_MAX = Math.PI * 2;
private const double X_MIN = 0;
private const double INTERVAL = Math.PI / 90d;
private double m_numPlotPoints;
private double m_noiseValue = 0;
public double StopThreshold { get { return m_stopThreshold; } }
private double m_stopThreshold;
private string m_function = "";
private List<double> m_inputs, m_targets;
private List<RadialBasisFunctionData> m_rbfsData;
private RadialBasisFunctionNetwork m_rbf;
private int m_numRBFs = 1;
private double m_rbfWidth = 1d, m_rbfOffset = 0d, m_rbfSeperation = 0d;
private bool m_changed = true;
private const int testCases = 180;
public RBFForm()
{
InitializeComponent();
ChartArea functionArea = m_functionGraph.ChartAreas.Add("function");
functionArea.AxisX.Maximum = X_MAX;
functionArea.AxisX.Minimum = X_MIN;
functionArea.AxisY.Maximum = 1.5;
functionArea.AxisY.Minimum = -1.5;
ChartArea rbfArea = m_rbfGraph.ChartAreas.Add("RBFs");
rbfArea.AxisX.Maximum = X_MAX;
rbfArea.AxisX.Minimum = X_MIN;
rbfArea.AxisY.Maximum = 1;
rbfArea.AxisY.Minimum = 0;
m_functionGraph.Series.Add("Neural Network");
m_functionGraph.Series.Add("Function");
m_functionGraph.Series.Add("Points");
m_rbfGraph.Series.Add("RBFs");
Neuron.LearningRate = ((double)(m_learningRateSelector).Value);
m_numRBFs = ((int)(m_numRBFsInput).Value);
m_rbfOffset = ((double)(m_rbfOffsetController).Value);
m_rbfSeperation = ((double)(m_rbfOffsetController).Value);
m_rbfWidth = ((double)(m_rbfWidthController).Value);
m_rbf = new RadialBasisFunctionNetwork(this);
}
private void InitialiseFunctionGraph()
{
Series func = m_functionGraph.Series.FindByName("Function");
func.Points.Clear();
func.ChartType = SeriesChartType.Line;
func.Color = Color.Green;
func.BorderWidth = 1;
for (double x = X_MIN; x < X_MAX; x += INTERVAL)
{
double y = 0;
switch (m_function)
{
case "Sin":
y = Math.Sin(x);
break;
case "Cos":
y = Math.Cos(x);
break;
};
func.Points.AddXY(x, y);
}
}
private void InitialiseRBFs()
{
m_rbfsData = new List<RadialBasisFunctionData>();
Series rbfs = m_rbfGraph.Series.FindByName("RBFs");
rbfs.Points.Clear();
rbfs.ChartType = SeriesChartType.Line;
rbfs.Color = Color.IndianRed;
rbfs.BorderWidth = 1;
for(int i=0; i<m_numRBFs; i++)
{
double centre = X_MIN + m_rbfOffset + m_rbfSeperation * i;
RadialBasisFunctionData data = new RadialBasisFunctionData();
data.Centre = centre;
data.Width = m_rbfWidth;
m_rbfsData.Add(data);
DrawRBF(centre, m_rbfWidth, rbfs.Points);
}
}
private void DrawRBF(double centre, double width, DataPointCollection points)
{
if(width > 0)
{
IActivationFunction function = new RadialBasisFunction(centre, width);
for (double x = X_MIN; x < X_MAX; x += INTERVAL)
{
double y = function.Function(x);
points.AddXY(x, y);
}
}
}
private void InitialiseInputPoints()
{
m_inputs = new List<double>();
m_targets = new List<double>();
Series points = m_functionGraph.Series.FindByName("Points");
points.Points.Clear();
points.ChartType = SeriesChartType.Point;
points.Color = Color.Blue;
points.BorderWidth = 1;
double interval = 0d;
if (m_numPlotPoints > 1)
interval = (X_MAX - X_MIN) / (m_numPlotPoints - 1);
for (int point = 0; point < m_numPlotPoints; point++)
{
double x = X_MIN + point * interval;
double y = 0;
switch (m_function)
{
case "Sin":
y = Math.Sin(x);
break;
case "Cos":
y = Math.Cos(x);
break;
};
y += (Program.rand.NextDouble() - 0.5d) * 2d * m_noiseValue;
m_targets.Add(y);
m_inputs.Add(x);
points.Points.AddXY(x, y);
}
}
public void SetNumEpochs(int num)
{
m_numEpochLabel.Text = num.ToString();
}
public void SetNumEpochsAsync(int num)
{
try
{
if (m_numEpochLabel.InvokeRequired)
{
m_numEpochLabel.Invoke((MethodInvoker)delegate
{
m_numEpochLabel.Text = num.ToString();
});
}
}
catch (Exception) { }
}
private void m_rbfSeperationController_ValueChanged(object sender, EventArgs e)
{
double value = ((double)((NumericUpDown)sender).Value);
m_rbfSeperation = value;
InitialiseRBFs();
m_changed = true;
}
private void m_numRBFsInput_ValueChanged(object sender, EventArgs e)
{
int value = ((int)((NumericUpDown)sender).Value);
m_numRBFs = value;
InitialiseRBFs();
m_changed = true;
}
private void m_rbfWidthController_ValueChanged(object sender, EventArgs e)
{
double value = ((double)((NumericUpDown)sender).Value);
m_rbfWidth = value;
InitialiseRBFs();
m_changed = true;
}
private void m_rbfOffsetController_ValueChanged(object sender, EventArgs e)
{
double value = ((double)((NumericUpDown)sender).Value);
m_rbfOffset = value;
InitialiseRBFs();
m_changed = true;
}
private void m_learningRateSelector_ValueChanged(object sender, EventArgs e)
{
double value = ((double)((NumericUpDown)sender).Value);
Neuron.LearningRate = value;
m_changed = true;
}
private void m_momentumController_ValueChanged(object sender, EventArgs e)
{
double value = ((double)((NumericUpDown)sender).Value);
Neuron.MomentumAlpha = value;
m_changed = true;
}
private void m_thresholdController_ValueChanged(object sender, EventArgs e)
{
double value = ((double)((NumericUpDown)sender).Value);
m_stopThreshold = value;
m_changed = true;
}
private void m_functionSelector_SelectedIndexChanged(object sender, EventArgs e)
{
m_function = ((ComboBox)sender).SelectedItem.ToString();
InitialiseFunctionGraph();
m_changed = true;
}
private void m_plotPointsController_ValueChanged(object sender, EventArgs e)
{
double value = ((double)((NumericUpDown)sender).Value);
m_numPlotPoints = value;
InitialiseInputPoints();
m_changed = true;
}
private void m_noiseController_ValueChanged(object sender, EventArgs e)
{
double value = ((double)((NumericUpDown)sender).Value);
m_noiseValue = value;
InitialiseInputPoints();
m_changed = true;
}
private void m_trainButton_Click(object sender, EventArgs e)
{
if (m_rbf != null)
{
if (RadialBasisFunctionNetwork.Running)
{
RadialBasisFunctionNetwork.Running = false;
}
else
{
if (m_changed)
{
m_rbf.Initialise(1, m_rbfsData, 1);
m_changed = false;
}
RadialBasisFunctionNetwork.ErrorStopThreshold = m_stopThreshold;
List<List<double>> inputPatterns = new List<List<double>>();
List<List<double>> targetPatterns = new List<List<double>>();
for (int i = 0; i < m_inputs.Count; i++)
{
List<double> newInputPattern = new List<double>();
newInputPattern.Add(m_inputs[i]);
List<double> newTargetPattern = new List<double>();
newTargetPattern.Add(m_targets[i]);
inputPatterns.Add(newInputPattern);
targetPatterns.Add(newTargetPattern);
}
m_rbf.Train(inputPatterns, targetPatterns);
}
}
}
public void TestAndPresent()
{
List<double> finalData = new List<double>();
for (double x = X_MIN; x < X_MAX; x += INTERVAL)
{
List<double> input = new List<double>();
input.Add(x);
finalData.AddRange(m_rbf.Test(input));
}
PlotNeuralOutput(finalData);
}
public void TestAndPresentAsync()
{
List<double> finalData = new List<double>();
for (double x = X_MIN; x < X_MAX; x += INTERVAL)
{
List<double> input = new List<double>();
input.Add(x);
finalData.AddRange(m_rbf.Test(input));
}
PlotNeuralOutputAsync(finalData);
}
public void PlotNeuralOutput(List<double> output)
{
Series network = m_functionGraph.Series["Neural Network"];
network.Points.Clear();
network.ChartType = SeriesChartType.Line;
network.Color = Color.Red;
network.BorderWidth = 3;
double x = 0;
for (int i = 0; i < output.Count; i++)
{
network.Points.AddXY(x, output[i]);
x += INTERVAL;
}
}
public void PlotNeuralOutputAsync(List<double> output)
{
try
{
if (m_functionGraph.InvokeRequired)
{
m_functionGraph.Invoke((MethodInvoker)delegate
{
Series network = m_functionGraph.Series["Neural Network"];
network.Points.Clear();
network.ChartType = SeriesChartType.Line;
network.Color = Color.Red;
network.BorderWidth = 3;
double x = 0;
for (int i = 0; i < output.Count; i++)
{
network.Points.AddXY(x, output[i]);
x += INTERVAL;
}
});
}
}
catch (Exception) { }
}
}
Here is the RadialBasisFunction class where most of the RBF algorithm takes place, specificaly in FeedForward().
class RadialBasisFunctionNetwork
{
private NeuronLayer m_inputLayer;
private NeuronLayer m_radialFunctions;
private NeuronLayer m_outputLayer;
private int m_numRadialFunctions = 0;
public static bool Running = false;
public static double ErrorStopThreshold {get; set;}
private static int m_epoch = 0;
public static int Epoch { get { return m_epoch; } }
private RBFForm m_RBFForm = null;
public RadialBasisFunctionNetwork(RBFForm RBFForm)
{
m_RBFForm = RBFForm;
m_inputLayer = new NeuronLayer();
m_radialFunctions = new NeuronLayer();
m_outputLayer = new NeuronLayer();
}
public void Initialise(int numInputs, List<RadialBasisFunctionData> radialFunctions, int numOutputs)
{
ErrorStopThreshold = 0d;
m_epoch = 0;
m_numRadialFunctions = radialFunctions.Count;
m_inputLayer.Neurons.Clear();
//Add bias neuron
/*Neuron inputBiasNeuron = new Neuron(1d);
inputBiasNeuron.Initialise(m_numRadialFunctions);
m_inputLayer.Neurons.Add(inputBiasNeuron);*/
for(int i=0; i<numInputs; i++)
{
Neuron newNeuron = new Neuron();
newNeuron.Initialise(m_numRadialFunctions);
m_inputLayer.Neurons.Add(newNeuron);
}
m_outputLayer.Neurons.Clear();
for (int i = 0; i < numOutputs; i++)
{
Neuron newNeuron = new Neuron();
m_outputLayer.Neurons.Add(newNeuron);
}
m_radialFunctions.Neurons.Clear();
//Add bias neuron
/* Neuron outputBiasNeuron = new Neuron(1d);
outputBiasNeuron.Initialise(numOutputs);
outputBiasNeuron.ActivationFunction = new ConstantActivationFunction();
m_radialFunctions.Neurons.Add(outputBiasNeuron);*/
for (int i = 0; i < m_numRadialFunctions; i++)
{
Neuron newNeuron = new Neuron();
newNeuron.Initialise(numOutputs);
newNeuron.ActivationFunction = new RadialBasisFunction(radialFunctions[i].Centre, radialFunctions[i].Width);
m_radialFunctions.Neurons.Add(newNeuron);
}
}
public void Train(List<List<double>> inputs, List<List<double>> targets)
{
Running = true;
BackgroundWorker bw = new BackgroundWorker();
bw.DoWork += new DoWorkEventHandler(
delegate(object o, DoWorkEventArgs args)
{
while (Running)
{
TrainPatterns(inputs, targets);
m_RBFForm.SetNumEpochsAsync(m_epoch);
m_RBFForm.TestAndPresentAsync();
}
});
bw.RunWorkerAsync();
}
private void TrainPatterns(List<List<double>> inputs, List<List<double>> targets)
{
Queue<int> randomIndices = GenRandomNonRepNumbers(inputs.Count, 0, inputs.Count, Program.rand);
bool trained = true;
while(randomIndices.Count > 0)
{
int index = randomIndices.Dequeue();
TrainPattern(inputs[index], targets[index]);
foreach(Neuron neuron in m_outputLayer.Neurons)
{
if (Math.Abs(neuron.Error) > ErrorStopThreshold)
trained = false;
}
}
m_epoch++;
if (trained)
Running = false;
}
public void TrainPattern(List<double> inputs, List<double> targets)
{
InitialisePatternSet(inputs, targets);
FeedForward();
}
public List<double> Test(List<double> inputs)
{
InitialisePatternSet(inputs, null);
FeedForward();
return GetOutput();
}
public void FeedForward()
{
//Feed from input
for(int i=0; i<m_radialFunctions.NumNeurons; i++)
{
Neuron radialFunctionNeuron = m_radialFunctions.Neurons[i];
radialFunctionNeuron.Output = 0d;
for(int j=0; j<m_inputLayer.NumNeurons; j++)
{
Neuron inputNeuron = m_inputLayer.Neurons[j];
radialFunctionNeuron.Output += radialFunctionNeuron.ActivationFunction.Function(inputNeuron.Output);
}
}
//Feed to output
for (int i = 0; i < m_outputLayer.NumNeurons; i++)
{
Neuron outputNeuron = m_outputLayer.Neurons[i];
outputNeuron.Output = 0d;
for (int j = 0; j < m_radialFunctions.NumNeurons; j++)
{
Neuron radialFunctionNeuron = m_radialFunctions.Neurons[j];
outputNeuron.Output += radialFunctionNeuron.Weight(i) * radialFunctionNeuron.Output;
}
outputNeuron.Error = (outputNeuron.Target - outputNeuron.Output);
}
//Update weights
for (int i = 0; i < m_radialFunctions.NumNeurons; i++)
{
Neuron radialFunctionNeuron = m_radialFunctions.Neurons[i];
for (int j = 0; j < m_outputLayer.NumNeurons; j++)
{
Neuron outputNeuron = m_outputLayer.Neurons[j];
if(Math.Abs(outputNeuron.Error) > m_RBFForm.StopThreshold)
radialFunctionNeuron.m_weights[j] += Neuron.LearningRate * outputNeuron.Error * radialFunctionNeuron.Output;
}
}
}
public List<double> GetOutput()
{
List<double> output = new List<double>();
for (int i = 0; i < m_outputLayer.NumNeurons; i++)
{
output.Add(m_outputLayer.Neurons[i].Output);
}
return output;
}
private void InitialisePatternSet(List<double> inputs, List<double> targets)
{
m_inputLayer.SetInputs(inputs, false);
if(targets != null)
{
m_outputLayer.SetTargets(targets);
}
}
private Queue<int> GenRandomNonRepNumbers(int num, int min, int max, Random generator)
{
if (max - min < num)
return null;
Queue<int> numbers = new Queue<int>(num);
for (int i = 0; i < num; i++)
{
int randNum = 0;
do
{
randNum = generator.Next(min, max);
} while (numbers.Contains(randNum));
numbers.Enqueue(randNum);
}
return numbers;
}
}
This is the Radial Basis Function I am using as an activation function
class RadialBasisFunction : IActivationFunction
{
private double m_centre = 0d, m_width = 0d;
public RadialBasisFunction(double centre, double width)
{
m_centre = centre;
m_width = width;
}
double IActivationFunction.Function(double activation)
{
double dist = activation - m_centre;
return Math.Exp(-(dist * dist) / (2 * m_width * m_width));
//return Math.Exp(-Math.Pow(dist / (2 * m_width), 2d));
//return Math.Exp(-Math.Pow(dist, 2d));
}
}
The NeuronLayer class is really just a wrapper around a List of Neurons, and isn't entirely necessary anymore, but I've been focussing on getting everything working rather than keeping my code clean and well designed.
class NeuronLayer
{
public int NumNeurons { get { return Neurons.Count; } }
public List<Neuron> Neurons { get; set; }
public NeuronLayer ()
{
Neurons = new List<Neuron>();
}
public void SetInputs(List<double> inputs, bool skipBias)
{
for (int i = 0; i < Neurons.Count; i++)
{
if(skipBias)
{
if (i != 0)
Neurons[i].Input = inputs[i-1];
}
else
{
Neurons[i].Input = inputs[i];
}
}
}
public void SetTargets(List<double> targets)
{
for (int i = 0; i < Neurons.Count; i++)
{
Neurons[i].Target = targets[i];
}
}
}
And finally the Neuron class. This class was made while I was coding the MLP and while I was still trying to figure out exactly how Neural Nets work. So unfortunately a lot of the code in it is specific to MLPs. I hope to change this once I've got everything working and can start cleaning everything and make the application more user-friendly. I'm going to add all the functions for completeness, but I've checked and double checked and I shouldn't be using any of the MLP specific code of Neuron anywhere in my RBF network. The MLP specific stuff is WithinThreshold, and all the functions after Weight(int).
class Neuron
{
private IActivationFunction m_activationFunction = null;
public IActivationFunction ActivationFunction { get { return m_activationFunction; } set { m_activationFunction = value; } }
public double Input { get { return Output; } set { Output = value; } }
public double Output { get; set; }
public double Error { get; set; }
public double Target { get; set; }
private double m_activation = 0d;
public bool WithinThreshold { get { return Math.Abs(Error) < MultilayerPerceptron.ErrorStopThreshold; } }
public static double LearningRate { get; set; }
public static double MomentumAlpha { get; set; }
public List<double> m_weights;
private List<double> m_deltaWeights;
public Neuron()
{
Output = 0d;
m_weights = new List<double>();
m_deltaWeights = new List<double>();
m_activationFunction = new TanHActFunction();
}
public Neuron(double input)
{
Input = input;
Output = input;
m_weights = new List<double>();
m_deltaWeights = new List<double>();
m_activationFunction = new TanHActFunction();
}
public void Initialise(int numWeights)
{
for(int i=0; i<numWeights; i++)
{
m_weights.Add(Program.rand.NextDouble()*2d - 1d);
}
}
public double Weight(int index)
{
if (m_weights != null && m_weights.Count > index)
return m_weights[index];
return 0d;
}
public void Feed(NeuronLayer layer, int neuronIndex)
{
List<Neuron> inputNeurons = layer.Neurons;
m_activation = 0;
for (int j = 0; j < layer.NumNeurons; j++)
{
m_activation += inputNeurons[j].Output * inputNeurons[j].Weight(neuronIndex);
}
Output = m_activationFunction.Function(m_activation);
}
public void CalculateError(NeuronLayer successor, bool outputLayer)
{
if(outputLayer)
{
Error = (Target - Output) * ActivationFunction.FunctionDeriv(Output);
}
else
{
Error = 0d;
for(int i=0; i<successor.NumNeurons; i++)
{
Neuron neuron = successor.Neurons[i];
Error += (neuron.Error * m_weights[i] * ActivationFunction.FunctionDeriv(Output));
}
}
}
public void UpdateWeights(NeuronLayer successor)
{
if (MomentumAlpha != 0)
{
for (int i = 0; i < successor.NumNeurons; i++)
{
var neuron = successor.Neurons[i];
if (m_deltaWeights.Count <= i)
{
double deltaWeight = LearningRate * neuron.Error * Output;
m_weights[i] += deltaWeight;
m_deltaWeights.Add(deltaWeight);
}
else
{
double deltaWeight = /*(1 - MomentumAlpha)*/LearningRate * neuron.Error * Output + MomentumAlpha * m_deltaWeights[i];
m_weights[i] += deltaWeight;
m_deltaWeights[i] = deltaWeight;
}
}
}
else
{
for (int i = 0; i < successor.NumNeurons; i++)
{
var neuron = successor.Neurons[i];
double deltaWeight = LearningRate * neuron.Error * Output;
m_weights[i] += deltaWeight;
}
}
}
}
I hope some extra eyes and opinions helps find my problem.
PS If you do download the source code from the repository and try run the application, please be aware that it will break if you don't set all necessary values before training, or if you press the reset button. I should get rid of the reset button, but I haven't yet. Sorry!
I found where the problem was.
I was updating the weights inside the FeedForward method. However, that method is called both when training and testing the network. So I was updating weights when I shouldn't have been, while testing the network.
Related
Specific Algorithm in C# for a speed dating [closed]
Closed. This question needs details or clarity. It is not currently accepting answers. Want to improve this question? Add details and clarify the problem by editing this post. Closed yesterday. Improve this question For expert in algorithm and C#. The goal is to divide the 50 people into 10 tables. So 5 people per table. Each person has 2 minutes to introduce themselves to the 4 others. Once everyone has introduced themselves, everyone changes tables. And that in 10 rounds. Two people cannot meet twice Here's the code I have done : internal class Program { private static void Main(string[] args) { int biggestNumberOfGroupCreated = 0; List<Group> biggestGroupes = new List<Group>(); Start: // Créer une liste de toutes les personnes List<int> people = new List<int>(); List<Group> groups = new List<Group>(); List<PersonAlreadyUsed> PeopleAlreadyUsed = CreateListPersonAlreadyUsed(); // Generate person name from 1 to 50 for (int i = 0; i < 50; i++) { people.Add(i); } List<Couple> couples = new(); GenerateCouples(couples); //Generate 10 rounds for (int round = 0; round < 10; round++) { ResetAlreadyUsedPeople(PeopleAlreadyUsed); for (int table = 0; table < 9; table++) { Group groupe = new(); for (int chair = 0; chair < 5; chair++) { if (chair == 0) { int randomElement = GetRandomElement(PeopleAlreadyUsed); groupe.users[chair] = people[randomElement]; Console.WriteLine($"First seated person {people[randomElement]} for round : {round} in table : {table}"); var alreadyUsed = PeopleAlreadyUsed.FirstOrDefault(x => x.Person == randomElement); alreadyUsed.AlreadyUsed = true; } else { bool CanBeAdd = false; int MemoUser = 0; int u = 0; int cptInWhileLoop = 0; while (!CanBeAdd) { bool ret = false; //Check if the new people can be added for (int c = 0; c < chair; c++) { MemoUser = u; ret = CheckedIfCanBeAdd(u, groupe.users[c], couples); if (ret == false) { break; } } CanBeAdd = ret; u = GetUnusedPerson(PeopleAlreadyUsed, cptInWhileLoop); if (u == -1) { Console.WriteLine($"All person already used {groups.Count}"); if (groups.Count > biggestNumberOfGroupCreated) { biggestNumberOfGroupCreated = groups.Count; biggestGroupes = new(); foreach (var g in groups) { biggestGroupes.Add(g); } } goto Start; } cptInWhileLoop++; } if (CanBeAdd == true) { groupe.users[chair] = MemoUser; var personAlreadyUsed = PeopleAlreadyUsed.FirstOrDefault(x => x.Person == MemoUser); personAlreadyUsed.AlreadyUsed = true; } } } groups.Add(groupe); } } } private static void GenerateCouples(List<Couple> couples) { for (int i = 0; i < 50; i++) { for (int j = 0; j < 50; j++) { if (i != j) { couples.Add(new Couple { x = i, y = j, Affected = false }); } } } } private static int GetUnusedPerson(List<PersonAlreadyUsed> personneDejUtilisees, int cptInWhileLoop) { var unusedPeople = personneDejUtilisees.Where(x=>x.AlreadyUsed==false).ToList(); if (unusedPeople.Count==0) { return -1; } // Retrieve a random element from the list using the random index try { var randomPerson = unusedPeople[cptInWhileLoop]; return randomPerson.Person; } catch (Exception) { return -1; } } private static void ResetAlreadyUsedPeople(List<PersonAlreadyUsed> personAlreadyUsed) { for (int i = 0; i < 50; i++) { personAlreadyUsed[i].AlreadyUsed = false; } } private static List<PersonAlreadyUsed> CreateListPersonAlreadyUsed() { List < PersonAlreadyUsed > people = new(); for (int i = 0; i < 50; i++) { PersonAlreadyUsed dejUtilisee = new() { Person = i, AlreadyUsed = false }; people.Add(dejUtilisee); } return people; } private static bool CheckedIfCanBeAdd(int p, int v, List<Couple> couples) { bool ret = false; if (p != v) { var comb1 = couples.FirstOrDefault(c => c.x == p && c.y == v); var comb2 = couples.FirstOrDefault(c => c.x == v && c.y == p); if (comb1.Affected == false && comb2.Affected == false) { ret = true; comb1.Affected = true; comb2.Affected = true; } } return ret; } private static int GetRandomElement(List<PersonAlreadyUsed> personAlreadyUsed) { Random rand = new Random(); var peronStillAvailble = personAlreadyUsed.Where(x=>x.AlreadyUsed==false).ToList(); int randomIndex = rand.Next(peronStillAvailble.Count); int randomElement = peronStillAvailble[randomIndex].Person; return randomElement; } private static void AddCouple(Group groupe, List<Couple> couples) { foreach (var u1 in groupe.users) { foreach (var u2 in groupe.users) { if (u1 != u2) { var comb1 = couples.FirstOrDefault(c => c.x == u1 && c.y == u2); var comb2 = couples.FirstOrDefault(c => c.x == u2 && c.y == u1); comb1.Affected = true; comb2.Affected = true; } } } } } public class PersonAlreadyUsed { public int Person { get; set; } =0; public bool AlreadyUsed { get; set; }=false; } public class Couple { public int x { get; set; } public int y { get; set; } public bool Affected { get; set; } } public class Group { public int[] users { get; set; } = new int[5]; } It seems that there is no solution. Do you confirm?
How do you create buttons on a form through a method created in a class?
I'm trying to create a grid of buttons based on what number of rows and columns the user enters and my method that creates the grid isn't working. When I call it the grid doesn't get created. The method is inside my TileClass and I'm trying to call it in my GameBoard form. I feel like I'm not using the class properly. I don't think I'm calling the method correctly because I'm thinking this should work. This is what the form looks like class TileClass : Button { public const int LEFT = 20; public const int WIDTH = 50; public const int HEIGHT = 50; public const int TOP = 50; public const int VGAP = 30; public int x; public int y; public int column; public int row; private int incomingRow; private int incomingColumn; public int IncomingRow { get => incomingRow; set => incomingRow = value; } public int IncomingColumn { get => incomingColumn; set => incomingColumn = value; } public TileClass() { } public void CreateGrid() { x = LEFT; y = TOP; column = IncomingColumn; row = IncomingRow; for (int i = 0; i < row; i++) { for (int j = 0; j < column; j++) { Button b = new Button(); b.Left = x; b.Top = y; b.Width = WIDTH; b.Height = HEIGHT; b.Text = j.ToString(); x += VGAP + HEIGHT; this.Controls.Add(b); } } } } Gameboard Form public partial class GameBoard : Form { TileClass tileClass = new TileClass(); public GameBoard() { InitializeComponent(); } private void txtEnter_Click(object sender, EventArgs e) { tileClass.IncomingColumn = int.Parse(txtColumn.Text); tileClass.IncomingRow = int.Parse(txtRow.Text); tileClass.CreateGrid(); }
There's a lot to do to make it happen: class TileClass : Panel { ... public int IncomingRow {get; set;} public int IncomingColumn { get; set; } ... } and remove: private int incomingRow; private int incomingColumn; and the ideal approach would be using ResumeLayout before adding buttons and let the Gameboard form to be redrawn by calling Invalidate. What does invalidate method do? note: try col=100, row=100 with and without ResumeLayout&Invalidate public partial class GameBoard : Form { public GameBoard () { InitializeComponent(); tileClass.Dock = DockStyle.Fill; this.Controls.Add(tileClass); } TileClass tileClass = new TileClass(); private void txtEnter_Click(object sender, EventArgs e) { tileClass.IncomingColumn = int.Parse(txtColumn.Text); tileClass.IncomingRow = int.Parse(txtRow.Text); this.ResumeLayout(); //Important tileClass.CreateGrid(); this.Invalidate(); // Important } } and you can set more properties like, it needs more than this: //tileClass.Location = new Point(10, 10); // not sure tileClass.Dock = DockStyle.Fill; //tileClass.Size = new Size(200, 200); // not sure and instead of j < 5 you should use col and row: for (int i = 0; i < row; i++) { for (int j = 0; j < column; j++) { Button b = new Button(); b.Left = x; b.Top = y; b.Width = WIDTH; b.Height = HEIGHT; b.Text = string.Format("({0},{1})" , i, j); x += VGAP + HEIGHT; this.Controls.Add(b); } x = LEFT; // not sure, plz calculate! y += Top * (i+1); // not sure, plz calculate! }
Calculating Aroon Indicator Series
I'm trying to build a class to create Aroon series. But it seems I don't understand the steps well. I'm not sure about for what purpose I have to use the period parameter. Here is my first attempt: /// <summary> /// Aroon /// </summary> public class Aroon : IndicatorCalculatorBase { public override List<Ohlc> OhlcList { get; set; } public int Period { get; set; } public Aroon(int period) { this.Period = period; } /// <summary> /// Aroon up: {((number of periods) - (number of periods since highest high)) / (number of periods)} x 100 /// Aroon down: {((number of periods) - (number of periods since lowest low)) / (number of periods)} x 100 /// </summary> /// <see cref="http://www.investopedia.com/ask/answers/112814/what-aroon-indicator-formula-and-how-indicator-calculated.asp"/> /// <returns></returns> public override IIndicatorSerie Calculate() { AroonSerie aroonSerie = new AroonSerie(); int indexToProcess = 0; while (indexToProcess < this.OhlcList.Count) { List<Ohlc> tempOhlc = this.OhlcList.Skip(indexToProcess).Take(Period).ToList(); indexToProcess += tempOhlc.Count; for (int i = 0; i < tempOhlc.Count; i++) { int highestHighIndex = 0, lowestLowIndex = 0; double highestHigh = tempOhlc.Min(x => x.High), lowestLow = tempOhlc.Max(x => x.Low); for (int j = 0; j < i; j++) { if (tempOhlc[j].High > highestHigh) { highestHighIndex = j; highestHigh = tempOhlc[j].High; } if (tempOhlc[j].Low < lowestLow) { lowestLowIndex = j; lowestLow = tempOhlc[j].Low; } } int up = ((this.Period - (i - highestHighIndex)) / this.Period) * 100; aroonSerie.Up.Add(up); int down = ((this.Period - (i - lowestLowIndex)) / this.Period) * 100; aroonSerie.Down.Add(down); } } return aroonSerie; } } Is there anyone else tried to do that before? Here is the csv file I use: https://drive.google.com/file/d/0Bwv_-8Q17wGaRDVCa2FhMWlyRUk/view But the result sets for Aroon up and down don't match with the results of aroon function in TTR package for R. table <- read.csv("table.csv", header = TRUE, sep = ",") trend <- aroon(table[,c("High", "Low")], n=5) View(trend) Screenshot of R result: Thanks in advance,
#anilca, for full Disclosure, I learned alot of things to answer your question(i knew nothing in the Finetec...). Thank you! it was an interesting experience! There are several problems in your implementation: In the statements: i - highestHighIndex and i - lowestLowIndex the variable "i" is less or equal to highestHighIndex, lowestLowIndex so statements: this.Period - (i - highestHighIndex) and this.Period - (i - lowestLowIndex) will returns the wrong values (most of the time...) Aroon up and down both of them are percentage, therefore "int" is a wrong data structure. Because all variables in: (this.Period - (i - highestHighIndex)) / this.Period) and ((this.Period - (i - lowestLowIndex)) / this.Period) are integers you won't recieve the right value. one more thing the numbers in your excel are sorted from the newest to the oldest.(it effects on the R package) I've implemented the algorithm based on your code(and your data order...) public class Aroon : IndicatorCalculatorBase { public override List<OhlcSample> OhlcList { get; set; } private readonly int _period; public int Period { get { return _period; } } public Aroon(int period) { _period = period; } public override IIndicatorSerie Calculate() { var aroonSerie = new AroonSerie(); for (var i = _period; i < OhlcList.Count; i++) { var aroonUp = CalculateAroonUp(i); var aroonDown = CalculateAroonDown(i); aroonSerie.Down.Add(aroonDown); aroonSerie.Up.Add(aroonUp); } return aroonSerie; } private double CalculateAroonUp(int i) { var maxIndex = FindMax(i - _period, i); var up = CalcAroon(i - maxIndex); return up; } private double CalculateAroonDown(int i) { var minIndex = FindMin(i - _period, i); var down = CalcAroon(i - minIndex); return down; } private double CalcAroon(int numOfDays) { var result = ((_period - numOfDays)) * ((double)100 / _period); return result; } private int FindMin(int startIndex, int endIndex) { var min = double.MaxValue; var index = startIndex; for (var i = startIndex; i <= endIndex; i++) { if (min < OhlcList[i].Low) continue; min = OhlcList[i].Low; index = i; } return index; } private int FindMax(int startIndex, int endIndex) { var max = double.MinValue; var index = startIndex; for (var i = startIndex; i <= endIndex; i++) { if (max > OhlcList[i].High) continue; max = OhlcList[i].High; index = i; } return index; } } public abstract class IndicatorCalculatorBase { public abstract List<OhlcSample> OhlcList { get; set; } public abstract IIndicatorSerie Calculate(); } public interface IIndicatorSerie { List<double> Up { get; } List<double> Down { get; } } internal class AroonSerie : IIndicatorSerie { public List<double> Up { get; private set; } public List<double> Down { get; private set; } public AroonSerie() { Up = new List<double>(); Down = new List<double>(); } } public class OhlcSample { public double High { get; private set; } public double Low { get; private set; } public OhlcSample(double high, double low) { High = high; Low = low; } } Use this test method for debugging: private Aroon _target; [TestInitialize] public void TestInit() { _target=new Aroon(5) { OhlcList = new List<OhlcSample> { new OhlcSample(166.90, 163.65), new OhlcSample(165.00, 163.12), new OhlcSample(165.91, 163.21), new OhlcSample(167.29, 165.11), new OhlcSample(169.99, 166.84), new OhlcSample(170.92, 167.90), new OhlcSample(168.47, 165.90), new OhlcSample(167.75, 165.75), new OhlcSample(166.14, 161.89), new OhlcSample(164.77, 161.44), new OhlcSample(163.19, 161.49), new OhlcSample(162.50, 160.95), new OhlcSample(163.25, 158.84), new OhlcSample(159.20, 157.00), new OhlcSample(159.33, 156.14), new OhlcSample(160.00, 157.00), new OhlcSample(159.35, 158.07), new OhlcSample(160.70, 158.55), new OhlcSample(160.90, 157.66), new OhlcSample(164.38, 158.45), new OhlcSample(167.75, 165.70), new OhlcSample(168.93, 165.60), new OhlcSample(165.73, 164.00), new OhlcSample(167.00, 164.66), new OhlcSample(169.35, 165.01), new OhlcSample(168.12, 164.65), new OhlcSample(168.89, 165.79), new OhlcSample(168.65, 165.57), new OhlcSample(170.85, 166.00), new OhlcSample(171.61, 169.10) } }; } [TestMethod] public void JustToHelpYou() { var result = _target.Calculate(); var expectedUp = new List<double>() { 100,80,60,40,20,0,0,0, 0,0,40,20,0,100,100,100,100,80, 60,100,80,60,40,100,100 }; var expectedDown = new List<double> { 20,0,0,100,100,80,100,100,100,100,80,60,40,20,0,0,40,20,0,0,40,20,0,40,20 }; Assert.IsTrue( result.Up.SequenceEqual(expectedUp)); Assert.IsTrue( result.Down.SequenceEqual(expectedDown)); }
Just Add implementation of method HighestBarNum and LowestBarnum from your code public class Aroon { public bool AroonDown { get; set; } public double Period { get; set; } public Aroon() { } public IList<double> Execute(IList<double> src) { if (!this.AroonDown) { return this.ExecuteUp(src); } return this.ExecuteDown(src); } public IList<double> ExecuteDown(IList<double> src) { double[] period = new double[src.Count]; for (int i = 1; i < src.Count; i++) { double num = LowestBarNum(src, i, Period); period[i] = 100 * (Period - num) / Period; } return period; } public IList<double> ExecuteUp(IList<double> src) { double[] period = new double[src.Count]; for (int i = 1; i < src.Count; i++) { double num = HighestBarNum(src, i, Period); period[i] = 100 * ((Period - num) / Period; } return period; }}
class AroonData { public double AroonUp; public double AroonDown; } Calculation Class: class AroonCalculationData { public double PeriodHigh; public double PeriodLow; public double SetAroonUp(List<MarketData> period, double lastHigh) { /*reverse the set so we can look at it from the current tick on back, and ticksSinceHigh will set correctly*/ period.Reverse(); int ticksSinceHigh = 0; double high = 0.0;//Set 0 so anything will be higher for (int i = 0; i < period.Count; i++) { if (period[i].high > high) { high = period[i].high; ticksSinceHigh = i; } } /*This bit if for if the high just ticked out of List<MarketData>. Including the current tick (i = 0), List<MarketData> period only looks back (period - 1) days. This Checks to see if the last high is still in the set. if it's not, and is still the high for the period, then ticksSinceHigh is set to (period)*/ PeriodHigh = high; if (PeriodHigh < lastHigh) { ticksSinceHigh = period.Count; } /*Aroon-Up Formula*/ return (double)(period.Count - ticksSinceHigh ) / (double)period.Count * 100.0; } //ASIDE FROM LOOKING FOR LOWS INSTEAD OF HIGHS, SAME AS AROON-UP public double SetAroonDown(List<MarketData> period, double lastLow) { period.Reverse(); int daysSinceLow = 0; double low = double.MaxValue;//Set max so anything will be lower for (int i = 0; i < period.Count; i++) { if (period[i].low < low) { low = period[i].low; daysSinceLow = i; } } PeriodLow = low; if (PeriodLow > lastLow) { daysSinceLow = period.Count; } return (double)(period.Count - daysSinceLow) / (double)period.Count * 100.0; } } Calling code: public AroonData[] Aroon(List<MarketData> marketData, int period) { AroonCalculationData[] calculationData = new AroonCalculationData[marketData.Count] AroonData[] aroon= new AroonData[marketData.Count] for (int i = period; i < marketData.Count; i++) { /*GetRange(i - period + 1, period) add 1 so that the current tick is included in look back. For example, if (period = 10) the first loop (i = 10) then (i - period + 1) = 1 the set will be marketData 1 - 10.*/ /*calculationData[i - 1].PeriodHigh and calculationData[i - 1].PeriodLow are for looking back at the last tick's look back period high and low*/ data[i].AroonUp = calculationData[i].SetAroonUp(marketData.GetRange(i - period + 1, period), calculationData[i - 1].PeriodHigh); data[i].AroonDown = calculationData[i].SetAroonDown(marketData.GetRange(i - period + 1, period), calculationData[i - 1].PeriodLow); } } Side note: One problem I had was comparing my data to TD Ameritrades Aroon, until i figured out their period is really period-1, so if you're comparing to TD keep that in mind.
Need help in data Binding in datagridview
I am new to c# and absolutely new to windows form application. I am Implementing a Sudoku Solver I coded in C++ since i need a GUI. Using a DataGridView and need help with data binding without databases. Posting my code, please help. Need idea about 1. How to enable user enter data in datagridview and "inital" array. 2. How to read that data and copy in "copied" array. 3. How to Bind data to the DataGridView. namespace WindowsFormsApplication3 { public partial class Form1 : Form { public Form1() { InitializeComponent(); bindGrid(); } private void bindGrid() { List<TestCode> list = new List<TestCode>(); TestCode tt = new TestCode(); list.Add(tt); list.Add(tt); list.Add(tt); list.Add(tt); list.Add(tt); list.Add(tt); list.Add(tt); list.Add(tt); list.Add(tt); this.dataGridView1.DataSource = list; } public int i; public int[,] initial; public int[,] copied; int inputvalue(int x, int y, int value) { initial = new int[9, 9]; copied = new int[9, 9]; for (int i = 0; i < 9; i++) { if (value == copied[x, i] || value == copied[i, y]) return 0; } for (i = (x / 3) * 3; i <= ((x / 3) * 3) + 2; i++) for (int j = (y / 3) * 3; j <= ((y / 3) * 3) + 2; j++) if (copied[i, j] == value) return 0; return value; } bool solve(int x, int y) { int i; int temp; if (copied[x, y] == 0) { for (i = 1; i < 10; i++) { temp = inputvalue(x, y, i); if (temp > 0) { copied[x, y] = temp; if (x == 8 && y == 8) return true; else if (x == 8) { if (solve(0, y + 1)) return true; } else { if (solve(x + 1, y)) return true; } } } if (i == 10) { if (copied[x, y] != initial[x, y]) copied[x, y] = 0; return false; } } if (x == 8 && y == 8) return true; else if (x == 8) { if (solve(0, y + 1)) return true; } else { if (solve(x + 1, y)) return true; } return true; } private void fillDatagrid() { } private void dataGridView1_CellContentClick(object sender, DataGridViewCellEventArgs e) { //this.dataGridView1.Rows.Add(new object[] { "Column1", "Column2", "Column3", "Column4","Column5","Column5","Column6","Column7","Column8","Column9", true }); //this.dataGridView1.DataSource = copied; } private void button1_Click(object sender, EventArgs e) { int i, j; Form1 P = new Form1(); for (i = 0; i < 9; i++) for (j = 0; j < 9; j++) { //dataGridView1[i, j]; // Console.SetCursorPosition(i + 1, j + 1); //P.initial[i,j] = Console.ReadLine(); } for (i = 0; i < 9; i++) for (j = 0; j < 9; j++) P.copied = P.initial; if (P.solve(0, 0)) { for (i = 0; i < 9; i++) for (j = 0; j < 9; j++) { Console.SetCursorPosition(i + 1, j + 1); Console.Write(" ", +P.copied[i, j]); } } else Console.Write(" NO Solution"); } } public class TestCode { public int Value1 { get; set; } public int Value2 { get; set; } public int Value3 { get; set; } public int Value4 { get; set; } public int Value5 { get; set; } public int Value6 { get; set; } public int Value7 { get; set; } public int Value8 { get; set; } public int Value9 { get; set; } } }
private void bindGrid() { List <TestCode> list = new List<TestCode>(); TestCode tt = new TestCode(); list.Add(tt); list.Add(tt); list.Add(tt); list.Add(tt); list.Add(tt); list.Add(tt); list.Add(tt); list.Add(tt); list.Add(tt); this.dataGridView1.DataSource = list; this.dataGridView1.DataBind(); }
Save control state when form is closed
Hi I am trying to save the color state of this 4x4 grid of buttons created dynamically using a 2D array into an xml document: However when I press save I keep getting this message: I can make this work if I use a one dimensional array for the buttons but that will not give me the grid that I want but when I use a 2D array for the buttons it wont work: What can I change so I can get this to work any suggestion are much appreciated: This is my code that I have: FormState class: public class FormState { public string ButtonBackColor { get; set; } } Form code: public partial class Form1 : Form { int col = 4; int row = 4; Button[,] buttons; FormState[,] states; public Form1() { InitializeComponent(); buttons = new Button[col, row]; states = new FormState[col, row]; } public void placeRows() { for (int r = 0; r < row; r++) { createColumns(r); } } public void createColumns(int r) { int s = r * 25; //gap for (int c = 0; c < col; c++) { buttons[r, c] = new Button(); buttons[r, c].SetBounds(75 * c, s, 75, 25); buttons[r, c].Text = Convert.ToString(c); buttons[r, c].Click += new EventHandler(grid_Click); panel1.Controls.Add(buttons[r, c]); } } int count = 0; //backcolor change void grid_Click(object sender, EventArgs e) { Button button = sender as Button; if (count == 0) { button.BackColor = Color.Red; count++; } else if (count == 1) { button.BackColor = Color.Blue; count--; } } private void Form1_Load(object sender, EventArgs e) { placeRows(); if (File.Exists("config.xml")) { loadConfig(); } for (int i = 0; i < col; ++i) { for (int j = 0; j < row; ++j) { if (states[i,j] != null) { buttons[i,j].BackColor = ColorTranslator.FromHtml(states[i,j].ButtonBackColor); } } } } //method to load file private void loadConfig() { XmlSerializer ser = new XmlSerializer(typeof(FormState[])); using (FileStream fs = File.OpenRead("config.xml")) { states = (FormState[,])ser.Deserialize(fs); } } private void writeConfig() { for (int i = 0; i < col; i++) { for (int j = 0; j < row; j++) { if (states[i,j] == null) { states[i,j] = new FormState(); } states[i,j].ButtonBackColor = ColorTranslator.ToHtml(buttons[i,j].BackColor); } using (StreamWriter sw = new StreamWriter("config.xml")) { XmlSerializer ser = new XmlSerializer(typeof(FormState[])); ser.Serialize(sw, states); } } } private void btnSave_Click(object sender, EventArgs e) { writeConfig(); } }
This may not be an ideal solution (and I haven't tried it, so it may not even work), but you could create a nested array instead of a 2-D array. Something like FormStates[][] states = new FormStates[row][]; for(Int32 i = 0; i < row; i++) { states[i] = new FormStates[col]; } Instead of indexing using states[i, j], you would use states[i][j]. Since 1-D arrays are serializable, this might work. Edit Slightly longer example, based on your code: public partial class Form1 : Form { int col = 4; int row = 4; Button[][] buttons; FormState[][] states; public Form1() { InitializeComponent(); buttons = new Button[col][]; states = new FormState[col][]; for(Int32 c = 0; c < col; c++) { buttons[c] = new Button[row]; states[c] = new FormState[row]; } } public void createColumns(int r) { int s = r * 25; //gap for (int c = 0; c < col; c++) { buttons[r][c] = new Button(); buttons[r][c].SetBounds(75 * c, s, 75, 25); buttons[r][c].Text = Convert.ToString(c); buttons[r][c].Click += new EventHandler(grid_Click); panel1.Controls.Add(buttons[r][c]); } } } This should be enough to give you the syntax to change the rest of your code. You will also want to change your XmlSerializer declarations to use typeof(FormState[][]) instead of just typeof(FormState[]).
Admittedly I haven't put much thought into this but as per comments you cant serialize multi dimensional arrays so you might: [Serializable] public class FormState { public int RowIndex { get; set; } public int ColIndex { get; set; } public string BackColor { get; set; } } [Serializable] public class Layout : Collection<FormState> { public Layout(){} } .. public void SomeCallingMethod() { Layout l = new Layout(); foreach (FormState fs in l) { buttons[fs.RowIndex, fs.ColIndex].BackColor = ColorTranslator.FromHtml(fs.BackColor); } } Or use a List if necessary and serialize that.