"""Deterministic signal transformations; no state or probability register.""" import cmath from dataclasses import dataclass import math def _algebra_simbolica(): try: import sympy except ModuleNotFoundError as error: raise ModuleNotFoundError( 'Las relaciones simbolicas necesitan SymPy. Instala con: ' 'python -m pip install -r requirements_relaciones.txt') from error return sympy @dataclass(frozen=True) class Bloque: nombre: str puertos: tuple angulo: float | None = None def transformar_par(self, s0, s1, *, simbolico=False): """Apply the formula to signals or formal variables, never a readout.""" algebra = _algebra_simbolica() if simbolico else None imaginary = algebra.I if simbolico else 1j exponential = algebra.exp if simbolico else cmath.exp if self.nombre in ('fase', 'rx', 'rz') and self.angulo is None: raise ValueError('Falta el angulo de la operacion') if self.nombre == 'h': scale = algebra.sqrt(2) / 2 if simbolico else math.sqrt(.5) return (s0 + s1) * scale, (s0 - s1) * scale if self.nombre == 'fase': return s0, exponential(imaginary * self.angulo) * s1 if self.nombre == 'rx': cosine, sine = (algebra.cos, algebra.sin) if simbolico else (math.cos, math.sin) c, s = cosine(self.angulo / 2), sine(self.angulo / 2) return c*s0 - imaginary*s*s1, -imaginary*s*s0 + c*s1 if self.nombre == 'rz': return exponential(-imaginary*self.angulo/2)*s0, exponential(imaginary*self.angulo/2)*s1 raise ValueError('Este bloque no es una transformacion de dos caminos') def transformar_conexion_doble(self, s00, s01, s10, s11, *, simbolico=False): """Local two-unit relations in order 00,01,10,11, first unit control. Formal joint-channel variables, not products inferred from independent single-unit pairs. This does not allocate a whole-machine register. """ if self.nombre == 'cnot': return s00, s01, s11, s10 if self.nombre == 'swap': return s00, s10, s01, s11 if self.nombre == 'cz': return s00, s01, s10, -s11 if self.nombre == 'cp': if self.angulo is None: raise ValueError('Falta el angulo de fase controlada') algebra = _algebra_simbolica() if simbolico else None factor = (algebra.exp(algebra.I * self.angulo) if simbolico else cmath.exp(1j * self.angulo)) return s00, s01, s10, factor*s11 raise ValueError('No hay relacion de dos unidades para este bloque') def fourier_inversa(puertos): """Inverse Fourier wiring as H, controlled phase and SWAP: quadratic size.""" ports = tuple(puertos) if not ports or len(set(ports)) != len(ports): raise ValueError('Los puertos de Fourier deben ser distintos y no vacios') forward = [] for j in range(len(ports)-1, -1, -1): forward.append(Bloque('h', (ports[j],))) for k in range(j-1, -1, -1): forward.append(Bloque('cp', (ports[j], ports[k]), math.ldexp(math.pi, k-j))) for j in range(len(ports)//2): forward.append(Bloque('swap', (ports[j], ports[-j-1]))) return tuple(Bloque(block.nombre, block.puertos, None if block.angulo is None else -block.angulo) for block in reversed(forward)) def compilar_bloque(operation, unidades): name, *arguments = operation if name == 'fourier_inversa': if any(type(i) is not int or not 0 <= i < unidades for i in arguments): raise ValueError('Puertos de Fourier invalidos') return fourier_inversa(arguments) angle = None if name in ('rx', 'rz', 'fase', 'cp'): if not arguments: raise ValueError('Falta el angulo definido por el protocolo') angle = arguments.pop() if type(angle) not in (int, float) or not math.isfinite(angle): raise ValueError('El angulo debe ser finito') arity = 2 if name in ('cz', 'cp') else 1 if name not in ('h', 'fase', 'rx', 'rz', 'cz', 'cp'): raise ValueError('Bloque de senales no definido') if len(arguments) != arity or any(type(i) is not int or not 0 <= i < unidades for i in arguments): raise ValueError('Puertos invalidos') if len(set(arguments)) != len(arguments): raise ValueError('Los puertos deben ser distintos') return (Bloque(name, tuple(arguments), angle),)