How to use the Vcsn library

Writing your own code

Writing programs with Vcsn is quite simple once you're familiar with the library.

Static

When using the static version of the library, use algorithms from the vcsn:: namespace. These algorithms are highly templated and hence require the user to know the context and parameters of these algorithms at compile time. This part of the library provides less flexibility but ensures that all of the required code will be compiled before runtime.

Dyn

The dyn version of the library consists of algorithms from the vcsn::dyn:: namespace. These algorithms provide an abstraction from the static library that hides from users the actual fully templated versions of algorithms. This allows more flexibility as the user does not have to know the context of automata at compile time and new types of automata can be created at runtime. However, algorithms may need to be compiled at runtime, lowering the program's performances (once compiled algorithms are cached and can be used without further compilation though).

Compiling with Vcsn

Compiling your program will then require to use the Vcsn program with the compiler option and can be done as such:

$ vcsn compile source.cc

It is also possible to modify the programs compilation options and compiler in the command line option:

$ vcsn compile CXX=g++ CXXFLAGS+=-g3 source.cc

Run vcsn compile --help for additional options.

Running the code

When actually running the program you just compiled you might have to use the Vcsn program as well through the run option:

$ vcsn run bin

The command can be followed directly by the arguments of your program.

Possibly just running bin would work too, but it depends on your setup. In case of doubt, use vcsn run.

Examples

N.B.: Some other samples of code are present in the tests/demo directory of the project from which you can inspire yourself.

In [1]:
import vcsn
In [2]:
!cat ../../tests/demo/prod-eval.cc
// demaille.13.ciaa
#include <iostream>
#include <stdexcept>
#include <string>

#include <vcsn/core/mutable-automaton.hh>
#include <vcsn/ctx/traits.hh>
#include <vcsn/ctx/lal_char_z.hh>
#include <vcsn/dyn/algos.hh>
#include <vcsn/dyn/context.hh>
#include <vcsn/dyn/automaton.hh>
#include <vcsn/misc/raise.hh>

namespace vcsn
{
  namespace dyn
  {
    /// Dyn version of the read_automaton function.
    ///
    /// Read an automaton without knowing its context.
    static automaton read_automaton(const std::string& f)
    {
      auto is = open_input_file(f);
      return read_automaton(*is);
    }

    static label read_word(const context& ctx, const std::string& w)
    {
      std::istringstream is{w};
      auto res = read_label(make_word_context(ctx), is);
      require(is.peek() == EOF, "unexpected trailing characters: ", is);
      return res;
    }
  }

  /// Static version of the read_automaton function.
  ///
  /// Read an automaton with a specified context (from the Aut
  /// template parameter).
  template <Automaton Aut>
  Aut
  read_automaton(const std::string& f)
  {
    dyn::automaton res = dyn::read_automaton(f);
    // Automaton typename.
    auto vname = res->vname();
    VCSN_REQUIRE(vname == Aut::element_type::sname(),
                 f, ": invalid context: ", vname,
                 ", expected: ", Aut::element_type::sname());
    return std::move(res->as<Aut>());
  }

  template <typename Ctx>
  word_t_of<Ctx>
  read_word(const Ctx& ctx, const std::string& w)
  {
    auto c = make_word_context(ctx);
    std::istringstream is{w};
    auto res = read_label(make_word_context(ctx), is);
    require(is.peek() == EOF, "unexpected trailing characters: ", is);
    return res;
  }
}

/// Static implementation of the prod_eval.
///
/// Read two automata and a word. Compute the product of these automata and
/// evaluate the word from it.
/// \param Ctx the specified context of the automata and the word. Each of them
/// have to be from these context or the program will fail.
template <typename Ctx>
void
sta_prod_eval(const std::string& lhs, const std::string& rhs,
              const std::string& word)
{
  using namespace vcsn;
  // The automata's exact type.
  using automaton_t = mutable_automaton<Ctx>;
  // Left and right automata.
  automaton_t l = read_automaton<automaton_t>(lhs);
  automaton_t r = read_automaton<automaton_t>(rhs);
  // The synchronized product.  Stripped to retrieve the same type of
  // automata as l and r.
  automaton_t prod = conjunction<automaton_t, automaton_t>(l, r)->strip();
  // The word to evaluate in prod.
  word_t_of<Ctx> input = read_word<Ctx>(prod->context(), word);
  // The result weight from the evaluation.
  weight_t_of<Ctx> w = eval<automaton_t>(prod, input);
  // Display of the result, we need to use the automaton's weightset to be able
  // to print the weight as the print function cannot be generic in static.
  prod->context().weightset()->print(w, std::cout);
}

