使用Reactive Extensions在Logic Gate Simulator中创建稳定的反馈环路?

时间:2013-03-29 03:32:24

标签: c# recursion system.reactive

我创建了一个非常简单的逻辑门模拟器,它可以在输入端采用IEnumerable馈送,并流经一系列门,在输出端准确地产生高/低位。

这个想法是将概念扩展到多门网络,将存储在内存数据库中的数组中的值提供给网络的输入,并复制逻辑门网络中的场景评估规则集。假设是Rx允许的流动作应该提供一种机制来及时评估400,000个场景,避免搜索和数组迭代。大多数输入仅仅是前馈,但有些需要使用网络输出,因为下一个方案的决策依赖于前一个方案的输出。

一切正常,直到我尝试在网络中创建反馈循环,试图将网络的输出流用作门的输入流,其输出是网络输出分辨率的一部分

我想知道如何获取输出流并以这种方式使用它。我确实看过"Recursive / fan-out in Reactive Extensions"感觉它可能会有所帮助,但我对Rx很新,并没有完全理解它。

这是我到目前为止的代码。很简单,有点凌乱(道歉)并且目前已经破解(尽管如果反馈尝试被删除并替换为前馈代码,它将起作用。)

        class Program
        {
            private static AndGate _andGate;
            private static XorGate _xorGate;

            static void Main(string[] args)
            {
                var data = new List<decimal>();
                var data2 = new List<decimal>();

                var output = new List<Tuple<Bit, Bit, Bit>>();

                var rnd = new Random();

                //for (var i = 0; i < 10000; i++)
                //{
                //    data.Add(rnd.Next(0, 100));
                //    data2.Add(rnd.Next(0, 100));
                //}
                data.Add(10);
                data.Add(30);
                data.Add(80);
                data.Add(5);
                data.Add(34);
                data.Add(27); 



                data2.Add(10);
                data2.Add(5);
                data2.Add(10);
                data2.Add(34);
                data2.Add(67);
                data2.Add(80);



                var stream = data.ToObservable();
                var stream2 = data2.ToObservable();

                var filter = new DataFilter(stream, a => a > 27);
                var filter2 = new DataFilter(stream2, a => a < 12);

                _andGate = new AndGate {InputA = filter.OutputStream};

                _andGate.InputB = new XorGate(filter2.OutputStream, _andGate.Output.Publish()).Output;

                _andGate.SetOutput();


                //filter.OutputStream.Subscribe(x => Console.WriteLine(x));
                //filter2.OutputStream.Subscribe(x => Console.WriteLine(x));
                _andGate.Output.Subscribe(x=>Console.WriteLine(x));

                //var zippedSequence =
                //    Observable.When(
                //        filter.OutputStream.And(filter2.OutputStream)
                //              .Then((first, second) => new [] { first, second }));


                //zippedSequence.Subscribe(x=>Console.WriteLine("{0}, {1}", x[0], x[1]));

                Console.ReadLine();
            }

        }

        public class DataFilter
        {
            protected IObservable<Bit> ValueStream;

            public IObservable<Bit> OutputStream
            {
                get { return ValueStream; }
            }

            public DataFilter(IObservable<decimal> stream, Func<decimal, bool> operation )
            {
                ValueStream = stream.Select(a => operation(a)? Bit.High : Bit.Low);
            }

        }

        public class AndGate
        {
            protected IObservable<Bit> StreamA; 
            protected IObservable<Bit> StreamB;
            protected IObservable<Bit> OutputStream;

            public IObservable<Bit> InputA { get { return StreamA; } set { StreamA = value; } }
            public IObservable<Bit> InputB { get { return StreamB; } set { StreamB = value; } }

            public IObservable<Bit> Output
            {
                get { return OutputStream; }
            }

            public AndGate()
            {
                OutputStream = Observable.When(StreamA.And(StreamB).Then(Determine));
            }

            public AndGate(IObservable<Bit> streamA, IObservable<Bit> streamB)
            {
                OutputStream = Observable.When(streamA.And(streamB).Then(Determine));
            }

            private Bit Determine(Bit bitA, Bit bitB)
            {
                return bitA == Bit.High && bitB == Bit.High ? Bit.High : Bit.Low;
            }
        }

        public class OrGate
        {
            protected IObservable<Bit> InputStreamA;
            protected IObservable<Bit> InputStreamB;
            protected IObservable<Bit> OutputStream;

            public IObservable<Bit> Output
            {
                get { return OutputStream; }
            }

            public OrGate()
            {

            }

            public OrGate(IObservable<Bit> streamA, IObservable<Bit> streamB)
            {
                OutputStream = Observable.When(streamA.And(streamB).Then(Determine));
            }

            private Bit Determine(Bit bitA, Bit bitB)
            {
                return bitA == Bit.High || bitB == Bit.High ? Bit.High : Bit.Low;
            }
        }

        public class XorGate
        {
            protected IObservable<Bit> StreamA;
            protected IObservable<Bit> StreamB;
            protected IObservable<Bit> OutputStream;

            public IObservable<Bit> Output
            {
                get { return OutputStream; }
            }

            public XorGate(IObservable<Bit> streamA, IObservable<Bit> streamB)
            {
                OutputStream = Observable.When(streamA.And(streamB).Then(Determine));
            }

            private Bit Determine(Bit bitA, Bit bitB)
            {
                return bitA == Bit.High ^ bitB == Bit.High ? Bit.High : Bit.Low;
            }
        }

        public enum Bit { Low = 0, High = 1}

