قم بإنشاء محاكي Q # الخاص بك - الجزء 1

تعد أجهزة المحاكاة ميزة متعددة الاستخدامات بشكل خاص في QDK. إنها تسمح لك بأداء مهام مختلفة في برنامج Q # دون تغييره. تتضمن مثل هذه المهام محاكاة الحالة الكاملة أو تقدير الموارد أو محاكاة التتبع . IQuantumProcessorتجعل الواجهة الجديدة من السهل جدًا إنشاء أجهزة محاكاة خاصة بك ودمجها في مشاريع Q # الخاصة بك.



هذا المنشور هو الأول في سلسلة على هذه الواجهة. سنبدأ بتطبيق محاكي عكسي كمثال أول ، والذي سنتوسع فيه في منشورات المدونة المستقبلية. محاكاة عكسها يمكن محاكاة برامج الكم التي تتكون فقط من عمليات الكلاسيكية: X، CNOT،CCNOT(بوابة توفولي) أو عمليات X ذات التحكم العشوائي. نظرًا لأن المحاكي القابل للانعكاس يمكن أن يمثل حالة كمومية من خلال تعيين قيمة منطقية واحدة لكل كيوبت ، فإنه يمكنه حتى تشغيل برامج كمومية لآلاف الكيوبتات. هذا المحاكي مفيد جدًا لاختبار العمليات الكمية التي تقيم الوظائف المنطقية.







تنفيذ المحاكي في C #



تسلط مشاركة المدونة هذه الضوء على مقتطفات التعليمات البرمجية الأساسية. يمكن العثور على الكود المصدري الكامل في مستودع نماذج Microsoft QDK.


تبدأ في كتابة المحاكاة الخاصة بك عن طريق تمديد الفصل QuantumProcessorBase:



class ReversibleSimulatorProcessor : QuantumProcessorBase {
    private IDictionary<Qubit, bool> simulationValues = new Dictionary<Qubit, bool>();

    //       (intrinsic operations)...
}


تمت إضافة قاموس يقوم بتخزين قيمة المحاكاة الحالية لكل كيوبت في البرنامج بالفعل إلى الفصل. يتم تمثيل حالات الكم الكلاسيكية | 0⟩ و | 1⟩ كقيم منطقية كاذبة وصحيحة ، على التوالي. تحدد المحاكيات العمليات الداخلية في برنامج Q #. QuantumProcessorBaseيحتوي على طريقة لكل عملية داخلية يمكنك تجاوزها لتحديد سلوكها في المحاكي الجديد. لاحظ أن الطرق غير المطبقة ستؤدي إلى استثناء افتراضي. سيساعدك هذا في تحديد الحالات التي لا تزال فيها العملية بحاجة إلى التنفيذ وسيبلغ مستخدم جهاز المحاكاة أن العملية المضمنة غير مدعومة من قبل المحاكي. على سبيل المثال ، لا يمكن استخدام المحاكاة العكسية لمحاكاة البرامج الكمية التي تحتوي على عمليات غير تقليدية مثل H أو T.



لنبدأ من خلال التهيئة الخاطئة للكيوبتات المخصصة حديثًا عن طريق تجاوز طريقة OnAllocateQubits. وبالمثل ، تتم إزالة الكيوبتات من القاموس عند إصدارها. في هذه الحالة ، يتم استدعاء OnReleaseQubits.



public override void OnAllocateQubits(IQArray qubits) {
    foreach (var qubit in qubits) {
        simulationValues[qubit] = false;
    }
}

public override void OnReleaseQubits(IQArray qubits) {
    foreach (var qubit in qubits) {
        simulationValues.Remove(qubit);
    }
}


من خلال هاتين العمليتين ، نضمن أن تكون قيم المحاكاة متاحة عند تطبيق العملية على بعض الكيوبت ، وأنه لا توجد قيم محاكاة متبقية في القاموس للكيوبتات غير الموجودة في المنطقة الحالية.



