def _process_expr(self):
        self._num_vars = self._expr.degree
        ast = self._expr.to_ast() if self._expr.is_cnf() else self._expr.to_cnf().to_ast()
        ast = LogicalExpressionOracle._normalize_literal_indices(ast, self._expr.usupport)

        if self._optimization == 'off':
            if ast[0] == 'or':
                self._nf = DNF(ast, num_vars=self._num_vars)
            else:
                self._nf = CNF(ast, num_vars=self._num_vars)
        else:  # self._optimization == 'espresso':
            expr_dnf = self._expr.to_dnf()
            if expr_dnf.is_zero() or expr_dnf.is_one():
                self._nf = CNF(('const', 0 if expr_dnf.is_zero() else 1), num_vars=self._num_vars)
            else:
                expr_dnf_m = espresso_exprs(expr_dnf)[0]
                expr_dnf_m_ast = LogicalExpressionOracle._normalize_literal_indices(
                    expr_dnf_m.to_ast(), expr_dnf_m.usupport
                )
                if isinstance(expr_dnf_m, AndOp) or isinstance(expr_dnf_m, Variable):
                    self._nf = CNF(expr_dnf_m_ast, num_vars=self._num_vars)
                elif isinstance(expr_dnf_m, OrOp):
                    self._nf = DNF(expr_dnf_m_ast, num_vars=self._num_vars)
                else:
                    raise AquaError('Unexpected espresso optimization result expr: {}'.format(expr_dnf_m))
    def _process_expr(self):
        self._num_vars = len(self._expr.binary_symbols)
        self._lit_to_var = [None] + sorted(self._expr.binary_symbols, key=str)
        self._var_to_lit = dict(zip(self._lit_to_var[1:], range(1, self._num_vars + 1)))
        cnf = to_cnf(self._expr, simplify=self._optimization)

        if isinstance(cnf, BooleanTrue):
            ast = 'const', 1
        elif isinstance(cnf, BooleanFalse):
            ast = 'const', 0
        else:
            ast = get_ast(self._var_to_lit, cnf)

        if ast[0] == 'or':
            self._nf = DNF(ast, num_vars=self._num_vars)
        else:
            self._nf = CNF(ast, num_vars=self._num_vars)
Esempio n. 3
0
    def _process_expr(self):
        self._num_vars = len(self._expr.binary_symbols)
        self._lit_to_var = [None] + sorted(self._expr.binary_symbols, key=str)
        self._var_to_lit = dict(
            zip(self._lit_to_var[1:], range(1, self._num_vars + 1)))

        if self._optimization or (not is_cnf(self._expr)
                                  and not is_dnf(self._expr)):
            expr = simplify_logic(self._expr)
        else:
            expr = self._expr

        if isinstance(expr, BooleanTrue):
            ast = 'const', 1
        elif isinstance(expr, BooleanFalse):
            ast = 'const', 0
        else:
            ast = get_ast(self._var_to_lit, expr)

        if ast[0] == 'or':
            self._nf = DNF(ast, num_vars=self._num_vars)
        else:
            self._nf = CNF(ast, num_vars=self._num_vars)
