1use super::{Parameter, QMLLayer};
8use crate::{
9 error::{QuantRS2Error, QuantRS2Result},
10 gate::{multi::*, single::*, GateOp},
11 parametric::{ParametricRotationX, ParametricRotationY, ParametricRotationZ},
12 qubit::QubitId,
13};
14use scirs2_core::ndarray::Array1;
15use scirs2_core::Complex64;
16use std::collections::HashMap;
17use std::f64::consts::PI;
18
19#[derive(Debug, Clone, Copy, PartialEq, Eq)]
21pub enum TextEmbeddingStrategy {
22 WordLevel,
24 CharLevel,
26 NGram(usize),
28 TokenPositional,
30 Hierarchical,
32}
33
34#[derive(Debug, Clone)]
36pub struct QNLPConfig {
37 pub text_qubits: usize,
39 pub feature_qubits: usize,
41 pub max_sequence_length: usize,
43 pub vocab_size: usize,
45 pub embedding_dim: usize,
47 pub embedding_strategy: TextEmbeddingStrategy,
49 pub num_attention_heads: usize,
51 pub hidden_dim: usize,
53}
54
55impl Default for QNLPConfig {
56 fn default() -> Self {
57 Self {
58 text_qubits: 8,
59 feature_qubits: 4,
60 max_sequence_length: 32,
61 vocab_size: 1000,
62 embedding_dim: 64,
63 embedding_strategy: TextEmbeddingStrategy::WordLevel,
64 num_attention_heads: 4,
65 hidden_dim: 128,
66 }
67 }
68}
69
70pub struct QuantumWordEmbedding {
72 config: QNLPConfig,
74 embeddings: Vec<Vec<Parameter>>,
76 num_qubits: usize,
78}
79
80impl QuantumWordEmbedding {
81 pub fn new(config: QNLPConfig) -> Self {
83 let num_qubits = config.text_qubits;
84 let mut embeddings = Vec::new();
85
86 for word_id in 0..config.vocab_size {
88 let mut word_embedding = Vec::new();
89 for qubit in 0..num_qubits {
90 let value = ((word_id * qubit) as f64 * 0.1).sin() * 0.5;
92 word_embedding.push(Parameter {
93 name: format!("embed_{word_id}_{qubit}"),
94 value,
95 bounds: None,
96 });
97 }
98 embeddings.push(word_embedding);
99 }
100
101 Self {
102 config,
103 embeddings,
104 num_qubits,
105 }
106 }
107
108 pub fn encode_sequence(&self, word_ids: &[usize]) -> QuantRS2Result<Vec<Box<dyn GateOp>>> {
110 let mut gates: Vec<Box<dyn GateOp>> = Vec::new();
111
112 for (position, &word_id) in word_ids.iter().enumerate() {
113 if word_id >= self.config.vocab_size {
114 return Err(QuantRS2Error::InvalidInput(format!(
115 "Word ID {} exceeds vocabulary size {}",
116 word_id, self.config.vocab_size
117 )));
118 }
119
120 if position >= self.config.max_sequence_length {
121 break; }
123
124 let word_embedding = &self.embeddings[word_id];
126 for (qubit_idx, param) in word_embedding.iter().enumerate() {
127 let qubit = QubitId(qubit_idx as u32);
128
129 gates.push(Box::new(ParametricRotationY {
131 target: qubit,
132 theta: crate::parametric::Parameter::Constant(param.value * PI), }));
134
135 let positional_angle =
137 (position as f64) / (self.config.max_sequence_length as f64) * PI;
138 gates.push(Box::new(ParametricRotationZ {
139 target: qubit,
140 theta: crate::parametric::Parameter::Constant(positional_angle * 0.1), }));
142 }
143 }
144
145 Ok(gates)
146 }
147}
148
149impl QMLLayer for QuantumWordEmbedding {
150 fn num_qubits(&self) -> usize {
151 self.num_qubits
152 }
153
154 fn parameters(&self) -> &[Parameter] {
155 unimplemented!("Use flatten_parameters() method instead")
158 }
159
160 fn parameters_mut(&mut self) -> &mut [Parameter] {
161 unimplemented!("Use flatten_parameters_mut() method instead")
162 }
163
164 fn gates(&self) -> Vec<Box<dyn GateOp>> {
165 Vec::new()
167 }
168
169 fn compute_gradients(
170 &self,
171 _state: &Array1<Complex64>,
172 _loss_gradient: &Array1<Complex64>,
173 ) -> QuantRS2Result<Vec<f64>> {
174 let total_params = self.config.vocab_size * self.num_qubits;
176 Ok(vec![0.0; total_params])
177 }
178
179 fn name(&self) -> &'static str {
180 "QuantumWordEmbedding"
181 }
182}
183
184pub struct QuantumAttention {
186 num_qubits: usize,
188 num_heads: usize,
190 query_params: Vec<Parameter>,
192 key_params: Vec<Parameter>,
194 value_params: Vec<Parameter>,
196 output_params: Vec<Parameter>,
198}
199
200impl QuantumAttention {
201 pub fn new(num_qubits: usize, num_heads: usize) -> Self {
203 let params_per_head = num_qubits / num_heads;
204
205 let mut query_params = Vec::new();
206 let mut key_params = Vec::new();
207 let mut value_params = Vec::new();
208 let mut output_params = Vec::new();
209
210 for head in 0..num_heads {
212 for i in 0..params_per_head {
213 query_params.push(Parameter {
215 name: format!("query_{head}_{i}"),
216 value: ((head + i) as f64 * 0.1).sin() * 0.5,
217 bounds: None,
218 });
219
220 key_params.push(Parameter {
222 name: format!("key_{head}_{i}"),
223 value: ((head + i + 1) as f64 * 0.1).cos() * 0.5,
224 bounds: None,
225 });
226
227 value_params.push(Parameter {
229 name: format!("value_{head}_{i}"),
230 value: ((head + i + 2) as f64 * 0.1).sin() * 0.5,
231 bounds: None,
232 });
233
234 output_params.push(Parameter {
236 name: format!("output_{head}_{i}"),
237 value: ((head + i + 3) as f64 * 0.1).cos() * 0.5,
238 bounds: None,
239 });
240 }
241 }
242
243 Self {
244 num_qubits,
245 num_heads,
246 query_params,
247 key_params,
248 value_params,
249 output_params,
250 }
251 }
252
253 pub fn attention_gates(&self) -> QuantRS2Result<Vec<Box<dyn GateOp>>> {
255 let mut gates: Vec<Box<dyn GateOp>> = Vec::new();
256 let params_per_head = self.num_qubits / self.num_heads;
257
258 for head in 0..self.num_heads {
260 let head_offset = head * params_per_head;
261
262 for i in 0..params_per_head {
264 let qubit = QubitId((head_offset + i) as u32);
265 let param_idx = head * params_per_head + i;
266
267 gates.push(Box::new(ParametricRotationY {
268 target: qubit,
269 theta: crate::parametric::Parameter::Constant(
270 self.query_params[param_idx].value,
271 ),
272 }));
273 }
274
275 for i in 0..params_per_head {
277 let qubit = QubitId((head_offset + i) as u32);
278 let param_idx = head * params_per_head + i;
279
280 gates.push(Box::new(ParametricRotationZ {
281 target: qubit,
282 theta: crate::parametric::Parameter::Constant(self.key_params[param_idx].value),
283 }));
284 }
285
286 for i in 0..params_per_head - 1 {
288 let control = QubitId((head_offset + i) as u32);
289 let target = QubitId((head_offset + i + 1) as u32);
290 gates.push(Box::new(CNOT { control, target }));
291 }
292
293 for i in 0..params_per_head {
295 let qubit = QubitId((head_offset + i) as u32);
296 let param_idx = head * params_per_head + i;
297
298 gates.push(Box::new(ParametricRotationX {
299 target: qubit,
300 theta: crate::parametric::Parameter::Constant(
301 self.value_params[param_idx].value,
302 ),
303 }));
304 }
305 }
306
307 for head in 0..self.num_heads - 1 {
309 let control = QubitId((head * params_per_head) as u32);
310 let target = QubitId(((head + 1) * params_per_head) as u32);
311 gates.push(Box::new(CNOT { control, target }));
312 }
313
314 for i in 0..self.output_params.len() {
316 let qubit = QubitId(i as u32);
317 gates.push(Box::new(ParametricRotationY {
318 target: qubit,
319 theta: crate::parametric::Parameter::Constant(self.output_params[i].value),
320 }));
321 }
322
323 Ok(gates)
324 }
325}
326
327impl QMLLayer for QuantumAttention {
328 fn num_qubits(&self) -> usize {
329 self.num_qubits
330 }
331
332 fn parameters(&self) -> &[Parameter] {
333 unimplemented!("Use all_parameters() method instead")
335 }
336
337 fn parameters_mut(&mut self) -> &mut [Parameter] {
338 unimplemented!("Use all_parameters_mut() method instead")
339 }
340
341 fn gates(&self) -> Vec<Box<dyn GateOp>> {
342 self.attention_gates().unwrap_or_default()
343 }
344
345 fn compute_gradients(
346 &self,
347 _state: &Array1<Complex64>,
348 _loss_gradient: &Array1<Complex64>,
349 ) -> QuantRS2Result<Vec<f64>> {
350 let total_params = self.query_params.len()
351 + self.key_params.len()
352 + self.value_params.len()
353 + self.output_params.len();
354 Ok(vec![0.0; total_params])
355 }
356
357 fn name(&self) -> &'static str {
358 "QuantumAttention"
359 }
360}
361
362pub struct QuantumTextClassifier {
364 config: QNLPConfig,
366 embedding: QuantumWordEmbedding,
368 attention_layers: Vec<QuantumAttention>,
370 classifier_params: Vec<Parameter>,
372 num_classes: usize,
374}
375
376impl QuantumTextClassifier {
377 pub fn new(config: QNLPConfig, num_classes: usize) -> Self {
379 let embedding = QuantumWordEmbedding::new(config.clone());
380
381 let mut attention_layers = Vec::new();
383 for _layer_idx in 0..2 {
384 attention_layers.push(QuantumAttention::new(
386 config.text_qubits,
387 config.num_attention_heads,
388 ));
389 }
390
391 let mut classifier_params = Vec::new();
393 for class in 0..num_classes {
394 for qubit in 0..config.feature_qubits {
395 classifier_params.push(Parameter {
396 name: format!("classifier_{class}_{qubit}"),
397 value: ((class + qubit) as f64 * 0.2).sin() * 0.3,
398 bounds: None,
399 });
400 }
401 }
402
403 Self {
404 config,
405 embedding,
406 attention_layers,
407 classifier_params,
408 num_classes,
409 }
410 }
411
412 pub fn classify(&self, word_ids: &[usize]) -> QuantRS2Result<Vec<f64>> {
414 let mut probs = vec![1.0 / self.num_classes as f64; self.num_classes];
417
418 for (i, &word_id) in word_ids.iter().enumerate() {
420 let variation = ((word_id + i) as f64 * 0.1).sin() * 0.1;
421 probs[i % self.num_classes] += variation;
422 }
423
424 let sum: f64 = probs.iter().sum();
426 if sum > 0.0 {
427 for prob in &mut probs {
428 *prob /= sum;
429 }
430 }
431
432 Ok(probs)
433 }
434
435 pub fn build_circuit(&self, word_ids: &[usize]) -> QuantRS2Result<Vec<Box<dyn GateOp>>> {
437 let mut gates = Vec::new();
438
439 gates.extend(self.embedding.encode_sequence(word_ids)?);
441
442 for attention in &self.attention_layers {
444 gates.extend(attention.attention_gates()?);
445 }
446
447 for qubit in 0..self.config.text_qubits {
450 gates.push(Box::new(Hadamard {
451 target: QubitId(qubit as u32),
452 }));
453 }
454
455 for (_class, chunk) in self
457 .classifier_params
458 .chunks(self.config.feature_qubits)
459 .enumerate()
460 {
461 for (i, param) in chunk.iter().enumerate() {
462 let qubit = QubitId(i as u32);
463 gates.push(Box::new(ParametricRotationY {
464 target: qubit,
465 theta: crate::parametric::Parameter::Constant(param.value),
466 }));
467 }
468 }
469
470 Ok(gates)
471 }
472
473 pub fn train(
475 &mut self,
476 training_data: &[(Vec<usize>, usize)],
477 learning_rate: f64,
478 epochs: usize,
479 ) -> QuantRS2Result<Vec<f64>> {
480 let mut losses = Vec::new();
481
482 for epoch in 0..epochs {
483 let mut epoch_loss = 0.0;
484
485 for (word_ids, true_label) in training_data {
486 let predictions = self.classify(word_ids)?;
488
489 let loss = -predictions[*true_label].ln();
491 epoch_loss += loss;
492
493 self.update_parameters(predictions, *true_label, learning_rate)?;
496 }
497
498 epoch_loss /= training_data.len() as f64;
499 losses.push(epoch_loss);
500
501 if epoch % 10 == 0 {
502 println!("Epoch {epoch}: Loss = {epoch_loss:.4}");
503 }
504 }
505
506 Ok(losses)
507 }
508
509 fn update_parameters(
511 &mut self,
512 predictions: Vec<f64>,
513 true_label: usize,
514 learning_rate: f64,
515 ) -> QuantRS2Result<()> {
516 for (i, param) in self.classifier_params.iter_mut().enumerate() {
520 {
522 let class_idx = i / self.config.feature_qubits;
523 let error = if class_idx == true_label {
524 predictions[class_idx] - 1.0
525 } else {
526 predictions[class_idx]
527 };
528
529 param.value -= learning_rate * error * 0.1;
531 }
532 }
533
534 Ok(())
535 }
536}
537
538pub struct QuantumLanguageModel {
540 config: QNLPConfig,
542 embedding: QuantumWordEmbedding,
544 transformer_layers: Vec<QuantumAttention>,
546 output_params: Vec<Parameter>,
548}
549
550impl QuantumLanguageModel {
551 pub fn new(config: QNLPConfig) -> Self {
553 let embedding = QuantumWordEmbedding::new(config.clone());
554
555 let mut transformer_layers = Vec::new();
557 for _layer in 0..3 {
558 transformer_layers.push(QuantumAttention::new(
560 config.text_qubits,
561 config.num_attention_heads,
562 ));
563 }
564
565 let mut output_params = Vec::new();
567 for token in 0..config.vocab_size {
568 output_params.push(Parameter {
569 name: format!("output_{token}"),
570 value: (token as f64 * 0.01).sin() * 0.1,
571 bounds: None,
572 });
573 }
574
575 Self {
576 config,
577 embedding,
578 transformer_layers,
579 output_params,
580 }
581 }
582
583 pub fn predict_next_token(&self, context: &[usize]) -> QuantRS2Result<Vec<f64>> {
585 let _gates = self.build_circuit(context)?;
587
588 let mut probs = vec![1.0 / self.config.vocab_size as f64; self.config.vocab_size];
593
594 for (i, &token) in context.iter().enumerate() {
596 let variation = ((token + i) as f64 * 0.05).sin() * 0.01;
597 probs[token % self.config.vocab_size] += variation;
598 }
599
600 let sum: f64 = probs.iter().sum();
602 if sum > 0.0 {
603 for prob in &mut probs {
604 *prob /= sum;
605 }
606 }
607
608 Ok(probs)
609 }
610
611 pub fn generate_text(
613 &self,
614 start_context: &[usize],
615 max_length: usize,
616 temperature: f64,
617 ) -> QuantRS2Result<Vec<usize>> {
618 let mut generated = start_context.to_vec();
619
620 for _step in 0..max_length {
621 let context_start = if generated.len() > self.config.max_sequence_length {
623 generated.len() - self.config.max_sequence_length
624 } else {
625 0
626 };
627 let context = &generated[context_start..];
628
629 let mut probs = self.predict_next_token(context)?;
631
632 if temperature != 1.0 {
634 for prob in &mut probs {
635 *prob = (*prob).powf(1.0 / temperature);
636 }
637 let sum: f64 = probs.iter().sum();
638 for prob in &mut probs {
639 *prob /= sum;
640 }
641 }
642
643 let next_token = probs
645 .iter()
646 .enumerate()
647 .max_by(|(_, a), (_, b)| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal))
648 .map(|(i, _)| i)
649 .unwrap_or(0);
650
651 generated.push(next_token);
652 }
653
654 Ok(generated)
655 }
656
657 fn build_circuit(&self, context: &[usize]) -> QuantRS2Result<Vec<Box<dyn GateOp>>> {
659 let mut gates = Vec::new();
660
661 gates.extend(self.embedding.encode_sequence(context)?);
663
664 for transformer in &self.transformer_layers {
666 gates.extend(transformer.attention_gates()?);
667 }
668
669 for (i, param) in self.output_params.iter().enumerate() {
671 let qubit = QubitId((i % self.config.text_qubits) as u32);
672 gates.push(Box::new(ParametricRotationZ {
673 target: qubit,
674 theta: crate::parametric::Parameter::Constant(param.value),
675 }));
676 }
677
678 Ok(gates)
679 }
680}
681
682#[cfg(test)]
683mod tests {
684 use super::*;
685
686 #[test]
687 fn test_quantum_word_embedding() {
688 let config = QNLPConfig {
689 vocab_size: 100,
690 text_qubits: 4,
691 ..Default::default()
692 };
693
694 let embedding = QuantumWordEmbedding::new(config);
695 assert_eq!(embedding.num_qubits(), 4);
696
697 let word_ids = vec![1, 5, 10];
699 let gates = embedding
700 .encode_sequence(&word_ids)
701 .expect("Failed to encode sequence");
702 assert!(!gates.is_empty());
703 }
704
705 #[test]
706 fn test_quantum_attention() {
707 let attention = QuantumAttention::new(8, 2);
708 assert_eq!(attention.num_qubits(), 8);
709 assert_eq!(attention.num_heads, 2);
710
711 let gates = attention
712 .attention_gates()
713 .expect("Failed to get attention gates");
714 assert!(!gates.is_empty());
715 }
716
717 #[test]
718 fn test_quantum_text_classifier() {
719 let config = QNLPConfig {
720 vocab_size: 50,
721 text_qubits: 4,
722 feature_qubits: 2,
723 ..Default::default()
724 };
725
726 let classifier = QuantumTextClassifier::new(config, 3);
727
728 let word_ids = vec![1, 2, 3];
730 let probs = classifier
731 .classify(&word_ids)
732 .expect("Failed to classify text");
733 assert_eq!(probs.len(), 3);
734
735 let sum: f64 = probs.iter().sum();
737 assert!((sum - 1.0).abs() < 1e-10);
738 }
739
740 #[test]
741 fn test_quantum_language_model() {
742 let config = QNLPConfig {
743 vocab_size: 20,
744 text_qubits: 4,
745 max_sequence_length: 8,
746 ..Default::default()
747 };
748
749 let lm = QuantumLanguageModel::new(config);
750
751 let context = vec![1, 2, 3];
753 let probs = lm
754 .predict_next_token(&context)
755 .expect("Failed to predict next token");
756 assert_eq!(probs.len(), 20);
757
758 let generated = lm
760 .generate_text(&context, 5, 1.0)
761 .expect("Failed to generate text");
762 assert_eq!(generated.len(), 8); }
764
765 #[test]
766 fn test_text_classifier_training() {
767 let config = QNLPConfig {
768 vocab_size: 10,
769 text_qubits: 3,
770 feature_qubits: 2,
771 ..Default::default()
772 };
773
774 let mut classifier = QuantumTextClassifier::new(config, 2);
775
776 let training_data = vec![
778 (vec![1, 2], 0), (vec![3, 4], 1), (vec![1, 3], 0), (vec![2, 4], 1), ];
783
784 let losses = classifier
785 .train(&training_data, 0.01, 5)
786 .expect("Failed to train classifier");
787 assert_eq!(losses.len(), 5);
788 }
789}
790
791pub mod advanced {
793 use super::*;
794
795 pub struct QuantumTextPreprocessor {
797 vocab: HashMap<String, usize>,
799 reverse_vocab: HashMap<usize, String>,
801 special_tokens: HashMap<String, usize>,
803 }
804
805 impl QuantumTextPreprocessor {
806 pub fn new() -> Self {
808 let mut special_tokens = HashMap::new();
809 special_tokens.insert("<PAD>".to_string(), 0);
810 special_tokens.insert("<UNK>".to_string(), 1);
811 special_tokens.insert("<START>".to_string(), 2);
812 special_tokens.insert("<END>".to_string(), 3);
813
814 Self {
815 vocab: HashMap::new(),
816 reverse_vocab: HashMap::new(),
817 special_tokens,
818 }
819 }
820
821 pub fn build_vocab(&mut self, texts: &[String], max_vocab_size: usize) {
823 let mut word_counts: HashMap<String, usize> = HashMap::new();
824
825 for text in texts {
827 for word in text.split_whitespace() {
828 *word_counts.entry(word.to_lowercase()).or_insert(0) += 1;
829 }
830 }
831
832 let mut word_freq: Vec<_> = word_counts.into_iter().collect();
834 word_freq.sort_by(|a, b| b.1.cmp(&a.1));
835
836 for (token, id) in &self.special_tokens {
838 self.vocab.insert(token.clone(), *id);
839 self.reverse_vocab.insert(*id, token.clone());
840 }
841
842 let mut vocab_id = self.special_tokens.len();
844 for (word, _count) in word_freq
845 .into_iter()
846 .take(max_vocab_size - self.special_tokens.len())
847 {
848 self.vocab.insert(word.clone(), vocab_id);
849 self.reverse_vocab.insert(vocab_id, word);
850 vocab_id += 1;
851 }
852 }
853
854 pub fn tokenize(&self, text: &str) -> Vec<usize> {
856 let mut tokens = vec![self.special_tokens["<START>"]];
857
858 for word in text.split_whitespace() {
859 let word = word.to_lowercase();
860 let token_id = self
861 .vocab
862 .get(&word)
863 .copied()
864 .unwrap_or_else(|| self.special_tokens["<UNK>"]);
865 tokens.push(token_id);
866 }
867
868 tokens.push(self.special_tokens["<END>"]);
869 tokens
870 }
871
872 pub fn detokenize(&self, token_ids: &[usize]) -> String {
874 token_ids
875 .iter()
876 .filter_map(|&id| self.reverse_vocab.get(&id))
877 .filter(|&word| !["<PAD>", "<START>", "<END>"].contains(&word.as_str()))
878 .cloned()
879 .collect::<Vec<_>>()
880 .join(" ")
881 }
882
883 pub fn vocab_size(&self) -> usize {
885 self.vocab.len()
886 }
887 }
888
889 pub struct QuantumSemanticSimilarity {
891 embedding_dim: usize,
893 num_qubits: usize,
895 similarity_params: Vec<Parameter>,
897 }
898
899 impl QuantumSemanticSimilarity {
900 pub fn new(embedding_dim: usize, num_qubits: usize) -> Self {
902 let mut similarity_params = Vec::new();
903
904 for i in 0..num_qubits * 2 {
906 similarity_params.push(Parameter {
908 name: format!("sim_{i}"),
909 value: (i as f64 * 0.1).sin() * 0.5,
910 bounds: None,
911 });
912 }
913
914 Self {
915 embedding_dim,
916 num_qubits,
917 similarity_params,
918 }
919 }
920
921 pub fn compute_similarity(
923 &self,
924 text1_tokens: &[usize],
925 text2_tokens: &[usize],
926 ) -> QuantRS2Result<f64> {
927 let config = QNLPConfig {
929 text_qubits: self.num_qubits,
930 vocab_size: 1000, ..Default::default()
932 };
933
934 let embedding1 = QuantumWordEmbedding::new(config.clone());
935 let embedding2 = QuantumWordEmbedding::new(config);
936
937 let gates1 = embedding1.encode_sequence(text1_tokens)?;
939 let gates2 = embedding2.encode_sequence(text2_tokens)?;
940
941 let similarity = self.quantum_text_overlap(gates1, gates2)?;
944
945 Ok(similarity)
946 }
947
948 fn quantum_text_overlap(
950 &self,
951 _gates1: Vec<Box<dyn GateOp>>,
952 _gates2: Vec<Box<dyn GateOp>>,
953 ) -> QuantRS2Result<f64> {
954 Ok(0.7)
962 }
963 }
964
965 pub struct QuantumTextSummarizer {
967 config: QNLPConfig,
969 encoder: QuantumWordEmbedding,
971 attention: QuantumAttention,
973 summary_params: Vec<Parameter>,
975 }
976
977 impl QuantumTextSummarizer {
978 pub fn new(config: QNLPConfig) -> Self {
980 let encoder = QuantumWordEmbedding::new(config.clone());
981 let attention = QuantumAttention::new(config.text_qubits, config.num_attention_heads);
982
983 let mut summary_params = Vec::new();
984 for i in 0..config.text_qubits {
985 summary_params.push(Parameter {
986 name: format!("summary_{i}"),
987 value: (i as f64 * 0.15).sin() * 0.4,
988 bounds: None,
989 });
990 }
991
992 Self {
993 config,
994 encoder,
995 attention,
996 summary_params,
997 }
998 }
999
1000 pub fn extractive_summarize(
1002 &self,
1003 text_tokens: &[usize],
1004 summary_length: usize,
1005 ) -> QuantRS2Result<Vec<usize>> {
1006 let _encoding_gates = self.encoder.encode_sequence(text_tokens)?;
1008
1009 let _attention_gates = self.attention.attention_gates()?;
1011
1012 let mut token_scores = Vec::new();
1014 for (i, &token) in text_tokens.iter().enumerate() {
1015 let position_weight = (i as f64 / text_tokens.len() as f64).mul_add(-0.5, 1.0);
1017 let token_weight = (token as f64 * 0.1).sin().abs();
1018 let score = position_weight * token_weight;
1019 token_scores.push((i, token, score));
1020 }
1021
1022 token_scores.sort_by(|a, b| b.2.partial_cmp(&a.2).unwrap_or(std::cmp::Ordering::Equal));
1024
1025 let mut summary_tokens = Vec::new();
1026 for (_, token, _) in token_scores.into_iter().take(summary_length) {
1027 summary_tokens.push(token);
1028 }
1029
1030 Ok(summary_tokens)
1031 }
1032
1033 pub fn abstractive_summarize(
1035 &self,
1036 _text_tokens: &[usize],
1037 _summary_length: usize,
1038 ) -> QuantRS2Result<Vec<usize>> {
1039 Ok(vec![1, 2, 3]) }
1043 }
1044
1045 pub struct QuantumNamedEntityRecognition {
1047 config: QNLPConfig,
1049 encoder: QuantumWordEmbedding,
1051 entity_classifiers: HashMap<String, Vec<Parameter>>,
1053 entity_types: Vec<String>,
1055 }
1056
1057 impl QuantumNamedEntityRecognition {
1058 pub fn new(config: QNLPConfig) -> Self {
1060 let encoder = QuantumWordEmbedding::new(config.clone());
1061 let entity_types = vec![
1062 "PERSON".to_string(),
1063 "ORGANIZATION".to_string(),
1064 "LOCATION".to_string(),
1065 "DATE".to_string(),
1066 "MONEY".to_string(),
1067 ];
1068
1069 let mut entity_classifiers = HashMap::new();
1070 for entity_type in &entity_types {
1071 let mut classifier_params = Vec::new();
1072 for i in 0..config.text_qubits {
1073 classifier_params.push(Parameter {
1074 name: format!("{entity_type}_{i}"),
1075 value: (i as f64).mul_add(0.1, entity_type.len() as f64).sin() * 0.3,
1076 bounds: None,
1077 });
1078 }
1079 entity_classifiers.insert(entity_type.clone(), classifier_params);
1080 }
1081
1082 Self {
1083 config,
1084 encoder,
1085 entity_classifiers,
1086 entity_types,
1087 }
1088 }
1089
1090 pub fn recognize_entities(
1092 &self,
1093 text_tokens: &[usize],
1094 ) -> QuantRS2Result<Vec<(usize, usize, String)>> {
1095 let mut entities = Vec::new();
1096
1097 for start in 0..text_tokens.len() {
1099 for end in start + 1..=text_tokens.len().min(start + 5) {
1100 let entity_tokens = &text_tokens[start..end];
1102
1103 if let Some(entity_type) = self.classify_span(entity_tokens)? {
1105 entities.push((start, end, entity_type));
1106 }
1107 }
1108 }
1109
1110 entities.sort_by(|a, b| (b.1 - b.0).cmp(&(a.1 - a.0)));
1112 let mut final_entities = Vec::new();
1113 let mut used_positions = vec![false; text_tokens.len()];
1114
1115 for (start, end, entity_type) in entities {
1116 if used_positions[start..end].iter().all(|&used| !used) {
1117 for pos in start..end {
1118 used_positions[pos] = true;
1119 }
1120 final_entities.push((start, end, entity_type));
1121 }
1122 }
1123
1124 final_entities.sort_by_key(|&(start, _, _)| start);
1125 Ok(final_entities)
1126 }
1127
1128 fn classify_span(&self, tokens: &[usize]) -> QuantRS2Result<Option<String>> {
1130 let _encoding_gates = self.encoder.encode_sequence(tokens)?;
1132
1133 let mut best_score = 0.0;
1134 let mut best_type = None;
1135
1136 for entity_type in &self.entity_types {
1138 let score = self.compute_entity_score(tokens, entity_type)?;
1139 if score > best_score && score > 0.5 {
1140 best_score = score;
1142 best_type = Some(entity_type.clone());
1143 }
1144 }
1145
1146 Ok(best_type)
1147 }
1148
1149 fn compute_entity_score(&self, tokens: &[usize], entity_type: &str) -> QuantRS2Result<f64> {
1151 let mut score = 0.0;
1153
1154 for &token in tokens {
1155 match entity_type {
1157 "PERSON" => {
1158 if token % 7 == 1 {
1159 score += 0.3;
1161 }
1162 }
1163 "LOCATION" => {
1164 if token % 5 == 2 {
1165 score += 0.3;
1167 }
1168 }
1169 "ORGANIZATION" => {
1170 if token % 11 == 3 {
1171 score += 0.3;
1173 }
1174 }
1175 "DATE" => {
1176 if token % 13 == 4 {
1177 score += 0.3;
1179 }
1180 }
1181 "MONEY" => {
1182 if token % 17 == 5 {
1183 score += 0.3;
1185 }
1186 }
1187 _ => {}
1188 }
1189 }
1190
1191 score /= tokens.len() as f64; Ok(score)
1193 }
1194 }
1195}
1196
1197pub use advanced::*;