编辑: 我添加了这个图表以帮助理解。红线是引起问题的线。 ; - )

Simple Logic diagram

另一个编辑: 我继续贬低这个小问题并发现了Rx的Generate和Publish / Connect元素,这些元素已经解决了这个问题。通过发布门网络的输出,可以检查输出并基于输出值设置网络元素的变化。 Generate方法允许我设置一个带有两个内部比特流的xor门,1个High和1个Low(最初),这个排列的输出为High。 xor门有一个状态位,可以使用公布的输出流从门外设置。此方法将xor门的输出锁存为High或Low。

        class Program
        {
            private static AndGate _andGate1;
            private static AndGate _andGate2;
            private static XorGate _xorGate;

            static void Main(string[] args)
            {
                var data = new List<decimal>();
                var data2 = new List<decimal>();

                var output = new List<Tuple<Bit, Bit, Bit>>();

                var rnd = new Random();

                //for (var i = 0; i < 10000; i++)
                //{
                //    data.Add(rnd.Next(0, 100));
                //    data2.Add(rnd.Next(0, 100));
                //}

                data.Add(10);
                data.Add(23);
                data.Add(80);
                data.Add(5);
                data.Add(34);
                data.Add(27); 

                data2.Add(10);
                data2.Add(5);
                data2.Add(10);
                data2.Add(34);
                data2.Add(67);
                data2.Add(80);

                //Raw Data streams
                var stream = data.ToObservable();
                var stream2 = data2.ToObservable();

                //Converted to Bit streams
                var filter = new DataFilter(stream, a => a > 27);
                var filter2 = new DataFilter(stream2, a => a < 12);

                //Gate network
                _xorGate = new XorGate();
                _andGate1 = new AndGate(filter.OutputStream, _xorGate.Output);
                _andGate2 = new AndGate(_andGate1.Output, filter2.OutputStream );

                //Publish and Connect to the outcome of the network
                var observable = _andGate2.Output.SubscribeOn(NewThreadScheduler.Default).Publish();
                observable.Connect();

                //Subscribe to the outcome to allow changes to be made to the XorGate
                observable.Subscribe(x => { if (x == Bit.High) { _xorGate.SetStatusBitHigh(x); } });

                //View the results
                observable.Subscribe(x => Console.WriteLine(x));

                Console.ReadLine();
            }

        }

        public class DataFilter
        {
            protected IObservable<Bit> ValueStream;

            public IObservable<Bit> OutputStream
            {
                get { return ValueStream; }
            }

            public DataFilter(IObservable<decimal> stream, Func<decimal, bool> operation )
            {
                ValueStream = stream.Select(a => operation(a)? Bit.High : Bit.Low);
            }

        }

        public class AndGate
        {
            protected IObservable<Bit> OutputStream;

            public IObservable<Bit> Output
            {
                get { return OutputStream; }
            }


            public AndGate(IObservable<Bit> streamA, IObservable<Bit> streamB)
            {
                OutputStream = Observable.When(streamA.And(streamB).Then(Determine));
            }

            private Bit Determine(Bit bitA, Bit bitB)
            {
                return bitA == Bit.High && bitB == Bit.High ? Bit.High : Bit.Low;
            }
        }

        /// <summary>
        /// This is really a bastardized XorGate as I force two Bit streams into the inputs
        /// Keep one High, start the other Low and then send it High when a high signal results from the output of the network
        /// This allows me to force the network output low i.e. take no action...
        /// ...until the Bit is sent low again by another network (no done yet)
        /// </summary>
        public class XorGate
        {
            protected IObservable<Bit> StreamA;
            protected IObservable<Bit> StreamB;
            protected IObservable<Bit> OutputStream;

            protected Bit StatusBit;

            public void SetStatusBitHigh(Bit input)
            {
                //No action if circumstances are as you want them
                if (input == StatusBit) return;

                //Belt and Braces check here
                if (StatusBit == Bit.Low && input == Bit.High)
                {
                    StatusBit = Bit.High;
                }

            }

            public void SetStatusBitLow(Bit input)
            {
                //No action if circumstances are as you want them
                if (input == StatusBit) return;

                //Belt and Braces check here
                if (StatusBit == Bit.High && input == Bit.Low)
                {
                    StatusBit = Bit.Low;
                }
            }

            /// <summary>
            /// Output the stream safely
            /// </summary>
            public IObservable<Bit> Output
            {
                get { return OutputStream; }
            }


            public XorGate()
            {
                //Set the StatusBit to Low initially
                StatusBit = Bit.Low;

                //Create a permanent high stream for one of the (imaginary) gate pins
                var streamA = Observable.Generate(Bit.High, bit => true, bit => bit, bit => bit);

                //Create a low stream for the other (imaginary) pin
                //One which sets itself to the value of the StatusBit so that a StatusBit = Bit.High will produce a High stream
                var streamB = Observable.Generate(Bit.Low, bit => true, bit => StatusBit, bit => bit);

                //Produce the output
                OutputStream = Observable.When(streamA.And(streamB).Then(Determine));
            }

            private Bit Determine(Bit bitA, Bit bitB)
            {
                return bitA == Bit.High ^ bitB == Bit.High ? Bit.High : Bit.Low;
            }
        }

        public class OrGate
        {
            protected IObservable<Bit> OutputStream;

            public IObservable<Bit> Output
            {
                get { return OutputStream; }
            }

            public OrGate(IObservable<Bit> streamA, IObservable<Bit> streamB)
            {
                OutputStream = Observable.When(streamA.And(streamB).Then(Determine));
            }

            private Bit Determine(Bit bitA, Bit bitB)
            {
                return bitA == Bit.High || bitB == Bit.High ? Bit.High : Bit.Low;
            }
        }

        public enum Bit { Low = 0, High = 1}