class LogicalExpressionOracle(Oracle):

    CONFIGURATION = {
        'name': 'LogicalExpressionOracle',
        'description': 'Logical Expression Oracle',
        'input_schema': {
            '$schema': 'http://json-schema.org/schema#',
            'id': 'logical_expression_oracle_schema',
            'type': 'object',
            'properties': {
                'expression': {
                    'type': ['string', 'null'],
                    'default': None
                },
                "optimization": {
                    "type": "string",
                    "default": "off",
                    'oneOf': [
                        {
                            'enum': [
                                'off',
                                'espresso'
                            ]
                        }
                    ]
                },
                'mct_mode': {
                    'type': 'string',
                    'default': 'basic',
                    'oneOf': [
                        {
                            'enum': [
                                'basic',
                                'advanced',
                                'noancilla'
                            ]
                        }
                    ]
                },
            },
            'additionalProperties': False
        }
    }

    def __init__(self, expression=None, optimization='off', mct_mode='basic'):
        """
        Constructor.

        Args:
            expression (str): The string of the desired logical expression.
                It could be either in the DIMACS CNF format,
                or a general boolean logical expression, such as 'a ^ b' and 'v[0] & (~v[1] | v[2])'
            optimization (str): The mode of optimization to use for minimizing the circuit.
                Currently, besides no optimization ('off'), Aqua also supports an 'espresso' mode
                <https://en.wikipedia.org/wiki/Espresso_heuristic_logic_minimizer>
            mct_mode (str): The mode to use for building Multiple-Control Toffoli.
        """

        self.validate(locals())
        super().__init__()

        self._mct_mode = mct_mode
        self._optimization = optimization

        if expression is None:
            raw_expr = expr(None)
        else:
            try:
                raw_expr = expr(expression)
            except:
                try:
                    raw_expr = ast2expr(parse_cnf(expression.strip(), varname='v'))
                except:
                    raise AquaError('Failed to parse the input expression: {}.'.format(expression))

        self._expr = raw_expr
        self._process_expr()
        self.construct_circuit()

    @staticmethod
    def _normalize_literal_indices(raw_ast, raw_indices):
        idx_mapping = {r: i + 1 for r, i in zip(sorted(raw_indices), range(len(raw_indices)))}
        if raw_ast[0] == 'and' or raw_ast[0] == 'or':
            clauses = []
            for c in raw_ast[1:]:
                if c[0] == 'lit':
                    clauses.append(('lit', (idx_mapping[c[1]]) if c[1] > 0 else (-idx_mapping[-c[1]])))
                elif (c[0] == 'or' or c[0] == 'and') and (raw_ast[0] != c[0]):
                    clause = []
                    for l in c[1:]:
                        clause.append(('lit', (idx_mapping[l[1]]) if l[1] > 0 else (-idx_mapping[-l[1]])))
                    clauses.append((c[0], *clause))
                else:
                    raise AquaError('Unrecognized logical expression: {}'.format(raw_ast))
        elif raw_ast[0] == 'const' or raw_ast[0] == 'lit':
            return raw_ast
        else:
            raise AquaError('Unrecognized root expression type: {}.'.format(raw_ast[0]))
        return (raw_ast[0], *clauses)

    def _process_expr(self):
        self._num_vars = self._expr.degree
        ast = self._expr.to_ast() if self._expr.is_cnf() else self._expr.to_cnf().to_ast()
        ast = LogicalExpressionOracle._normalize_literal_indices(ast, self._expr.usupport)

        if self._optimization == 'off':
            if ast[0] == 'or':
                self._nf = DNF(ast, num_vars=self._num_vars)
            else:
                self._nf = CNF(ast, num_vars=self._num_vars)
        else:  # self._optimization == 'espresso':
            expr_dnf = self._expr.to_dnf()
            if expr_dnf.is_zero() or expr_dnf.is_one():
                self._nf = CNF(('const', 0 if expr_dnf.is_zero() else 1), num_vars=self._num_vars)
            else:
                expr_dnf_m = espresso_exprs(expr_dnf)[0]
                expr_dnf_m_ast = LogicalExpressionOracle._normalize_literal_indices(
                    expr_dnf_m.to_ast(), expr_dnf_m.usupport
                )
                if isinstance(expr_dnf_m, AndOp) or isinstance(expr_dnf_m, Variable):
                    self._nf = CNF(expr_dnf_m_ast, num_vars=self._num_vars)
                elif isinstance(expr_dnf_m, OrOp):
                    self._nf = DNF(expr_dnf_m_ast, num_vars=self._num_vars)
                else:
                    raise AquaError('Unexpected espresso optimization result expr: {}'.format(expr_dnf_m))

    @property
    def variable_register(self):
        return self._variable_register

    @property
    def ancillary_register(self):
        return self._ancillary_register

    @property
    def output_register(self):
        return self._output_register

    def construct_circuit(self):
        if self._circuit is None:
            if self._nf is not None:
                self._circuit = self._nf.construct_circuit(mct_mode=self._mct_mode)
                self._variable_register = self._nf.variable_register
                self._output_register = self._nf.output_register
                self._ancillary_register = self._nf.ancillary_register
            else:
                self._variable_register = QuantumRegister(self._num_vars, name='v')
                self._output_register = QuantumRegister(1, name='o')
                self._ancillary_register = None
                self._circuit = QuantumCircuit(self._variable_register, self._output_register)
        return self._circuit

    def evaluate_classically(self, measurement):
        assignment = [(var + 1) * (int(tf) * 2 - 1) for tf, var in zip(measurement[::-1], range(len(measurement)))]
        if self._expr.is_zero():
            return False, assignment
        elif self._expr.is_one():
            return True, assignment
        else:
            prime_implicants = self._expr.complete_sum()
            if prime_implicants.is_zero():
                sols = []
            elif isinstance(prime_implicants, AndOp):
                prime_implicants_ast = LogicalExpressionOracle._normalize_literal_indices(
                    prime_implicants.to_ast(), prime_implicants.usupport
                )
                sols = [[l[1] for l in prime_implicants_ast[1:]]]
            elif isinstance(prime_implicants, OrOp):
                expr_complete_sum = self._expr.complete_sum()
                complete_sum_ast = LogicalExpressionOracle._normalize_literal_indices(
                    expr_complete_sum.to_ast(), expr_complete_sum.usupport
                )
                sols = [[c[1]] if c[0] == 'lit' else [l[1] for l in c[1:]] for c in complete_sum_ast[1:]]
            elif isinstance(prime_implicants, Variable):
                sols = [[prime_implicants.to_ast()[1]]]
            else:
                raise AquaError('Unexpected solution: {}'.format(prime_implicants))
            for sol in sols:
                if set(sol).issubset(assignment):
                    return True, assignment
            else:
                return False, assignment