/// <summary> /// Constructs a Circuit given a Dictionary of all the gates, /// List of all outputs, and List of all inputs /// </summary> public Circuit(Dictionary <Sketch.Shape, Dictionary <int, Tuple <Shape, int> > > shapesAndTheirInputs, List <Shape> outputs, List <Shape> inputs, Dictionary <Sketch.Shape, CircuitElement> subCircuits) : this() { // Build global inputs foreach (Shape inputShape in inputs) { INPUT input = new INPUT(inputShape.Name, inputShape.Bounds, inputShape.Orientation); shapesToElements.Add(inputShape, input); globalInputs.Add(input); } // Build global outputs foreach (Shape outputShape in outputs) { OUTPUT output = new OUTPUT(outputShape.Name, outputShape.Bounds); shapesToElements.Add(outputShape, output); globalOutputs.Add(output); } // Build logic gates within the circuit foreach (var shapeAndInputs in shapesAndTheirInputs) { Shape gateShape = shapeAndInputs.Key; Gate gateElement; // If the shape is not a gate, or is already in the circuit, skip it. if (!LogicDomain.IsGate(gateShape.Type) || shapesToElements.ContainsKey(gateShape)) { continue; } // If it's a subcircuit, roll our own circuit element. else if (subCircuits.Keys.Contains(gateShape)) { SubCircuit associatedSub = (SubCircuit)subCircuits[gateShape]; associatedSub = new SubCircuit(gateShape.Name, gateShape.Bounds, gateShape, associatedSub.inputs, associatedSub.outputs, associatedSub.behavior, gateShape.Orientation); gateElement = associatedSub; subCircuits[gateShape] = associatedSub; } // Otherwise, create an element normally. else { gateElement = createGate(gateShape.Name, gateShape.Bounds, gateShape.Orientation, gateShape.Type); } if (gateElement == null) { throw new Exception("failed to create gate!"); } shapesToElements.Add(gateShape, gateElement); gates.Add(gateElement); } connectEverything(shapesAndTheirInputs); }
/// <summary> /// Constructor /// </summary> /// <param name="sketch">SketchPanel to add a label to</param> /// <param name="inkStrokes">InkOverlay strokes</param> /// <param name="inkStrokes">A StrokeCollection strokes</param> /// <param name="label">Label to apply</param> public ApplyLabelCmd(SketchPanel sketch, StrokeCollection inkStrokes, string label, bool userSpecifiedGroup = true, bool userSpecifiedLabel = true) { isUndoable = false; this.sketchPanel = sketch; this.label = label; this.inkStrokes = inkStrokes; this.userSpecifiedGroup = userSpecifiedGroup; this.userSpecifiedLabel = userSpecifiedLabel; labelColor = LogicDomain.getType(label).Color; // Save the original labels of the substrokes origLabels = new Dictionary <string, Data.Pair <ShapeType, StrokeCollection> >(); unlabeledStrokes = new StrokeCollection(); foreach (Stroke stroke in inkStrokes) { Sketch.Substroke sub = sketchPanel.InkSketch.GetSketchSubstrokeByInk(stroke); if (sub.ParentShape != null) { if (!origLabels.ContainsKey(sub.ParentShape.Name)) { origLabels[sub.ParentShape.Name] = new Data.Pair <ShapeType, StrokeCollection>(sub.ParentShape.Type, new StrokeCollection()); } origLabels[sub.ParentShape.Name].B.Add(stroke); } else { unlabeledStrokes.Add(stroke); } } }
/// <summary> /// Figures out everything a given logic gate should be /// connected to, and connects them appropriately. /// /// Assumption: Every wire-mesh has a unique name. /// /// Note: this currently only deals with connections to wires. /// Hence, it does not make any connections for notbubbles yet. /// </summary> /// <param name="gate">The gate to connect</param> private void connectWiresTo(LogicGate gate) { // We keep track of what wire-meshes are inputs and outputs // so we can do sanity checks before actually connecting // them to the logic gate. List <string> inputWires = new List <string>(); List <string> outputWires = new List <string>(); int maxOutputs = LogicDomain.MaxOutputs(gate.Type); // Cycle through everything connected to the gate's associated // shape and categorize their connection type accordingly foreach (Sketch.Shape connectedShape in gate.Shape.ConnectedShapes) { if (!Domain.LogicDomain.IsWire(connectedShape.Type)) { throw new Exception("Gate " + gate + " was connected to non-wire shape " + connectedShape); } Sketch.EndPoint connectingEndpoint = gate.Shape.ClosestEndpointFrom(connectedShape); if (gate.ShouldBeInput(connectingEndpoint)) { inputWires.Add(connectedShape.Name); } else { outputWires.Add(connectedShape.Name); } } // If it looks like we mixed up the output and input wires, // swap them so they're more correct (this can happen if the // gate's orientation was recognized in the wrong direction) if ((outputWires.Count > maxOutputs) && (inputWires.Count <= maxOutputs)) { swap(ref outputWires, ref inputWires); } // Make the connections. foreach (string wireName in inputWires) { WireMesh inputWire = _wireMeshes[wireName]; gate.ConnectInput(inputWire); } foreach (string wireName in outputWires) { WireMesh outputWire = _wireMeshes[wireName]; gate.ConnectOutput(outputWire); } gate.ConnectAllInputs(); gate.ConnectAllOutputs(); }
private Rect centerDrawing() { removeGate(); Rect bounds; if (gateChooser.SelectedItem == defaultChoice || (string)((ComboBoxItem)gateChooser.SelectedItem).Content == freehandString) { gate = null; return(new Rect()); } gate = LogicDomain.getType((string)((ComboBoxItem)(gateChooser.SelectedItem)).Content); // Bounds are currently arbitrary, place shape template in the middle of canvas if (gate != LogicDomain.NOTBUBBLE) { bounds = new Rect(inkCanvas.Width / 2 - GATE_WIDTH / 2, inkCanvas.Height / 2 - GATE_HEIGHT / 2, GATE_WIDTH, GATE_HEIGHT); } else { bounds = new Rect(inkCanvas.Width / 2 - NOTBUBBLE_DIAMETER / 2, inkCanvas.Height / 2 - NOTBUBBLE_DIAMETER / 2, NOTBUBBLE_DIAMETER, NOTBUBBLE_DIAMETER); } // This is so NANDs look like ANDs with NOTBUBBLEs // Same goes for all gates in the OR family if (gate == LogicDomain.AND) { bounds.Width = bounds.Width - bounds.Width / 4; } else if (gate == LogicDomain.OR) { bounds.Width = bounds.Width - bounds.Width / 6 - bounds.Width / 4; } else if (gate == LogicDomain.NOR) { bounds.Width = bounds.Width - bounds.Width / 6; } else if (gate == LogicDomain.XOR) { bounds.Width = bounds.Width - bounds.Width / 4; } DrawingImage drawingImage = new DrawingImage(gateDrawer.DrawGate(gate, bounds, false, true)); gateImage = new Image(); gateImage.Source = drawingImage; InkCanvas.SetLeft(gateImage, bounds.Left); InkCanvas.SetTop(gateImage, bounds.Top); inkCanvas.Children.Add(gateImage); return(bounds); }
/// <summary> /// Returns a pair of the minimum (first) and maximum (second) number of outputs that this shape can have. /// </summary> /// <param name="shape"></param> /// <returns></returns> public IntRange NumberOutputs(ShapeType shape) { if (shape == LogicDomain.SUBCIRCUIT) { return(new IntRange(1, int.MaxValue)); // we check validity in checkSubcircuits() } else if (LogicDomain.IsGate(shape)) { return(new IntRange(1, 1)); } return(new IntRange(1, int.MaxValue)); }
/// <summary> /// Compute the probability that a given set of substrokes has the given type. /// </summary> /// <param name="substrokes"></param> /// <param name="type"></param> /// <param name="featureSketch"></param> /// <returns>a pair containing the recognition probability and the orientation</returns> private RecognitionResult computeRecognitionProbabilityForTextOrWire(SubstrokeCollection substrokes, ShapeType type, FeatureSketch featureSketch) { double probability = 0; double orientation = 0; PhantomShape shape = new PhantomShape(); shape.AddSubstrokes(substrokes); if (LogicDomain.IsWire(type)) { // the probability it is a wire is defined as // (# substrokes classified as wires) / (total # substrokes) int numSubstrokes = substrokes.Count; int numWireSubstrokes = 0; foreach (Substroke substroke in substrokes) { if (_classifications[substroke] == LogicDomain.WIRE_CLASS) { numWireSubstrokes++; } } probability = (double)numWireSubstrokes / numSubstrokes; return(new RecognitionResult(LogicDomain.WIRE, probability, orientation)); } else if (LogicDomain.IsText(type)) { // the probability it is text is defined as // (# substrokes classified as text) / (total # substrokes) int numSubstrokes = substrokes.Count; int numTextSubstrokes = 0; foreach (Substroke substroke in substrokes) { if (_classifications[substroke] == LogicDomain.TEXT_CLASS) { numTextSubstrokes++; } } probability = (double)numTextSubstrokes / numSubstrokes; return(new TextRecognitionResult(probability, _textRecognizer.read(shape))); } return(null); }
/// <summary> /// Convert a sketch from /// </summary> /// <param name="originalSketch"></param> public void translateSketch(ref Sketch.Sketch originalSketch) { foreach (Shape shape in originalSketch.Shapes) { ShapeType type = shape.Type; if (labelMap.ContainsKey(type.Name)) // Convert it if it's in our map { shape.Type = LogicDomain.getType(labelMap[type.Name]); } else if (this.verbose) { System.Console.Error.WriteLine("Type {0} not specified in label map", type); } } }
/// <summary> /// Creates new EndpointPainters whenever /// a circuit is created /// </summary> public void UpdateEndpoints() { if (!subscribed) { return; } // Reinit // Highlight endpoints foreach (Shape shape in sketchPanel.InkSketch.Sketch.Shapes) { if (LogicDomain.IsWire(shape.Type)) { highlightEndpoints(shape); } } }
/// <summary> /// Constructor /// </summary> public RemoveLabelCmd(SketchPanel sketch, StrokeCollection inkStrokes, string label) { isUndoable = true; sketchPanel = sketch; this.inkStrokes = inkStrokes; this.label = Domain.LogicDomain.getType(label); labelColor = LogicDomain.getType(label).Color; labeledStrokes = new StrokeCollection(); foreach (Stroke stroke in inkStrokes) { if (stroke.DrawingAttributes.Color == labelColor) { labeledStrokes.Add(stroke); } } }
/// <summary> /// Loads the given domain file. /// <seealso cref="Labeler.MainForm.LoadDomain"/> /// </summary> /// <param name="domainFilePath">the file path to load</param> /// <returns>the DomainInfo loaded</returns> public static DomainInfo LoadDomainInfo(string domainFilePath) { // Check to see if there is a domain file to load for this feedback mechanism if (domainFilePath == null) { return(null); } // Make sure file exists if (!System.IO.File.Exists(domainFilePath)) { return(null); } // Load domain file System.IO.StreamReader sr = new System.IO.StreamReader(domainFilePath); DomainInfo domain = new DomainInfo(); string line = sr.ReadLine(); string[] words = line.Split(null); // The first two lines are useless line = sr.ReadLine(); line = sr.ReadLine(); // Then the rest are labels while (line != null && line != "") { words = line.Split(null); string label = words[0]; string color = words[1]; domain.AddLabel(LogicDomain.getType(label), (System.Windows.Media.Color)System.Windows.Media.ColorConverter.ConvertFromString(color)); line = sr.ReadLine(); } sr.Close(); return(domain); }
/// <summary> /// Runs the tests! /// </summary> /// <param name="sketch"></param> /// <param name="filename"></param> public override void run(Sketch.Sketch sketch, string filename) { // store the first filename as a way to tell what user we're working on. if (_filename == null) { _filename = filename; } Dictionary <ShapeType, MutablePair <int, int> > sketchResults = new Dictionary <ShapeType, MutablePair <int, int> >(); foreach (ShapeType type in LogicDomain.Gates) { sketchResults.Add(type, MutablePair.Create(0, 0)); } Sketch.Sketch handLabeled = sketch.Clone(); _pipeline.process(sketch); foreach (Sketch.Shape correctShape in handLabeled.Shapes) { if (!LogicDomain.IsGate(correctShape.Type)) { continue; } Sketch.Shape resultShape = sketch.ShapesL.Find(delegate(Sketch.Shape s) { return(s.GeometricEquals(correctShape)); }); if (resultShape == null) { throw new Exception("Could not find shape."); } sketchResults[correctShape.Type].Item1++; if (resultShape.Type == correctShape.Type) { sketchResults[correctShape.Type].Item2++; } } _results.Add(sketchResults); }
/// <summary> /// Highlights the endpoints of a single Wire by /// creating EndPointPainters for all endpoints. /// /// Main function for endpoint highlighting. /// /// Wire must have "AlreadyLabeled" set to true. /// </summary> /// <param name="wire">The Wire to highlight</param> /// <param name="g">The graphics handle to use while creating painters</param> private void highlightEndpoints(Shape wire) { // If this hasn't been recognized or isn't a wire, don not highlight. if (!wire.AlreadyLabeled || !LogicDomain.IsWire(wire.Type)) { return; } List <int> newEppIds = new List <int>(); EndPointPainter epp; int eppId; foreach (Sketch.EndPoint endpoint in wire.Endpoints) { epp = createEndpointPainter(endpoint); eppId = endPointPainterMap.Keys.Count + 1; epp.Id = eppId; newEppIds.Add(eppId); endPointPainterMap.Add(eppId, epp); } // Update stroke map to point to endpoint painters foreach (Sketch.Substroke substroke in wire.Substrokes) { String iStrokeId = sketchPanel.InkSketch.GetInkStrokeIdBySubstrokeId(substroke.XmlAttrs.Id); System.Windows.Ink.Stroke iStroke = sketchPanel.InkSketch.GetInkStrokeById(iStrokeId); if (iStrokeId2EndPtPainterId.ContainsKey(iStroke)) { throw new ApplicationException("Error: encountered one stroke " + "that is part of two or more unique meshes"); } if (iStrokeId != null) { iStrokeId2EndPtPainterId[iStroke] = newEppIds; } } PaintAllEndpoints(); }
/// <summary> /// Connects not bubbles to gates and wires /// </summary> /// <param name="gate"></param> private void connectNotBubble(LogicGate bubble) { LogicGate parentGate = null; WireMesh wire = null; // Get the components this not bubble is connected to foreach (Shape connected in bubble.Shape.ConnectedShapes) { if (LogicDomain.IsGate(connected.Type) && parentGate == null) { parentGate = _logicGates[connected]; } else if (LogicDomain.IsWire(connected.Type)) { wire = _wireMeshes[connected]; } } // If this is not connected to a gate, connect it like a normal logic gate if (parentGate == null) { connectWiresTo(bubble); return; } // Is this bubble on the output or the input? Sketch.EndPoint connectingEndpoint = parentGate.Shape.ClosestEndpointFrom(bubble.Shape); bool isInput = parentGate.ShouldBeInput(connectingEndpoint); if (isInput) { wire.ConnectDependent(bubble); parentGate.ConnectInput(bubble, 0); } else { wire.ConnectSource(bubble, 0); parentGate.ConnectOutput(bubble, 0); } }
/// <summary> /// Uses the cached result if it is available, or makes a call to the inner /// recognizer otherwise. This method is thread-safe provided that /// 1. the underlying recognizer's "recognize" method is thread-safe /// 2. this method will not be called concurrently on the same shape /// </summary> /// <param name="shape">the shape to recognize</param> /// <param name="featureSketch">the featureSketch to work on</param> public override void recognize(Sketch.Shape shape, Featurefy.FeatureSketch featureSketch) { int hash = shape.GetHashCode(); // Safely determine if the value is already cached. We never remove cached // results, so it is safe to release the lock afterward. bool isCached; lock (_typeResults) isCached = _typeResults.ContainsKey(hash); if (isCached) { // Write the cached results to the shape. Since we can assume that // we don't need to lock the shape, all we need to synchronize on is // the dictionary when we retreive results. ShapeType type; float prob; lock (_typeResults) { type = LogicDomain.getType(_typeResults[hash]); prob = _probabilityResults[hash]; } shape.setRecognitionResults(type, prob); } else { // We only need a lock here when we write results to the dictionary. _innerRecognizer.recognize(shape, featureSketch); lock (_typeResults) { _typeResults.Add(hash, shape.Type.Name); _probabilityResults.Add(hash, shape.Probability); } } }
/// <summary> /// Recalculate the connectedShapes of every shape in a given list, /// and correctly update all related shapes. /// </summary> /// <param name="shapeList">the list of shapes to reconnect</param> /// <param name="featureSketch">the sketch the shapes belong to</param> public void recomputeConnectedShapes(IEnumerable <Sketch.Shape> shapeList, Sketch.Sketch sketch) { // Keep a unique list of shapes HashSet <Sketch.Shape> allRelevantShapes = new HashSet <Sketch.Shape>(shapeList); // Add connected shapes to the list that needs to change foreach (Sketch.Shape shape in shapeList) { foreach (Sketch.Shape connected in shape.ConnectedShapes) { allRelevantShapes.Add(connected); } } // clear ALL connections first foreach (Sketch.Shape shape in allRelevantShapes) { shape.ClearConnections(); } sketch.CheckConsistency(); // connect every shape foreach (Sketch.Shape shape in allRelevantShapes) { connect(shape, sketch); } // Make sure connected wires are the same shape Tuple <Sketch.Shape, Sketch.Shape> pair = null; while ((pair = wiresToMerge(allRelevantShapes)) != null) { sketch.mergeShapes(pair.Item1, pair.Item2); allRelevantShapes.Remove(pair.Item2); sketch.connectShapes(pair.Item1, pair.Item1);; } sketch.CheckConsistency(); #if DEBUG foreach (Shape wire in sketch.Shapes) { if (!LogicDomain.IsWire(wire.Type)) { continue; } foreach (Shape shape in wire.ConnectedShapes) { if (LogicDomain.IsWire(shape.Type) && shape != wire) { throw new Exception("Found two connected wires (" + wire + " and " + shape + ") that were not merged!"); } bool found = false; foreach (EndPoint endpoint in wire.Endpoints) { if (endpoint.ConnectedShape == shape) { found = true; break; } } if (!found) { throw new Exception("The wire " + wire + " is connected to " + shape + " as a connected shape, but not by an endpoint!"); } } } #endif }
/// <summary> /// Connects the given shape to the other shapes in the sketch. /// /// Postcondition: /// If the shape is a wire, the following things are true: /// - For every substroke in the wire, both endpoints are connected to the closest shape /// - The shapes the endpoints are connected to are also in the list of connected shapes /// - The shapes that the wire is connected to also know they are connected to the wire /// If the shape is a NOTBUBBLE /// - It is connected to the two closest shapes?? /// </summary> /// <param name="shape">The shape to check.</param> /// <param name="sketch">The sketch containing the shape.</param> public override void ConnectShape(Sketch.Shape shape, Sketch.Sketch sketch) { if (!sketch.ShapesL.Contains(shape)) { throw new ArgumentException("The given shape " + shape + " is not in the given sketch!"); } // Connect wires to adjacent shapes if (shape.Type == LogicDomain.WIRE) { foreach (Sketch.Substroke substroke in shape.Substrokes) { // Find the shape closest to the start point of the wire Shape shape2 = findClosest(true, substroke, sketch); if (shape2 != null) { EndPoint start = substroke.Endpoints[0]; if (!start.IsConnected) { sketch.connectShapes(shape, shape2); start.ConnectedShape = shape2; } } // Find the shape closest to the end point of the wire Shape shape3 = findClosest(false, substroke, sketch); if (shape3 != null) { EndPoint end = substroke.Endpoints[1]; if (!end.IsConnected) { sketch.connectShapes(shape, shape3); end.ConnectedShape = shape3; } } } } // Connect NotBubbles to its two closest shapes. // FIXME: This could potentially cause problems. For instance, // on the off chance that one of the closest shapes was a wire // whose connections were already figured out, this could leave // the wire in an inconsistent state. else if (shape.Type == LogicDomain.NOTBUBBLE) { List <Sketch.Shape> shapes = twoClosest(shape, sketch); foreach (Shape closeShape in shapes) { if (closeShape == null) { continue; } // FIXME: For now, we're only connecting this notbubble to a wire if that wire's closest endpoint is free. if (closeShape.Type == LogicDomain.WIRE) { EndPoint endpoint = shape.ClosestEndpointFrom(closeShape); if (!endpoint.IsConnected) { closeShape.ConnectNearestEndpointTo(shape); } } else { sketch.connectShapes(closeShape, shape); } } } #if DEBUG foreach (Shape wire in sketch.Shapes) { if (!LogicDomain.IsWire(wire.Type)) { continue; } foreach (Shape connected in wire.ConnectedShapes) { bool found = false; // If a wire is connected to something, then either the wire is // connected by an endpoint to the other shape... foreach (EndPoint endpoint in wire.Endpoints) { if (endpoint.ConnectedShape == connected) { found = true; break; } } // ...or the other shape is connected by an endpoint to the wire foreach (EndPoint endpoint in connected.Endpoints) { if (endpoint.ConnectedShape == wire) { found = true; break; } } if (!found) { throw new Exception("The wire " + wire + " is connected to " + connected + " as a connected shape, but neither has an endpoint connection to the other!"); } } } #endif }
/// <summary> /// Determines whether or not a given shape is a circuit /// input, and creates and connects it accordingly. /// Will ignore any text that is not connected to anything. /// /// Assumptions: /// * The names of wire-meshes correspond directly to /// their associated shapes. /// * Wire-meshes all have unique names. /// * The given shape is not a label connected to two wires. /// </summary> /// <param name="shape">The shape to analyze</param> /// <returns>True if loading was successful, false /// if the given shape cannot be recognized as an input /// or output</returns> private void loadInputOrOutput(Sketch.Shape shape) { // Make sure we're actually dealing with something sensical. // Note: If there are no connected shapes, we don't make an input or output, it is just ignored in the circuit. if (shape.Type != LogicDomain.TEXT || shape.ConnectedShapes.Count == 0) { return; } // Retreive the wire connected to this label shape, while // also checking that the shape has the correct number and // kinds of connections. List <WireMesh> connectedWires = new List <WireMesh>(); // Assume that we have an input until we are told otherwise. bool input = true; foreach (Sketch.Shape connectedShape in shape.ConnectedShapes) { // Is this a wire? if (!LogicDomain.IsWire(connectedShape.Type)) { continue; } // Get the connected wire WireMesh connected = _wireMeshes[connectedShape.Name]; // Have we already seen this? if (connectedWires.Contains(connected)) { continue; } connectedWires.Add(connected); // If we're dealing with any wire that already has a source, this will not be an input if (connected.HasSource) { input = false; } } if (input) { CircuitInput newInput = new CircuitInput(shape); foreach (WireMesh wire in connectedWires) { newInput.Connect(wire); } _circuitInputs.Add(newInput.Name, newInput); } else { CircuitOutput newOutput = new CircuitOutput(shape); foreach (WireMesh wire in connectedWires) { newOutput.Connect(wire); } _circuitOutputs.Add(newOutput.Name, newOutput); } }
/// <summary> /// Checks whether the sketch is valid, and returns a bool indicating this. /// Adds any errors it comes across to the _parseErrors list. /// /// Looks for: /// * Basic consistency via sketch.CheckConsistency /// * Gates connected to only wires /// * Wires not connected to wires /// * Text connected to at most one wire /// </summary> /// <param name="sketch">The sketch to check the validity of</param> /// <returns>A bool, which is true if the sketch is valid</returns> private bool CheckSketch(Sketch.Sketch sketch) { bool valid = true; // Check basic sketch consistency, will (rightly) throw an exception if it finds something wrong. // Eventually we should not need this, for the sketch should always pass this, but it's a useful check for now. sketch.CheckConsistency(); foreach (Sketch.Shape shape in sketch.Shapes) { #region Gate Checks if (LogicDomain.IsGate(shape.Type)) { // Each gate should be connected to only wires foreach (Sketch.Shape connected in shape.ConnectedShapes) { if (!LogicDomain.IsWire(connected.Type)) { valid = false; _parseErrors.Add(new ParseError("Gate " + shape.Name + " is connected to something that is not a wire, " + connected.Name + "of type " + connected.Type.Name, "This gate is connected to something that is not a wire. Draw wires between them or group them together.", shape)); } } } #endregion #region Wire Checks else if (LogicDomain.IsWire(shape.Type)) { // Wires shouldn't be connected to other wires foreach (Sketch.Shape connected in shape.ConnectedShapes) { if (shape != connected && LogicDomain.IsWire(connected.Type)) { valid = false; _parseErrors.Add(new ParseError("Wire " + shape.Name + " is connected to another wire, " + connected.Name, "This wire is connected to another wire. Try grouping these wires together.", shape)); } } } #endregion #region Input/Output Checks else if (LogicDomain.IsText(shape.Type)) { // Text should only be connected to wires, if anything. foreach (Sketch.Shape wire in shape.ConnectedShapes) { if (!LogicDomain.IsWire(wire.Type)) { valid = false; _parseErrors.Add(new ParseError("Text " + shape.Name + " should only be connected to a wire, but is connected to a " + wire.Type.Name + ".", shape.Name + " should only be connected to a wire, but is connected to a " + wire.Type.Name + ".", shape)); } } } #endregion } return(valid); }
/// <summary> /// Essentially re-trains the classifier using the stored instances /// </summary> public void UpdateClassifier() { // List of each class - go through all examples once to get complete list of classes List <ShapeType> classes = new List <ShapeType>(); foreach (KeyValuePair <string, Dictionary <string, object> > example in _examples) { if (!classes.Contains(LogicDomain.getType(example.Key))) { classes.Add(LogicDomain.getType(example.Key)); } } #region Initialize Probability stuff ////////////////////////////////////////////////// ////////////////////////////////////////////////// // 4 Pieces of information needed to calculate all // feature likelyhoods given class // needed for priors and feature value likelyhood int numExamples = _examples.Count; if (numExamples == 0) { return; } // Prior probabilities of a class // Initialize them all to have 0.0 prior probability Dictionary <ShapeType, double> Priors = new Dictionary <ShapeType, double>(); foreach (ShapeType cls in classes) { Priors.Add(cls, 0.0); } // Likelyhood of a feature value // Initialize all top level dictionary entries Dictionary <string, Dictionary <object, double> > FeatureValue_Likelyhood = new Dictionary <string, Dictionary <object, double> >(); foreach (string feature in _featureNames) { FeatureValue_Likelyhood.Add(feature, new Dictionary <object, double>()); } // Likelyhood of a class given a feature value // Initialize all top and 2nd level dictionary entries Dictionary <string, Dictionary <object, Dictionary <ShapeType, double> > > Class_Given_FeatureValue_Likelyhood = new Dictionary <string, Dictionary <object, Dictionary <ShapeType, double> > >(); foreach (string feature in _featureNames) { Class_Given_FeatureValue_Likelyhood.Add(feature, new Dictionary <object, Dictionary <ShapeType, double> >()); } ////////////////////////////////////////////////// ////////////////////////////////////////////////// #endregion #region Initialize Counting Stuff ////////////////////////////////////////////////// ////////////////////////////////////////////////// // Counts necessary to calculate likelyhoods // How many occurances there are of each class - for prior probabilities // Initialize all the class counts to 0 Dictionary <string, int> Count_Class = new Dictionary <string, int>(); foreach (ShapeType cls in classes) { Count_Class.Add(cls.Name, 0); } // For FeatureValueLikelyhood // - Count of the number of times Feature_i = f // Initialize all top level dictionary entries Dictionary <string, Dictionary <object, int> > Count_Feature_Eq_f = new Dictionary <string, Dictionary <object, int> >(_featureNames.Count); foreach (string feature in _featureNames) { Count_Feature_Eq_f.Add(feature, new Dictionary <object, int>()); } // Given a class, how many times was this value observed // For ClassGivenFeatureLikelyhood // - Count of the number of times Class_j = c AND Feature_i = f // Initialize all top and 2nd level dictionary entries // First dictionary = feature name to 2nd dictionary // Second dictionary = feature value to 3rd dictionary // Third dictionary = class name to occurance count Dictionary <string, Dictionary <object, Dictionary <string, int> > > Count_Class_Eq_c_given_Feature_Eq_f = new Dictionary <string, Dictionary <object, Dictionary <string, int> > >(); foreach (string feature in _featureNames) { Count_Class_Eq_c_given_Feature_Eq_f.Add(feature, new Dictionary <object, Dictionary <string, int> >()); } ////////////////////////////////////////////////// ////////////////////////////////////////////////// #endregion #region Go through every single example and count feature occurances and class occurances foreach (KeyValuePair <string, Dictionary <string, object> > example in _examples) { string className = example.Key; Count_Class[className]++; Dictionary <string, object> features = example.Value; // Count the number of occurances of each feature value. foreach (string fName in _featureNames) { // feature value object value; if (features.TryGetValue(fName, out value)) { // count of instances of this value across all classes Dictionary <object, int> count; if (Count_Feature_Eq_f.TryGetValue(fName, out count)) { if (count.ContainsKey(value)) { count[value]++; } else { count.Add(value, 1); } } // count of instances of this value in this class ONLY Dictionary <string, int> countPerClass; if (Count_Class_Eq_c_given_Feature_Eq_f[fName].TryGetValue(value, out countPerClass)) { if (countPerClass.ContainsKey(className)) { countPerClass[className]++; } else { countPerClass.Add(className, 1); } } else { Dictionary <string, int> clsCount = new Dictionary <string, int>(); clsCount.Add(className, 1); Count_Class_Eq_c_given_Feature_Eq_f[fName].Add(value, clsCount); } } } } #endregion #region Calculate all the probabilities // Get the prior probabilities for each class foreach (ShapeType cls in classes) { int count; if (Count_Class.TryGetValue(cls.Name, out count)) { double prior = (double)count / numExamples; Priors[cls] = prior; } } // Likelyhood for feature value throughout all classes foreach (string fName in _featureNames) { Dictionary <object, int> count_for_Feature_i; if (Count_Feature_Eq_f.TryGetValue(fName, out count_for_Feature_i)) { foreach (KeyValuePair <object, int> pair in count_for_Feature_i) { double p_F = (double)pair.Value / numExamples; p_F = Math.Min(Math.Max(p_F, MIN_PROBABILITY), 1.0 - MIN_PROBABILITY); if (FeatureValue_Likelyhood[fName].ContainsKey(pair.Key)) { FeatureValue_Likelyhood[fName][pair.Key] = p_F; } else { FeatureValue_Likelyhood[fName].Add(pair.Key, p_F); } } } } // Likelyhood for feature value per class foreach (string fName in _featureNames) { foreach (KeyValuePair <object, Dictionary <string, int> > pair in Count_Class_Eq_c_given_Feature_Eq_f[fName]) { object value = pair.Key; Class_Given_FeatureValue_Likelyhood[fName].Add(value, new Dictionary <ShapeType, double>()); double p_F = FeatureValue_Likelyhood[fName][value]; int sum = 0; foreach (KeyValuePair <string, int> clsCount in pair.Value) { sum += clsCount.Value; } foreach (ShapeType cls in classes) { if (pair.Value.ContainsKey(cls.Name)) { double p_C = Priors[cls]; double v = (double)pair.Value[cls.Name] / sum; double p_C_given_F = Math.Min(Math.Max(v, MIN_PROBABILITY), 1.0 - MIN_PROBABILITY); double p_F_given_C = p_C_given_F * p_F / p_C; Class_Given_FeatureValue_Likelyhood[fName][value].Add(cls, p_F_given_C); } else { Class_Given_FeatureValue_Likelyhood[fName][value].Add(cls, MIN_PROBABILITY); } } } } #endregion // Sort the dictionaries...for fun and ease of reading when debugging Dictionary <string, Dictionary <object, double> > fvl = new Dictionary <string, Dictionary <object, double> >(); foreach (KeyValuePair <string, Dictionary <object, double> > pair in FeatureValue_Likelyhood) { List <object> v_keys = new List <object>(pair.Value.Keys); v_keys.Sort(); Dictionary <object, double> v = new Dictionary <object, double>(); foreach (object value in v_keys) { v.Add(value, pair.Value[value]); } fvl.Add(pair.Key, v); } List <ShapeType> p_keys = new List <ShapeType>(Priors.Keys); p_keys.Sort(); Dictionary <ShapeType, double> p = new Dictionary <ShapeType, double>(); foreach (ShapeType key in p_keys) { p.Add(key, Priors[key]); } Dictionary <string, Dictionary <object, Dictionary <ShapeType, double> > > cgfvl = new Dictionary <string, Dictionary <object, Dictionary <ShapeType, double> > >(); foreach (KeyValuePair <string, Dictionary <object, Dictionary <ShapeType, double> > > pair in Class_Given_FeatureValue_Likelyhood) { List <object> v_keys = new List <object>(pair.Value.Keys); v_keys.Sort(); Dictionary <object, Dictionary <ShapeType, double> > v = new Dictionary <object, Dictionary <ShapeType, double> >(); foreach (object value in v_keys) { v.Add(value, pair.Value[value]); } cgfvl.Add(pair.Key, v); } // Update the Classifier _classifier = new NaiveBayes(classes, _featureNames, p, fvl, cgfvl); }
public Color GetColor(string label) { return(GetColor(LogicDomain.getType(label))); }
/// <summary> /// Compute the energy function that we are trying to maximize. /// </summary> /// <param name="sketch"></param> /// <returns>an unbounded double representing the energy of the sketch</returns> private double computeEnergy(FeatureSketch featureSketch) { Sketch.Sketch sketch = featureSketch.Sketch; featureSketch.hasConsistentSubstrokes(); sketch.CheckConsistency(); double energy = 0; var shapes = sketch.ShapesL; int numShapes = shapes.Count; int numGates = shapes.FindAll(s => LogicDomain.IsGate(s.Type)).Count; foreach (Shape shape in shapes) { // Add energy for every connection #if false // This is a bad idea. It favors interpretations with more connections, which basically means // that everything should alternate wire-text-wire-text-wire-... foreach (Shape connected in shape.ConnectedShapes) { if (connected == shape) { continue; } if (connected.Type != LogicDomain.WIRE) { continue; } double connectionDistance = double.PositiveInfinity; foreach (EndPoint endpoint in connected.Endpoints) { connectionDistance = Math.Min(shape.minDistanceTo(endpoint.X, endpoint.Y), connectionDistance); } connectionDistance = Math.Max(connectionDistance, 0.001); // avoid problems when connection distance is close to zero energy += 1 + 1 / connectionDistance; } #endif // Add the context match score energy += (double)_domain.ClosestContext(shape).Item1 / numShapes; // Get recognition results RecognitionResult result = RecognizeAsType(shape, shape.Type, featureSketch); double confidence = result.Confidence; // Add the recognition score energy += confidence / numShapes; #if false // Gate orientation also contributes if (LogicDomain.IsGate(shape.Type)) { // Determine the recognizer's and the connector's orientation values, // in the range [0, 2Pi] double orientation = result.Orientation; double otherOrientation = _domain.OrientShape(shape, sketch); // Orientation might be off by PI... double dist1 = Math.Abs(otherOrientation - orientation); double dist2 = Math.Abs(otherOrientation - Math.PI - orientation); double dist = Math.Min(dist1, dist2); // Add orientation score double twoPI = 2 * Math.PI; energy += (1 - (dist / twoPI)) / numGates; } #endif } return(energy); }