Inference Methods¶
This guide covers the various inference algorithms available in the Probabilistic Quantum Reasoner for extracting information from quantum-classical hybrid networks.
Overview of Inference¶
Inference in quantum Bayesian networks involves computing probability distributions over query variables given evidence, leveraging both classical probabilistic reasoning and quantum amplitude-based computation.
Basic Inference¶
Simple Queries¶
from probabilistic_quantum_reasoner import QuantumBayesianNetwork
from probabilistic_quantum_reasoner.backends import ClassicalSimulator
import numpy as np
# Create example network
backend = ClassicalSimulator()
network = QuantumBayesianNetwork("InferenceDemo", backend)
# Add nodes
weather = network.add_quantum_node(
"weather",
outcome_space=["sunny", "rainy"],
initial_amplitudes=np.array([0.8, 0.6], dtype=complex)
)
mood = network.add_stochastic_node("mood", outcome_space=["happy", "sad"])
activity = network.add_hybrid_node("activity", outcome_space=["indoor", "outdoor"], mixing_parameter=0.7)
# Add edges
network.add_edge(weather, mood)
network.add_edge(mood, activity)
# Basic inference without evidence
result = network.infer(query_nodes=["mood", "activity"])
print(f"Mood distribution: {result.marginal_probabilities['mood']}")
print(f"Activity distribution: {result.marginal_probabilities['activity']}")
Conditional Inference¶
# Inference with evidence
evidence = {"weather": "sunny"}
conditional_result = network.infer(
query_nodes=["mood", "activity"],
evidence=evidence
)
print(f"Mood given sunny weather: {conditional_result.marginal_probabilities['mood']}")
print(f"Activity given sunny weather: {conditional_result.marginal_probabilities['activity']}")
# Multiple evidence variables
multi_evidence = {"weather": "sunny", "mood": "happy"}
multi_result = network.infer(
query_nodes=["activity"],
evidence=multi_evidence
)
print(f"Activity given sunny weather and happy mood: {multi_result.marginal_probabilities['activity']}")
Joint Distributions¶
# Joint probability distributions
joint_result = network.infer(
query_nodes=["weather", "mood"],
return_joint=True
)
print("Joint distribution P(weather, mood):")
for weather_val in ["sunny", "rainy"]:
for mood_val in ["happy", "sad"]:
joint_prob = joint_result.joint_probabilities.get((weather_val, mood_val), 0)
print(f"P(weather={weather_val}, mood={mood_val}) = {joint_prob:.3f}")
Belief Propagation¶
Quantum Message Passing¶
# Use belief propagation algorithm explicitly
bp_result = network.infer(
query_nodes=["activity"],
evidence={"weather": "rainy"},
method="belief_propagation",
max_iterations=50,
convergence_threshold=1e-6
)
print(f"Belief propagation result: {bp_result.marginal_probabilities['activity']}")
print(f"Converged: {bp_result.converged}")
print(f"Iterations: {bp_result.iterations}")
Message Analysis¶
# Access detailed message passing information
detailed_result = network.infer(
query_nodes=["mood"],
method="belief_propagation",
return_messages=True
)
# Examine messages between nodes
for edge, message in detailed_result.messages.items():
parent, child = edge
print(f"Message from {parent} to {child}:")
print(f" Amplitudes: {message.amplitudes}")
print(f" Probabilities: {np.abs(message.amplitudes)**2}")
Variational Inference¶
Variational Quantum Eigensolver (VQE)¶
# Use variational inference for complex distributions
variational_result = network.infer(
query_nodes=["weather", "mood"],
method="variational",
max_iterations=100,
optimization_method="L-BFGS-B"
)
print(f"Variational inference result: {variational_result.marginal_probabilities}")
print(f"Final cost: {variational_result.final_cost}")
print(f"Optimization success: {variational_result.optimization_success}")
Circuit Parameters¶
# Access optimized quantum circuit parameters
if hasattr(variational_result, 'optimal_parameters'):
params = variational_result.optimal_parameters
print(f"Optimized circuit parameters: {params}")
# Visualize parameter evolution
if hasattr(variational_result, 'parameter_history'):
import matplotlib.pyplot as plt
history = variational_result.parameter_history
plt.figure(figsize=(10, 6))
for i, param_trace in enumerate(history.T):
plt.plot(param_trace, label=f'Parameter {i}')
plt.xlabel('Optimization Step')
plt.ylabel('Parameter Value')
plt.title('Variational Parameter Evolution')
plt.legend()
plt.show()
Custom Ansatz¶
# Define custom variational ansatz
def custom_ansatz(parameters, n_qubits):
"""Custom variational circuit ansatz."""
from probabilistic_quantum_reasoner.core.operators import QuantumGate
circuit = []
# Layer 1: Individual rotations
for i in range(n_qubits):
circuit.append(('RY', i, parameters[i]))
# Layer 2: Entangling gates
for i in range(n_qubits - 1):
circuit.append(('CNOT', i, i + 1))
# Layer 3: Final rotations
for i in range(n_qubits):
circuit.append(('RZ', i, parameters[n_qubits + i]))
return circuit
# Use custom ansatz in inference
custom_result = network.infer(
query_nodes=["weather", "mood"],
method="variational",
ansatz=custom_ansatz,
n_parameters=4 # 2 qubits × 2 parameters each
)
Sampling-Based Inference¶
Quantum Sampling¶
# Sampling-based approximate inference
sampling_result = network.infer(
query_nodes=["mood", "activity"],
evidence={"weather": "sunny"},
method="sampling",
n_samples=1000
)
print(f"Sampling result: {sampling_result.marginal_probabilities}")
print(f"Sample variance: {sampling_result.sample_variance}")
print(f"Effective sample size: {sampling_result.effective_sample_size}")
Markov Chain Monte Carlo¶
# MCMC sampling for complex posterior distributions
mcmc_result = network.infer(
query_nodes=["weather", "mood", "activity"],
method="mcmc",
n_samples=5000,
burn_in=1000,
thin=5
)
print(f"MCMC marginals: {mcmc_result.marginal_probabilities}")
print(f"Acceptance rate: {mcmc_result.acceptance_rate}")
print(f"Chain convergence: {mcmc_result.r_hat}") # Gelman-Rubin statistic
Importance Sampling¶
# Importance sampling with quantum proposal distribution
importance_result = network.infer(
query_nodes=["activity"],
evidence={"weather": "sunny"},
method="importance_sampling",
n_samples=2000,
proposal="quantum_uniform" # Use quantum superposition as proposal
)
print(f"Importance sampling result: {importance_result.marginal_probabilities}")
print(f"Effective sample size: {importance_result.effective_sample_size}")
Exact Inference¶
Quantum Amplitude Enumeration¶
# Exact inference by enumerating all quantum amplitudes
exact_result = network.infer(
query_nodes=["weather", "mood"],
method="exact",
enumerate_amplitudes=True
)
print(f"Exact marginals: {exact_result.marginal_probabilities}")
print(f"Computation time: {exact_result.computation_time}")
print(f"Memory usage: {exact_result.memory_usage_mb} MB")
Junction Tree Algorithm¶
# Use junction tree for efficient exact inference
if network.is_tree_decomposable():
junction_result = network.infer(
query_nodes=["activity"],
method="junction_tree"
)
print(f"Junction tree result: {junction_result.marginal_probabilities}")
else:
print("Network is not tree-decomposable, using alternative method")
Approximate Inference¶
Loopy Belief Propagation¶
# Loopy belief propagation for networks with cycles
loopy_result = network.infer(
query_nodes=["mood"],
method="loopy_belief_propagation",
max_iterations=100,
damping_factor=0.5 # Damping to improve convergence
)
print(f"Loopy BP result: {loopy_result.marginal_probabilities}")
print(f"Converged: {loopy_result.converged}")
Mean Field Approximation¶
# Mean field variational approximation
mean_field_result = network.infer(
query_nodes=["weather", "mood", "activity"],
method="mean_field",
max_iterations=50
)
print(f"Mean field marginals: {mean_field_result.marginal_probabilities}")
print(f"ELBO (Evidence Lower Bound): {mean_field_result.elbo}")
Quantum-Specific Inference¶
Grover-Enhanced Search¶
# Use Grover's algorithm for probabilistic search
def search_condition(state_dict):
"""Search condition: weather is sunny AND mood is happy."""
return state_dict.get("weather") == "sunny" and state_dict.get("mood") == "happy"
grover_result = network.infer(
query_nodes=["weather", "mood"],
method="grover_search",
search_condition=search_condition,
max_iterations=None # Optimal number of iterations
)
print(f"Grover search result: {grover_result.marginal_probabilities}")
print(f"Search success probability: {grover_result.success_probability}")
Quantum Fourier Transform¶
# QFT-based inference for periodic patterns
qft_result = network.infer(
query_nodes=["weather"],
method="quantum_fourier_transform",
basis="frequency" # Frequency domain analysis
)
print(f"QFT frequency components: {qft_result.frequency_amplitudes}")
print(f"Dominant frequencies: {qft_result.dominant_frequencies}")
Inference Configuration¶
Performance Tuning¶
# Configure inference algorithms for performance
config = {
"belief_propagation": {
"max_iterations": 100,
"convergence_threshold": 1e-8,
"parallel_messages": True
},
"variational": {
"optimizer": "Adam",
"learning_rate": 0.01,
"batch_size": 32
},
"sampling": {
"n_samples": 10000,
"parallel_chains": 4,
"adaptive_step_size": True
}
}
# Apply configuration
network.set_inference_config(config)
# Run with optimized settings
optimized_result = network.infer(
query_nodes=["activity"],
evidence={"weather": "rainy"},
method="belief_propagation"
)
Memory Management¶
# Monitor and control memory usage during inference
memory_config = {
"max_memory_mb": 4096, # 4 GB limit
"state_compression": True,
"lazy_evaluation": True,
"garbage_collection": "aggressive"
}
network.set_memory_config(memory_config)
# Memory-efficient inference
efficient_result = network.infer(
query_nodes=["weather", "mood", "activity"],
method="variational",
memory_efficient=True
)
print(f"Peak memory usage: {efficient_result.peak_memory_mb} MB")
Inference Diagnostics¶
Convergence Analysis¶
# Detailed convergence diagnostics
diagnostic_result = network.infer(
query_nodes=["mood"],
method="belief_propagation",
return_diagnostics=True
)
diagnostics = diagnostic_result.diagnostics
print(f"Convergence rate: {diagnostics['convergence_rate']}")
print(f"Message fidelities: {diagnostics['message_fidelities']}")
print(f"Quantum coherence: {diagnostics['quantum_coherence']}")
# Plot convergence
import matplotlib.pyplot as plt
plt.figure(figsize=(12, 4))
plt.subplot(1, 3, 1)
plt.plot(diagnostics['cost_history'])
plt.title('Cost Function')
plt.xlabel('Iteration')
plt.subplot(1, 3, 2)
plt.plot(diagnostics['fidelity_history'])
plt.title('State Fidelity')
plt.xlabel('Iteration')
plt.subplot(1, 3, 3)
plt.plot(diagnostics['coherence_history'])
plt.title('Quantum Coherence')
plt.xlabel('Iteration')
plt.tight_layout()
plt.show()
Uncertainty Quantification¶
# Quantify uncertainty in inference results
uncertainty_result = network.infer(
query_nodes=["activity"],
evidence={"weather": "sunny"},
method="sampling",
uncertainty_quantification=True,
confidence_level=0.95
)
activity_dist = uncertainty_result.marginal_probabilities["activity"]
activity_ci = uncertainty_result.confidence_intervals["activity"]
print("Activity distribution with uncertainty:")
for outcome, prob in activity_dist.items():
ci_lower, ci_upper = activity_ci[outcome]
print(f" P(activity={outcome}) = {prob:.3f} [{ci_lower:.3f}, {ci_upper:.3f}]")
Inference Comparison¶
Algorithm Benchmarking¶
# Compare different inference algorithms
algorithms = ["belief_propagation", "variational", "sampling", "exact"]
evidence = {"weather": "sunny"}
query = ["mood", "activity"]
results = {}
for algorithm in algorithms:
try:
start_time = time.time()
result = network.infer(
query_nodes=query,
evidence=evidence,
method=algorithm
)
end_time = time.time()
results[algorithm] = {
"marginals": result.marginal_probabilities,
"time": end_time - start_time,
"memory": result.memory_usage_mb if hasattr(result, 'memory_usage_mb') else 0
}
except Exception as e:
print(f"{algorithm} failed: {e}")
# Compare results
print("Algorithm comparison:")
for algorithm, data in results.items():
print(f"\n{algorithm.upper()}:")
print(f" Time: {data['time']:.3f} seconds")
print(f" Memory: {data['memory']:.1f} MB")
print(f" Activity marginal: {data['marginals']['activity']}")
Best Practices¶
Algorithm Selection¶
def select_inference_algorithm(network, query_nodes, evidence=None):
"""Automatically select best inference algorithm."""
# Small networks: use exact inference
if len(network.nodes) <= 10:
return "exact"
# Tree-structured networks: use belief propagation
elif network.is_tree():
return "belief_propagation"
# Many quantum nodes: use variational methods
elif network.count_quantum_nodes() > 5:
return "variational"
# Large classical networks: use sampling
elif len(network.nodes) > 50:
return "sampling"
# Default: loopy belief propagation
else:
return "loopy_belief_propagation"
# Auto-select algorithm
optimal_algorithm = select_inference_algorithm(network)
auto_result = network.infer(
query_nodes=["activity"],
evidence={"weather": "sunny"},
method=optimal_algorithm
)
Error Handling¶
# Robust inference with error handling
def robust_inference(network, query_nodes, evidence=None, fallback_methods=None):
"""Perform inference with automatic fallback methods."""
if fallback_methods is None:
fallback_methods = ["belief_propagation", "variational", "sampling"]
for method in fallback_methods:
try:
result = network.infer(
query_nodes=query_nodes,
evidence=evidence,
method=method,
timeout=60 # 1 minute timeout
)
# Validate result
if result.is_valid():
return result, method
except Exception as e:
print(f"Method {method} failed: {e}")
continue
raise RuntimeError("All inference methods failed")
# Use robust inference
try:
final_result, used_method = robust_inference(
network,
query_nodes=["mood"],
evidence={"weather": "rainy"}
)
print(f"Successfully used method: {used_method}")
print(f"Result: {final_result.marginal_probabilities}")
except RuntimeError as e:
print(f"Inference failed: {e}")
Next Steps¶
- Learn about Causal Reasoning and interventions
- Explore Variational Methods in detail
- See practical Examples
- Check API Reference for complete documentation