/// <summary> /// Generates a knowledge structure (KStructure object) from a ranked order (RankOrder object) by applying Rule 1. /// </summary> /// /// <param name="rankOrder">RankOrder object that is used to generate a knowledge structure.</param> /// /// <returns>KStructure object, or null if error occured.</returns> public KStructure createKStructure(RankOrder rankOrder) { // Implements Rule 1: Given a set GR of problems with a rank R and a set G<R of problems of lower ranks, // a union of any subset of GR with G<R is a knowledge state. // [SC] make sure the rankOrder object is not null if (rankOrder == null) { Log(Severity.Error, "createKStructure: Null object is passed as RankOrder parameter. Returning null."); return(null); } // [SC] make sure there is at least one rank in the rank order if (rankOrder.getRankCount() == 0) { Log(Severity.Error, "createKStructure: rank order has no ranks. Returning null."); return(null); } // [SC] make sure the ranks are sorted in an ascending order rankOrder.sortAscending(); // [SC] creating knowledge states List <KState> allStates = new List <KState>(); List <PCategory> prevCategories = new List <PCategory>(); foreach (Rank rank in rankOrder.getRanks()) { // [SC] getting all unique subsets of categories in this rank List <List <PCategory> > subsets = rank.getSubsets(); // [SC] for each subset, create a knowledge state by combining with all categories of lower ranks foreach (List <PCategory> subset in subsets) { KState state = new KState(); foreach (PCategory category in prevCategories) { state.addCategory(category); } foreach (PCategory category in subset) { state.addCategory(category); } allStates.Add(state); } prevCategories.AddRange(rank.getCategories()); } // [SC] sort states by their sizes in an ascending order allStates.Sort((stateOne, stateTwo) => stateOne.getCategoryCount().CompareTo(stateTwo.getCategoryCount())); // [SC] creating an empty knowledge structure object KStructure ks = new KStructure(rankOrder); // [SC] creating 0th rank with an empty root state int rankIndex = 0; int stateCounter = 0; // [SC] used to generate an ID for each state KSRank prevRank = null; KSRank currRank = new KSRank(rankIndex); // [SC] the root rank will automatically add an empty root state ks.addRank(currRank); // [SC] adding all states in respective ranks foreach (KState state in allStates) { if (state.getCategoryCount() > rankIndex) { stateCounter = 0; prevRank = currRank; currRank = new KSRank(++rankIndex); ks.addRank(currRank); } if (currRank.addState(state)) { state.Id = KSGenerator.getStateID(rankIndex, ++stateCounter); foreach (KState prevState in prevRank.getStates()) { if (prevState.isSubsetOf(state)) { prevState.addNextState(state); state.addPrevState(prevState); } } } } return(ks); }
/// <summary> /// Deserializes XML into KStructure object /// </summary> /// /// <param name="doc">XDocument instance</param> /// /// <returns>KStructure object</returns> public KStructure createKStructure(XDocument doc) { // [TODO] validate against schema XName[] nodeNames; // [SC] a hash table of all categories Dictionary <string, PCategory> categories = new Dictionary <string, PCategory>(); // [SC] a hash table of all states Dictionary <string, KState> states = new Dictionary <string, KState>(); // [SC] iterate through 'TwoA/PCategories/PCategory' elements nodeNames = new XName[] { XMLFactory.PCATS_ELEM, XMLFactory.PCAT_ELEM }; foreach (XElement categoryElem in SelectNodes(doc.Root, nodeNames)) { // [SC] get the value of 'TwoA/PCategories/PCategory@xsd:id' attribute string id = categoryElem.Attribute(XMLFactory.ID_ATTR).Value; // [SC] get the value of 'TwoA/PCategories/PCategory/Rating' element double rating; if (!Double.TryParse(categoryElem.Element(XMLFactory.RATING_ELEM).Value, out rating)) { Log(Severity.Error, String.Format("createKStructure: unable to parse rating for category {0}. Returning null.", id)); return(null); // [TODO] no need due to schema check? } PCategory category = new PCategory(id, rating); categories.Add(id, category); } RankOrder rankOrder = new RankOrder(); // [SC] parse the value of 'TwoA/RankOrder/Params/Threshold' element nodeNames = new XName[] { XMLFactory.RANKORDER_ELEM, XMLFactory.PARAMS_ELEM, XMLFactory.THRESHOLD_ELEM }; double threshold; if (Double.TryParse(SelectSingleNode(doc.Root, nodeNames).Value, out threshold)) { rankOrder.Threshold = threshold; } else { Log(Severity.Error, "createKStructure: unable to parse the threshold value. Returning null value. Returning null."); return(null); // [TODO] no need due to schema check? } // [SC] iterate through 'TwoA/RankOrder/Ranks/Rank' elements nodeNames = new XName[] { XMLFactory.RANKORDER_ELEM, XMLFactory.RANKS_ELEM, XMLFactory.RANK_ELEM }; foreach (XElement rankElem in SelectNodes(doc.Root, nodeNames)) { Rank rank = new Rank(); // [SC] parse the value of 'TwoA/RankOrder/Ranks/Rank@Index' atttribute int rankIndex; if (Int32.TryParse(rankElem.Attribute(XMLFactory.INDEX_ATTR).Value, out rankIndex)) { rank.RankIndex = rankIndex; } else { Log(Severity.Error, "createKStructure: unable to parse the index of a rank in the rank order. Returning null."); return(null); // [TODO] no need due to schema check? } // [SC] iterate through 'TwoA/RankOrder/Ranks/Rank/PCategory' elements foreach (XElement categoryElem in rankElem.Elements(XMLFactory.PCAT_ELEM)) { // [SC] parse 'TwoA/RankOrder/Ranks/Rank/PCategory@xsd:idref' attribute if (categoryElem.Attribute(XMLFactory.IDREF_ATTR) == null) { Log(Severity.Error, String.Format("createKStructure: unable to parse ID for a category in rank {0} of the rank order. Returning null.", rankIndex)); return(null); // [TODO] no need due to schema check? } string id = categoryElem.Attribute(XMLFactory.IDREF_ATTR).Value; // [SC] retrieve PCategory object by its id and add it to the rank object PCategory category = categories[id]; if (category == null) { Log(Severity.Error , String.Format("createKStructure: category {0} from rank {1} of rank order is not found in the list of categories. Returning null." , id, rankIndex)); return(null); // [TODO] no need due to schema check? } rank.addCategory(category); } rankOrder.addRank(rank); } KStructure kStructure = new KStructure(rankOrder); // [SC] iterate through 'TwoA/KStructure/KSRank' elements nodeNames = new XName[] { XMLFactory.KSTRUCTURE_ELEM, XMLFactory.KSRANK_ELEM }; foreach (XElement ksrankElem in SelectNodes(doc.Root, nodeNames)) { KSRank ksrank = new KSRank(); // [SC] parse the value of 'TwoA/KStructure/KSRank@Index' attribute int rankIndex; if (Int32.TryParse(ksrankElem.Attribute(XMLFactory.INDEX_ATTR).Value, out rankIndex)) { ksrank.RankIndex = rankIndex; } else { Log(Severity.Error, "createKStructure: unable to parse index of a rank in the knowledge structure. Returning null."); return(null); // [TODO] no need due to schema check? } if (rankIndex == 0) { XElement rootStateElem = ksrankElem.Element(XMLFactory.KSTATE_ELEM); // [SC] parse 'TwoA/KStructure/KSRank/KState@xsd:id' attribute if (rootStateElem.Attribute(XMLFactory.ID_ATTR) == null) { Log(Severity.Error, "createKStructure: unable to parse ID of the root state in the knowledge structure. Returning null."); return(null); // [TODO] no need due to schema check? } ksrank.getStateAt(0).Id = rootStateElem.Attribute(XMLFactory.ID_ATTR).Value; states.Add(ksrank.getStateAt(0).Id, ksrank.getStateAt(0)); kStructure.addRank(ksrank); continue; } // [SC] iterate through 'TwoA/KStructure/KSRank/KState' elements foreach (XElement stateElem in ksrankElem.Elements(XMLFactory.KSTATE_ELEM)) { KState kstate = new KState(); // [SC] parse 'TwoA/KStructure/KSRank/KState@xsd:id' attribute if (stateElem.Attribute(XMLFactory.ID_ATTR) == null) { Log(Severity.Error, String.Format("createKStructure: unable to parse ID of a state in the rank {0} of the knowledge structure. Returning null.", rankIndex)); return(null); // [TODO] no need due to schema check? } kstate.Id = stateElem.Attribute(XMLFactory.ID_ATTR).Value; // [SC] parse 'TwoA/KStructure/KSRank/KState@Type' attribute if (stateElem.Attribute(XMLFactory.TYPE_ATTR) == null) { Log(Severity.Error, String.Format("createKStructure: unable to parse state type in the rank {0} of the knowledge structure. Returning null.", rankIndex)); return(null); // [TODO] no need due to schema check? } kstate.StateType = stateElem.Attribute(XMLFactory.TYPE_ATTR).Value; // [SC] iterate through 'TwoA/KStructure/KSRank/KState/PCategories/PCategory' elements nodeNames = new XName[] { XMLFactory.PCATS_ELEM, XMLFactory.PCAT_ELEM }; foreach (XElement categoryElem in SelectNodes(stateElem, nodeNames)) { // [SC] parse 'TwoA/KStructure/KSRank/KState/PCategories/PCategory@xsd:idref' attribute if (categoryElem.Attribute(XMLFactory.IDREF_ATTR) == null) { Log(Severity.Error, String.Format("createKStructure: unable to parse ID of a category in the state {0}. Returning null.", kstate.Id)); return(null); // [TODO] no need due to schema check? } string id = categoryElem.Attribute(XMLFactory.IDREF_ATTR).Value; // [SC] retrieve PCategory object by its id and add it to the rank object PCategory category = categories[id]; if (category == null) { Log(Severity.Error , String.Format("createKStructure: category {0} from the state {1} is not found in the list of categories. Returning null." , id, kstate.Id)); return(null); // [TODO] no need due to schema check? } kstate.addCategory(category); } // [SC] iterate through 'TwoA/KStructure/KSRank/KState/PreviousStates/KState' elements nodeNames = new XName[] { XMLFactory.PREV_STATES_ELEM, XMLFactory.KSTATE_ELEM }; foreach (XElement prevStateElem in SelectNodes(stateElem, nodeNames)) { // [SC] parse 'TwoA/KStructure/KSRank/KState/PreviousStates/KState@xsd:idref' attribute if (prevStateElem.Attribute(XMLFactory.IDREF_ATTR) == null) { Log(Severity.Error, String.Format("createKStructure: unable to parse ID of a previous state for a state {0}. Returning null.", kstate.Id)); return(null); // [TODO] no need due to schema check? } string id = prevStateElem.Attribute(XMLFactory.IDREF_ATTR).Value; // [SC] retrieve prev state object by its id and add it to the current state object KState prevState = states[id]; if (prevState == null) { Log(Severity.Error, String.Format("createKStructure: unable to find previously created state object with id '{0}'. Returning null.", id)); return(null); // [TODO] no need due to schema check? } kstate.addPrevState(prevState); prevState.addNextState(kstate); } states.Add(kstate.Id, kstate); ksrank.addState(kstate); } kStructure.addRank(ksrank); } return(kStructure); }
/// <summary> /// Expands the specified knowledge structure with new states by applying Rule 2. /// </summary> /// /// <param name="ks">Knowledge structure to be expanded with new states.</param> /// /// <returns>Expanded knowledge structure</returns> public void createExpandedKStructure(KStructure ks) { // Implements Rule 2: Given a set GR of problems with a rank R and a set GR-1 of problems with rank R-1, // a union of any subset of GR with any knowledge state KR-1 containing at least one problem from GR-1 is a state. // [SC] make sure the knowledge structure object is not null if (ks == null) { Log(Severity.Error, "createExpandedKStructure: KStructure object is null. Returning from method."); return; } // [SC] make sure the rank order of categories is available if (!ks.hasRankOrder()) { Log(Severity.Error, "createExpandedKStructure: KStructure object contains no rank order. Returning from method."); return; } // [SC] make sure the knowledge structure has ranks if (!ks.hasRanks()) { Log(Severity.Error, "createExpandedKStructure: KStructure object contains no ranks with states. Returning from method."); return; } Rank prevRank = null; foreach (Rank rank in ks.rankOrder.getRanks()) { if (prevRank != null) { // [SC] getting all unique subsets of categories in this rank List <List <PCategory> > subsets = rank.getSubsets(); // [SC] retrieve all KS ranks with minimum required state size List <KSRank> ksRanks = ks.getRanks().FindAll(rankOne => rankOne.RankIndex >= prevRank.RankIndex); if (ksRanks == null || ksRanks.Count == 0) { continue; } // [SC] this list contains all relevant states that contain any category from GR-1 List <KState> states = new List <KState>(); foreach (KSRank ksRank in ksRanks) { // [SC] From given KS rank, retrieve all states that contain at least one problem from GR-1 and add them to the common list states.AddRange(ksRank.getStates().FindAll(stateOne => stateOne.getCategories().Intersect(prevRank.getCategories()).Any())); } if (states.Count == 0) { continue; } // [SC] iterate through subsets of GR foreach (List <PCategory> subset in subsets) { foreach (KState state in states) { // [SC] if state already contains the entire subset then skip it if (state.getCategories().Intersect(subset).Count() == subset.Count) { continue; } // [SC] creating a new state KState newState = new KState(KSGenerator.EXPANDED_STATE); foreach (PCategory category in state.getCategories()) { newState.addCategory(category); } foreach (PCategory category in subset) { newState.addCategory(category); } // [SC] add the new state to the respective rank KSRank newStateRank = ks.getRanks().Find(rankOne => rankOne.RankIndex == newState.getCategoryCount()); if (newStateRank.addState(newState)) { newState.Id = KSGenerator.getStateID(newStateRank.RankIndex, newStateRank.getStateCount()); // [SC] link the new state with previous states of lower rank KSRank prevStateRank = ks.getRanks().Find(rankOne => rankOne.RankIndex == newState.getCategoryCount() - 1); if (prevStateRank != null) { foreach (KState prevState in prevStateRank.getStates()) { if (prevState.isSubsetOf(newState)) { prevState.addNextState(newState); newState.addPrevState(prevState); } } } // [SC] link the new state with next states of higher rank KSRank nextStateRank = ks.getRanks().Find(rankOne => rankOne.RankIndex == newState.getCategoryCount() + 1); if (nextStateRank != null) { foreach (KState nextState in nextStateRank.getStates()) { if (newState.isSubsetOf(nextState)) { nextState.addPrevState(newState); newState.addNextState(nextState); } } } } } } } prevRank = rank; } }