/// Dyn implementation of the prod_eval.
///
/// Read two automata and a word. Compute the product of these automata and
/// evaluate the word from it. The context does not have to be specified as
/// the context of the parameters is computed dynamically.
static void
dyn_prod_eval(const std::string& lhs, const std::string& rhs,
              const std::string& word)
{
  using namespace vcsn::dyn;
  using vcsn::dyn::label;
  // Left and right automata.  A simple type, not parameterized.
  automaton l = read_automaton(lhs);
  automaton r = read_automaton(rhs);
  // The synchronized product.
  automaton prod = conjunction(l, r);
  // The word to evaluate in prod.
  label input = read_word(context_of(prod), word);
  // The result weight from the evaluation.
  weight w = eval(prod, input);
  // Display of the result, no need to use the weightset.
  std::cout << w;
}

int
main(int argc, const char* argv[])
try
  {
    vcsn::require(argc == 4, "not enough arguments");
    dyn_prod_eval(argv[1], argv[2], argv[3]);
    std::cout << ", ";
    sta_prod_eval<vcsn::ctx::lal_char_z>(argv[1], argv[2], argv[3]);
    std::cout << '\n';
  }
 catch (const std::exception& e)
   {
     std::cerr << argv[0] << ": " << e.what() << '\n';
     exit(EXIT_FAILURE);
   }
 catch (...)
   {
     std::cerr << argv[0] << ": unknown exception caught\n";
     exit(EXIT_FAILURE);
   }

Compile the prod-eval program using vcsn compile. To know exactly what is going on, pass the option -v/--verbose.

In [3]:
!vcsn compile ../../tests/demo/prod-eval.cc

Now we generate two input automata for the prod-eval program. Here, we generate them via the command line tool vcsn (see Executables), with the command standard, which corresponds to expression.standard.

In [4]:
!vcsn standard -C 'lal_char(01), z' -Ee '(0+1)*1(<2>0+<2>1)*' -o bin.gv
!vcsn standard -C 'lal_char(01), z' -Ee '(0+1)*0'             -o even.gv

The following automaton, bin, decodes binary into decimal values.

In [5]:
bin = vcsn.automaton(filename='bin.gv')
bin
Out[5]:
%3 I0 0 0 I0->0 F3 F4 F5 1 1 0->1 0 2 2 0->2 1 3 3 0->3 1 1->1 0 1->2 1 1->3 1 2->1 0 2->2 1 2->3 1 3->F3 4 4 3->4 ⟨2⟩0 5 5 3->5 ⟨2⟩1 4->F4 4->4 ⟨2⟩0 4->5 ⟨2⟩1 5->F5 5->4 ⟨2⟩0 5->5 ⟨2⟩1

This automaton, even, only accepts even numbers.

In [6]:
even = vcsn.automaton(filename='even.gv')
even
Out[6]:
%3 I0 0 0 I0->0 F3 1 1 0->1 0 2 2 0->2 1 3 3 0->3 0 1->1 0 1->2 1 1->3 0 2->1 0 2->2 1 2->3 0 3->F3

Now we run prod-eval to evaluate words on the synchronized product of bin and even. The result is the value denoted by this binary number if it is even else 0. The result is displayed twice, once for the dyn implementation and once for static one.

In [7]:
!for i in 0 1 10 1110 101010 101011;                       \
do                                                         \
    printf "%6s: " $i;                                     \
    vcsn run ../../tests/demo/prod-eval bin.gv even.gv $i; \
done
     0: 0, 0
     1: 0, 0
    10: 2, 2
  1110: 14, 14
101010: 42, 42
101011: 0, 0

For the record, this could have been done this way in Python:

In [8]:
for i in [0, 1, 10, 1110, 101010, 101011]:
    print('{:6d}: {}'.format(i, (bin & even).eval(i)))
     0: 0
     1: 0
    10: 2
  1110: 14
101010: 42
101011: 0