예제 #1
0
        /// <summary>
        /// This looks at the transition from state pair
        /// (f1,f2) -> (t1,t2), withthe given cost.
        /// </summary>
        public void Relax(int f1, int f2,   // input state pair
                          int t1, int t2,   // output state pair
                          float cost,       // transition cost
                          int arc_id1,      // (unused)
                          int arc_id2,      // (unused)
                          int input,        // input label
                          int intermediate, // (unused)
                          int output,       // output label
                          float base_cost,  // cost of the path so far
                          int trail_index)
        {
            //logger.format("relaxing %d %d -> %d %d (bcost %f, cost %f)", f1, f2, t1, t2, base_cost, cost);

            if (!nbest.AddReplacingId(t1 * fst2.nStates() + t2,
                                      all_costs.Length(),
                                      -base_cost - cost))
            {
                return;
            }

            //logger.format("nbest changed");
            //nbest.log(logger);

            if (input > 0)
            {
                // The candidate for the next beam is stored in all_XX arrays.
                // (can we store it in the stree instead?)
                all_inputs.Push(input);
                all_targets1.Push(t1);
                all_targets2.Push(t2);
                all_outputs.Push(output);
                all_costs.Push(cost);
                parent_trails.Push(trail_index);
            }
            else
            {
                // Beam control hack
                // -----------------
                // if a node is important (changes nbest) AND its input is 0,
                // then it's added to the CURRENT beam.

                //logger.format("pushing control point from trail %d to %d, %d",
                //trail_index, t1, t2);
                int new_node = stree.Add(beam[trail_index], t1, t2, input, output, (float)cost);
                beam.Push(new_node);
                beamcost.Push(base_cost + cost);

                // This is a stub entry indicating that the node should not
                // be added to the next generation beam.
                all_inputs.Push(0);
                all_targets1.Push(-1);
                all_targets2.Push(-1);
                all_outputs.Push(0);
                all_costs.Push(0);
                parent_trails.Push(-1);
            }
        }
예제 #2
0
        public void BestPath(Intarray v1, Intarray v2, Intarray inputs,
                             Intarray outputs, Floatarray costs)
        {
            stree.Clear();

            beam.Resize(1);
            beamcost.Resize(1);
            beam[0]     = stree.Add(-1, fst1.GetStart(), fst2.GetStart(), 0, 0, 0);
            beamcost[0] = 0;

            best_so_far      = 0;
            best_cost_so_far = fst1.GetAcceptCost(fst1.GetStart()) +
                               fst2.GetAcceptCost(fst1.GetStart());

            while (beam.Length() > 0)
            {
                Radiate();
            }

            stree.Get(v1, v2, inputs, outputs, costs, best_so_far);
            costs.Push(fst1.GetAcceptCost(stree.v1[best_so_far]) +
                       fst2.GetAcceptCost(stree.v2[best_so_far]));

            //logger("costs", costs);
        }
예제 #3
0
        public void Get(Intarray r_vertices1,
                 Intarray r_vertices2,
                 Intarray r_inputs,
                 Intarray r_outputs,
                 Floatarray r_costs,
                 int id)
        {
            Intarray t_v1 = new Intarray(); // vertices
            Intarray t_v2 = new Intarray(); // vertices
            Intarray t_i = new Intarray(); // inputs
            Intarray t_o = new Intarray(); // outputs
            Floatarray t_c = new Floatarray(); // costs
            int current = id;
            while (current != -1)
            {
                t_v1.Push(v1[current]);
                t_v2.Push(v2[current]);
                t_i.Push(inputs[current]);
                t_o.Push(outputs[current]);
                t_c.Push(costs[current]);
                current = parents[current];
            }

            NarrayUtil.Reverse(r_vertices1, t_v1);
            NarrayUtil.Reverse(r_vertices2, t_v2);
            NarrayUtil.Reverse(r_inputs, t_i);
            NarrayUtil.Reverse(r_outputs, t_o);
            NarrayUtil.Reverse(r_costs, t_c);
        }
