use super::*;
use crate::circuit::Operation;
use crate::circuit::ansatz::{Ansatz, EntanglementTopology, PauliFeatureMap};
use crate::circuit::circuit_param::CircuitParam;
use crate::circuit::gate::standard_gate::StandardGate;
use crate::qis::pauli::PauliString;
fn is_h_gate_on_qubit(op: &Operation, expected_qubit: usize) -> bool {
match op.standard_gate() {
Some(StandardGate::H) => op.qubits.len() == 1 && op.qubits[0].index() == expected_qubit,
_ => false,
}
}
fn is_rz_gate_with_param(op: &Operation, expected_qubit: usize) -> bool {
match op.standard_gate() {
Some(StandardGate::RZ) => {
op.qubits.len() == 1 && op.qubits[0].index() == expected_qubit && op.params.len() == 1
}
_ => false,
}
}
fn is_cnot_gate(op: &Operation, control: usize, target: usize) -> bool {
match op.standard_gate() {
Some(StandardGate::CX) => {
op.qubits.len() == 2
&& op.qubits[0].index() == control
&& op.qubits[1].index() == target
}
_ => false,
}
}
fn is_x_gate_on_qubit(op: &Operation, expected_qubit: usize) -> bool {
match op.standard_gate() {
Some(StandardGate::X) => op.qubits.len() == 1 && op.qubits[0].index() == expected_qubit,
_ => false,
}
}
#[test]
fn test_basis_encoding_prepares_selected_basis_bits() {
let encoder = BasisEncoding::new(vec![true, false, true]);
let circuit = encoder.build_circuit("ignored").unwrap();
let ops = circuit.operations();
assert_eq!(encoder.num_qubits(), 3);
assert_eq!(encoder.num_parameters(), 0);
assert_eq!(ops.len(), 2);
assert!(is_x_gate_on_qubit(&ops[0], 0));
assert!(is_x_gate_on_qubit(&ops[1], 2));
assert!(circuit.parameters().is_empty());
}
#[test]
fn test_basis_encoding_rejects_empty_bitstring() {
assert!(BasisEncoding::new(vec![]).validate().is_err());
}
#[test]
fn test_z_feature_map_structure() {
let fm = ZFeatureMap::new(2).reps(1);
let circuit = fm.build_circuit("x").unwrap();
let ops = circuit.operations();
assert_eq!(fm.num_parameters(), 2);
assert_eq!(ops.len(), 4);
assert!(is_h_gate_on_qubit(&ops[0], 0));
assert!(is_h_gate_on_qubit(&ops[1], 1));
assert!(is_rz_gate_with_param(&ops[2], 0));
assert!(is_rz_gate_with_param(&ops[3], 1));
}
#[test]
fn test_z_feature_map_zero_reps_has_no_parameters() {
let fm = ZFeatureMap::new(3).reps(0);
let circuit = fm.build_circuit("x").unwrap();
assert!(circuit.operations().is_empty());
assert_eq!(fm.num_parameters(), 0);
assert!(circuit.parameters().is_empty());
}
#[test]
fn test_iqp_feature_map_matches_zz_feature_map_structure() {
let iqp = IQPFeatureMap::new(2)
.reps(1)
.entanglement(EntanglementTopology::Linear);
let zz = ZZFeatureMap::new(2)
.reps(1)
.entanglement(EntanglementTopology::Linear);
let iqp_circuit = iqp.build_circuit("x").unwrap();
let zz_circuit = zz.build_circuit("x").unwrap();
assert_eq!(iqp.num_parameters(), zz.num_parameters());
assert_eq!(iqp.num_qubits(), zz.num_qubits());
assert_eq!(
iqp_circuit.operations().len(),
zz_circuit.operations().len()
);
assert_eq!(iqp_circuit.symbols(), zz_circuit.symbols());
}
#[test]
fn test_iqp_feature_map_zero_reps_has_no_parameters() {
let fm = IQPFeatureMap::new(3).reps(0);
let circuit = fm.build_circuit("x").unwrap();
assert!(circuit.operations().is_empty());
assert_eq!(fm.num_parameters(), 0);
assert!(circuit.parameters().is_empty());
}
#[test]
fn test_pauli_feature_map_uses_default_parameter_prefix() {
let fm = PauliFeatureMap::new(2);
let circuit = fm.build_circuit("").unwrap();
let syms = circuit.symbols();
assert!(syms.contains("x_0"));
assert!(syms.contains("x_1"));
}
#[test]
fn test_pauli_feature_map_custom_parameter_prefix() {
let fm = PauliFeatureMap::new(2).parameter_prefix("feature");
let circuit = fm.build_circuit("").unwrap();
let syms = circuit.symbols();
assert!(syms.contains("feature_0"));
assert!(syms.contains("feature_1"));
}
#[test]
fn test_pauli_feature_map_explicit_prefix_overrides() {
let fm = PauliFeatureMap::new(2).parameter_prefix("feature");
let circuit = fm.build_circuit("theta").unwrap();
let syms = circuit.symbols();
assert!(syms.contains("theta_0"));
assert!(syms.contains("theta_1"));
}
#[test]
fn test_pauli_feature_map_num_parameters_equals_num_qubits() {
let fm = PauliFeatureMap::new(3);
assert_eq!(fm.num_parameters(), 3);
assert_eq!(fm.num_qubits(), 3);
}
#[test]
fn test_pauli_feature_map_reps_affects_circuit_depth() {
let fm_1rep = PauliFeatureMap::new(2).reps(1);
let circuit_1rep = fm_1rep.build_circuit("x").unwrap();
let ops_1rep = circuit_1rep.operations();
let fm_2rep = PauliFeatureMap::new(2).reps(2);
let circuit_2rep = fm_2rep.build_circuit("x").unwrap();
let ops_2rep = circuit_2rep.operations();
assert_eq!(ops_2rep.len(), 2 * ops_1rep.len());
}
#[test]
fn test_pauli_feature_map_zero_reps_empty_circuit() {
let fm = PauliFeatureMap::new(2).reps(0);
let circuit = fm.build_circuit("x").unwrap();
let ops = circuit.operations();
assert_eq!(ops.len(), 0);
assert_eq!(fm.num_parameters(), 0);
assert!(circuit.parameters().is_empty());
}
#[test]
fn test_zz_feature_map_zero_reps_has_no_parameters() {
let fm = ZZFeatureMap::new(3).reps(0);
let circuit = fm.build_circuit("x").unwrap();
assert!(circuit.operations().is_empty());
assert_eq!(fm.num_parameters(), 0);
assert!(circuit.parameters().is_empty());
}
#[test]
fn test_pauli_feature_map_default_z_only_structure() {
let fm = PauliFeatureMap::new(2)
.reps(1)
.paulis(vec![("Z".parse::<PauliString>().unwrap(), "Z".to_string())])
.entanglement(EntanglementTopology::Linear);
let circuit = fm.build_circuit("x").unwrap();
let ops = circuit.operations();
assert_eq!(ops.len(), 4);
assert!(is_h_gate_on_qubit(&ops[0], 0));
assert!(is_h_gate_on_qubit(&ops[1], 1));
assert!(is_rz_gate_with_param(&ops[2], 0));
assert!(is_rz_gate_with_param(&ops[3], 1));
}
#[test]
fn test_pauli_feature_map_default_zz_structure() {
let fm = PauliFeatureMap::new(2)
.reps(1)
.paulis(vec![(
"ZZ".parse::<PauliString>().unwrap(),
"ZZ".to_string(),
)])
.entanglement(EntanglementTopology::Linear);
let circuit = fm.build_circuit("x").unwrap();
let ops = circuit.operations();
assert_eq!(ops.len(), 5);
assert!(is_h_gate_on_qubit(&ops[0], 0));
assert!(is_h_gate_on_qubit(&ops[1], 1));
assert!(is_cnot_gate(&ops[2], 0, 1));
assert!(is_rz_gate_with_param(&ops[3], 1));
assert!(is_cnot_gate(&ops[4], 0, 1));
}
#[test]
fn test_pauli_feature_map_default_z_and_zz_structure() {
let fm = PauliFeatureMap::new(2)
.reps(1)
.entanglement(EntanglementTopology::Linear);
let circuit = fm.build_circuit("x").unwrap();
let ops = circuit.operations();
assert_eq!(ops.len(), 7);
assert!(is_h_gate_on_qubit(&ops[0], 0));
assert!(is_h_gate_on_qubit(&ops[1], 1));
assert!(is_rz_gate_with_param(&ops[2], 0));
assert!(is_rz_gate_with_param(&ops[3], 1));
assert!(is_cnot_gate(&ops[4], 0, 1));
assert!(is_rz_gate_with_param(&ops[5], 1));
assert!(is_cnot_gate(&ops[6], 0, 1));
}
#[test]
fn test_pauli_feature_map_3qubits_linear_entanglement() {
let fm = PauliFeatureMap::new(3)
.reps(1)
.paulis(vec![(
"ZZ".parse::<PauliString>().unwrap(),
"ZZ".to_string(),
)])
.entanglement(EntanglementTopology::Linear);
let circuit = fm.build_circuit("x").unwrap();
let ops = circuit.operations();
assert_eq!(ops.len(), 9);
assert!(is_h_gate_on_qubit(&ops[0], 0));
assert!(is_h_gate_on_qubit(&ops[1], 1));
assert!(is_h_gate_on_qubit(&ops[2], 2));
assert!(is_cnot_gate(&ops[3], 0, 1));
assert!(is_rz_gate_with_param(&ops[4], 1));
assert!(is_cnot_gate(&ops[5], 0, 1));
assert!(is_cnot_gate(&ops[6], 1, 2));
assert!(is_rz_gate_with_param(&ops[7], 2));
assert!(is_cnot_gate(&ops[8], 1, 2));
}
#[test]
fn test_pauli_feature_map_3qubits_circular_entanglement() {
let fm = PauliFeatureMap::new(3)
.reps(1)
.paulis(vec![(
"ZZ".parse::<PauliString>().unwrap(),
"ZZ".to_string(),
)])
.entanglement(EntanglementTopology::Circular);
let circuit = fm.build_circuit("x").unwrap();
let ops = circuit.operations();
assert_eq!(ops.len(), 12);
assert!(is_h_gate_on_qubit(&ops[0], 0));
assert!(is_h_gate_on_qubit(&ops[1], 1));
assert!(is_h_gate_on_qubit(&ops[2], 2));
assert!(is_cnot_gate(&ops[3], 0, 1));
assert!(is_rz_gate_with_param(&ops[4], 1));
assert!(is_cnot_gate(&ops[5], 0, 1));
assert!(is_cnot_gate(&ops[6], 1, 2));
assert!(is_rz_gate_with_param(&ops[7], 2));
assert!(is_cnot_gate(&ops[8], 1, 2));
assert!(is_cnot_gate(&ops[9], 0, 2));
assert!(is_rz_gate_with_param(&ops[10], 2));
assert!(is_cnot_gate(&ops[11], 0, 2));
}
#[test]
fn test_pauli_feature_map_custom_topology() {
let fm = PauliFeatureMap::new(4)
.reps(1)
.paulis(vec![(
"ZZ".parse::<PauliString>().unwrap(),
"ZZ".to_string(),
)])
.entanglement(EntanglementTopology::Custom(vec![(0, 2), (1, 3)]));
let circuit = fm.build_circuit("x").unwrap();
let ops = circuit.operations();
assert_eq!(ops.len(), 10);
assert!(is_h_gate_on_qubit(&ops[0], 0));
assert!(is_h_gate_on_qubit(&ops[1], 1));
assert!(is_h_gate_on_qubit(&ops[2], 2));
assert!(is_h_gate_on_qubit(&ops[3], 3));
assert!(is_cnot_gate(&ops[4], 0, 2));
assert!(is_rz_gate_with_param(&ops[5], 2));
assert!(is_cnot_gate(&ops[6], 0, 2));
assert!(is_cnot_gate(&ops[7], 1, 3));
assert!(is_rz_gate_with_param(&ops[8], 3));
assert!(is_cnot_gate(&ops[9], 1, 3));
}
#[test]
fn test_pauli_feature_map_x_pauli_structure() {
let fm = PauliFeatureMap::new(2)
.reps(1)
.paulis(vec![("X".parse::<PauliString>().unwrap(), "X".to_string())])
.entanglement(EntanglementTopology::Linear);
let circuit = fm.build_circuit("x").unwrap();
let ops = circuit.operations();
assert_eq!(ops.len(), 8);
assert!(is_h_gate_on_qubit(&ops[0], 0));
assert!(is_h_gate_on_qubit(&ops[1], 1));
assert!(is_h_gate_on_qubit(&ops[2], 0));
assert!(is_rz_gate_with_param(&ops[3], 0));
assert!(is_h_gate_on_qubit(&ops[4], 0));
assert!(is_h_gate_on_qubit(&ops[5], 1));
assert!(is_rz_gate_with_param(&ops[6], 1));
assert!(is_h_gate_on_qubit(&ops[7], 1));
}
#[test]
fn test_pauli_feature_map_y_pauli_structure() {
let fm = PauliFeatureMap::new(2)
.reps(1)
.paulis(vec![("Y".parse::<PauliString>().unwrap(), "Y".to_string())])
.entanglement(EntanglementTopology::Linear);
let circuit = fm.build_circuit("x").unwrap();
let ops = circuit.operations();
assert_eq!(ops.len(), 12);
assert!(is_h_gate_on_qubit(&ops[0], 0));
assert!(is_h_gate_on_qubit(&ops[1], 1));
}
#[test]
fn test_pauli_feature_map_xy_pauli_structure() {
let fm = PauliFeatureMap::new(2)
.reps(1)
.paulis(vec![(
"XY".parse::<PauliString>().unwrap(),
"XY".to_string(),
)])
.entanglement(EntanglementTopology::Linear);
let circuit = fm.build_circuit("x").unwrap();
let ops = circuit.operations();
assert_eq!(ops.len(), 11);
}
#[test]
fn test_pauli_feature_map_validation_zero_qubits() {
let fm = PauliFeatureMap::new(0);
let result = fm.build_circuit("x");
assert!(result.is_err());
match result.unwrap_err() {
CircuitError::InvalidOperation(msg) => {
assert_eq!(msg, "PauliFeatureMap requires at least 1 qubit");
}
_ => panic!("Expected InvalidOperation error"),
}
}
#[test]
fn test_pauli_feature_map_validation_pauli_too_long() {
let fm = PauliFeatureMap::new(1).paulis(vec![(
"ZZ".parse::<PauliString>().unwrap(),
"ZZ".to_string(),
)]);
let result = fm.build_circuit("x");
assert!(result.is_err());
match result.unwrap_err() {
CircuitError::InvalidOperation(msg) => {
assert!(msg.contains("has length 2 which exceeds num_qubits 1"));
}
_ => panic!("Expected InvalidOperation error"),
}
}
#[test]
fn test_pauli_feature_map_validation_custom_topology_out_of_bounds() {
let fm = PauliFeatureMap::new(2)
.paulis(vec![(
"ZZ".parse::<PauliString>().unwrap(),
"ZZ".to_string(),
)])
.entanglement(EntanglementTopology::Custom(vec![(0, 5)]));
let result = fm.build_circuit("x");
assert!(result.is_err());
match result.unwrap_err() {
CircuitError::InvalidOperation(msg) => {
assert!(msg.contains("out-of-bounds"));
}
_ => panic!("Expected InvalidOperation error"),
}
}
#[test]
fn test_pauli_feature_map_validation_custom_topology_self_loop() {
let fm = PauliFeatureMap::new(3)
.paulis(vec![(
"ZZ".parse::<PauliString>().unwrap(),
"ZZ".to_string(),
)])
.entanglement(EntanglementTopology::Custom(vec![(1, 1)]));
let result = fm.build_circuit("x");
assert!(result.is_err());
match result.unwrap_err() {
CircuitError::InvalidOperation(msg) => {
assert!(msg.contains("self-loop"));
}
_ => panic!("Expected InvalidOperation error"),
}
}
#[test]
fn test_pauli_feature_map_validation_custom_topology_duplicate_edge() {
let fm = PauliFeatureMap::new(3)
.paulis(vec![(
"ZZ".parse::<PauliString>().unwrap(),
"ZZ".to_string(),
)])
.entanglement(EntanglementTopology::Custom(vec![(0, 1), (1, 0)]));
let result = fm.build_circuit("x");
assert!(result.is_err());
match result.unwrap_err() {
CircuitError::InvalidOperation(msg) => {
assert!(msg.contains("duplicate edge"));
}
_ => panic!("Expected InvalidOperation error"),
}
}
#[test]
fn test_pauli_feature_map_zzz_3local_structure() {
let fm = PauliFeatureMap::new(3)
.reps(1)
.paulis(vec![(
"ZZZ".parse::<PauliString>().unwrap(),
"ZZZ".to_string(),
)])
.entanglement(EntanglementTopology::Full);
let circuit = fm.build_circuit("x").unwrap();
let ops = circuit.operations();
assert_eq!(ops.len(), 8);
assert!(is_h_gate_on_qubit(&ops[0], 0));
assert!(is_h_gate_on_qubit(&ops[1], 1));
assert!(is_h_gate_on_qubit(&ops[2], 2));
assert!(is_cnot_gate(&ops[3], 0, 1));
assert!(is_cnot_gate(&ops[4], 1, 2));
assert!(is_rz_gate_with_param(&ops[5], 2));
assert!(is_cnot_gate(&ops[6], 1, 2));
assert!(is_cnot_gate(&ops[7], 0, 1));
}
#[test]
fn test_pauli_feature_map_zzz_4qubits_full_topology() {
let fm = PauliFeatureMap::new(4)
.reps(1)
.paulis(vec![(
"ZZZ".parse::<PauliString>().unwrap(),
"ZZZ".to_string(),
)])
.entanglement(EntanglementTopology::Full);
let circuit = fm.build_circuit("x").unwrap();
let ops = circuit.operations();
assert_eq!(ops.len(), 24);
assert_eq!(fm.num_parameters(), 4);
}
#[test]
fn test_pauli_feature_map_z_parameter_value() {
let fm = PauliFeatureMap::new(1)
.reps(1)
.paulis(vec![("Z".parse::<PauliString>().unwrap(), "Z".to_string())]);
let circuit = fm.build_circuit("x").unwrap();
let ops = circuit.operations();
assert_eq!(ops.len(), 2); assert!(is_h_gate_on_qubit(&ops[0], 0));
assert!(is_rz_gate_with_param(&ops[1], 0));
match &ops[1].params[0] {
CircuitParam::Index(_) => {
let params = circuit.parameters();
let param_str = params[0].to_string();
assert!(param_str.contains("2") || param_str.contains("x_0"));
}
CircuitParam::Fixed(_) => {
}
}
}
#[test]
fn test_pauli_feature_map_full_entanglement_3qubits() {
let fm = PauliFeatureMap::new(3)
.reps(1)
.paulis(vec![(
"ZZ".parse::<PauliString>().unwrap(),
"ZZ".to_string(),
)])
.entanglement(EntanglementTopology::Full);
let circuit = fm.build_circuit("x").unwrap();
let ops = circuit.operations();
assert_eq!(ops.len(), 12);
for (i, op) in ops.iter().enumerate().take(3) {
assert!(is_h_gate_on_qubit(op, i));
}
}
#[test]
fn test_pauli_feature_map_matches_zz_feature_map_for_equivalent_config() {
use crate::circuit::ansatz::ZZFeatureMap;
let pauli_fm = PauliFeatureMap::new(2)
.reps(1)
.entanglement(EntanglementTopology::Linear);
let zz_fm = ZZFeatureMap::new(2)
.reps(1)
.entanglement(EntanglementTopology::Linear);
assert_eq!(pauli_fm.num_qubits(), zz_fm.num_qubits());
assert_eq!(pauli_fm.num_parameters(), zz_fm.num_parameters());
}