电路现在看起来像这样

Modified Circuit

我会欢迎评论更正和建议,以其他更好的方式实现这一点,因为它仍然让我觉得有点hacky。

1 个答案:

答案 0 :(得分:3)

您需要将系统输出推迟到输入后。这是模拟计算反馈系统的关键部分。

    public delegate IObservable<T> Feedback<T>(IObservable<T> feedback, out IObservable<T> output);
    public static IObservable<T> FeedbackSystem<T>(Feedback<T> closure)
    {
        IObservable<T> source = Observable.Empty<T>(), output;
        source = closure(Observable.Defer(() => source), out output);
        return output;
    }

这是一个速度管理器的示例系统,即使存在波动,也能将速度稳定在100。 它根据系统运行速度的反馈调整加速度。 如果您不熟悉运动定律,基本上速度是加速度的结果,加速度由速度反馈控制。

        var system =
        FeedbackSystem((IObservable<double> acceleration, out IObservable<double> velocity) =>
        {
            //Time axis: moves forward every 0.1s
            double t = 0.1; var timeaxis = Observable.Interval(TimeSpan.FromSeconds(t));

            velocity = acceleration.Sample(timeaxis)                //move in time
                                   .Scan((u, a) => u + a * t)   //u' = u + at
                                   .Select(u => u + new Random().Next(10))  //some variations in speed
                                   .Publish().RefCount();                   //avoid recalculation

            //negative feedback
            var feedback = velocity.Select(u => 0.5 * (100 - u));

            return feedback.Select(a => Math.Min(a, 15.0))  //new limited acceleration
                           .StartWith(0);                   //initial value          
        });

        system.Subscribe(Console.WriteLine);

反馈通过时间步计算。甚至逻辑门也给出了切换时间。