예제 #4
0
        public void Get(Intarray r_vertices1,
                        Intarray r_vertices2,
                        Intarray r_inputs,
                        Intarray r_outputs,
                        Floatarray r_costs,
                        int id)
        {
            Intarray   t_v1    = new Intarray();   // vertices
            Intarray   t_v2    = new Intarray();   // vertices
            Intarray   t_i     = new Intarray();   // inputs
            Intarray   t_o     = new Intarray();   // outputs
            Floatarray t_c     = new Floatarray(); // costs
            int        current = id;

            while (current != -1)
            {
                t_v1.Push(v1[current]);
                t_v2.Push(v2[current]);
                t_i.Push(inputs[current]);
                t_o.Push(outputs[current]);
                t_c.Push(costs[current]);
                current = parents[current];
            }

            NarrayUtil.Reverse(r_vertices1, t_v1);
            NarrayUtil.Reverse(r_vertices2, t_v2);
            NarrayUtil.Reverse(r_inputs, t_i);
            NarrayUtil.Reverse(r_outputs, t_o);
            NarrayUtil.Reverse(r_costs, t_c);
        }
예제 #5
0
        /// <summary>
        /// Randomly sample an FST, assuming any input.
        /// </summary>
        /// <param name="result">The array of output symbols, excluding epsilons.</param>
        /// <param name="fst">The FST.</param>
        /// <param name="max">The maximum length of the result.</param>
        /// <returns>total cost</returns>
        public static double fst_sample(Intarray result, IGenericFst fst, int max = 1000)
        {
            double total_cost = 0;
            int    current    = fst.GetStart();

            for (int counter = 0; counter < max; counter++)
            {
                Intarray   inputs  = new Intarray();
                Intarray   outputs = new Intarray();
                Intarray   targets = new Intarray();
                Floatarray costs   = new Floatarray();

                fst.Arcs(inputs, targets, outputs, costs, current);

                // now we need to deal with the costs uniformly, so:
                costs.Push(fst.GetAcceptCost(current));
                int choice = sample_by_costs(costs);
                if (choice == costs.Length() - 1)
                {
                    break;
                }
                result.Push(outputs[choice]);
                total_cost += costs[choice];
                current     = targets[choice];
            }
            return(total_cost + fst.GetAcceptCost(current));
        }
예제 #6
0
        public static void line_segmentation_sort_x(Intarray segmentation)
        {
            if (NarrayUtil.Max(segmentation) > 100000)
            {
                throw new Exception("line_segmentation_merge_small_components: to many segments");
            }
            Narray <Rect> bboxes = new Narray <Rect>();

            ImgLabels.bounding_boxes(ref bboxes, segmentation);
            Floatarray x0s = new Floatarray();

            unchecked
            {
                x0s.Push((float)-999999);
            }
            for (int i = 1; i < bboxes.Length(); i++)
            {
                if (bboxes[i].Empty())
                {
                    x0s.Push(999999);
                }
                else
                {
                    x0s.Push(bboxes[i].x0);
                }
            }
            // dprint(x0s,1000); printf("\n");
            Narray <int> permutation  = new Intarray();
            Narray <int> rpermutation = new Intarray();

            NarrayUtil.Quicksort(permutation, x0s);
            rpermutation.Resize(permutation.Length());
            for (int i = 0; i < permutation.Length(); i++)
            {
                rpermutation[permutation[i]] = i;
            }
            // dprint(rpermutation,1000); printf("\n");
            for (int i = 0; i < segmentation.Length1d(); i++)
            {
                if (segmentation.At1d(i) == 0)
                {
                    continue;
                }
                segmentation.Put1d(i, rpermutation[segmentation.At1d(i)]);
            }
        }
예제 #7
0
        public List<List<float>> SpaceCosts(List<Candidate> candidates, Bytearray image)
        {
            /*
                Given a list of character recognition candidates and their
                classifications, and an image of the corresponding text line,
                compute a list of pairs of costs for putting/not putting a space
                after each of the candidate characters.

                The basic idea behind this simple algorithm is to try larger
                and larger horizontal closing operations until most of the components
                start having a "wide" aspect ratio; that's when characters have merged
                into words.  The remaining whitespace should be spaces.

                This is just a simple stopgap measure; it will be replaced with
                trainable space modeling.
             */
            int w = image.Dim(0);
            int h = image.Dim(1);

            Bytearray closed = new Bytearray();
            int r;
            for (r = 0; r < maxrange; r++)
            {
                if (r > 0)
                {
                    closed.Copy(image);
                    Morph.binary_close_circle(closed, r);
                }
                else
                    closed.Copy(image);
                Intarray labeled = new Intarray();
                labeled.Copy(closed);
                ImgLabels.label_components(ref labeled);
                Narray<Rect> rects = new Narray<Rect>();
                ImgLabels.bounding_boxes(ref rects, labeled);
                Floatarray aspects = new Floatarray();
                for (int i = 0; i < rects.Length(); i++)
                {
                    Rect rect = rects[i];
                    float aspect = rect.Aspect();
                    aspects.Push(aspect);
                }
                float maspect = NarrayUtil.Median(aspects);
                if (maspect >= this.aspect_threshold)
                    break;
            }

            // close with a little bit of extra space
            closed.Copy(image);
            Morph.binary_close_circle(closed, r+1);

            // compute the remaining aps
            //Morph.binary_dilate_circle();

            // every character box that ends near a cap gets a space appended

            return null;
        }
