/* * ARX: Powerful Data Anonymization * Copyright 2012 - 2017 Fabian Prasser, Florian Kohlmayer and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.deidentifier.arx.algorithm; import org.deidentifier.arx.ARXConfiguration.ARXConfigurationInternal; import org.deidentifier.arx.ARXConfiguration.Monotonicity; import org.deidentifier.arx.algorithm.FLASHPhaseConfiguration.PhaseAnonymityProperty; import org.deidentifier.arx.framework.check.NodeChecker; import org.deidentifier.arx.framework.check.history.History.StorageStrategy; import org.deidentifier.arx.framework.lattice.DependentAction; import org.deidentifier.arx.framework.lattice.DependentAction.NodeActionConstant; import org.deidentifier.arx.framework.lattice.DependentAction.NodeActionInverse; import org.deidentifier.arx.framework.lattice.SolutionSpace; import org.deidentifier.arx.framework.lattice.Transformation; /** * This class provides a static method for instantiating the FLASH algorithm. * * @author Fabian Prasser * @author Florian Kohlmayer */ public class FLASHAlgorithm { /** * Creates a new instance of the FLASH algorithm. * * @param solutionSpace * @param checker * @param strategy * @return */ public static AbstractAlgorithm create(final SolutionSpace solutionSpace, final NodeChecker checker, final FLASHStrategy strategy) { // Init ARXConfigurationInternal config = checker.getConfiguration(); Monotonicity monotonicityOfUtility = config.getMonotonicityOfUtility(); Monotonicity monotonicityOfPrivacy = config.getMonotonicityOfPrivacy(); // ****************************** // CASE 1 // ****************************** if ((monotonicityOfPrivacy == Monotonicity.FULL) && (monotonicityOfUtility == Monotonicity.FULL)) { return createFullFull(solutionSpace, checker, strategy); } // ****************************** // CASE 2 // ****************************** if ((monotonicityOfPrivacy == Monotonicity.FULL) && (monotonicityOfUtility == Monotonicity.NONE)) { return createFullNone(solutionSpace, checker, strategy); } // ****************************** // CASE 3 // ****************************** if ((monotonicityOfPrivacy == Monotonicity.PARTIAL) && (monotonicityOfUtility == Monotonicity.FULL)) { return createPartialFull(solutionSpace, checker, strategy); } // ****************************** // CASE 4 // ****************************** if ((monotonicityOfPrivacy == Monotonicity.PARTIAL) && (monotonicityOfUtility == Monotonicity.NONE)) { return createPartialNone(solutionSpace, checker, strategy); } // ****************************** // CASE 5 // ****************************** if ((monotonicityOfPrivacy == Monotonicity.NONE) && (monotonicityOfUtility == Monotonicity.FULL)) { return createNoneFull(solutionSpace, checker, strategy); } // ****************************** // CASE 6 // ****************************** if ((monotonicityOfPrivacy == Monotonicity.NONE) && (monotonicityOfUtility == Monotonicity.NONE)) { return createNoneNone(solutionSpace, checker, strategy); } throw new IllegalStateException("Oops"); } /** * Semantics of method name: monotonicity of privacy + monotonicity of utility. * * @param solutionSpace * @param checker * @param strategy * @return */ private static AbstractAlgorithm createFullFull(final SolutionSpace solutionSpace, final NodeChecker checker, final FLASHStrategy strategy) { // We focus on the anonymity property PhaseAnonymityProperty anonymityProperty = PhaseAnonymityProperty.ANONYMITY; // Skip nodes for which the anonymity property is known DependentAction triggerSkip = new DependentAction() { @Override public boolean appliesTo(Transformation node) { return node.hasProperty(solutionSpace.getPropertyAnonymous()) || node.hasProperty(solutionSpace.getPropertyNotAnonymous()); } }; // We predictively tag the anonymity property DependentAction triggerTag = new DependentAction() { @Override public void action(Transformation node) { node.setProperty(solutionSpace.getPropertySuccessorsPruned()); } @Override public boolean appliesTo(Transformation node) { return node.hasProperty(solutionSpace.getPropertyAnonymous()); } }; // No evaluation DependentAction triggerEvaluate = new NodeActionConstant(false); DependentAction triggerCheck = new NodeActionInverse(triggerSkip); // Only one binary phase // Deactivate pruning due to lower bound as it increases number of checks needed FLASHConfiguration config = FLASHConfiguration.createBinaryPhaseConfiguration(new FLASHPhaseConfiguration(anonymityProperty, triggerTag, triggerCheck, triggerEvaluate, triggerSkip), StorageStrategy.NON_ANONYMOUS, false, true); return new FLASHAlgorithmImpl(solutionSpace, checker, strategy, config); } /** * Semantics of method name: monotonicity of privacy + monotonicity of utility. * * @param solutionSpace * @param checker * @param strategy * @return */ private static AbstractAlgorithm createFullNone(final SolutionSpace solutionSpace, final NodeChecker checker, final FLASHStrategy strategy) { /* ******************************* * BINARY PHASE * ******************************* */ // We focus on the anonymity property PhaseAnonymityProperty binaryAnonymityProperty = PhaseAnonymityProperty.ANONYMITY; // Skip nodes for which the anonymity property is known DependentAction binaryTriggerSkip = new DependentAction() { @Override public boolean appliesTo(Transformation node) { return node.hasProperty(solutionSpace.getPropertyAnonymous()) || node.hasProperty(solutionSpace.getPropertyNotAnonymous()); } }; // We predictively tag the anonymity property DependentAction binaryTriggerTag = new DependentAction() { @Override public void action(Transformation node) { // Empty by design } @Override public boolean appliesTo(Transformation node) { return false; // Empty trigger } }; // No evaluation DependentAction binaryTriggerCheck = new NodeActionInverse(binaryTriggerSkip); DependentAction binaryTriggerEvaluate = new NodeActionConstant(false); /* ******************************* * LINEAR PHASE * ******************************* */ // We focus on the anonymity property PhaseAnonymityProperty linearAnonymityProperty = PhaseAnonymityProperty.ANONYMITY; // We skip nodes which are not anonymous or which have already been visited during the second phase DependentAction linearTriggerSkip = new DependentAction() { @Override public boolean appliesTo(Transformation node) { return node.hasProperty(solutionSpace.getPropertyNotAnonymous()) || node.hasProperty(solutionSpace.getPropertyVisited()); } }; // We evaluate nodes which have not been skipped, if the metric is independent DependentAction linearTriggerEvaluate = new DependentAction() { @Override public boolean appliesTo(Transformation node) { return checker.getMetric().isIndependent() && !node.hasProperty(solutionSpace.getPropertyChecked()) && !node.hasProperty(solutionSpace.getPropertyNotAnonymous()); } }; // We check nodes which have not been skipped, if the metric is dependent DependentAction linearTriggerCheck = new DependentAction() { @Override public boolean appliesTo(Transformation node) { return !checker.getMetric().isIndependent() && !node.hasProperty(solutionSpace.getPropertyChecked()) && !node.hasProperty(solutionSpace.getPropertyNotAnonymous()); } }; // Mark nodes as already visited during the second phase DependentAction linearTriggerTag = new DependentAction() { @Override public void action(Transformation node) { node.setProperty(solutionSpace.getPropertyVisited()); } @Override public boolean appliesTo(Transformation node) { return true; } }; // Two interwoven phases FLASHConfiguration config = FLASHConfiguration.createTwoPhaseConfiguration(new FLASHPhaseConfiguration(binaryAnonymityProperty, binaryTriggerTag, binaryTriggerCheck, binaryTriggerEvaluate, binaryTriggerSkip), new FLASHPhaseConfiguration(linearAnonymityProperty, linearTriggerTag, linearTriggerCheck, linearTriggerEvaluate, linearTriggerSkip), StorageStrategy.ALL, true, true); return new FLASHAlgorithmImpl(solutionSpace, checker, strategy, config); } /** * Semantics of method name: monotonicity of privacy + monotonicity of utility. * * @param solutionSpace * @param checker * @param strategy * @return */ private static AbstractAlgorithm createNoneFull(final SolutionSpace solutionSpace, final NodeChecker checker, final FLASHStrategy strategy) { // We focus on the anonymity property PhaseAnonymityProperty anonymityProperty = PhaseAnonymityProperty.ANONYMITY; // We skip nodes for which the anonymity property is known or which have insufficient utility DependentAction triggerSkip = new DependentAction() { @Override public boolean appliesTo(Transformation node) { return node.hasProperty(solutionSpace.getPropertyAnonymous()) || node.hasProperty(solutionSpace.getPropertyNotAnonymous()) || node.hasProperty(solutionSpace.getPropertyInsufficientUtility()); } }; // No evaluation DependentAction triggerEvaluate = new NodeActionConstant(false); DependentAction triggerCheck = new NodeActionInverse(triggerSkip); // We predictively tag nodes with insufficient utility because of the monotonic metric DependentAction triggerTag = new DependentAction() { @Override public void action(Transformation node) { node.setPropertyToNeighbours(solutionSpace.getPropertyInsufficientUtility()); node.setProperty(solutionSpace.getPropertySuccessorsPruned()); } @Override public boolean appliesTo(Transformation node) { return node.hasProperty(solutionSpace.getPropertyAnonymous()); } }; // Only one linear phase FLASHConfiguration config = FLASHConfiguration.createLinearPhaseConfiguration(new FLASHPhaseConfiguration(anonymityProperty, triggerTag, triggerCheck, triggerEvaluate, triggerSkip), StorageStrategy.ALL, true, false); return new FLASHAlgorithmImpl(solutionSpace, checker, strategy, config); } /** * Semantics of method name: monotonicity of privacy + monotonicity of utility. * * @param solutionSpace * @param checker * @param strategy * @return */ private static AbstractAlgorithm createNoneNone(final SolutionSpace solutionSpace, NodeChecker checker, FLASHStrategy strategy) { // We focus on the anonymity property PhaseAnonymityProperty anonymityProperty = PhaseAnonymityProperty.ANONYMITY; // Skip nodes for which the anonymity property is known DependentAction triggerSkip = new DependentAction() { @Override public boolean appliesTo(Transformation node) { return node.hasProperty(solutionSpace.getPropertyAnonymous()) || node.hasProperty(solutionSpace.getPropertyNotAnonymous()); } }; // No evaluation, no tagging DependentAction triggerEvaluate = new NodeActionConstant(false); DependentAction triggerCheck = new NodeActionInverse(triggerSkip); DependentAction triggerTag = new NodeActionConstant(false); // Only one linear phase FLASHConfiguration config = FLASHConfiguration.createLinearPhaseConfiguration(new FLASHPhaseConfiguration(anonymityProperty, triggerTag, triggerCheck, triggerEvaluate, triggerSkip), StorageStrategy.ALL, true, false); return new FLASHAlgorithmImpl(solutionSpace, checker, strategy, config); } /** * Semantics of method name: monotonicity of privacy + monotonicity of utility. * * @param solutionSpace * @param checker * @param strategy * @return */ private static AbstractAlgorithm createPartialFull(final SolutionSpace solutionSpace, final NodeChecker checker, final FLASHStrategy strategy) { /* ******************************* * BINARY PHASE * ******************************* */ // We focus on the k-anonymity property PhaseAnonymityProperty binaryAnonymityProperty = PhaseAnonymityProperty.K_ANONYMITY; // Skip nodes for which the k-anonymity property is known or which have insufficient utility DependentAction binaryTriggerSkip = new DependentAction() { @Override public boolean appliesTo(Transformation node) { return node.hasProperty(solutionSpace.getPropertyKAnonymous()) || node.hasProperty(solutionSpace.getPropertyNotKAnonymous()) || node.hasProperty(solutionSpace.getPropertyInsufficientUtility()); } }; // We predictively tag the k-anonymity property and the insufficient utility property DependentAction binaryTriggerTag = new DependentAction() { @Override public void action(Transformation node) { // Tag insufficient utility node.setPropertyToNeighbours(solutionSpace.getPropertyInsufficientUtility()); node.setProperty(solutionSpace.getPropertySuccessorsPruned()); } @Override public boolean appliesTo(Transformation node) { return node.hasProperty(solutionSpace.getPropertyAnonymous()); } }; // No evaluation DependentAction binaryTriggerCheck = new NodeActionInverse(binaryTriggerSkip); DependentAction binaryTriggerEvaluate = new NodeActionConstant(false); /* ******************************* * LINEAR PHASE * ******************************* */ // We focus on the anonymity property PhaseAnonymityProperty linearAnonymityProperty = PhaseAnonymityProperty.ANONYMITY; // We skip nodes for which the anonymity property is known, which are not k-anonymous, // or which have insufficient utility DependentAction linearTriggerSkip = new DependentAction() { @Override public boolean appliesTo(Transformation node) { return node.hasProperty(solutionSpace.getPropertyVisited()) || node.hasProperty(solutionSpace.getPropertyNotKAnonymous()) || node.hasProperty(solutionSpace.getPropertyInsufficientUtility()); } }; // No evaluation DependentAction linearTriggerEvaluate = new NodeActionConstant(false); // We check nodes which have not been skipped DependentAction linearTriggerCheck = new DependentAction() { @Override public boolean appliesTo(Transformation node) { return !node.hasProperty(solutionSpace.getPropertyVisited()) && !node.hasProperty(solutionSpace.getPropertyNotKAnonymous()) && !node.hasProperty(solutionSpace.getPropertyInsufficientUtility()) && !node.hasProperty(solutionSpace.getPropertyChecked()); } }; // We predictively tag the insufficient utility property DependentAction linearTriggerTag = new DependentAction() { @Override public void action(Transformation node) { node.setProperty(solutionSpace.getPropertyVisited()); if (node.hasProperty(solutionSpace.getPropertyAnonymous())) { node.setPropertyToNeighbours(solutionSpace.getPropertyInsufficientUtility()); node.setProperty(solutionSpace.getPropertySuccessorsPruned()); } } @Override public boolean appliesTo(Transformation node) { return true; } }; // Two interwoven phases FLASHConfiguration config = FLASHConfiguration.createTwoPhaseConfiguration(new FLASHPhaseConfiguration(binaryAnonymityProperty, binaryTriggerTag, binaryTriggerCheck, binaryTriggerEvaluate, binaryTriggerSkip), new FLASHPhaseConfiguration(linearAnonymityProperty, linearTriggerTag, linearTriggerCheck, linearTriggerEvaluate, linearTriggerSkip), StorageStrategy.ALL, true, false); return new FLASHAlgorithmImpl(solutionSpace, checker, strategy, config); } /** * Semantics of method name: monotonicity of privacy + monotonicity of utility. * * @param solutionSpace * @param checker * @param strategy * @return */ private static AbstractAlgorithm createPartialNone(final SolutionSpace solutionSpace, final NodeChecker checker, final FLASHStrategy strategy) { /* ******************************* * BINARY PHASE * ******************************* */ // We focus on the k-anonymity property PhaseAnonymityProperty binaryAnonymityProperty = PhaseAnonymityProperty.K_ANONYMITY; // Skip nodes for which the k-anonymity property is known DependentAction binaryTriggerSkip = new DependentAction() { @Override public boolean appliesTo(Transformation node) { return node.hasProperty(solutionSpace.getPropertyKAnonymous()) || node.hasProperty(solutionSpace.getPropertyNotKAnonymous()); } }; // We predictively tag the k-anonymity property DependentAction binaryTriggerTag = new DependentAction() { @Override public void action(Transformation node) { // Empty by design } @Override public boolean appliesTo(Transformation node) { return false; // Empty trigger } }; // No evaluation DependentAction binaryTriggerCheck = new NodeActionInverse(binaryTriggerSkip); DependentAction binaryTriggerEvaluate = new NodeActionConstant(false); /* ******************************* * LINEAR PHASE * ******************************* */ // We focus on the anonymity property PhaseAnonymityProperty linearAnonymityProperty = PhaseAnonymityProperty.ANONYMITY; // We skip nodes for which are not k-anonymous and which have not been visited yet in the 2nd phase DependentAction linearTriggerSkip = new DependentAction() { @Override public boolean appliesTo(Transformation node) { return node.hasProperty(solutionSpace.getPropertyVisited()) || node.hasProperty(solutionSpace.getPropertyNotKAnonymous()); } }; // No evaluation DependentAction linearTriggerEvaluate = new NodeActionConstant(false); // We check nodes which are k-anonymous and have not been checked already DependentAction linearTriggerCheck = new DependentAction() { @Override public boolean appliesTo(Transformation node) { return !node.hasProperty(solutionSpace.getPropertyChecked()) && !node.hasProperty(solutionSpace.getPropertyNotKAnonymous()); } }; // Mark nodes as already visited during the second phase DependentAction linearTriggerTag = new DependentAction() { @Override public void action(Transformation node) { node.setProperty(solutionSpace.getPropertyVisited()); } @Override public boolean appliesTo(Transformation node) { return true; } }; // Two interwoven phases FLASHConfiguration config = FLASHConfiguration.createTwoPhaseConfiguration(new FLASHPhaseConfiguration(binaryAnonymityProperty, binaryTriggerTag, binaryTriggerCheck, binaryTriggerEvaluate, binaryTriggerSkip), new FLASHPhaseConfiguration(linearAnonymityProperty, linearTriggerTag, linearTriggerCheck, linearTriggerEvaluate, linearTriggerSkip), StorageStrategy.ALL, true, false); return new FLASHAlgorithmImpl(solutionSpace, checker, strategy, config); } }