#region License Information
/* HeuristicLab
* Copyright (C) 2002-2012 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
*
* This file is part of HeuristicLab.
*
* HeuristicLab is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* HeuristicLab is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with HeuristicLab. If not, see .
*/
#endregion
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using HeuristicLab.Common.Resources;
using HeuristicLab.Core.Views;
using HeuristicLab.Data;
using HeuristicLab.Encodings.IntegerVectorEncoding;
using HeuristicLab.MainForm;
using HeuristicLab.MainForm.WindowsForms;
namespace HeuristicLab.Problems.GeneralizedQuadraticAssignment.Views {
[View("GQAPAssignmentView")]
[Content(typeof(GQAPAssignment), IsDefaultView = true)]
public partial class GQAPAssignmentView : ItemView {
private static readonly double BrightnessLevel = 0.5;
public new GQAPAssignment Content {
get { return (GQAPAssignment)base.Content; }
set { base.Content = value; }
}
public GQAPAssignmentView() {
InitializeComponent();
recalculateButton.Image = VSImageLibrary.Refresh;
}
#region Register Content Events
protected override void DeregisterContentEvents() {
Content.PropertyChanged -= new PropertyChangedEventHandler(Content_PropertyChanged);
base.DeregisterContentEvents();
}
protected override void RegisterContentEvents() {
base.RegisterContentEvents();
Content.PropertyChanged += new PropertyChangedEventHandler(Content_PropertyChanged);
}
#endregion
protected override void OnContentChanged() {
base.OnContentChanged();
UpdateQuality();
UpdateFlowDistanceQuality();
UpdateInstallationQuality();
UpdateOverbookedCapacity();
UpdateAssignment();
if (Content != null) assignmentView.Content = Content.Assignment;
else assignmentView.Content = null;
}
protected override void SetEnabledStateOfControls() {
base.SetEnabledStateOfControls();
}
#region Content Event Handlers
private void Content_PropertyChanged(object sender, PropertyChangedEventArgs e) {
switch (e.PropertyName) {
case "Quality": UpdateQuality(); break;
case "FlowDistanceQuality": UpdateFlowDistanceQuality(); break;
case "InstallationQuality": UpdateInstallationQuality(); break;
case "OverbookedCapacity": UpdateOverbookedCapacity(); break;
case "Assignment": UpdateAssignment(); break;
case "EquipmentNames": UpdateAssignment(); break;
case "LocationNames": UpdateAssignment(); break;
default: break;
}
}
private void Content_Assignment_ToStringChanged(object sender, EventArgs e) {
UpdateAssignment();
}
#endregion
#region Event Handlers
private void assignmentTreeView_AfterSelect(object sender, TreeViewEventArgs e) {
if (Content != null) {
assignmentTreeView.BeginUpdate();
try {
if (assignmentTreeView.SelectedNode == null) {
ColorByInstallationCosts();
} else {
var equipmentNode = (assignmentTreeView.SelectedNode as EquipmentNode);
if (equipmentNode != null) {
ColorByWeight(equipmentNode);
}
var locationNode = (assignmentTreeView.SelectedNode as LocationNode);
if (locationNode != null) {
ColorByWeight(locationNode);
}
}
} finally { assignmentTreeView.EndUpdate(); }
}
}
private void assignmentTreeView_MouseUp(object sender, MouseEventArgs e) {
var hit = assignmentTreeView.HitTest(e.X, e.Y);
if (hit.Node == null || hit.Location == TreeViewHitTestLocations.None || hit.Location == TreeViewHitTestLocations.RightOfLabel) {
assignmentTreeView.SelectedNode = null;
ColorByInstallationCosts();
}
}
#endregion
#region Helpers
private void UpdateQuality() {
if (InvokeRequired) Invoke((Action)UpdateQuality);
else {
if (Content == null) {
qualityLabel.Text = "-";
} else {
qualityLabel.Text = Content.Quality.ToString();
}
}
}
private void UpdateFlowDistanceQuality() {
if (InvokeRequired) Invoke((Action)UpdateFlowDistanceQuality);
else {
if (Content == null || Content.FlowDistanceQuality == null) {
flowDistanceQualityLabel.Text = "-";
} else {
flowDistanceQualityLabel.Text = Content.FlowDistanceQuality.ToString();
}
}
}
private void UpdateInstallationQuality() {
if (InvokeRequired) Invoke((Action)UpdateInstallationQuality);
else {
if (Content == null || Content.InstallationQuality == null) {
installationQualityLabel.Text = "-";
} else {
installationQualityLabel.Text = Content.InstallationQuality.ToString();
}
}
}
private void UpdateOverbookedCapacity() {
if (InvokeRequired) Invoke((Action)UpdateOverbookedCapacity);
else {
if (Content == null || Content.OverbookedCapacity == null) {
overbookedCapacityLabel.Text = "-";
} else {
overbookedCapacityLabel.Text = Content.OverbookedCapacity.ToString();
}
}
}
private void UpdateAssignment() {
if (InvokeRequired) Invoke((Action)UpdateAssignment);
else {
assignmentTreeView.Nodes.Clear();
if (Content != null) {
IntegerVector assignment = Content.Assignment;
Dictionary locationNodes = new Dictionary();
for (int i = 0; i < assignment.Length; i++) {
if (!locationNodes.ContainsKey(assignment[i])) {
string locationName = assignment[i].ToString();
if (Content.LocationNames != null && Content.LocationNames.Length > assignment[i])
locationName = Content.LocationNames[assignment[i]];
locationNodes.Add(assignment[i], new LocationNode(assignment[i], locationName));
}
string equipmentName = i.ToString();
if (Content.EquipmentNames != null && Content.EquipmentNames.Length > i)
equipmentName = Content.EquipmentNames[i];
locationNodes[assignment[i]].Nodes.Add(new EquipmentNode(i, equipmentName));
}
assignmentTreeView.BeginUpdate();
try {
foreach (var node in locationNodes.OrderBy(x => x.Key).Select(x => x.Value)) {
assignmentTreeView.Nodes.Add(node);
node.Expand();
}
ColorByInstallationCosts();
} finally { assignmentTreeView.EndUpdate(); }
}
}
}
private void ColorByInstallationCosts() {
var installationCosts = new Dictionary();
foreach (var node in GetAllSubNodes(assignmentTreeView.Nodes).OfType()) {
int location = Content.Assignment[node.Equipment];
installationCosts[node] = Content.InstallationCosts[node.Equipment, location];
}
double max = installationCosts.Values.Max();
foreach (var kvp in installationCosts) {
if (max == 0) {
kvp.Key.BackColor = assignmentTreeView.BackColor;
kvp.Key.ForeColor = assignmentTreeView.ForeColor;
} else {
int colorComponent = (int)(255 * Math.Pow((max - kvp.Value) / max, 2));
kvp.Key.BackColor = Color.FromArgb(255, colorComponent, colorComponent);
if (kvp.Key.BackColor.GetBrightness() < BrightnessLevel) kvp.Key.ForeColor = Color.White;
else kvp.Key.ForeColor = Color.Black;
}
}
}
private void ColorByWeight(EquipmentNode selectedNode) {
int equipment = selectedNode.Equipment;
double rowSum = 0, colSum = 0;
for (int i = 0; i < Content.Weights.Columns; i++)
if (i != equipment) rowSum += Content.Weights[equipment, i];
for (int i = 0; i < Content.Weights.Rows; i++)
if (i != equipment) colSum += Content.Weights[i, equipment];
var otherEquipments = GetAllSubNodes(assignmentTreeView.Nodes)
.OfType()
.Where(x => x != selectedNode);
selectedNode.BackColor = assignmentTreeView.BackColor;
selectedNode.ForeColor = assignmentTreeView.ForeColor;
foreach (var other in otherEquipments) {
if (rowSum == 0 && colSum == 0) {
other.BackColor = assignmentTreeView.BackColor;
other.ForeColor = assignmentTreeView.ForeColor;
} else {
double rowInvProportion = rowSum == 0 ? 1.0 : (rowSum - Content.Weights[equipment, other.Equipment]) / rowSum;
double colInvProportion = colSum == 0 ? 1.0 : (colSum - Content.Weights[other.Equipment, equipment]) / colSum;
int colorComponent = (int)(255 * Math.Pow(Math.Min(rowInvProportion, colInvProportion), 2));
other.BackColor = Color.FromArgb(colorComponent, 255, colorComponent);
if (other.BackColor.GetBrightness() < BrightnessLevel) other.ForeColor = Color.White;
else other.ForeColor = Color.Black;
}
}
}
private void ColorByWeight(LocationNode selectedNode) {
var equipments = new HashSet(Content.Assignment.Select((v, i) => new { Index = i, Value = v })
.Where(x => x.Value == selectedNode.Location).Select(x => x.Index));
var rowSums = new Dictionary(equipments.Count);
var colSums = new Dictionary(equipments.Count);
foreach (var e in equipments) {
rowSums[e] = 0;
colSums[e] = 0;
for (int i = 0; i < Content.Weights.Columns; i++) {
if (!equipments.Contains(i)) rowSums[e] += Content.Weights[e, i];
if (!equipments.Contains(i)) colSums[e] += Content.Weights[i, e];
}
}
var equipmentNodes = GetAllSubNodes(assignmentTreeView.Nodes)
.OfType();
var relevantEquipments = new HashSet(equipments.Where(x => rowSums[x] > 0 || colSums[x] > 0));
foreach (var other in equipmentNodes) {
if (!relevantEquipments.Any() || equipments.Contains(other.Equipment)) {
other.BackColor = assignmentTreeView.BackColor;
other.ForeColor = assignmentTreeView.ForeColor;
} else {
double rowInvProportion = relevantEquipments.Min(x => rowSums[x] == 0 ? 1.0 : (rowSums[x] - Content.Weights[x, other.Equipment]) / rowSums[x]);
double colInvProportion = relevantEquipments.Min(x => colSums[x] == 0 ? 1.0 : (colSums[x] - Content.Weights[other.Equipment, x]) / colSums[x]);
int colorComponent = (int)(255 * Math.Pow(Math.Min(rowInvProportion, colInvProportion), 2));
other.BackColor = Color.FromArgb(colorComponent, 255, colorComponent);
if (other.BackColor.GetBrightness() < BrightnessLevel) other.ForeColor = Color.White;
else other.ForeColor = Color.Black;
}
}
}
private IEnumerable GetAllSubNodes(TreeNodeCollection nodes) {
foreach (TreeNode node in nodes) {
yield return node;
foreach (TreeNode subNode in GetAllSubNodes(node.Nodes))
yield return subNode;
}
}
#endregion
#region Helper Classes
private class LocationNode : TreeNode {
public int Location { get; set; }
public LocationNode() : base() { }
public LocationNode(int location, string name)
: base(name) {
Location = location;
}
}
private class EquipmentNode : TreeNode {
public int Equipment { get; set; }
public EquipmentNode() : base() { }
public EquipmentNode(int equipment, string name)
: base(name) {
Equipment = equipment;
}
}
#endregion
private void recalculateButton_Click(object sender, EventArgs e) {
double fdq, iq, oc;
Content.Evaluator.Evaluate(Content.Assignment, Content.Weights,
Content.Distances, Content.InstallationCosts, Content.Demands,
Content.Capacities, out fdq, out iq, out oc);
Content.FlowDistanceQuality = new DoubleValue(fdq);
Content.InstallationQuality = new DoubleValue(iq);
Content.OverbookedCapacity = new DoubleValue(oc);
Content.Quality = new DoubleValue(
Content.Evaluator.GetFitness(fdq, iq, oc,
Content.TransportationCosts.Value, Content.ExpectedRandomQuality.Value));
}
}
}