예제 #8
0
        /// <summary>
        /// The main loop iteration.
        /// </summary>
        public void Radiate()
        {
            Clear();

            //logger("beam", beam);
            //logger("beamcost", beamcost);

            int control_beam_start = beam.Length();

            for (int i = 0; i < control_beam_start; i++)
            {
                TryAccept(i);
            }

            // in this loop, traversal may add "control nodes" to the beam
            for (int i = 0; i < beam.Length(); i++)
            {
                Traverse(stree.v1[beam[i]], stree.v2[beam[i]],
                         beamcost[i], i);
            }

            // try accepts from control beam nodes
            // (they're not going to the next beam)
            for (int i = control_beam_start; i < beam.Length(); i++)
            {
                TryAccept(i);
            }


            Intarray   new_beam     = new Intarray();
            Floatarray new_beamcost = new Floatarray();

            for (int i = 0; i < nbest.Length(); i++)
            {
                int k = nbest.Tag(i);
                if (parent_trails[k] < 0) // skip the control beam nodes
                {
                    continue;
                }
                new_beam.Push(stree.Add(beam[parent_trails[k]],
                                        all_targets1[k], all_targets2[k],
                                        all_inputs[k], all_outputs[k],
                                        all_costs[k]));
                new_beamcost.Push(beamcost[parent_trails[k]] + all_costs[k]);
                //logger.format("to new beam: trail index %d, stree %d, target %d,%d",
                //k, new_beam[new_beam.length() - 1], all_targets1[k], all_targets2[k]);
            }
            //move(beam, new_beam);
            beam.Move(new_beam);
            //move(beamcost, new_beamcost);
            beamcost.Move(new_beamcost);
        }
예제 #9
0
        public void Copy(Floatarray v, float eps = 1e-11f)
        {
            Clear();
            int n = v.Length();

            for (int i = 0; i < n; i++)
            {
                float value = v.At1d(i);
                if (Math.Abs(value) >= eps)
                {
                    _keys.Push(i);
                    _values.Push(value);
                }
            }
            _len = v.Length();
            _keys.Resize(_len);
            for (int i = 0; i < _len; i++)
            {
                _keys.Put1d(i, i);
            }
            _values.Copy(v);
        }
예제 #10
0
        // writing
        public override int NewState()
        {
            accept_costs.Push(1e38f);
            m_targets.Push();
            m_inputs.Push();
            m_outputs.Push();
            m_costs.Push();
            int i = accept_costs.Length() - 1;

            CorrectTargetsNull(i);
            CorrectOutputsNull(i);
            CorrectInputsNull(i);
            CorrectCostsNull(i);
            return(i);
        }
예제 #11
0
        public int Add(int parent, int vertex1, int vertex2,
                       int input, int output, float cost)
        {
            int n = parents.Length();

            //logger.format("stree: [%d]: parent %d, v1 %d, v2 %d, cost %f",
            //               n, parent, vertex1, vertex2, cost);
            parents.Push(parent);
            v1.Push(vertex1);
            v2.Push(vertex2);
            inputs.Push(input);
            outputs.Push(output);
            costs.Push(cost);
            return(n);
        }
예제 #12
0
 public virtual void Extract(Floatarray outa, Floatarray ina)
 {
     outa.Clear();
     Narray<Floatarray> items = new Narray<Floatarray>();
     Extract(items, ina);
     //int num = 0;
     for (int i = 0; i < items.Length(); i++)
     {
         Floatarray a = items[i];
         outa.ReserveTo(outa.Length() + a.Length());    // optimization
         for (int j = 0; j < a.Length(); j++)
         {
             outa.Push(a.At1d(j));
             //outa[num++] = a.At1d(j);
         }
     }
 }
