/** * * * <pre> * abstract LogicalBus * realizedBy -> PhysicalBus 1..* * [parent in this.realizes.ref] * * // Physical Connection Mediums * abstract PhysicalBus * realizes -> LogicalBus 1..* * [parent in this.realizedBy.ref] * * logBusA : LogicalBus * logBusB : LogicalBus * * physBusA : PhysicalBus ? * physBusB : PhysicalBus ? * physBusC : PhysicalBus ? * * [logBusA => physBusA] * [logBusA => physBusB] * [logBusA.realizedBy = (physBusA, physBusB)] * * [logBusB => physBusC] * [logBusB.realizedBy = (physBusC)] * </pre> */ @Test(timeout = 60000) public void testInverse() { AstModel m = newModel(); AstAbstractClafer logicalBus = m.addAbstract("LogicalBus"); AstAbstractClafer physicalBus = m.addAbstract("PhysicalBus"); AstConcreteClafer realizedBy = logicalBus.addChild("realizedBy").refToUnique(physicalBus).withCard(Many); AstConcreteClafer realizes = physicalBus.addChild("realizes").refToUnique(logicalBus).withCard(Many); realizedBy.addConstraint(in(joinParent($this()), joinRef(join(joinRef($this()), realizes)))); realizes.addConstraint(in(joinParent($this()), joinRef(join(joinRef($this()), realizedBy)))); AstConcreteClafer logBusA = m.addChild("logBusA").extending(logicalBus).withCard(Mandatory); AstConcreteClafer logBusB = m.addChild("logBusB").extending(logicalBus).withCard(Mandatory); AstConcreteClafer physBusA = m.addChild("physBusA").extending(physicalBus).withCard(Mandatory); AstConcreteClafer physBusB = m.addChild("physBusB").extending(physicalBus).withCard(Mandatory); AstConcreteClafer physBusC = m.addChild("physBusC").extending(physicalBus).withCard(Mandatory); m.addConstraint(implies(some(logBusA), some(physBusA))); m.addConstraint(implies(some(logBusA), some(physBusB))); m.addConstraint( equal( joinRef(join(global(logBusA), realizedBy)), union(global(physBusA), global(physBusB)))); m.addConstraint(implies(some(logBusB), some(physBusC))); m.addConstraint(equal(joinRef(join(global(logBusB), realizedBy)), global(physBusC))); ClaferSolver solver = ClaferCompiler.compile(m, Scope.defaultScope(10)); assertEquals(1, solver.allInstances().length); }
/** * * * <pre> * Person 2 * Likes -> Person * </pre> */ @Test(timeout = 60000) public void dontBreakSingleCircularRef() { AstModel model = newModel(); AstConcreteClafer person = model.addChild("Person").withCard(2, 2); AstConcreteClafer likes = person.addChild("Likes").refTo(person).withCard(Mandatory); ClaferSolver solver = ClaferCompiler.compile(model, Scope.defaultScope(2)); assertEquals(3, solver.allInstances().length); }
/** * * * <pre> * Patron 2 * Money ->> integer 1..2 * </pre> */ @Test(timeout = 60000) public void dontBreakUnrelatedRefs() { AstModel model = newModel(); AstConcreteClafer patron = model.addChild("Patron").withCard(2, 2); AstConcreteClafer money = patron.addChild("Money").refTo(IntType).withCard(1, 2); ClaferSolver solver = ClaferCompiler.compile(model, Scope.defaultScope(4).intLow(0).intHigh(1)); assertEquals(15, solver.allInstances().length); }
/** * * * <pre> * Patron 2 * Food 1..2 * Drink 1..2 * </pre> */ @Test(timeout = 60000) public void breakChildrenSwap() { AstModel model = newModel(); AstConcreteClafer patron = model.addChild("Patron").withCard(2, 2); AstConcreteClafer food = patron.addChild("Food").withCard(1, 2); AstConcreteClafer drink = patron.addChild("Drink").withCard(1, 2); ClaferSolver solver = ClaferCompiler.compile(model, Scope.defaultScope(3)); assertEquals(5, solver.allInstances().length); }
/** * * * <pre> * a : A * * setRefToA -> a 3 * multisetRefToA ->> a 3 * </pre> */ @Test(timeout = 60000) public void breakRefSwap() { AstModel model = newModel(); AstConcreteClafer a = model.addChild("a"); AstConcreteClafer setRefToA = model.addChild("setRefToA").refToUnique(a).withCard(3, 3); AstConcreteClafer multisetRefToA = model.addChild("multisetRefToA").refTo(a).withCard(3, 3); ClaferSolver solver = ClaferCompiler.compile(model, Scope.defaultScope(3)); assertEquals(3, solver.allInstances().length); }
/** * * * <pre> * abstract Eater * Food 1..2 * Drink 1..2 * Patron : Eater 2 * </pre> */ @Test(timeout = 60000) public void breakOnlyInheritedChildrenSwap() { AstModel model = newModel(); AstAbstractClafer eater = model.addAbstract("Eater"); AstConcreteClafer food = eater.addChild("Food").withCard(1, 2); AstConcreteClafer drink = eater.addChild("Drink").withCard(1, 2); AstConcreteClafer patron = model.addChild("Patron").extending(eater).withCard(2, 2); ClaferSolver solver = ClaferCompiler.compile(model, Scope.defaultScope(3)); assertEquals(5, solver.allInstances().length); }
/** * * * <pre> * A 3 * B ? * C -> A * D -> A * </pre> */ @Test(timeout = 60000) public void breakTargetRefWithChildren() { AstModel model = newModel(); AstConcreteClafer a = model.addChild("A").withCard(3, 3); AstConcreteClafer b = a.addChild("B").withCard(Optional); AstConcreteClafer c = model.addChild("C").refTo(a).withCard(Mandatory); AstConcreteClafer d = model.addChild("D").refTo(a).withCard(Mandatory); ClaferSolver solver = ClaferCompiler.compile(model, Scope.setScope(b, 1).defaultScope(3)); assertEquals(7, solver.allInstances().length); }
/** * * * <pre> * A ->> B 3 * B ->> A 3 * </pre> */ @Test(timeout = 60000) public void breakHalfPairCircularRef() { AstModel model = newModel(); AstConcreteClafer a = model.addChild("A").withCard(3, 3); AstConcreteClafer b = model.addChild("B").withCard(3, 3); a.refTo(b); b.refTo(a); ClaferSolver solver = ClaferCompiler.compile(model, Scope.defaultScope(3)); // Ideally, this should be 25 but the current strategy should have 54 instances. assertEquals(54, solver.allInstances().length); }
/** * * * <pre> * abstract A * a : A 4 * b -> A * c -> A * d -> a * e -> a * </pre> */ @Test(timeout = 60000) public void breakNonIsomorphicSourceIsomorphicTarget() { AstModel model = newModel(); AstAbstractClafer A = model.addAbstract("A"); AstConcreteClafer a = model.addChild("1").extending(A).withCard(4, 4); AstConcreteClafer b = model.addChild("b").refTo(A).withCard(Mandatory); AstConcreteClafer c = model.addChild("c").refTo(A).withCard(Mandatory); AstConcreteClafer d = model.addChild("d").refTo(a).withCard(Mandatory); AstConcreteClafer e = model.addChild("e").refTo(a).withCard(Mandatory); ClaferSolver solver = ClaferCompiler.compile(model, Scope.defaultScope(4)); assertEquals(15, solver.allInstances().length); }
/** * * * <pre> * abstract A * a : A * b : A * c : A * setRefToA -> A 3 * multisetRefToA ->> A 3 * </pre> */ @Test(timeout = 60000) public void breakAbstractRefSwapMultipleConcrete() { AstModel model = newModel(); AstAbstractClafer A = model.addAbstract("A"); AstConcreteClafer a = model.addChild("a").extending(A).withCard(Mandatory); AstConcreteClafer b = model.addChild("b").extending(A).withCard(Mandatory); AstConcreteClafer c = model.addChild("c").extending(A).withCard(Mandatory); AstConcreteClafer setRefToA = model.addChild("setRefToA").refToUnique(A).withCard(3, 3); AstConcreteClafer multisetRefToA = model.addChild("multisetRefToA").refTo(A).withCard(3, 3); ClaferSolver solver = ClaferCompiler.compile(model, Scope.defaultScope(3)); assertEquals(10, solver.allInstances().length); }
/** * * * <pre> * Patron 2 * Food + * Cheese * * </pre> */ @Test(timeout = 60000) public void breakGrandChildrenSwap() { /* * 19 nonisomorphic solutions: * Run "length $ nubBy isomorphic $ makeModel 3" with the Haskell code below. * * import Control.Monad * import Data.Functor * import Data.List * * data Model = Model {person1::Person, person2::Person} deriving (Eq, Ord, Show) * data Person = Person {foods::[Food]} deriving (Eq, Ord, Show) * data Food = Food {cheeses::[Cheese]} deriving (Eq, Ord, Show) * data Cheese = Cheese deriving (Eq, Ord, Show) * * people model = [person1 model, person2 model] * * class Iso a where * isomorphic :: a -> a -> Bool * * instance Iso Model where * isomorphic model1 model2 = * ((person1 model1 `isomorphic` person1 model2) && (person2 model1 `isomorphic` person2 model2)) || * ((person1 model1 `isomorphic` person2 model2) && (person2 model1 `isomorphic` person1 model2)) * * instance Iso Person where * isomorphic Person{foods = foods1} Person{foods = foods2} * | length foods1 == length foods2 = * or $ do * pfoods2 <- permutations foods2 * return $ and $ zipWith isomorphic foods1 pfoods2 * | otherwise = False * * instance Iso Food where * isomorphic food1 food2 = length (cheeses food1) == length (cheeses food2) * * instance Iso Cheese where * isomorphic _ _ = True * * numberOfFood = length . (>>= foods) . people * numberOfCheese = length . (>>= cheeses) . (>>= foods) . people * * printModel model = * printPerson =<< people model * where * printPerson person = "Person\n" ++ (printFood =<< foods person) * printFood food = " Food\n" ++ (printCheese =<< cheeses food) * printCheese _ = " Cheese\n" * * {- * - Person 2 * - Food + * - Cheese * * -} * makeModel scope = * do * p1 <- makePerson * p2 <- makePerson * let m = Model p1 p2 * guard $ numberOfFood m <= scope * guard $ numberOfCheese m <= scope * return m * where * makePerson = do * feed <- [1..scope] * Person <$> replicateM feed makeFood * makeFood = do * topping <- [0..scope] * return $ Food $ replicate topping Cheese */ AstModel model = newModel(); AstConcreteClafer patron = model.addChild("Patron").withCard(2, 2); AstConcreteClafer food = patron.addChild("Food").withCard(1); AstConcreteClafer cheese = food.addChild("Cheese").withCard(0); ClaferSolver solver = ClaferCompiler.compile(model, Scope.defaultScope(3)); assertEquals(19, solver.allInstances().length); }
/** * * * <pre> * abstract Service * cpu: integer * machine -> Machine // the -> means every service must be allocated to some machine * [ this in machine.services ] * * MailService : Service * [cpu = 8] * * SearchService : Service * [cpu = 7] * * CalendarService : Service * [cpu = 5] * * DriveService : Service * [cpu = 4] * * GroupsService : Service * [cpu = 5] * * abstract Machine * services -> Service* * [this.machine = parent] * total_cpu : integer = sum services.cpu * [total_cpu <= this.cpuLimit] * cpuLimit : integer * isFree : integer = (if (#this.services = 0) then 1 else 0) * * GoogleCA : Machine * [cpuLimit = 20] * * GoogleNY : Machine * [cpuLimit = 10] * * GoogleTX : Machine * [cpuLimit = 11] * * abstract Task * total_free : integer = sum Machine.isFree * * MyTask: Task * <<max MyTask.total_free>> * </pre> */ @Test(timeout = 60000) public void breakServices() { AstModel model = newModel(); AstAbstractClafer c0_Service = model.addAbstract("Service"); AstAbstractClafer c0_Machine = model.addAbstract("c0_Machine"); AstAbstractClafer c0_Task = model.addAbstract("c0_Task"); AstConcreteClafer c0_cpu = c0_Service.addChild("c0_cpu").withCard(1, 1); AstConcreteClafer c0_machine = c0_Service.addChild("c0_machine").withCard(1, 1); AstConcreteClafer c0_MailService = model.addChild("c0_MailService").withCard(1, 1).extending(c0_Service); AstConcreteClafer c0_SearchService = model.addChild("c0_SearchService").withCard(1, 1).extending(c0_Service); AstConcreteClafer c0_CalendarService = model.addChild("c0_CalendarService").withCard(1, 1).extending(c0_Service); AstConcreteClafer c0_DriveService = model.addChild("c0_DriveService").withCard(1, 1).extending(c0_Service); AstConcreteClafer c0_GroupsService = model.addChild("c0_GroupsService").withCard(1, 1).extending(c0_Service); AstConcreteClafer c0_services = c0_Machine.addChild("c0_services"); AstConcreteClafer c0_total_cpu = c0_Machine.addChild("c0_total_cpu").withCard(1, 1); AstConcreteClafer c0_cpuLimit = c0_Machine.addChild("c0_cpuLimit").withCard(1, 1); AstConcreteClafer c0_isFree = c0_Machine.addChild("c0_isFree").withCard(1, 1); AstConcreteClafer c0_GoogleCA = model.addChild("c0_GoogleCA").withCard(1, 1).extending(c0_Machine); AstConcreteClafer c0_GoogleNY = model.addChild("c0_GoogleNY").withCard(1, 1).extending(c0_Machine); AstConcreteClafer c0_GoogleTX = model.addChild("c0_GoogleTX").withCard(1, 1).extending(c0_Machine); AstConcreteClafer c0_total_free = c0_Task.addChild("c0_total_free").withCard(1, 1); AstConcreteClafer c0_MyTask = model.addChild("c0_MyTask").withCard(1, 1).extending(c0_Task); c0_cpu.refTo(IntType); c0_machine.refToUnique(c0_Machine); c0_services.refToUnique(c0_Service); c0_total_cpu.refTo(IntType); c0_cpuLimit.refTo(IntType); c0_isFree.refTo(IntType); c0_total_free.refTo(IntType); c0_Service.addConstraint( in($this(), joinRef(join(joinRef(join($this(), c0_machine)), c0_services)))); c0_MailService.addConstraint(equal(joinRef(join($this(), c0_cpu)), constant(8))); c0_SearchService.addConstraint(equal(joinRef(join($this(), c0_cpu)), constant(7))); c0_CalendarService.addConstraint(equal(joinRef(join($this(), c0_cpu)), constant(5))); c0_DriveService.addConstraint(equal(joinRef(join($this(), c0_cpu)), constant(4))); c0_GroupsService.addConstraint(equal(joinRef(join($this(), c0_cpu)), constant(5))); c0_Machine.addConstraint( equal( joinRef(join($this(), c0_total_cpu)), sum(join(joinRef(join($this(), c0_services)), c0_cpu)))); c0_Machine.addConstraint( lessThanEqual(joinRef(join($this(), c0_total_cpu)), joinRef(join($this(), c0_cpuLimit)))); c0_Machine.addConstraint( equal( joinRef(join($this(), c0_isFree)), ifThenElse( equal(card(join($this(), c0_services)), constant(0)), constant(1), constant(0)))); c0_services.addConstraint( equal(joinRef(join(joinRef($this()), c0_machine)), joinParent($this()))); c0_GoogleCA.addConstraint(equal(joinRef(join($this(), c0_cpuLimit)), constant(20))); c0_GoogleNY.addConstraint(equal(joinRef(join($this(), c0_cpuLimit)), constant(10))); c0_GoogleTX.addConstraint(equal(joinRef(join($this(), c0_cpuLimit)), constant(11))); c0_Task.addConstraint( equal(joinRef(join($this(), c0_total_free)), sum(join(global(c0_Machine), c0_isFree)))); ClaferOptimizer solver = ClaferCompiler.compile( model, Scope.setScope(c0_services, 5) .setScope(c0_Machine, 4) .setScope(c0_Service, 5) .setScope(c0_Task, 1) .setScope(c0_cpu, 5) .setScope(c0_cpuLimit, 4) .setScope(c0_isFree, 4) .setScope(c0_machine, 5) .setScope(c0_total_cpu, 4) .setScope(c0_total_free, 1) .intLow(-128) .intHigh(128), Objective.maximize(joinRef(join(global(c0_MyTask), c0_total_free)))); int count = 0; while (solver.find()) { assertArrayEquals(new int[] {1}, solver.optimalValues()); count++; } assertEquals(7, count); }