/// <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);
        }
Esempio n. 2
0
        /// <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;
            }
        }