예제 #13
0
        public virtual void Extract(Floatarray outa, Floatarray ina)
        {
            outa.Clear();
            Narray <Floatarray> items = new Narray <Floatarray>();

            Extract(items, ina);
            //int num = 0;
            for (int i = 0; i < items.Length(); i++)
            {
                Floatarray a = items[i];
                outa.ReserveTo(outa.Length() + a.Length());    // optimization
                for (int j = 0; j < a.Length(); j++)
                {
                    outa.Push(a.At1d(j));
                    //outa[num++] = a.At1d(j);
                }
            }
        }
예제 #14
0
파일: Heap.cs 프로젝트: liaoheping/OCRonet
        /// <summary>
        /// Push the node in the heap if it's not already there, otherwise promote.
        /// </summary>
        /// <returns>
        /// True if the heap was changed, false if the item was already
        /// in the heap and with a better cost.
        /// </returns>
        public bool Push(int node, float cost)
        {
            int i = heapback[node];

            if (i != -1)
            {
                if (cost < costs[i])
                {
                    costs[i] = cost;
                    heapify_up(i);
                    return(true);
                }
                return(false);
            }
            else
            {
                heap.Push(node);
                costs.Push(cost);
                heapback[node] = heap.Length() - 1;
                heapify_up(heap.Length() - 1);
                return(true);
            }
        }
예제 #15
0
        public override void Arcs(Intarray ids, Intarray targets, Intarray outputs, Floatarray costs, int node)
        {
            int        n1   = node / l2.nStates();
            int        n2   = node % l2.nStates();
            Intarray   ids1 = new Intarray();
            Intarray   ids2 = new Intarray();
            Intarray   t1   = new Intarray();
            Intarray   t2   = new Intarray();
            Intarray   o1   = new Intarray();
            Intarray   o2   = new Intarray();
            Floatarray c1   = new Floatarray();
            Floatarray c2   = new Floatarray();

            l1.Arcs(ids1, t1, o1, c1, n1);
            l2.Arcs(ids2, t2, o2, c2, n2);

            // sort & permute
            Intarray p1 = new Intarray();
            Intarray p2 = new Intarray();

            NarrayUtil.Quicksort(p1, o1);
            NarrayUtil.Permute(ids1, p1);
            NarrayUtil.Permute(t1, p1);
            NarrayUtil.Permute(o1, p1);
            NarrayUtil.Permute(c1, p1);

            NarrayUtil.Quicksort(p2, ids2);
            NarrayUtil.Permute(ids2, p2);
            NarrayUtil.Permute(t2, p2);
            NarrayUtil.Permute(o2, p2);
            NarrayUtil.Permute(c2, p2);

            int k1, k2;

            // l1 epsilon moves
            for (k1 = 0; k1 < o1.Length() && o1.At1d(k1) == 0; k1++)
            {
                ids.Push(ids1.At1d(k1));
                targets.Push(Combine(t1.At1d(k1), n2));
                outputs.Push(0);
                costs.Push(c1.At1d(k1));
            }
            // l2 epsilon moves
            for (k2 = 0; k2 < o2.Length() && ids2.At1d(k2) == 0; k2++)
            {
                ids.Push(0);
                targets.Push(Combine(n1, t2.At1d(k2)));
                outputs.Push(o2.At1d(k2));
                costs.Push(c2.At1d(k2));
            }
            // non-epsilon moves
            while (k1 < o1.Length() && k2 < ids2.Length())
            {
                while (k1 < o1.Length() && o1.At1d(k1) < ids2.At1d(k2))
                {
                    k1++;
                }
                if (k1 >= o1.Length())
                {
                    break;
                }
                while (k2 < ids2.Length() && o1.At1d(k1) > ids2.At1d(k2))
                {
                    k2++;
                }
                while (k1 < o1.Length() && k2 < ids2.Length() && o1.At1d(k1) == ids2.At1d(k2))
                {
                    for (int j = k2; j < ids2.Length() && o1.At1d(k1) == ids2.At1d(j); j++)
                    {
                        ids.Push(ids1.At1d(k1));
                        targets.Push(Combine(t1.At1d(k1), t2.At1d(j)));
                        outputs.Push(o2.At1d(j));
                        costs.Push(c1.At1d(k1) + c2.At1d(j));
                    }
                    k1++;
                }
            }
        }
