diff --git a/.project b/.project
new file mode 100644
index 0000000..5ca30de
--- /dev/null
+++ b/.project
@@ -0,0 +1,17 @@
+
+
+ DedekindNumber
+
+
+
+
+
+ org.python.pydev.PyDevBuilder
+
+
+
+
+
+ org.python.pydev.pythonNature
+
+
diff --git a/src/solamanDedekind/.gitignore b/src/solamanDedekind/.gitignore
new file mode 100644
index 0000000..14b9f47
--- /dev/null
+++ b/src/solamanDedekind/.gitignore
@@ -0,0 +1,28 @@
+# Compiled source #
+###################
+*.com
+*.class
+*.dll
+*.exe
+*.o
+*.so
+*.pyc
+
+# OS generated files #
+######################
+.DS_Store
+.DS_Store?
+._*
+.Spotlight-V100
+.Trashes
+ehthumbs.db
+Thumbs.db
+
+# Output Files #
+################
+DedekindLattice/GeneratedDedekindLattices/*
+!DedekindLattice/GeneratedDedekindLattices/preserve.txt
+
+# Experimental Files #
+######################
+**/.noworkflow/**
\ No newline at end of file
diff --git a/src/solamanDedekind/Dedekind/Algorithms/BottomUp/BottomUp.py b/src/solamanDedekind/Dedekind/Algorithms/BottomUp/BottomUp.py
new file mode 100644
index 0000000..0e0fc61
--- /dev/null
+++ b/src/solamanDedekind/Dedekind/Algorithms/BottomUp/BottomUp.py
@@ -0,0 +1,65 @@
+'''
+Created on May 4, 2015
+
+@author: Solaman
+'''
+from sets import ImmutableSet
+
+def computeAllMIS(setDescription, constraints):
+ '''
+ Finds the Minimum Inconsistent Subsets for the given constraints and set Description
+ using a bottom up approach (where bottom is empty set). We check the powerset of constraints, and rule out possible
+ MIS based on whether or not a given set is consistent.
+ NOTE: There are probably far more optimal implementations of such an algorithm. However,
+ for the sake of comparison, we are only interested in the number of times
+ 'isConsistent' is called and so we refrain from these optimizations.
+ @param setDescription- A set of rules linking several items together.
+ Think of this as boolean equation in Conjunctive Normal Form.
+ @param Constraints- a set of items we would like to include.
+ Think of this as a value assignment for the previous boolean equation.
+ '''
+ setsToCheck = generatePowerset(constraints)
+ misSet = set()
+ while len(setsToCheck) > 0:
+ checkNextSet(setsToCheck, misSet, setDescription)
+
+ return misSet
+
+
+def checkNextSet(setsToCheck, misSet, setDescription):
+ '''
+ Takes the smallest set of possible MIS and finds its parents. Then checks if it is consistent.
+ If it is, then it is an MIS and we should rule out all of its parents.
+ If it is not, then we rule it out as a possible MIS.
+ '''
+ parentSets = set()
+
+ chosenSet = setsToCheck.pop()
+ setsToCheck.add(chosenSet)
+ for aSet in setsToCheck:
+ if len(chosenSet) > len(aSet):
+ chosenSet = aSet
+
+ for possibleParent in setsToCheck:
+ if possibleParent.issuperset(chosenSet) and possibleParent != chosenSet:
+ parentSets.add(possibleParent)
+
+ if setDescription.isConsistent(chosenSet):
+ misSet.add(chosenSet)
+ for parentSet in parentSets:
+ setsToCheck.remove(parentSet)
+
+ setsToCheck.remove(chosenSet)
+
+
+
+def generatePowerset(theSet):
+ '''
+ Generates powerset of a given set.
+ Original code found at http://stackoverflow.com/questions/18826571/python-powerset-of-a-given-set-with-generators
+ '''
+ powerSet = set()
+ from itertools import chain, combinations
+ for subset in chain.from_iterable(combinations(theSet, r) for r in range(len(theSet)+1)):
+ powerSet.add( ImmutableSet(subset))
+ return powerSet
\ No newline at end of file
diff --git a/src/solamanDedekind/Dedekind/Algorithms/BottomUp/__init__.py b/src/solamanDedekind/Dedekind/Algorithms/BottomUp/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/src/solamanDedekind/Dedekind/Algorithms/Hitting_Set_Tree/HittingSetTree.py b/src/solamanDedekind/Dedekind/Algorithms/Hitting_Set_Tree/HittingSetTree.py
new file mode 100644
index 0000000..a9e7fc8
--- /dev/null
+++ b/src/solamanDedekind/Dedekind/Algorithms/Hitting_Set_Tree/HittingSetTree.py
@@ -0,0 +1,90 @@
+'''
+Created on Apr 23, 2015
+
+@author: Solaman
+'''
+from LogarithmicExtraction import computeSingleMIS
+import LogarithmicExtraction
+from sets import ImmutableSet
+
+def computeAllMIS(setDescription, constraints):
+ '''
+ Taken from 'A Hybrid Diagnosis Approach Combining Black-Box
+ and White-Box Reasoning'. This attempts to find all Minimal Subset of Constraints
+ that will be inconsistent for the given Set Description.
+ @param setDescription- A set of rules linking several items together.
+ Think of this as boolean equation in Conjunctive Normal Form.
+ @param Constraints- a set of items we would like to include.
+ Think of this as an assignment for the previous boolean equation.
+ '''
+ misSet= set()
+ currPath = ImmutableSet()
+ paths = set()
+ LogarithmicExtraction.newRun = True
+ computeAllMISHelper(setDescription, constraints,
+ misSet, currPath, paths)
+ return misSet
+
+def computeAllMISHelper(setDescription, constraints, misSet, currPath, paths):
+ #paths holds all previously visited paths of the hitting set tree
+ #currPath is the current. If any previous path is a subset of this one
+ #the we have already computed all MIS that would be found in the current path's subtree.
+ for path in paths:
+ if path in currPath:
+ return
+
+ #if the current set of constraints is consistent
+ #Then there cannot be anymore MIS in its subtree
+ #so we add the current path to the set of paths enumerated and return.
+ if not setDescription.isConsistent(constraints):
+ paths.add(currPath)
+ return
+ #In order to avoid redundant MIS computations
+ #We check the current set of MIS misSet
+ #If it is possible to find any of the already computed MIS in the current iteration
+ #(it does not share an element in the currPath) then we just use that MIS
+ #and continue down the tree
+ currentMIS = ImmutableSet()
+ for mis in misSet:
+ if len(mis.intersection(currPath)) == 0:
+ currentMIS = mis
+ break
+ #If not MIS matches the previous description, we will need to
+ #compute a new one.
+ if currentMIS == ImmutableSet():
+ currentMIS = computeSingleMIS(setDescription, constraints)
+ misSet.add(currentMIS)
+
+ #iterate through the children of the current path
+ for element in currentMIS:
+ childPath = currPath.union( set(element))
+ computeAllMISHelper(setDescription, constraints - ImmutableSet(element), misSet, childPath, paths)
+
+import sets
+def computeAllJust(setDescription, artSet, justSet, curpath, allpaths):
+ '''
+ Implementation of Hitting Set Tree found directly from EulerX.
+ A few modifications are made to ensure that it is compatible with this library's
+ implementation of logarathmic Extraction, otherwise everything else is the same
+ '''
+ for path in allpaths:
+ if path.issubset(curpath):
+ return
+ #must be 'not' to be consistent with this library's implementation.
+ #Without it, it does not compute the MIS properly
+ #i.e. it does not pass any of the algorithm tests.
+ if not setDescription.isConsistent(artSet):
+ allpaths.add(curpath)
+ return
+ j = sets.Set()
+ for s in justSet:
+ if len(s.intersection(curpath)) == 0:
+ j = s
+ if len(j) == 0:
+ j = computeSingleMIS(setDescription, artSet)
+ if len(j) != 0:
+ justSet.add(j)
+ for a in j:
+ tmpcur = curpath.union( set(a))
+ tmpart = artSet - ImmutableSet(a)
+ computeAllJust(setDescription, tmpart, justSet, tmpcur, allpaths)
diff --git a/src/solamanDedekind/Dedekind/Algorithms/Hitting_Set_Tree/LogarithmicExtraction.py b/src/solamanDedekind/Dedekind/Algorithms/Hitting_Set_Tree/LogarithmicExtraction.py
new file mode 100644
index 0000000..cc78c04
--- /dev/null
+++ b/src/solamanDedekind/Dedekind/Algorithms/Hitting_Set_Tree/LogarithmicExtraction.py
@@ -0,0 +1,60 @@
+'''
+Created on Apr 23, 2015
+
+@author: Solaman
+'''
+import random
+from sets import ImmutableSet
+
+#to avoid checking the empty set as an MIS more than once for
+#a given set description, an algorithm can set this to "True" before calling.
+#This way the Logarithmic Extraction will know that the check is not excessive
+newRun = False
+
+def computeSingleMIS(setDescription, constraints):
+ '''
+ Taken from 'A Hybrid Diagnosis Approach Combining Black-Box
+ and White-Box Reasoning'. This attempts to find the Minimal Subset of Constraints
+ that will be inconsistent for the given Set Description.
+ @param setDescription- A set of rules linking several items together.
+ Think of this as boolean equation in Conjunctive Normal Form.
+ @param Constraints- a set of items we would like to include.
+ Think of this as a value assignment for the previous boolean equation.
+ '''
+ potentialMIS = computeSingleMISHelper(setDescription, ImmutableSet(), constraints)
+
+ #The Euler Implentation does not correctly compute the MIS for a set description
+ #where everything is always inconsistent (an empty set is inconsistent)
+ #This makes sense, but this library also considers this set description,
+ #so we must check the empty configuration here.
+ global newRun
+ if newRun == True and len(potentialMIS) == 1 \
+ and setDescription.isConsistent(ImmutableSet()):
+ newRun = False
+ return ImmutableSet()
+ else:
+ newRun = False
+ return potentialMIS
+
+def computeSingleMISHelper(setDescription, currentConstraints, constraintsToAdd):
+ if len(constraintsToAdd) <= 1:
+ return constraintsToAdd
+
+ constraintsToAddLeft = ImmutableSet(random.sample(constraintsToAdd, len(constraintsToAdd)/2))
+ constraintsToAddRight = constraintsToAdd - constraintsToAddLeft
+
+ #If either subset unioned with the current constraints is inconsistent
+ #then an MIS exists in the subset of them
+ if setDescription.isConsistent( currentConstraints.union(constraintsToAddLeft) ):
+ return computeSingleMISHelper(setDescription, currentConstraints, constraintsToAddLeft)
+ if setDescription.isConsistent( currentConstraints.union(constraintsToAddRight)):
+ return computeSingleMISHelper(setDescription, currentConstraints, constraintsToAddRight)
+
+ #If both subsets unioned with the current constraints is consistent
+ #Then an MIS of the current constraints must use elements from both subsets.
+ #This will find such an MIS
+ potentialSolutionLeft = computeSingleMISHelper(setDescription,
+ currentConstraints.union(constraintsToAddRight), constraintsToAddLeft)
+ potentialSolutionRight = computeSingleMISHelper(setDescription,
+ currentConstraints.union(potentialSolutionLeft), constraintsToAddRight)
+ return potentialSolutionLeft.union(potentialSolutionRight)
\ No newline at end of file
diff --git a/src/solamanDedekind/Dedekind/Algorithms/Hitting_Set_Tree/__init__.py b/src/solamanDedekind/Dedekind/Algorithms/Hitting_Set_Tree/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/src/solamanDedekind/Dedekind/Algorithms/Random/Random.py b/src/solamanDedekind/Dedekind/Algorithms/Random/Random.py
new file mode 100644
index 0000000..fd0df3e
--- /dev/null
+++ b/src/solamanDedekind/Dedekind/Algorithms/Random/Random.py
@@ -0,0 +1,81 @@
+'''
+Created on May 4, 2015
+
+@author: Solaman
+'''
+from sets import ImmutableSet
+checkedMap = {}
+
+def computeAllMIS(setDescription, constraints):
+ '''
+ Finds the Minimum Inconsistent Subsets for the given constraints and set Description
+ using a randomized approach. We check the powerset of constraints, and rule out possible
+ MIS based on whether or not a given set is consistent.
+ NOTE: There are probably far more optimal implementations of such a random algorithm. However,
+ for the sake of comparison, we are only interested in the number of times
+ 'isConsistent' is called and so we refrain from these optimizations.
+ @param setDescription- A set of rules linking several items together.
+ Think of this as boolean equation in Conjunctive Normal Form.
+ @param Constraints- a set of items we would like to include.
+ Think of this as a value assignment for the previous boolean equation.
+ '''
+ global checkedMap
+ checkedMap = {}
+ setsToCheck = generatePowerset(constraints)
+ misSet = set()
+ while len(setsToCheck) > 0:
+ checkRandomSet(setsToCheck, misSet, setDescription)
+
+ return misSet
+
+
+def checkRandomSet(setsToCheck, misSet, setDescription):
+ '''
+ Takes a random set from the possible sets available and first finds its parent and children sets.
+ If it has none, then we know it is an MIS and add it to our solution.
+ If it has some, then we check if it is consistent and rule out the respective sets.
+ '''
+ global checkedMap
+ import random
+ chosenSet = random.sample(setsToCheck, 1)[0]
+ parentSets = set()
+ childrenSets = set()
+
+ for aSet in setsToCheck:
+ if chosenSet == aSet:
+ continue
+ elif aSet.issubset(chosenSet):
+ childrenSets.add(aSet)
+ elif aSet.issuperset(chosenSet):
+ parentSets.add(aSet)
+
+ if chosenSet in checkedMap:
+ if len(parentSets) + len(childrenSets) == 0:
+ setsToCheck.remove(chosenSet)
+ misSet.add(chosenSet)
+ return
+ else:
+ if setDescription.isConsistent(chosenSet):
+ #If consistent, then all parents are consistent and also not MIS
+ for aSet in parentSets:
+ setsToCheck.remove(aSet)
+ else:
+ #If inconsistent, then all children including this one are inconsistent and also not MIS
+ for aSet in childrenSets:
+ setsToCheck.remove(aSet)
+ setsToCheck.remove(chosenSet)
+ checkedMap[chosenSet] = 1
+
+
+
+
+def generatePowerset(theSet):
+ '''
+ Generates powerset of a given set.
+ Original code found at http://stackoverflow.com/questions/18826571/python-powerset-of-a-given-set-with-generators
+ '''
+ powerSet = set()
+ from itertools import chain, combinations
+ for subset in chain.from_iterable(combinations(theSet, r) for r in range(len(theSet)+1)):
+ powerSet.add( ImmutableSet(subset))
+ return powerSet
\ No newline at end of file
diff --git a/src/solamanDedekind/Dedekind/Algorithms/Random/__init__.py b/src/solamanDedekind/Dedekind/Algorithms/Random/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/src/solamanDedekind/Dedekind/Algorithms/TopDown/TopDown.py b/src/solamanDedekind/Dedekind/Algorithms/TopDown/TopDown.py
new file mode 100644
index 0000000..292362d
--- /dev/null
+++ b/src/solamanDedekind/Dedekind/Algorithms/TopDown/TopDown.py
@@ -0,0 +1,71 @@
+'''
+Created on May 4, 2015
+
+@author: Solaman
+'''
+from sets import ImmutableSet
+
+def computeAllMIS(setDescription, constraints):
+ '''
+ Finds the Minimum Inconsistent Subsets for the given constraints and set Description
+ using a top down approach (where top is full set). We check the powerset of constraints, and rule out possible
+ MIS based on whether or not a given set is consistent.
+ NOTE: There are probably far more optimal implementations of such an algorithm. However,
+ for the sake of comparison, we are only interested in the number of times
+ 'isConsistent' is called and so we refrain from these optimizations.
+ @param setDescription- A set of rules linking several items together.
+ Think of this as boolean equation in Conjunctive Normal Form.
+ @param Constraints- a set of items we would like to include.
+ Think of this as a value assignment for the previous boolean equation.
+ '''
+
+ setsToCheck = generatePowerset(constraints)
+ misSet = set()
+ findMISSets(setsToCheck, misSet, setDescription)
+
+ return misSet
+
+
+def findMISSets(setsToCheck, misSet, setDescription):
+ '''
+ Takes the largest set from potential MIS and find its children. Check if it is consistent
+ If it is, and it has children, then rule it out as an MIS, else add it to the MIS
+ If it is not, then rule it and its children out as MIS.
+ '''
+ setsToCheckList = list(setsToCheck)
+ setsToCheckList.sort(key = lambda self: len(self), reverse = True)
+
+ for currentSet in setsToCheckList:
+ if currentSet not in setsToCheck:
+ continue
+ childrenSets = set()
+ parentSets = set()
+ for aSet in setsToCheck:
+ if aSet.issubset(currentSet) and aSet != currentSet:
+ childrenSets.add(aSet)
+
+ if aSet.issuperset(currentSet) and aSet != currentSet:
+ parentSets.add(aSet)
+
+ if not setDescription.isConsistent(currentSet):
+ for child in childrenSets:
+ setsToCheck.remove(child)
+ setsToCheck.remove(currentSet)
+ else:
+ for parent in parentSets:
+ setsToCheck.remove(parent)
+
+ for currentSet in setsToCheckList:
+ if currentSet in setsToCheck:
+ misSet.add(currentSet)
+
+def generatePowerset(theSet):
+ '''
+ Generates powerset of a given set.
+ Original code found at http://stackoverflow.com/questions/18826571/python-powerset-of-a-given-set-with-generators
+ '''
+ powerSet = set()
+ from itertools import chain, combinations
+ for subset in chain.from_iterable(combinations(theSet, r) for r in range(len(theSet)+1)):
+ powerSet.add( ImmutableSet(subset))
+ return powerSet
\ No newline at end of file
diff --git a/src/solamanDedekind/Dedekind/Algorithms/TopDown/__init__.py b/src/solamanDedekind/Dedekind/Algorithms/TopDown/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/src/solamanDedekind/Dedekind/Algorithms/__init__.py b/src/solamanDedekind/Dedekind/Algorithms/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/src/solamanDedekind/Dedekind/Algorithms/analysis.py b/src/solamanDedekind/Dedekind/Algorithms/analysis.py
new file mode 100644
index 0000000..a01ce23
--- /dev/null
+++ b/src/solamanDedekind/Dedekind/Algorithms/analysis.py
@@ -0,0 +1,110 @@
+'''
+Created on Apr 28, 2015
+
+@author: Solaman
+
+Used to perform analysis on various
+algorithms for computing Minimum Inconsistent Subsets.
+'''
+from Algorithms.Hitting_Set_Tree import HittingSetTree
+from Algorithms.Random import Random
+from Algorithms.BottomUp import BottomUp
+from Algorithms.TopDown import TopDown
+
+from Model.DedekindLattice import DedekindLattice
+from Model.DedekindSetMapping import getFullSet, getConfAsSet
+
+algorithms = {}
+algorithms["hitting_set_tree"] = HittingSetTree.computeAllMIS
+algorithms["random"] = Random.computeAllMIS
+algorithms["bottom_up"] = BottomUp.computeAllMIS
+algorithms["top_down"] = TopDown.computeAllMIS
+isAcceptedOriginalFunction = None
+
+
+def runAnalysis(args):
+ if len(args) != 2:
+ print "must provide an algorithm and input size!"
+ return
+ algorithmKey = args[0]
+ if algorithmKey not in algorithms:
+ print "sorry! That algorithm was not recognized."\
+ , "\nHere are the options:"
+ for _algorithmKey in algorithms.keys():
+ print "\n\t", _algorithmKey
+ return
+ algorithm = algorithms[algorithmKey]
+
+ if int(args[1]) >5:
+ print "input larger than 5 is terribly slow, don't do it!"
+ return
+ inputSize = int(args[1])
+ lattice = DedekindLattice(inputSize)
+ callCounts = {}
+
+
+ while True:
+ currentNode = lattice.getNextNode()
+
+ if currentNode == None:
+ break
+
+ modifyisAccepted(currentNode)
+ fullConstraints = getFullSet(inputSize)
+ algorithm(currentNode, fullConstraints)
+
+ if currentNode.callCount not in callCounts:
+ callCounts[currentNode.callCount] = 1
+ else:
+ callCounts[currentNode.callCount] += 1
+
+
+ printStatistics(callCounts)
+
+def printStatistics(callCounts):
+ print "min: ", min ( callCounts.keys())
+ print "max: ", max( callCounts.keys())
+
+ totalAlgorithmCalls = sum( callCounts.values())
+ totalIsConsistentCalls = 0
+ for key in callCounts.keys():
+ totalIsConsistentCalls += key * callCounts[key]
+
+ print "mean: ", totalIsConsistentCalls/ totalAlgorithmCalls
+
+ mean = totalIsConsistentCalls/ totalAlgorithmCalls
+ variance = 0.0
+ for key in callCounts:
+ variance += (callCounts[key] - mean)**2
+ variance = float(variance)/float(totalAlgorithmCalls)
+
+ print "standard deviation: ", variance **.5
+ medianCount = totalAlgorithmCalls/2
+ for key in callCounts.keys():
+ medianCount -= callCounts[key]
+ if medianCount <= 0:
+ print "median: ", key
+ break
+
+ print "mode: ", max( callCounts.keys(), key= lambda x: callCounts[x])
+
+def modifyisAccepted(setDescription):
+ '''
+ In order to properly analyze an algorithm, we need to check how
+ many times it has to call "isAccepted" on the given setDescription.
+ We make this modification here.
+ '''
+
+ setDescription.callCount = 0
+ setDescription.tries = []
+ import types
+ setDescription.isConsistent = types.MethodType(isConsistent, setDescription)
+
+
+
+
+def isConsistent(self, configuration):
+ from Model.DedekindNode import isConsistent as isConsistentOld
+ self.callCount += 1
+ return isConsistentOld(self, configuration)
+
diff --git a/src/solamanDedekind/Dedekind/Controller/CommandOptions.py b/src/solamanDedekind/Dedekind/Controller/CommandOptions.py
new file mode 100644
index 0000000..2ac4f94
--- /dev/null
+++ b/src/solamanDedekind/Dedekind/Controller/CommandOptions.py
@@ -0,0 +1,85 @@
+'''
+Created on Mar 31, 2015
+
+@author: Solaman
+
+Used to store relevant information about command options.
+'''
+import sys
+
+class CommandOption(object):
+ '''
+ class for a particular console option
+ '''
+
+
+ def __init__(self, helpString, commandFunction):
+ '''
+ Constructor
+ '''
+ #Should describe what the option does
+ self.helpString = helpString
+
+ #Function used to execute this option
+ self.commandFunction = commandFunction
+
+
+class CommandOptions(object):
+ '''
+ Used to store and order console options
+ '''
+
+ def __init__(self):
+ #If the user wants to request a command by its command string, they can do so with this
+ self._commands = {}
+
+ self._commands["help"] = CommandOption("displays all available commands"\
+ + " along with a description for each", self.printCommandOptions)
+
+ def addCommand(self, commandString, helpString, commandFunction):
+ '''
+ Adds a command option. This option then can be requested by calling
+ 'selectOption'.
+ '''
+ if commandString == "help":
+ exceptMessage = "attempted to overwrite \"help\" option in ConsoleOptions. Don't do that plz."
+ raise Exception(exceptMessage)
+
+ if not callable(commandFunction):
+ exceptMessage = "Command function must be a function! (must be callable)"
+ raise Exception(exceptMessage)
+
+ self._commands[commandString] = CommandOption(helpString, commandFunction)
+
+ def printCommandOptions(self):
+ '''
+ Prints all commands available along with the help string associated with each
+ respective command.
+ '''
+ print "-----"
+ print "Available Commands"
+ print "-----"
+ for command in self._commands.iterkeys():
+ print command, ": ", self._commands[command].helpString
+ print "-----"
+
+
+ def selectCommand(self, userInput):
+ '''
+ Calls the function associated with a particular command.
+ '''
+ if len(userInput) == 0:
+ sys.stderr.write( "Must enter a command, type \"help\" to list all commands\n" )
+ return
+
+ command = userInput[0]
+ inputParams = userInput[1:]
+
+ if command not in self._commands:
+ sys.stderr.write("Command not recognized, type \"help\" to list all commands\n")
+ return
+
+ if len(inputParams) == 0:
+ self._commands[command].commandFunction()
+ else:
+ self._commands[command].commandFunction(inputParams)
\ No newline at end of file
diff --git a/src/solamanDedekind/Dedekind/Controller/Console.py b/src/solamanDedekind/Dedekind/Controller/Console.py
new file mode 100644
index 0000000..8acf541
--- /dev/null
+++ b/src/solamanDedekind/Dedekind/Controller/Console.py
@@ -0,0 +1,33 @@
+'''
+Created on Mar 31, 2015
+
+@author: Solaman
+This is the main interface with the application if the user chooses to use
+it as a console application.
+'''
+import os
+
+class Console(object):
+
+ def __init__(self, commandOptions):
+ self.commandOptions = commandOptions
+
+ self.commandOptions.addCommand("exit", "exits the console.", self.endConsole)
+
+ self.shouldContinue = True
+
+
+ def selectCommand(self, userInput):
+ self.commandOptions.selectCommand(userInput)
+
+ def endConsole(self):
+ print "Bye!"
+ self.shouldContinue = False
+
+def run(commandOptions):
+ console = Console(commandOptions)
+ print "hello! To list commands, type \"help\""
+ while console.shouldContinue:
+ userInput = raw_input("enter command: ")
+ userInput = userInput.split(" ")
+ console.selectCommand(userInput)
\ No newline at end of file
diff --git a/src/solamanDedekind/Dedekind/Controller/__init__.py b/src/solamanDedekind/Dedekind/Controller/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/src/solamanDedekind/Dedekind/Dedekind.py b/src/solamanDedekind/Dedekind/Dedekind.py
new file mode 100644
index 0000000..b42fdbd
--- /dev/null
+++ b/src/solamanDedekind/Dedekind/Dedekind.py
@@ -0,0 +1,37 @@
+'''
+Created on Apr 1, 2015
+
+@author: Solaman
+'''
+from Controller.CommandOptions import CommandOptions
+from Controller import Console
+from Model.DedekindLattice import getDedekindNumber, generateDotFiles
+
+import sys
+from Algorithms.analysis import runAnalysis
+
+def runConsole():
+ global standardCommands
+
+ del standardCommands._commands["console"]
+ Console.run(standardCommands)
+
+standardCommands = CommandOptions()
+
+standardCommands.addCommand("getNumber", "Finds Dedekind Number for a given input size."\
+ + "\n\tInput:" + " function input size", getDedekindNumber)
+
+standardCommands.addCommand("dotFiles", "Generates dot files of all monotone boolean functions"\
+ + " for a given input size." + "\n\tInput:" + " function input size", generateDotFiles)
+
+standardCommands.addCommand("analyze", "Runs a System-Diagnostic algorithm"\
+ + " and perform analysis on varying MBF's" +"\n\tInput: algorithm name, input size", runAnalysis )
+standardCommands.addCommand("console", "Run the program as a console.", runConsole)
+
+
+def main():
+ userInput = sys.argv[1:]
+ standardCommands.selectCommand(userInput)
+
+if __name__ == '__main__':
+ main()
\ No newline at end of file
diff --git a/src/solamanDedekind/Dedekind/DedekindAnalysis.py b/src/solamanDedekind/Dedekind/DedekindAnalysis.py
new file mode 100644
index 0000000..3baa304
--- /dev/null
+++ b/src/solamanDedekind/Dedekind/DedekindAnalysis.py
@@ -0,0 +1,22 @@
+'''
+Created on Mar 13, 2015
+
+@author: Solaman
+'''
+import cProfile
+from Model.DedekindNode import DedekindNode
+from Model.DedekindLattice import DedekindLattice
+
+node = DedekindNode(4, [15])
+lattice = DedekindLattice(5, lean = True)
+
+def analyzeDedekindNode():
+ cProfile.run("node.generatePossibleConfigurations()")
+
+def analyzeFindUniqueFunctions():
+ cProfile.run("lattice.getDedekindNumber()")
+
+if __name__ == '__main__':
+ #analyzeDedekindNode()
+ #analyzeDedekindLattice()
+ analyzeFindUniqueFunctions()
\ No newline at end of file
diff --git a/src/solamanDedekind/Dedekind/GeneratedDedekindLattices/preserve.txt b/src/solamanDedekind/Dedekind/GeneratedDedekindLattices/preserve.txt
new file mode 100644
index 0000000..13497a6
--- /dev/null
+++ b/src/solamanDedekind/Dedekind/GeneratedDedekindLattices/preserve.txt
@@ -0,0 +1,6 @@
+March 3rd, 2015: GeneratedDedekindLattices will not be included in a git commit
+unless a file is included. This is that file.
+
+Note: We could have the application generate this folder as needed, but I believe
+that because the folder's existence isn't tied to a run of the application
+("dedekind world 5" would be a folder that is tied) the user might expect the folder to already exist.
\ No newline at end of file
diff --git a/src/solamanDedekind/Dedekind/Model/DedekindLattice.py b/src/solamanDedekind/Dedekind/Model/DedekindLattice.py
new file mode 100644
index 0000000..3d53f7c
--- /dev/null
+++ b/src/solamanDedekind/Dedekind/Model/DedekindLattice.py
@@ -0,0 +1,203 @@
+'''
+Created on Feb 26, 2015
+
+@author: Solaman
+'''
+from DedekindNode import DedekindNode
+from Model.LevelPermutor import LevelPermutor
+from DedekindNode import getIndex
+
+class LatticeFiller(object):
+ '''
+ Constructed when the Lattice is constructed if the user wants to fill in the
+ lattice with each Monotone Boolean Function individually.
+ This class provides a level of abstraction from the inner workings of the module.
+ '''
+ def __init__(self, lattice):
+ self.lattice = lattice
+ self.nodeList = []
+ self.nodeList.append(lattice.emptyFunction)
+ self.nodeList.append(lattice.baseFunction)
+ self.wasFilled = False
+
+ def getNextNode(self):
+ '''
+ Returns the most recently added node to the queue
+ if it is not empty.
+ '''
+ if self.nodeList == []:
+ return None
+ node = self.nodeList.pop()
+ children = node.generateChildren()
+ for child in children:
+ self.nodeList.append(child)
+ self.lattice.lattice[getIndex(child)] = child
+ return node
+
+ def fillLattice(self):
+ while self.getNextNode() != None:
+ continue
+
+ def getDedekindNumber(self):
+ if self.wasFilled == False:
+ self.fillLattice()
+ self.wasFilled = True
+ return len(self.lattice.lattice.values())
+
+class LatticeFillerUnique(object):
+ '''
+ Constructed when the Lattice is constructed if the user would like to fill in
+ the lattice with Monotone Boolean Functions that are equivalent by level.
+ This class provides a level of abstraction from the inner workings of the module.
+ '''
+ def __init__(self, lattice, lean= False):
+ from Model.DedekindNodeLean import DedekindNodeLean
+ if lean == True:
+ lattice.baseFunction = DedekindNodeLean(lattice.baseFunction.inputSize,\
+ lattice.baseFunction.acceptedConfigurations[-1])
+
+ self.lattice = lattice
+ self.nodeList = []
+ lattice.emptyFunction.isVisited = False
+ lattice.emptyFunction.parent = None
+ self.nodeList.append(lattice.emptyFunction)
+ lattice.baseFunction.isVisited = False
+ lattice.baseFunction.parent = None
+ self.nodeList.append(lattice.baseFunction)
+ self.levelPermutor = LevelPermutor(lattice.inputSize)
+ self.mileMarker = 10000
+
+ self.wasFilled = False
+
+ def getNextNode(self):
+ #We don't need to compute functions that are isomorphisms of each other.
+ #We store each function by there level, and then counts by the number of possible children for the function
+ #It is proven that functions by level that have the same number of possible children are isomorphisms
+ #{"level" : {"isomorphismCount": count, "children" : children } }
+ if self.nodeList == []:
+ return None
+ node = self.nodeList.pop()
+ if node.isVisited == True:
+ if node.parent == None:
+ return node
+ else:
+ node.parent.childrenCount += node.childrenCount
+ if node.parent.childrenCount >= self.mileMarker:
+ print "marker: ", self.mileMarker
+ self.mileMarker = node.parent.childrenCount * 2
+ return self.getNextNode()
+
+ if self.getKey(node) in self.levelPermutor:
+ node.parent.childrenCount += self.levelPermutor[self.getKey(node)].childrenCount
+ return self.getNextNode()
+ else:
+ self.lattice.lattice[ getIndex(node.getAcceptedConfigurationsAsList()) ] = node
+ node.childrenCount = 1
+ self.levelPermutor[node.getLastLevel()] = node
+ children = node.generateChildren()
+ node.isVisited = True
+ self.nodeList.append(node)
+ for child in children:
+ child.parent = node
+ child.isVisited = False
+ self.nodeList.append(child)
+ return node
+
+ def getKey(self, node):
+ if hasattr(node, "key"):
+ return node.key
+ else:
+ node.key = getIndex(node.getLastLevel())
+ return node.key
+
+ def fillLattice(self):
+ while self.getNextNode() != None:
+ continue
+
+ def getDedekindNumber(self):
+ if self.wasFilled == False:
+ self.fillLattice()
+ self.wasFilled = True
+ return self.lattice.baseFunction.childrenCount + 1
+
+class DedekindLattice(object):
+ '''
+ We aim to generate the Dedekind Lattices using this class. Namely,
+ We want to generate all monotone boolean functions given an n input size.
+ Currently, we will aim to only generate the lattices in working memory.
+ Future implementations will hopefully be able to dynamically
+ Generate a node of a given Lattice.
+ '''
+
+ def __init__(self, inputSize, generateUnique = True, lean = False):
+ '''
+ Constructor. For now, we will store each monotone boolean function
+ as an object. Future implementations will store them as a single bit
+ for lean memory usage
+ '''
+ if inputSize < 0:
+ raise Exception("Input size must be greater than or equal to 0")
+ self.lattice = {}
+
+ #bit mask refers to the possible bit values
+ #of a given configuration. E.G. boolean functions with 4 inputs
+ #Will have a bit mask of 0xF
+ self.bitMask = 2**(inputSize) - 1
+ self.inputSize = inputSize
+
+ self.emptyFunction = DedekindNode(self.inputSize, [])
+ self.lattice[ getIndex(self.emptyFunction)] = self.emptyFunction
+
+ self.baseFunction = DedekindNode(self.inputSize, [self.bitMask])
+ self.lattice[ getIndex(self.baseFunction)] = self.baseFunction
+
+ if generateUnique:
+ self.latticeFiller = LatticeFillerUnique(self, lean)
+
+ else:
+ self.latticeFiller = LatticeFiller(self)
+
+ def getDedekindNumber(self):
+ return self.latticeFiller.getDedekindNumber()
+
+ def getNextNode(self):
+ return self.latticeFiller.getNextNode()
+
+
+ def generateDotFiles(self):
+ '''
+
+ '''
+ import os
+ directoryName = os.path.join("GeneratedDedekindLattices", str(self.inputSize) + "_DedekindLattice")
+ if not os.path.exists(directoryName):
+ os.mkdir(directoryName)
+ updateTime = self.monotoneCount/10
+ generatedFiles = 0
+ for function in self.lattice.itervalues():
+ function.writeToDotFile(directoryName)
+ generatedFiles += 1
+ if generatedFiles % updateTime == 0:
+ print generatedFiles, " written so far"
+ print "Done"
+
+
+def getDedekindNumber(userInput):
+ '''
+ Constructs the Dedekind Lattice for the given input size and
+ returns the dedekind number associated with that input.
+ Values that return within a minute are currently n <= 5.
+ '''
+ inputSize = int(userInput[0])
+ dedekindLattice = DedekindLattice(inputSize, lean =True)
+ print dedekindLattice.getDedekindNumber()
+
+def generateDotFiles(userInput):
+ '''
+ Constructs the Dedekind Lattice for the given input size and
+ generates the dot files for each monotone boolean function with the given input size.
+ '''
+ inputSize = int(userInput[0])
+ dedekindLattice = DedekindLattice(inputSize)
+ dedekindLattice.fillLattice()
+ dedekindLattice.generateDotFiles()
\ No newline at end of file
diff --git a/src/solamanDedekind/Dedekind/Model/DedekindNode.py b/src/solamanDedekind/Dedekind/Model/DedekindNode.py
new file mode 100644
index 0000000..261734d
--- /dev/null
+++ b/src/solamanDedekind/Dedekind/Model/DedekindNode.py
@@ -0,0 +1,290 @@
+'''
+Created on Mar 3, 2015
+
+@author: Solaman
+'''
+from itertools import combinations as genCombinations
+import os
+from collections import Iterable
+from DedekindNodeIter import DedekindNodeIter
+from DedekindSetMapping import getConfAsInt
+
+#Used for Dot file generation
+#Once a node is written to a dot file
+#A full Node is created to help
+fullNodes = {}
+
+#Used for Dot file generation
+#Once a node is written to a dot file
+#labels for each configuration are made
+configurationLabelss = {}
+
+#Used for Dot file Generation
+#Once a node is written to a dot file
+#edges between configurations are written as dot edges.
+dotEdgess = {}
+
+#To avoid cost of calculating configuration levels continuously,
+#We will calculate the levels from the very beginning.
+configurationLevelss = {}
+
+class DedekindNode(Iterable):
+ '''
+ A boolean function. It is up to the user to ensure that it is monotone.
+ '''
+
+
+ def __init__(self, inputSize, acceptedConfigurations, nodeToCopy = None):
+ '''
+ Each function has a set of accepted configurations. To aid in the book keeping of this algorithm,
+ each accepted configuration is stored by "level", namely, how many of the given inputs must be "off"
+ in the configuration. so if the configuration 0b0001 is accepted and the bitmask is 0b1111,
+ then the configuration would be stored at the 3rd level.
+ For rudimentary checking, the accepted configurations are added by descending level.
+ '''
+ self.childrenSize = 0
+ self.inputSize = inputSize
+ self.bitMask = self.bitMask = 2**(inputSize) - 1
+
+
+ if nodeToCopy != None:
+ temp = nodeToCopy.getAcceptedConfigurationsAsList()
+ temp.extend(acceptedConfigurations)
+ acceptedConfigurations = temp
+ self.acceptedConfigurations = []
+
+ for acceptedConfiguration in acceptedConfigurations:
+ level = getConfigurationLevel(self.inputSize, acceptedConfiguration)
+ if level > len( self.acceptedConfigurations):
+ exceptionMessage = "Cannot add configurations beyond the highest level + 1" \
+ + "\n attempted level: " + str(level) \
+ +"\n level limit: " + str(len(self.acceptedConfigurations))
+ raise Exception( exceptionMessage)
+
+ if level == len(self.acceptedConfigurations):
+ self.acceptedConfigurations.append( [acceptedConfiguration])
+ else:
+ self.acceptedConfigurations[level].append( acceptedConfiguration)
+
+ self.index = -1
+
+
+ def getAcceptedConfigurationsAsList(self):
+ acceptedConfigurations = []
+ for level in self.acceptedConfigurations:
+ acceptedConfigurations.extend(level)
+
+ return acceptedConfigurations
+
+ def isConsistent(self, configuration):
+ '''
+ Checks if a configuration would be accepted or not.
+ '''
+ return isConsistent(self, configuration)
+
+ def _generatePossibleConfigurations(self):
+ '''
+ Generates possible configurations to add to the function such that the new functions would be monotone
+ (given that this function is monotone). We observe that this can be done by level combinations.
+ E.G. if the input size is 4, and the last level of the function is 1->[0b1011, 0b0111], then the children
+ of the node in the lattice can have the level 2 configuration [0b0011].
+ (if any other, then it would not be monotone).
+ '''
+ possibleConfigurations = []
+ newMaxLevel = len(self.acceptedConfigurations)
+
+ #current max configuration level is [self.bitMask]
+ if newMaxLevel == 1:
+ possibleConfigurations = getLevelOneConfigurations(self.inputSize)
+
+ #Entire Dedekind Node Lattice is filled, can add [0] as an accepted configuration
+ elif newMaxLevel == self.inputSize \
+ and len(self.acceptedConfigurations[-1]) == self.inputSize:
+ possibleConfigurations = [0]
+ #current max configuration level is [] (none are accepted)
+ elif newMaxLevel == 0:
+ return []
+ elif newMaxLevel< self.inputSize:
+ combinations = genCombinations(self.acceptedConfigurations[-1], newMaxLevel)
+ possibleConfigurations = []
+ for combination in combinations:
+ possibleConfiguration = self.bitMask
+ for configuration in combination:
+ possibleConfiguration &= configuration
+ if getConfigurationLevel(self.inputSize, possibleConfiguration) == newMaxLevel:
+ possibleConfigurations.append(possibleConfiguration)
+ return possibleConfigurations
+
+ return possibleConfigurations
+
+
+ def generateChildren(self):
+ '''
+ Generates all Dedekind Nodes that would be considered the children of this
+ DedekindNode
+ '''
+ children = []
+ possibleConfigurations = self._generatePossibleConfigurations()
+
+ for numberOfConfigurations in range(1, len(possibleConfigurations) + 1):
+ combinations = genCombinations(possibleConfigurations, numberOfConfigurations)
+ for combination in combinations:
+ children.append( DedekindNode(self.inputSize, combination, self))
+
+ return children
+
+ def __iter__(self):
+ '''
+ Implemented this with good design in mind, however
+ If you want something fast, it is better to use
+ getAcceptedConfigurationsAsList
+ '''
+ return DedekindNodeIter(self)
+
+ def getLastLevel(self):
+ if len(self.acceptedConfigurations) > 0:
+ return self.acceptedConfigurations[-1]
+ else:
+ return []
+
+ def writeToDotFile(self, writeLocation):
+ global fullNodes, configurationLabelss, dotEdgess
+
+ dotFileName = os.path.join(writeLocation, "n_" + str(self.inputSize)\
+ + "." + "world_" + str(getIndex(self.getAcceptedConfigurationsAsList()))\
+ + ".dot")
+ dotFile = open( dotFileName, "w")
+ dotFile.write("""digraph{
+ rankdir=BT
+ node[shape=circle, style=filled, label=""]
+ edge[dir=none]\n""")
+
+ initDotVariables(self.inputSize)
+ fullNode = fullNodes[self.inputSize]
+ configurationLabels = configurationLabelss[self.inputSize]
+ dotEdges = dotEdgess[self.inputSize]
+
+ #configurationList = self.getAcceptedConfigurationsAsList()
+ for configuration in fullNode:
+ if configuration in self:
+ dotFile.write( configurationLabels[configuration] +" [ color = green, "\
+ + "label = \""+ configurationLabels[configuration] + "\"]\n")
+ else:
+ dotFile.write( configurationLabels[configuration] +" [ color = red, "\
+ + "label = \""+ configurationLabels[configuration] + "\"]\n")
+
+ dotFile.write(dotEdges)
+ dotFile.write("}")
+
+ dotFile.close()
+
+def isConsistent(node, configuration):
+ '''
+ Checks if a configuration would be deemed "inconsistent".
+ This is confusing! The DedekindNode represents a faulty system, and the
+ "accepted configurations" represent sets such that, if you deemed the given
+ components (represented by bits) as "faulty" and all others as safe, you would explain
+ erroneous output.
+ '''
+ from sets import ImmutableSet
+ if isinstance(configuration, ImmutableSet):
+ configuration = getConfAsInt(configuration, node.inputSize)
+ if (getIndex(node.getAcceptedConfigurationsAsList()) & 1 << configuration ) == 0:
+ return False
+ else:
+ return True
+
+def getFullNode(inputSize):
+ global fullNodes
+ if inputSize not in fullNodes:
+ bitMask = (1< " \
+ + configurationLabels[configuration] + "\n"
+ dotEdges += edgeString
+
+ fullNodes[inputSize] = fullNode
+ configurationLabelss[inputSize] = configurationLabels
+ dotEdgess[inputSize] = dotEdges
+
+def getIndex(configurationList):
+ '''
+ If we treat each configuration as its own integer value, we can combine each value into an integer
+ of size 2**inputSize bits. E.G. if the input size is 4, then each configuration has a value between 0-15.
+ So an integer of 16 bits, where each bit is for each configuration, will represent the function
+ and its accepted configurations. Since this value is unique, we can also use it as an index for the function
+ '''
+ index = 0
+ for configuration in configurationList:
+ index |= (1 << configuration)
+
+ return index
+
+def getConfigurationLevel(inputSize, configuration):
+ global configurationLevelss
+ if inputSize not in configurationLevelss:
+ bitMask = (1<= len(self.dedekindNode.acceptedConfigurations):
+ raise StopIteration
+
+ result = self.dedekindNode.acceptedConfigurations[self.levelIndex][self.configIndex]
+ self.configIndex += 1
+
+ if self.configIndex >= len(self.dedekindNode.acceptedConfigurations[self.levelIndex]):
+ self.configIndex = 0
+ self.levelIndex += 1
+
+ return result
+
\ No newline at end of file
diff --git a/src/solamanDedekind/Dedekind/Model/DedekindNodeLean.py b/src/solamanDedekind/Dedekind/Model/DedekindNodeLean.py
new file mode 100644
index 0000000..4f756a9
--- /dev/null
+++ b/src/solamanDedekind/Dedekind/Model/DedekindNodeLean.py
@@ -0,0 +1,133 @@
+'''
+Created on Apr 30, 2015
+
+@author: Solaman
+'''
+from itertools import combinations as genCombinations
+from DedekindSetMapping import getConfAsInt
+
+#To avoid cost of calculating configuration levels continuously,
+#We will calculate the levels from the very beginning.
+configurationLevelss = {}
+
+class DedekindNodeLean(object):
+ '''
+ Attempt to speed up generation of nodes. For computing the Dedekind number,
+ we only need the "highest" level of configurations, so we attempt
+ to reduce overhead by keeping only this level.
+ '''
+
+ def __init__(self, inputSize, acceptedConfigurations):
+ '''
+ Each function has a set of accepted configurations. To aid in the book keeping of this algorithm,
+ each accepted configuration is stored by "level", namely, how many of the given inputs must be "off"
+ in the configuration. so if the configuration 0b0001 is accepted and the bitmask is 0b1111,
+ then the configuration would be stored at the 3rd level.
+ For rudimentary checking, the accepted configurations are added by descending level.
+ '''
+ self.childrenSize = 0
+ self.inputSize = inputSize
+ self.bitMask = self.bitMask = 2**(inputSize) - 1
+ self.acceptedConfigurations = acceptedConfigurations
+ self.level = -1
+ if acceptedConfigurations != []:
+ self.level = getConfigurationLevel(inputSize, acceptedConfigurations[0])
+
+ self.index = -1
+
+ def _generatePossibleConfigurations(self):
+ '''
+ Generates possible configurations to add to the function such that the new functions would be monotone
+ (given that this function is monotone). We observe that this can be done by level combinations.
+ E.G. if the input size is 4, and the last level of the function is 1->[0b1011, 0b0111], then the children
+ of the node in the lattice can have the level 2 configuration [0b0011].
+ (if any other, then it would not be monotone).
+ '''
+ possibleConfigurations = []
+
+ #current max configuration level is [self.bitMask]
+ if self.level == 0:
+ possibleConfigurations = getLevelOneConfigurations(self.inputSize)
+
+ #Entire Dedekind Node Lattice is filled, can add [0] as an accepted configuration
+ elif self.level == self.inputSize - 1 \
+ and len(self.acceptedConfigurations) == self.inputSize:
+ possibleConfigurations = [0]
+ #current max configuration level is [] (none are accepted)
+ elif self.level == -1:
+ return []
+ elif self.level< self.inputSize - 1:
+ combinations = genCombinations(self.acceptedConfigurations, self.level + 1)
+ possibleConfigurations = []
+ for combination in combinations:
+ possibleConfiguration = self.bitMask
+ for configuration in combination:
+ possibleConfiguration &= configuration
+ if getConfigurationLevel(self.inputSize, possibleConfiguration) == self.level + 1:
+ possibleConfigurations.append(possibleConfiguration)
+ return possibleConfigurations
+
+ return possibleConfigurations
+
+
+ def generateChildren(self):
+ '''
+ Generates all Dedekind Nodes that would be considered the children of this
+ DedekindNode
+ '''
+ children = []
+ possibleConfigurations = self._generatePossibleConfigurations()
+
+ for numberOfConfigurations in range(1, len(possibleConfigurations) + 1):
+ combinations = genCombinations(possibleConfigurations, numberOfConfigurations)
+ for combination in combinations:
+ children.append( DedekindNodeLean(self.inputSize, combination))
+
+ return children
+
+ def getLastLevel(self):
+ return self.acceptedConfigurations
+
+ def getAcceptedConfigurationsAsList(self):
+ return self.acceptedConfigurations
+
+def getConfigurationLevel(inputSize, configuration):
+ global configurationLevelss
+ if inputSize not in configurationLevelss:
+ bitMask = (1< 0111
+1111 -> 1011
+1111 -> 1101
+1111 -> 1110
+0111 -> 0011
+1011 -> 0011
+0111 -> 0101
+1101 -> 0101
+0111 -> 0110
+1110 -> 0110
+1011 -> 1001
+1101 -> 1001
+1011 -> 1010
+1110 -> 1010
+1101 -> 1100
+1110 -> 1100
+0011 -> 0001
+0101 -> 0001
+1001 -> 0001
+0011 -> 0010
+0110 -> 0010
+1010 -> 0010
+0101 -> 0100
+0110 -> 0100
+1100 -> 0100
+1001 -> 1000
+1010 -> 1000
+1100 -> 1000
+0001 -> 0000
+0010 -> 0000
+0100 -> 0000
+1000 -> 0000
+}
\ No newline at end of file
diff --git a/src/solamanDedekind/Dedekind/Test/ModelTest/n_4.world_57344.pdf b/src/solamanDedekind/Dedekind/Test/ModelTest/n_4.world_57344.pdf
new file mode 100644
index 0000000..2b580d6
Binary files /dev/null and b/src/solamanDedekind/Dedekind/Test/ModelTest/n_4.world_57344.pdf differ
diff --git a/src/solamanDedekind/Dedekind/Test/ModelTest/writeToDotTest.dot b/src/solamanDedekind/Dedekind/Test/ModelTest/writeToDotTest.dot
new file mode 100644
index 0000000..b828616
--- /dev/null
+++ b/src/solamanDedekind/Dedekind/Test/ModelTest/writeToDotTest.dot
@@ -0,0 +1,53 @@
+digraph{
+ rankdir=BT
+ node[shape=circle, style=filled, label=""]
+ edge[dir=none]
+1111 [ color = green, label = "1111"]
+0111 [ color = red, label = "0111"]
+1011 [ color = red, label = "1011"]
+1101 [ color = green, label = "1101"]
+1110 [ color = green, label = "1110"]
+0011 [ color = red, label = "0011"]
+0101 [ color = red, label = "0101"]
+0110 [ color = red, label = "0110"]
+1001 [ color = red, label = "1001"]
+1010 [ color = red, label = "1010"]
+1100 [ color = red, label = "1100"]
+0001 [ color = red, label = "0001"]
+0010 [ color = red, label = "0010"]
+0100 [ color = red, label = "0100"]
+1000 [ color = red, label = "1000"]
+0000 [ color = red, label = "0000"]
+1111 -> 0111
+1111 -> 1011
+1111 -> 1101
+1111 -> 1110
+0111 -> 0011
+1011 -> 0011
+0111 -> 0101
+1101 -> 0101
+0111 -> 0110
+1110 -> 0110
+1011 -> 1001
+1101 -> 1001
+1011 -> 1010
+1110 -> 1010
+1101 -> 1100
+1110 -> 1100
+0011 -> 0001
+0101 -> 0001
+1001 -> 0001
+0011 -> 0010
+0110 -> 0010
+1010 -> 0010
+0101 -> 0100
+0110 -> 0100
+1100 -> 0100
+1001 -> 1000
+1010 -> 1000
+1100 -> 1000
+0001 -> 0000
+0010 -> 0000
+0100 -> 0000
+1000 -> 0000
+}
\ No newline at end of file
diff --git a/src/solamanDedekind/Dedekind/Test/__init__.py b/src/solamanDedekind/Dedekind/Test/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/src/solamanDedekind/Dedekind/TransitionError.py b/src/solamanDedekind/Dedekind/TransitionError.py
new file mode 100644
index 0000000..75be7a4
--- /dev/null
+++ b/src/solamanDedekind/Dedekind/TransitionError.py
@@ -0,0 +1,16 @@
+'''
+Created on Mar 2, 2015
+
+@author: Solaman
+'''
+class TransitionError(Exception):
+ '''
+ Raised when an operation attempts a state transition that is
+ not allowed.
+ '''
+
+ def __init__(self, msg):
+ self.msg = msg
+
+ def __str__(self):
+ return repr(self.value)
\ No newline at end of file
diff --git a/src/solamanDedekind/Dedekind/resources/__init__.py b/src/solamanDedekind/Dedekind/resources/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/src/solamanDedekind/Dedekind/resources/setValues.csv b/src/solamanDedekind/Dedekind/resources/setValues.csv
new file mode 100644
index 0000000..1b91aad
--- /dev/null
+++ b/src/solamanDedekind/Dedekind/resources/setValues.csv
@@ -0,0 +1 @@
+A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z
\ No newline at end of file
diff --git a/src/solamanDedekind/Explanation.pdf b/src/solamanDedekind/Explanation.pdf
new file mode 100644
index 0000000..afc2edf
Binary files /dev/null and b/src/solamanDedekind/Explanation.pdf differ
diff --git a/src/solamanDedekind/README.md b/src/solamanDedekind/README.md
new file mode 100644
index 0000000..7a6c8d8
--- /dev/null
+++ b/src/solamanDedekind/README.md
@@ -0,0 +1,17 @@
+# System-Diagnostic-Analysis
+How to run: python Dedekind.py
+type "-help" to see a list of all available commands
+This project is meant to be used either as a command line, console, or library.
+If you wish create the monotone boolean functions and .dot files for each, try
+running "python Dedekind.py" and use the command line or console.
+Else, all of these functions are available through Model.DedekindLattice.py . Which also have useful
+features for System Diagnostic Analysis:
+by instantiated an instance of DedekindLattice.py, you can call "getNextNode()" which will return
+a new monotone boolean function within the DedekindLattice. From here, you can then call "isConsistent", passing
+ in a configuration, to see if the function would accept it.
+ DO NOT BE CONFUSED BY computeALLMIS and the "isConsistent" function of DedekindNodes!
+ In System-Diagnostic-Analysis, when we ask for a minimal inconsistent subset, we ask for
+ the smallest set which explains the inconsistency of an input to an answer.
+ When we ask if a set "isConsistent" however, we are asking if the set explains the inConsistency
+ of the input to an answer (would assuming the set is erroneous make the answer consistent with the input?).
+