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)
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