예제 #16
0
        public List <List <float> > SpaceCosts(List <Candidate> candidates, Bytearray image)
        {
            /*
             *  Given a list of character recognition candidates and their
             *  classifications, and an image of the corresponding text line,
             *  compute a list of pairs of costs for putting/not putting a space
             *  after each of the candidate characters.
             *
             *  The basic idea behind this simple algorithm is to try larger
             *  and larger horizontal closing operations until most of the components
             *  start having a "wide" aspect ratio; that's when characters have merged
             *  into words.  The remaining whitespace should be spaces.
             *
             *  This is just a simple stopgap measure; it will be replaced with
             *  trainable space modeling.
             */
            int w = image.Dim(0);
            int h = image.Dim(1);

            Bytearray closed = new Bytearray();
            int       r;

            for (r = 0; r < maxrange; r++)
            {
                if (r > 0)
                {
                    closed.Copy(image);
                    Morph.binary_close_circle(closed, r);
                }
                else
                {
                    closed.Copy(image);
                }
                Intarray labeled = new Intarray();
                labeled.Copy(closed);
                ImgLabels.label_components(ref labeled);
                Narray <Rect> rects = new Narray <Rect>();
                ImgLabels.bounding_boxes(ref rects, labeled);
                Floatarray aspects = new Floatarray();
                for (int i = 0; i < rects.Length(); i++)
                {
                    Rect  rect   = rects[i];
                    float aspect = rect.Aspect();
                    aspects.Push(aspect);
                }
                float maspect = NarrayUtil.Median(aspects);
                if (maspect >= this.aspect_threshold)
                {
                    break;
                }
            }

            // close with a little bit of extra space
            closed.Copy(image);
            Morph.binary_close_circle(closed, r + 1);

            // compute the remaining aps
            //Morph.binary_dilate_circle();

            // every character box that ends near a cap gets a space appended


            return(null);
        }
예제 #17
0
        public override void Arcs(Intarray ids, Intarray targets, Intarray outputs, Floatarray costs, int node)
        {
            int n1 = node / l2.nStates();
            int n2 = node % l2.nStates();
            Intarray ids1 = new Intarray();
            Intarray ids2 = new Intarray();
            Intarray t1 = new Intarray();
            Intarray t2 = new Intarray();
            Intarray o1 = new Intarray();
            Intarray o2 = new Intarray();
            Floatarray c1 = new Floatarray();
            Floatarray c2 = new Floatarray();
            l1.Arcs(ids1, t1, o1, c1, n1);
            l2.Arcs(ids2, t2, o2, c2, n2);

            // sort & permute
            Intarray p1 = new Intarray();
            Intarray p2 = new Intarray();

            NarrayUtil.Quicksort(p1, o1);
            NarrayUtil.Permute(ids1, p1);
            NarrayUtil.Permute(t1, p1);
            NarrayUtil.Permute(o1, p1);
            NarrayUtil.Permute(c1, p1);

            NarrayUtil.Quicksort(p2, ids2);
            NarrayUtil.Permute(ids2, p2);
            NarrayUtil.Permute(t2, p2);
            NarrayUtil.Permute(o2, p2);
            NarrayUtil.Permute(c2, p2);

            int k1, k2;
            // l1 epsilon moves
            for (k1 = 0; k1 < o1.Length() && o1.At1d(k1) == 0; k1++)
            {
                ids.Push(ids1.At1d(k1));
                targets.Push(Combine(t1.At1d(k1), n2));
                outputs.Push(0);
                costs.Push(c1.At1d(k1));
            }
            // l2 epsilon moves
            for (k2 = 0; k2 < o2.Length() && ids2.At1d(k2) == 0; k2++)
            {
                ids.Push(0);
                targets.Push(Combine(n1, t2.At1d(k2)));
                outputs.Push(o2.At1d(k2));
                costs.Push(c2.At1d(k2));
            }
            // non-epsilon moves
            while (k1 < o1.Length() && k2 < ids2.Length())
            {
                while (k1 < o1.Length() && o1.At1d(k1) < ids2.At1d(k2)) k1++;
                if (k1 >= o1.Length()) break;
                while (k2 < ids2.Length() && o1.At1d(k1) > ids2.At1d(k2)) k2++;
                while (k1 < o1.Length() && k2 < ids2.Length() && o1.At1d(k1) == ids2.At1d(k2))
                {
                    for (int j = k2; j < ids2.Length() && o1.At1d(k1) == ids2.At1d(j); j++)
                    {
                        ids.Push(ids1.At1d(k1));
                        targets.Push(Combine(t1.At1d(k1), t2.At1d(j)));
                        outputs.Push(o2.At1d(j));
                        costs.Push(c1.At1d(k1) + c2.At1d(j));
                    }
                    k1++;
                }
            }
        }