كخطوة تالية ، دعنا ننفذ إجراءات العمليات الكلاسيكية. في هذه الحالة ، تكفي طريقتان: يتم استدعاء طريقة X عند محاكاة عملية X ، ويتم استدعاء طريقة ControlledX عند محاكاة عملية X ، والتي تتضمن أيضًا CNOT و CCNOT. تعكس العملية X قيمة المحاكاة للكيوبت ، بينما في حالة عملية X التي يتم التحكم فيها بشكل تعسفي ، يتم عكس الكيوبت المستهدف إذا وفقط إذا تم ضبط جميع كيوبت التحكم على صحيح.



public override void X(Qubit qubit) {
    simulationValues[qubit] = !simulationValues[qubit];
}

public override void ControlledX(IQArray controls, Qubit qubit) {
    simulationValues[qubit] ^= And(controls);
}


أخيرًا ، يؤدي قياس كيوبت في برنامج Q # إلى إرجاع نتيجة (واحد أو صفر) ، والتي يمكن حسابها بناءً على قيمة المحاكاة الحالية للكيوبت. ننفذ أيضًا طريقة إعادة تعيين تسمى عند استدعاء عملية إعادة التعيين في برنامج Q #. هذا الأخير لا يزيل qubit من المنطقة الحالية ، ولكنه يعيد تعيين قيمة المحاكاة إلى قيمتها الأولية ، وهي خاطئة.



public override Result M(Qubit qubit) {
    return simulationValues[qubit] ? Result.One : Result.Zero;
}

public override void Reset(Qubit qubit) {
    simulationValues[qubit] = false;
}


يمكن استخدام المعالج ReversibleSimulatorProcessor كمحاكي عن طريق إنشاء مثيل لـ QuantumProcessorDispatcher. أسلوب التصميم الموصى به هو توفير فئة مخصصة للمحاكي:



public class ReversibleSimulator : QuantumProcessorDispatcher {
    public ReversibleSimulator() : base(new ReversibleSimulatorProcessor()) {}
}


استخدام جهاز المحاكاة الجديد



لنجرب المحاكي الجديد في عملية Q # التالية:



operation ApplyMajority(a : Qubit, b : Qubit, c : Qubit, f : Qubit) : Unit {
    within {
        CNOT(b, a);
        CNOT(b, c);
    } apply {
        CCNOT(a, c, f);
        CNOT(b, f);
    }
}


نكتب أيضًا عملية تؤدي عملية أغلبية من خلال توفير ثلاثة مدخلات منطقية:



operation RunMajority(a : Bool, b : Bool, c : Bool) : Bool {
    using ((qa, qb, qc, f) = (Qubit(), Qubit(), Qubit(), Qubit())) {
        within {
            ApplyPauliFromBitString(PauliX, true, [a, b, c], [qa, qb, qc]);
        } apply {
            ApplyMajority(qa, qb, qc, f);
        }
        return MResetZ(f) == One;
    }
}


أخيرًا ، يمكنك تجميعها جميعًا عن طريق استدعاء عملية Q # باستخدام محاكي جديد في برنامج مضيف C #. يقيّم المثال التالي العملية الرئيسية لجميع وجهات الإدخال المختلفة ويخرج جميع نتائج المحاكاة:



public static void Main(string[] args) {
    var sim = new ReversibleSimulator();
    var bits = new[] {false, true};

    foreach (var a in bits) {
        foreach (var b in bits) {
            foreach (var c in bits) {
                var f = RunMajority.Run(sim, a, b, c).Result;
                Console.WriteLine($"Majority({a,5}, {b,5}, {c,5})  =  {f,5}");
            }
        }
    }
}


على استعداد لكتابة المحاكاة الخاصة بك؟



نأمل أن يلهمك هذا المنشور لإنشاء جهاز محاكاة خاص بك. في الخطوات التالية ، سنناقش كيفية تحسين أداء التنفيذ الحالي (على سبيل المثال ، من خلال عدم استخدام قاموس لتخزين قيم المحاكاة) ، وكيفية تحويله إلى مشروع Q # مستقل ، وكيفية توفير إجراءات مخصصة للعمليات غير الجوهرية ، وكيفية توفير عمليات التشخيص التي مساعدة في التصحيح.



All Articles