예제 #18
0
        public void BestPath(Intarray v1, Intarray v2, Intarray inputs,
                      Intarray outputs, Floatarray costs)
        {
            stree.Clear();

            beam.Resize(1);
            beamcost.Resize(1);
            beam[0] = stree.Add(-1, fst1.GetStart(), fst2.GetStart(), 0, 0, 0);
            beamcost[0] = 0;

            best_so_far = 0;
            best_cost_so_far = fst1.GetAcceptCost(fst1.GetStart()) +
                               fst2.GetAcceptCost(fst1.GetStart());

            while (beam.Length() > 0)
                Radiate();

            stree.Get(v1, v2, inputs, outputs, costs, best_so_far);
            costs.Push(fst1.GetAcceptCost(stree.v1[best_so_far]) +
                       fst2.GetAcceptCost(stree.v2[best_so_far]));

            //logger("costs", costs);
        }
예제 #19
0
        /// <summary>
        /// The main loop iteration.
        /// </summary>
        public void Radiate()
        {
            Clear();

            //logger("beam", beam);
            //logger("beamcost", beamcost);

            int control_beam_start = beam.Length();
            for (int i = 0; i < control_beam_start; i++)
                TryAccept(i);

            // in this loop, traversal may add "control nodes" to the beam
            for (int i = 0; i < beam.Length(); i++)
            {
                Traverse(stree.v1[beam[i]], stree.v2[beam[i]],
                         beamcost[i], i);
            }

            // try accepts from control beam nodes
            // (they're not going to the next beam)
            for (int i = control_beam_start; i < beam.Length(); i++)
                TryAccept(i);

            Intarray new_beam = new Intarray();
            Floatarray new_beamcost = new Floatarray();
            for (int i = 0; i < nbest.Length(); i++)
            {
                int k = nbest.Tag(i);
                if (parent_trails[k] < 0) // skip the control beam nodes
                    continue;
                new_beam.Push(stree.Add(beam[parent_trails[k]],
                                        all_targets1[k], all_targets2[k],
                                        all_inputs[k], all_outputs[k],
                                        all_costs[k]));
                new_beamcost.Push(beamcost[parent_trails[k]] + all_costs[k]);
                //logger.format("to new beam: trail index %d, stree %d, target %d,%d",
                //k, new_beam[new_beam.length() - 1], all_targets1[k], all_targets2[k]);
            }
            //move(beam, new_beam);
            beam.Move(new_beam);
            //move(beamcost, new_beamcost);
            beamcost.Move(new_beamcost);
        }
예제 #20
0
파일: FstUtil.cs 프로젝트: nickun/OCRonet
        /// <summary>
        /// Randomly sample an FST, assuming any input.
        /// </summary>
        /// <param name="result">The array of output symbols, excluding epsilons.</param>
        /// <param name="fst">The FST.</param>
        /// <param name="max">The maximum length of the result.</param>
        /// <returns>total cost</returns>
        public static double fst_sample(Intarray result, IGenericFst fst, int max=1000)
        {
            double total_cost = 0;
            int current = fst.GetStart();

            for (int counter = 0; counter < max; counter++)
            {
                Intarray inputs  = new Intarray();
                Intarray outputs = new Intarray();
                Intarray targets = new Intarray();
                Floatarray costs = new Floatarray();

                fst.Arcs(inputs, targets, outputs, costs, current);

                // now we need to deal with the costs uniformly, so:
                costs.Push(fst.GetAcceptCost(current));
                int choice = sample_by_costs(costs);
                if (choice == costs.Length() - 1)
                    break;
                result.Push(outputs[choice]);
                total_cost += costs[choice];
                current = targets[choice];
            }
            return total_cost + fst.GetAcceptCost(current);
        }