Grid search by mobile agents is one of the fundamental primitives in swarm robotics and a natural abstraction of foraging behavior of animals. For example in the case of cost-efficient robots or insects, a single agent has relatively limited computation and communication capabilities and hence, many independent agents are required to efficiently solve tasks. To understand such collective problem solving better, knowledge from distributed computing has proven valuable. For instance, Feinerman et al. gave tight bounds on the time complexity of a collective grid search problem inspired by desert ants [FKLS12]. In this paper, we focus on the minimum number of agents required to solve the grid search problem. A series of papers [ELS15, CELU17, BUW18] nailed down the exact complexity of the -dimensional case, that is, discovered the exact number of synchronous/semi-synchronous and deterministic/randomized finite automata needed to explore a -dimensional grid. However, the approaches in these works do not generalize (well) to higher dimensions. The only known tight bound achieved by such a generalization is obtained by the recent protocol for the deterministic semi-synchronous 3-dimensional setting by Dobrev, Narayanan, Opatrny, and Pankratov [DNOP19a].
The authors of [DNOP19a] also gave a more general result: they showed how to implement a stack data structure using only a constant number of agents governed by finite automata. By employing this stack in their search protocols, they show how to explore an -dimensional grid using only a (small) constant number of agents, for any positive integer . In particular, the number of agents is independent of the dimension .
For the case of a -dimensional grid the required number of agents is fully understood. However, for higher dimensions there are still gaps between the best upper and lower bounds. Indeed, Dobrev et al. left as open questions the tight complexities of exploring high-dimensional grids in the synchronous/semi-synchronous and deterministic/randomized settings. In this work, we answer these questions for the deterministic setting. Moreover, building on our techniques we make progress on other open questions by Dobrev et al.
1.1 Results and Techniques
Similarly to the approach by Dobrev et al. [DNOP19a], our search protocols rely on an efficient implementation of a stack data structure. One agent is dedicated to do the actual search while the remaining agents implement a stack (together with the searching agent that indicates the base of the stack) with their positions on the grid. On a high level, the size of the stack encodes the cell the searching agent is supposed to explore next, relative to the current position of the searching agent. Both our protocol and the protocol from [DNOP19a] explore the grid by repeatedly reading the stack, moving the searching agent to the cell indicated by the stack, moving the searching agent back to its original cell, and incrementing the stack. The difficult part is to be able to effectively read the stack (without destroying the stack in the process) despite the fact that the size of the stack grows arbitrarily far beyond the number of states in the finite automaton reading the stack. The authors of [DNOP19a] managed to implement this data structure using agents in the synchronous and agents in the semi-synchronous setting and showed how to explore oriented grids with as many agents.
One of our main contributions is to implement this stack and the operations required for reading it with only (synchronous) agents (including the searching agent), which is optimal given the grid exploration lower bound by Emek et al. [ELS15]. We achieve this by a careful design of an encoding scheme that transforms the location of a cell to be explored into a single integer (that can be represented by the stack size) by interpreting the coordinates of the cell (relative to the current location of the searching agent) as exponents of distinct prime factors. One crucial advantage of this specific encoding is that there is a way to read the stack (using synchronous agents), i.e., to repeatedly provide the searching agent with different parts of the encoded information, that does not destroy the encoded information, but instead changes the encoding slightly: replacing the base (prime) for one of those exponents by a different prime (and then switching to the next base prime). The technical details why such replacement operations can be performed by synchronous agents and why they allow the searching agent to obtain the desired information are covered in Sections 3 and 4. Moreover, by adding one agent as a synchronizer, the protocol can be made to work in the semi-synchronous setting.
For any positive integer , the -dimensional (oriented) grid can be explored by synchronous finite automata, resp. semi-synchronous finite automata.
An underlying assumption of the setting considered so far is that the agents are aware of the cardinal directions, i.e., they know for each of the dimensions of the grid which two adjacent cells are neighbors in that dimension, and each dimension is oriented. Or, to put it simply, the agents know which directions are north, south, etc.; in particular the directions are globally consistent. In contrast, in the unoriented setting considered in [DNOP19a], each cell is endowed with a labeling that indicates for each cell which neighbor is north, south, etc. (and for each of the directions there is exactly one neighbor), but there is no consistency guarantee between the directions indicated by the labels of different cells. In fact, the setting does not preclude that after going north twice, you do not end up in the cell in which you started.
In their work, Dobrev et al. also ask “How many additional agents are necessary to solve the problem in unoriented grids?”. We show, perhaps surprisingly, that the unoriented case is no harder than the oriented case given the following (natural) assumption: If we follow some fixed direction, we never end up back in the same cell where we started.
Theorem 1.2 (Simplified).
Under a natural assumption, for any positive integer , synchronous finite automata, resp. semi-synchronous finite automata, suffice to explore any -dimensional unoriented grid.
The key idea to obtain Theorem 1.2 is that, even without a globally consistent orientation, we can implement a (virtual) stack. Due to the missing consistency, the same cell may occur repeatedly in the stack, but we can show that we can bound the number of occurrences for each cell and that the agents can distinguish between the different occurrences of the same cell. In essence, we will show that the stack corresponds to (a part of) a DFS exploration of an infinite tree consisting of those edges (between cells) that point north.
Polynomial Time Protocol.
The task of exploring the entire grid can equivalently be described as finding a treasure located at some distance from the starting point. This formulation allows us to discuss the efficiency of a protocol, i.e., its runtime with respect to . We observe that our encoding scheme for the oriented grid using only synchronous, resp. semi-synchronous, agents might result in exponential time. However, we show that with one additional agent, certain stack operations can be extended to work for non-constant values. This allows us to use a different exploration scheme, proposed by Dobrev et al. [DNOP19a], which is similar to the well-known spiral search, resulting in a polynomial runtime.
For any positive integer , the -dimensional (oriented) grid can be explored by: (1) 4 synchronous agents in time , and (2) 5 semi-synchronous agents in time , where is the volume of the -ball of radius .
1.2 Further Related Work
In a typical graph exploration setting, we are given a graph where initially, one or more mobile agents are placed on some vertices of the graph. The agents are able to traverse along the edges and their goal is to explore the graph, that is, visit every node or edge of the graph. Equivalently, one can think of searching for a treasure hidden on an edge or a node of the graph. Graph exploration has been widely studied in the literature (see, for example, [Rol79, PP98, DP99, AH00, DFKP04, FIP05]) and it comes in many variants.
A classic setting is the cow-path problem, where a single agent, the cow, is searching for adversarially hidden food on a path [Bec64, BYCR93]. The goal for the cow is to minimize the number of edge-traversals until the food is found. It is known that a simple spiral search is optimal and this algorithm also generalizes to the case of grids. This problem was also studied in the case of many cows [LOS01]. Closely related to our work is the exploration of labyrinths, i.e., -dimensional grids where some cells are blocked [Bud78]. It is known that two finite automata or one automaton with two pebbles (movable marker) suffice for co-finite labyrinths, where a finite amount of cells are not blocked [BK78]. Finite labyrinths, where a finite amount cells are blocked, can be explored with one automaton and four pebbles, whereas one automaton and one pebble is not enough [BS77, Hof81]. An agent with pebbles can explore all graphs and this bound is tight [DHK16].
In the case of many agents, the agents typically operate in look-compute-move cycles. First, the agents take a local snapshot, then decide on the next operation, and finally, execute the operation. Graph exploration can be divided into synchronous (), semi-synchronous (), and asynchronous () variants [SS96, SY96, SY99]. In the setting, the execution is divided into synchronous rounds, where in every round, every agent executes one cycle. The execution in consists of discrete time steps, where in each step, a subset of the agents executes one atomic cycle. In the setting, the cycles are not (necessarily) atomic. In this paper, we study the and the settings.
For finite graphs, a random walk provides a simple algorithm that explores the graph in polynomial time [AKL79]. In the case of an infinite
-dimensional grid, a random walk finds the treasure with probabilityif . However, the expected hitting time, i.e., the time to find the treasure, tends to infinity. Cohen et al. showed that even for the case of two (collaborating) randomized agents governed by finite automata, one cannot achieve any finite hitting time for [CELU17]. Very recently, Dobrev et al. showed that randomized and randomized agents suffice to achieve a finite hitting time for any [DNOP19a]. In this work, we achieve the same bounds with deterministic agents.
This work follows a series of papers inspired the work by Feinerman et al., where they studied the time it takes to find a treasure in a -dimensional grid by
non-communicating agents governed by Turing machines[FKLS12]. They showed that the time complexity of this task is , where is the distance from the origin to the treasure. This bound can be matched by finite automata that are allowed to communicate within the same cell [ELUW14]. Emek et al. asked what is the minimum number of agents required to find the treasure [ELS15]. They showed that at least synchronous deterministic agents are required and that synchronous deterministic, semi-synchronous deterministic, and semi-synchronous randomized agents are enough. Cohen et al. [CELU17] and Brandt et al. [BUW18] showed the matching lower bounds for the randomized and deterministic semi-synchronous cases, respectively.
We consider the problem of exploring the infinite -dimensional grid, whose vertices are the elements of , which we refer to as cells. A cell is described by its coordinates and two cells and are adjacent (i.e., connected by an edge) if they differ in one coordinate by , i.e., there is a dimension such that and for . When talking about distance, we will use the or Manhattan distance, which is defined as .
In the oriented case, we assume that there is a consistent labeling of the edges by both of its endpoints, which in the 2-dimensional case can be thought of as the directions of a compass: north, south, east, and west. In general, an edge is labeled by from the side of (and thus from the side of ) if we have that .
For unoriented grids, we assume that each endpoint of an edge has a label from . We will also refer to these labels as the ports of a cell. The only assumption we make is that the labels around each cell are pairwise distinct, i.e., each cell has every port from to exactly once. Thus, each edge can receive any pair of labels from .
The exploration is performed by agents, , which are initially all placed in the same cell, called the origin. W.l.o.g. we assume the origin to have coordinates . The agents cannot distinguish different cells (including the origin); in particular, they do not know the coordinates of the cell they are in. Their behavior and movement is controlled by a deterministic finite automaton. While we require all agents to use the same automaton, they may start in different initial states. (As we only consider protocols with constantly many agents, one can equivalently assume each agent to be controlled by an individual automaton, as we can combine automata into one by using disjoint state spaces.) Agents can only communicate if they are in the same cell: each agent senses the states for which there is an agent that occupies the same cell, and performs its next move and state transition based on this information. For oriented grids, such a move is described by a direction and dimension.
In the case of unoriented grids, we assume that agents can also see both labels of each incident edge, and perform their decisions based on this information as well. A move is then described by choosing a port of the current cell and moving along this edge. Previous work by Dobrev et al. [DNOP19a] used an essentially equivalent definition: Each agent could only see the label on its side of each incident edge, but once it arrived in the new cell by traversing some edge, it would obtain the information about the second label on the edge it traversed. We choose to formalize the model in a slightly different way, as it will simplify the description of our algorithms. However, we emphasize that for our purposes, the two models can be used interchangeably since within steps in the model of Dobrev et al., the agents can learn all information that we assume the agents can immediately see.
Formally, we have a state space , a transition function , and an initial state for every agent . For oriented grids, the transition function has the form: . The function maps an agent in state , which observes the set of states for which there is an agent occupying the same cell, to a new state and a movement, which is described by the direction ( or ) and the dimension (from to ) along which the agent moves to the respective neighboring cell, where an agent can also choose to stay in the same cell which is described by dimension . We will say that an agent moves north if its movement is , and south if it is .
For unoriented grids, we change the definition of the transition function slightly to . The function maps an agent in state , which observes both the set of states for which there is an agent occupying the same cell, and, for each port, the other label on the edge corresponding to that port, to a new state and a movement, which is specified by the port via which the agent leaves the current cell, or 0, in which case the agent does not move.
Time is divided into discrete units, where in each time step, a set of active agents performs a look-compute-move cycle. First, an agent senses the states of all agents in the same cell (and in the case of unoriented grids both of the labels on all incident edges), then it applies the transition function to its own state and all sensed information, and finally it changes its state and moves as indicated by the result. We assume that one such cycle is atomic, i.e., cycles that start at different times do not overlap.
For the synchronous or model, we assume that all agents are active at every time step. We call the system semi-synchronous, or the variant, if at every time step only a subset of agents, chosen by an adversary, is active. While the adversary knows all information about the agents and their behavior, it must schedule each agent infinitely often, to avoid trivial impossibilities.
Finally, if we discuss the efficiency of a protocol, we consider the following problem, which is equivalent to exploring the grid: the agents are tasked to find a treasure, which is hidden at some distance from the origin (without the agents knowing the value of ). This enables us to measure the time or exploration cost it takes to find the treasure with respect to . In the synchronous setting, we measure the exploration cost as the number of time steps needed for an agent to arrive at the cell containing the treasure. As, in the semi-synchronous model, this number of steps depends on the schedule, we instead define the exploration cost as the total distance traveled by all agents in this setting.
3 Building Blocks
Encoding Information as a Stack.
Dobrev et al. [DNOP19a] introduced the idea of using multiple agents to implement a stack. In its simplest form, a stack is just a pair of agents, whose distance encodes some information. However, to allow for manipulations of the stack, more agents are needed. Our protocol for exploring -dimensional grids with synchronous, resp. semi-synchronous, agents will consist of subroutines that involve manipulations of the stack. The relevant parameter will be the stack size, denoted by , which is defined as the distance between the base of the stack and the end of the stack. The base of the stack is the location of agent , and the end of the stack is the location of the other agents. We will only be interested in the stack and its size at the very beginning and very end of each subroutine; at these points in time all agents except are guaranteed to be in the same cell, and this cell is guaranteed to be reachable from the cell containing by going repeatedly north, making the notion of a stack well-defined. Whenever we refer to the base, end, or size of the stack during some subroutine, we mean the respective notion at the beginning of the subroutine.
In this section, we will describe the subroutines that form the building blocks of our exploration algorithm. Moreover, we will show for both the synchronous and the semi-synchronous setting how to implement the subroutines with the desired number of agents.
In [DNOP19a], the authors show how to multiply the current stack size by , resp. divide it by , using synchronous agents. This also provides a way to check whether the current stack size is divisible by . The idea behind the implementation is simple: while agent stays at the base of the stack, the other two agents, initially located at the end of the current stack, move with different speeds222An agent moves with speed in some direction if it repeatedly performs the following behavior: first it takes one step in the chosen direction, and then it waits for steps. Note that our speed of is the same as speed in [DNOP19a]., either away from or towards the base of the stack, and first towards the base, and then reversing direction when the base is reached. The operation is completed when and meet again (after visited the base). By choosing a speed of for , and a speed of for , and letting move towards the base, we achieve that the stack size is halved; by choosing the same speeds and letting move away from the base, we achieve that the stack size is doubled.
We will need similar subroutines as building blocks for both our synchronous and semi-synchronous protocols. More precisely, given a positive integer , we want the agents to be able to perform the following operations.
MultiplyStackSize(): Multiply the stack size by .
IsDivisible(): Check whether the current stack size is divisible by .
DivideStackSize(): If the stack size is divisible by , divide the stack size by .
We will only require the agents to be able to perform these operations for constantly many , where the constant depends (only) on the dimension of the grid.
To implement these operations, we simply adapt the protocols for the case from [DNOP19a] by choosing the speeds of (instead of ) for and (instead of ) for . More precisely, we implement the desired operations using synchronous agents as follows.
While it is usually easier to understand the behavior of an agent if it is described without specifying the exact states and the transition function, we will provide the latter for subroutine MultiplyStackSize() to give an example how to translate the agents’ behaviors described in this work into the formal specification of a finite automaton. Let be a positive integer. As usual we assume that and are in the same cell , and is in a cell such that can be reached from by going north repeatedly (i.e., and differ only in the first coordinate, and has a smaller first coordinate than ).
In subroutine MultiplyStackSize(), we denote the starting state of each agent by . Apart from state , we will use other states for agent , denoted by , and , and other states for agent , denoted by , and . Agent always stays in state and cell . Agents moves and changes its state according to the following rules, where “stay” indicates that the agents does not move to another cell.
For agent , the rules are as follows.
The protocol terminates when both and are in states and , respectively. The design of the protocol (in particular, of the two rules leading to the two terminal states) ensures that and terminate at the same point in time. As the rules of the protocol specify that walks with speed exactly , and with speed exactly , we see that the first time and are in the same cell in states , resp. (which is the configuration leading to termination in the next step), they are in a cell in distance from the base of the stack. The meeting happens after traversed cells ( towards the base, away from the base), whereas traversed cells.
Analogously, we can implement division by by letting walk towards the base, instead of away from the base, i.e., by replacing the first rule for by
while leaving all other rules (for all agents) unchanged. However, the two rules leading to the terminal states require and to be in states and , respectively, to ensure termination. If the initial stack size is divisible by , then the states of the two agents will align perfectly in the cell in distance from the base of the stack: after time steps, has traversed cells with speed , and has traversed cells with speed , hence both are in cell in the states leading to the terminal states. If, however, is not divisible by , then the states of the two agents do not align when they meet again after visited the base.
Hence, before dividing by , we will always check whether the current stack size is divisible by . This can be achieved by having walk towards the base with speed while increasing a counter modulo each time it takes a step. If the counter is at when reaches , the stack size is divisible by ; if not, then the stack size is not divisible by . The subroutine of checking for divisibility by terminates after has walked back to and informed it whether the current stack size is divisible by or not.
Further Building Blocks.
In order to be able to write our synchronous exploration protocol concisely, it will be useful to define a few other subroutines. As before, we will assume that, in the beginning of the subroutines, agents and will be in the same cell , representing the end of the stack, and is in a cell representing the base of the stack that differs from only in that its coordinate in dimension is strictly smaller. The only exception will be the subroutine InitializeStackSize() that initializes the stack to some positive integer by having and walk steps away from —here, all three agents are initially in the same cell. Apart from InitializeStackSize(), we define the subroutines IncreaseStackSize() for positive integers , and MoveStack(), where and . Subroutine IncreaseStackSize() simply increases the stack size by (additively) by having and walk steps away from .
A subroutine similar to our MoveStack() was already introduced in [DNOP19a]. The purpose of this subroutine is to move the whole stack in some direction specified by dimension and sign . In our definition, MoveStack() moves every agent to a new cell that differs from the old cell only by having its th coordinate increased by , i.e., effectively each agent takes one step in dimension . However, one has to be a bit careful when implementing this subroutine as we want to be able to concatenate it with other subroutines. In particular, in all other subroutines, agent does not know when the subroutine is started or terminates, while the other agents do know. In order to also obtain this property for MoveStack(), we implement the desired movement by having walk towards , notifying it about the desired step and the chosen direction (upon which performs the step) and then returning to , where both and perform the desired step as well.
All of the above subroutines can also be performed by (at most) semi-synchronous agents, as we show in the following. Similar to the approach in [DNOP19a], we will use one agent () to effectively synchronize the behavior of the other agents, which allows us to essentially execute the -agent synchronous subroutines described above with the remaining agents. In more detail, agent will visit the other agents in a suitable order, and each of the other agents will only move when they are in the same cell as (while will not leave the cell of the agent it wants to move next until the agent actually left the cell). We start by showing how this can be achieved for subroutine MultiplyStackSize().
As in the synchronous version of the subroutine, we would like the two agents and to move with (relative) speeds (first towards and, after meeting , away from ) and (away from ), respectively, while simply stays at the base of the stack. The purpose of this design—that when and meet next, they are in a cell that has the -fold distance to as they have currently—can also be achieved by having move steps, then having move steps, and so on, always alternating between the two agents, until they are both in the same cell again (which, by their relative “speeds” must have the desired distance to the base of the stack). This behavior can be ensured by using :
Agents and follow their designated route, but they only take one step of those routes if they are in the same cell as and is in a state indicating that , resp. should move (the latter condition is not strictly necessary, but simplifies things by ensuring that and never move at the same time). Agent alternates between visiting and , during each “visit” making sure that the respective agent takes the desired number of steps (i.e., or ). It does so by going to the cell of the respective agent (), indicating that should take a step of its route, waiting until takes a step and leaves the cell, incrementing an internal counter, following agent to the next cell, and repeating this behavior until the counter indicates that the desired number of steps has been taken by , upon which visits the other agent . Note that always knows in which direction it has to move to find the desired agent as the coordinates of and only differ in dimension , and always has a smaller (or equally large) first coordinate. Moreover, also knows in which direction it has to go to follow the agents to the next cell as the only change in direction is performed by and the reason for the change, namely meeting , is an information known to since when meets , it stays in the cell containing until also arrives there.
In an analogous fashion, we can implement DivideStackSize() with semi-synchronous agents. For the other four subroutines, the picture is even simpler: it is straightforward to check that these subroutines can already by implemented by semi-synchronous agents by having the agents perform the same steps as in the respective synchronous subroutines. The reason that these subroutines also work in the semi-synchronous setting is that either the synchronous version already contain one agent that effectively acts as a synchronizer (in the sense that every action is performed by that agent or directly instigated by a visit of that agent), as in IsDivisible() and MoveStack(), or the actions of the agents are independent of each other, as in InitializeStackSize() and IncreaseStackSize(). For these four subroutines, we will simply assume that is treated the same as ; in particular, at the beginning and end of each subroutine, we will always have , , and in the same cell, indicating the end of the stack.
We have to be a bit careful with the termination of each subroutine as we want to be able to concatenate the subroutines without problems. To this end, we will again use as a synchronizer: before terminating itself, will wait that and (which are in the same cell at the end of each subroutine) have terminated. Similarly, we can assume that will initialize the next subroutine by changing its state suitably, thereby making sure that the start and end of the subroutines align across all agents. A last detail is that in the semi-synchronous version of MoveStack() (which is the only subroutine where moves), after meeting , agent has to wait until takes its step before moving back to and , in order to make sure that does not terminate and initialize the next subroutine before takes its step.
4 The Exploration Protocol
In this section, we will combine the building blocks of Section 3 to a protocol that allows synchronous, resp. semi-synchronous, agents to explore the -dimensional (oriented) grid, and prove the protocol’s viability. Our protocol is given by algorithm Explore.
The underlying idea of algorithm Explore is the same as in the algorithms from [DNOP19a]: We generate each (non-zero)
-dimensional vectorwith non-negative integer coordinates, and for each such vector, we let one agent walk from the origin to each cell such that for all , and then back to the origin. More precisely, in each execution of FollowRoute(), agent walks to the respectively specified cell , and in each execution of FollowRoute(), walks back to the origin. To generate , a counter, represented by the stack size, is used that is incremented gradually, thereby iterating through the positive integers. Each time the counter is incremented, the new value will be transformed into some -dimensional vector, , where the design of the transformation has to make sure that every (non-zero) vector with non-negative integer coordinates is generated by some value
. For technical reasons we require the stack size to be odd, so whenever we increase the counter, we will increase it by, while still ensuring that all vectors are generated.
However, as we have one fewer agent available than in the protocols in [DNOP19a], our protocol requires a new way to implement this idea. In particular, we avoid using one separate agent to remember the stack size when the stack is read, instead making sure that even after the stack is read, no information about the previous stack size(s) is lost333Note that such information is still required after reading the stack: we will need it both to guide back to the origin and to retrieve the counter value that we want to increase repeatedly.. To this end, we define the vector we want to transform into as follows. Let denote the first odd primes, where . For all , we define to be the largest non-negative integer such that divides . In other words, represents how often occurs as a prime factor of .
Consider procedure FollowRoute(). The for loop of this procedure iterates through the dimensions. For each dimension , the first while loop repeatedly replaces one prime factor by prime factor , by dividing by and multiplying by . Each time such a replacement is performed, the whole stack is moved one cell w.r.t. dimension (either increasing or decreasing the respective coordinate by , depending on the value of ). After all (i.e., ) occurrences of as prime factors have been replaced by factors , the stack manipulations are reversed in the second while loop, resulting in the original stack size . Note that in the very beginning of algorithm Explore, the stack size is initialized to , and each time the counter represented by the stack size is increased, it is increased by ; hence, before starting the first while loop, the stack size is odd, ensuring that the second while loop goes through exactly the same number of iterations as the first one. Note further that we do not revert the steps that took (yet) when reversing the stack manipulations. After iterating through all dimensions, agent is now in cell , and we can consider this cell as explored, concluding the execution of FollowRoute().
The execution of FollowRoute() is identical to the execution of FollowRoute(), except that each step of is performed in the opposite direction. Hence, at the end of the execution of FollowRoute(), agent is back at the origin, while the stack size is (again) . The (outer) for loop in algorithm Explore simply iterates through all possible assignments of signs to the dimensions, making sure that for each generated vector , each corresponding cell is explored.
First, we observe that the design of Explore ensures that each grid cell will be explored at some point in time since each (non-zero) cell is explored in the iteration where the counter value is . Note that each such counter value is odd (and at least ) and thus indeed reached by starting with a counter value of and repeatedly increasing it by . Hence to prove the theorem, it suffices to show that Explore can be executed with synchronous finite automata, resp. semi-synchronous finite automata, which we do in the following.
For any fixed , there is only a finite number of distinct subroutines that we execute in Explore. Each such subroutine can be executed by synchronous agents, resp. semi-synchronous agents, due to the implementations provided in Section 3. Moreover, agents and (or, in the case of semi-synchronous agents, agent ) know when a subroutine has terminated and can start the next one, while agent only moves or changes its state in the subroutines upon a visit by another agent, and finishes its actions before the termination of the subroutine. Hence, the theorem follows if we can show that the choice of each subsequent subroutine by and , resp. , can be implemented using a finite automaton. However, this follows from the following observations.
If the currently executed subroutine is not IsDivisible() or IsDivisible(), then the subroutine executed next according to Explore is uniquely defined by the tuple consisting of the current subroutine (type), the current function , the current value of , the information whether we are in procedure FollowRoute() or in procedure FollowRoute() (or neither), and the information whether we are in the first or the second while loop of the respective procedure. If the current subroutine is IsDivisible() or IsDivisible(), then, in order to determine the next subroutine, we additionally need the answer to the question whether the current stack size is divisible by or , respectively. As there are only finitely many such tuples and the answer to the divisibility question adds just one bit of information, determining each subsequent subroutine can be performed by a finite automaton. ∎
5 Unoriented Grids
In [DNOP19a], the authors showed that any protocol for the oriented grid can be transformed into a protocol for unoriented grids by adding sufficiently many agents such that, at all times, each original agent moving across a non-constant distance is accompanied by one of the additional agents. In particular, for both their protocol and our improved protocol, this implies that additional agents are required in the synchronous case and additional agent in the semi-synchronous case (since in the protocols for the oriented grid, synchronous agents are traversing non-constant distances at the same time, while in the semi-synchronous case only agent does so). Hence, our protocol for the oriented grid improves also the state of the art for the minimum number of required agents on unoriented grids from to (in both the synchronous and the semi-synchronous setting).
On an informal level, it seems unlikely that our upper bound of can be improved since intuitively, as Dobrev et al. [DNOP19b, Section 7] write, “a lone agent cannot cross any non-constant distance, as the irregular nature of the port labels would lead it astray, never to meet any other agent”. The tightness of our bound on the oriented grid combined with the perceived necessity of having moving agents accompanied by a partner seems to indicate that we cannot do better. However, there is no formal proof of any lower bound beyond the synchronous -agent and semi-synchronous -agent lower bounds [BUW18, ELS15] that carry over from the case of the oriented grid. Admittedly, as such a formal lower bound might require us to find a “bad” input instance (i.e., a bad input edge labelings of the infinite -dimensional grid) for every potential protocol with more than synchronous, resp. semi-synchronous, agents, it is not particularly surprising that we do not have better lower bounds—yet, making at least some progress would be desirable.
In this section, we will show that under a natural assumption the current lower bounds are actually optimal by providing tight upper bounds. Our assumption states that you cannot walk in a cycle if you always follow the same direction, or, more formally:
Let be any port, any positive integer, and any sequence of cells such that, for each , we reach cell by leaving cell via port . Then .
Surprisingly, this assumption does not contradict the intuition about agents traveling alone discussed above, yet it still allows us to prove upper bounds for unoriented grids matching the lower bounds obtained on oriented grids. While our upper bounds answer the question for the minimally required number of agents in a natural444After all, it seems like a reasonable minimal requirement for a sense of direction that if you go north (or in any other direction) repeatedly, then you do not return to the starting point. setting very close to truly unoriented grids, we think that they also constitute a useful step on the way to a lower bound construction for the general unoriented setting (assuming that the current lower bounds are not optimal): any such construction necessarily has to contain cycles that violate Assumption 5.1.
The general idea behind our approach is to find a way to construct a stack also for unoriented grids. The natural idea of simply selecting one port and interpreting the sequence of cells obtained by successively leaving cells via port as the stack does not work: while it is easy for an agent to traverse the stack in the direction away from the base (it just has to leave each cell via port ), traversing the stack in the opposite direction runs into the problem that there might be different neighboring cells from which the current cell can be reached via port and the traversing agent cannot know which is the one that belongs to the intended stack. Instead, we will build the desired virtual stack by constructing an auxiliary (infinite) directed labeled forest and then traversing (a part of) the forest from some starting cell in a DFS-like fashion, which will ensure that agents can traverse the stack in both directions. In particular, the same cell can occur in the stack several times; to distinguish the occurrences (and make it possible for an agent to traverse the virtual stack), our stack will formally consist of pairs (cell, integer), where the integers come from the set . For an illustration of the auxiliary graph and the virtual stack, we refer to Figures 1 and 2.
The Auxiliary Graph.
We start by defining our auxiliary graph . The vertices of are the cells of our grid, and we have a directed edge between two cells if and are neighbors in the grid and cell is reached by leaving cell via port .555The choice of port here is arbitrary; choosing any other label from works equally well. In particular, this implies that each cell has exactly one outgoing edge in ; we call the cell reached by traversing this edge the parent of , and a child of . Note that Assumption 5.1 ensures that does not contain cycles, and hence, is an infinite forest. In particular, for any two neighboring cells , at most one of the two possible edges and is present in .
Let denote the indegree of a cell , i.e., the number of edges from incoming to . We assign to each edge a level as follows. For each cell , we order the incoming edges increasingly by the corresponding port of , and then assign (distinct) levels from to to the edges according to this order. For instance, if has two incoming edges and , corresponding to ports and of , respectively, then the order of the edges will be , , and we will assign level to , and level to .
The Virtual Stack.
Using the auxiliary graph , we now define, for each cell , the virtual stack rooted in as follows. Recall that consists of pairs (cell, integer). We will use the functions and to retrieve the first, resp. second, component of such a pair. The base of the stack is defined as . For each integer , we inductively define according to the following case distinction.
If , then
is defined as the parent of , and
If , then
is defined as the child of that is connected to
via an (outgoing) edge of level , and
In other words, we inductively build the virtual stack rooted in as follows. We start in and leave via the unique outgoing edge. Each time we enter a cell via an incoming edge, i.e., coming from a child , the next cell we visit is the next higher child of , i.e., the child that is connected to via an edge of level . If no higher child remains, i.e., if has level , then the next cell we visit is the parent of . Each time we enter a cell from its parent, the next cell we visit is the first child of , i.e., the child that is connected to via an edge of level . Hence, our stack corresponds to a DFS exploration of the tree in forest containing , where we assume that the part of the DFS that is executed before traversing the edge from to its parent has already happened. As the tree is infinite, we may not reach every cell contained in the tree in finite time, but to use such a DFS exploration as a stack, this is not relevant. What is relevant, however, is that once we traverse an edge from a child to its parent, the DFS will never return to the child in finite time as Assumption 5.1 ensures that the parent chain starting from (and therefore also any parent chain starting from any other cell visited by the partial DFS) is infinite. Combining this fact with the cyclic fashion in which each visited cell iterates through its children and parent to determine the neighbor visited next, we obtain the following observation.
Fix an arbitrary cell . For any two non-negative integers , we have .
In order to make use of the defined virtual stack, we need the agents to be able to represent their position in the stack in some way. However, given the specific design of the virtual stack, this is not difficult: each agent allocates a part of its state to keep track of the level of the current position in the stack, while the first component of the current position in the stack is simply represented by the cell the agent currently occupies. An advantage of this design is that each agent can determine which other agents are in the same stack position as , and which are not (despite possibly being in the same physical cell). In other words, each agent has all the necessary information to evaluate its transition function, even for moving on the virtual stack.
However, there is one piece still missing for using the virtual stack similar to a physical stack: we have to show that even a lone agent can traverse the virtual stack in either direction, i.e., that a finite automaton is sufficient to determine the physical cell that corresponds to the previous, resp. subsequent, position in the virtual stack, and similarly, to determine the level of that stack position. The following lemma takes care of this.
There is a finite automaton that, when located in cell in state , where is an arbitrary cell, , and an arbitrary integer, moves to cell and changes its state to in time steps.
Let denote the cell in which the finite automaton is located. By the definition of our auxiliary graph , the input labels on the grid edges incident on (which are part of the information available to the finite automaton) uniquely determine the edges incident to in , their orientations (hence, also ), and, if they are incoming edges, their levels. Denote the entirety of this uniquely determined information about the edges incident on by . By the definition of , which of the incident edges leads to is uniquely determined by and . Moreover, is uniquely determined by , , and , where . Hence, if , then there is a finite automaton that first moves to and then, (potentially) using input information obtained in the new cell, changes its state to .
For the case , we observe that, according to the definition of , the following hold.
If , then
is the parent of , and
If , then
is the child of that is connected to via an edge of level , and
It follows that, similarly to before, which of the edges that are incident to leads to is uniquely determined by and , and is uniquely determined by , , and , where . Now we obtain the lemma analogously to the case . ∎
In order to explore unoriented grids, we will rely heavily on our protocol for oriented grids. While the use of a virtual stack will take care of some of the difficulties resulting from the missing global consistency of the provided input edge labeling, there is an additional obstacle for using Explore to explore unoriented grids: In Explore, each cell is reached by a route that starts in the origin, proceeds along dimension , then proceeds along dimension , and so on, until we have exhausted all dimensions. On unoriented grids, the natural analogon would be to use the local orientations available to us in the same manner, i.e., starting in the origin we first leave cells via port , then we switch to port , and so on. However, due to the fact that there is no global consistency guarantee for those local orientations, it might be that some cells cannot be reached via such a route from the origin. To overcome this obstacle we will make sure that does not iterate through all possible ports in the manner described above, but instead through all globally consistent dimensions as in the oriented case, i.e., for the route that takes to the cell to be explored next, the input edge labels will be essentially irrelevant.
The underlying idea to make this approach work in the unoriented setting is to use the handrail technique, developed for -dimensional grids by Mans [Man97] and generalized to higher dimensions in [DNOP19b]. This technique ensures the following: Assume that agent in some cell is aware of a bijection from the set of the ports of to the set such that, for any , the two ports that are mapped to and lead to opposite neighbors of , i.e., to two cells that only differ in one coordinate (by ). Then, if there is another agent in the same cell , there is a -agent protocol (both in the synchronous and in the semi-synchronous setting) that moves both agents to any adjacent cell of their choice and provides with a new bijection with the same properties as such that the following holds for any and any : leaving via the port that maps to leads in the same global direction (i.e., along the same dimension with the same sign) as leaving via the port that maps to . In other words, once agent is aware of the different dimensions and has chosen a name for each dimension and an orientation of each dimension, it can preserve this knowledge (in a globally consistent manner) as long as another agent is available each time moves to an adjacent cell.
Moreover, the handrail technique also allows agent to generate an initial bijection as described above with the help of a second agent. Note that agent has no knowledge of the actual order of the dimensions specified by the global coordinates of the cells, i.e., the direction understands as, e.g., (via its bijection) might be in truth. Hence, there is no guarantee in which order Explore iterates through the true global dimensions if Explore uses the dimension names that maintains instead of the dimension names that the global coordinates specify. However, in order to reach every cell, the order in which Explore iterates through the dimensions is irrelevant as long as the same order is used for each route that takes, and the latter is guaranteed if each time moves to a new cell, there is another agent that helps maintain its knowledge during the move.
Now we are set to prove our main result for unoriented grids.
Suppose that Assumption 5.1 holds. Then, for any positive integer , synchronous finite automata, resp. semi-synchronous finite automata, suffice to explore any -dimensional unoriented grid.
As in the proof of Theorem 1.1, we will let the agents execute algorithm Explore to explore the given grid. However, there are two essential details. First, the stack that Explore refers to will now be the virtual stack defined in this section. More precisely, for each executed subroutine, the stack used in this subroutine is the virtual stack rooted in , where is the cell in which is located in the very beginning of the subroutine. Second, and in contrast, the directions that uses in its exploration will be given by the globally consistent dimensions and orientations of the dimensions. By our discussions regarding the handrail technique, can choose arbitrary (but fixed) names for the dimensions and an orientation for each dimension in the beginning, as initially all agents are in the origin. Moreover, as we will argue in the very end of the proof, each time moves, another agent will be available to help maintain the chosen globally consistent orientation. As the design of Explore ensures that every cell will be explored, what remains to be shown, apart from the availability of a helper agent when moves, is that each subroutine can be performed by synchronous, resp. semi-synchronous agents, also on unoriented grids.
In the synchronous case, this follows for MultiplyStackSize(), DivideStackSize(), IsDivisible(), InitializeStackSize(), and IncreaseStackSize() by Observation 5.2 and Lemma 5.3, which ensure that the virtual stack behaves like a physical stack and can be traversed by any agent, even if alone, in both directions. Note that it is not a problem that Lemma 5.3 only guarantees that a move on the virtual stack can be executed in time steps (and not necessarily )—on unoriented grids we simply allocate two time steps for every time step of the algorithm on oriented grids, and if an agent does not need the second time step to complete its action, it simply waits for one time step.
The only subroutine in the synchronous setting requiring special care is MoveStack() as this is the only subroutine where moves, and hence, the only subroutine where the cell in which our virtual stack is rooted changes. The main issue making the execution of this subroutine somewhat difficult is that in unoriented grids following different directions (specified by ports) is not commutative anymore: leaving a cell via some port and the reached cell via some port does not necessarily lead to the same cell as if we leave via port and the reached cell via port . In contrast, commutativity of directions holds on oriented grids, and we make use of this fact in MoveStack() by moving the endpoints of the stack one cell in the same direction and being assured that this does not change the fact that we can reach the end of the stack from the base by repeatedly walking north.
We overcome this obstacle by executing MoveStack() in unoriented grids in a completely different way: Let denote the root of the virtual stack before executing MoveStack(), and the root of the virtual stack after executing MoveStack(), i.e., the virtual stack changes from to . For agent nothing changes—it simply moves from to as it would on oriented grids. Agents and first traverse until they reach , then move from to , and finally traverse until the new virtual stack has the same size as had at the start of MoveStack(). In order to ensure that the new stack has the same size as the previous one, travels with speed when traversing and with speed when traversing whereas travels with speed when traversing and with speed when traversing ; when they meet again on (in aligned states, i.e., immediately before both would take another step), they must have traversed the same number of (virtual stack) cells on both stacks. Again, executing this behavior on virtual stacks is possible by Observation 5.2 and Lemma 5.3.
In the semi-synchronous setting, the theorem basically follows by using the semi-synchronous implementations of the subroutines (as presented in Section 3) and observing that the new synchronous implementation for MoveStack() can be transformed into a semi-synchronous implementation by using the additional agent as a synchronizer (analogous to how this is done for, e.g., subroutine MultiplyStackSize() in Section 3). The only change compared to the case of oriented grids (beyond the new implementation of MoveStack(), and requiring Observation 5.2 and Lemma 5.3 to guarantee that virtual stacks can be used in the same way as physical stacks) is that each movement on the virtual stack may take time steps according to Lemma 5.3. The only effect this has on the viability of our subroutine implementations is that an agent acting as a synchronizer cannot be sure that some other agent that leaves the synchronizer’s cell has completed its move on the virtual stack before the synchronizer is scheduled again (and possibly prompts some other agent to move). However, this is easily remedied: whenever the synchronizer wants some other agent to take a step on the virtual stack, it simply follows that agent to the respective cell and waits until the agent is in the correct state indicating that the step on the virtual stack is completed.
Finally, we take care of the requirement that, each time agent moves, a helper agent must be available. As the only subroutine in which moves is MoveStack(), and the new implementation of MoveStack() specifies that each agent first moves to the cell containing , then to the adjacent destination cell of , and only then onwards, such a helper agent is available:
In the synchronous case we require to be the helper agent as it arrives later than at cell (note that as soon as arrives, is aware of the port via which it reaches and can inform about the direction; the helper agent is only needed to let preserve its knowledge of the dimensions during its move). We remark that we need to make sure that and spend the same amount of time between finishing the traversal of stack and starting the traversal of stack to make sure that the sizes of the two stacks will be the same. However, this is not difficult: there is a finite upper bound (depending on the dimension of the grid) for the time it takes to perform the -agent protocol that moves (and ) from to and preserves ’s knowledge; hence both and can simply start the traversal of stack time steps after their respective completion of the traversal of stack . In the semi-synchronous case, we can simply use our synchronizer as the helper agent, at a point in time when both and have already completed their traversals of and reached .
There is a small detail that we omitted in the discussion of the semi-synchronous implementation of MoveStack() that we now take care of: when has already started its traversal of , but has not finished its traversal of yet in the new implementation of MoveStack(), our synchronizer will have to be able to move between those two agents despite the fact that you cannot necessarily reach one from the other by simply leaving cells always via the same port. When traveling from to , this can be achieved straightforwardly: knows how to travel on either stack, and it also knows when to perform the one step from to since can detect when it reached the base of stack by noticing the presence of in the physical cell and checking that it reached via an edge that is outgoing from in the auxiliary graph . The information that is missing in order to do the same when traveling from to is whether or not a reached (physical) cell is , since is not marked by the presence of as has not performed its move yet. However, this can be remedied by letting explore all adjacent cells each time it reaches a new cell after taking a step on ; if none of them contains , , and if one of them contains , agent can infer whether by asking about the port that leads from to . ∎
6 Polynomial-Time Exploration
All algorithms we considered so far require a super-polynomial number of steps to reach a treasure located at distance from the origin. In fact, even to encode the coordinates of a cell at distance , we need a stack of size exponential in . To speed up the exploration, Dobrev et al. [DNOP19a] changed the exploration strategy to the following: They explore a hypercube of side length centered at the origin (for increasing values of ). For this, they change their stack implementation, that was based on a binary encoding of -tuples, to an implementation based on an encoding of -tuples using a -ary alphabet, which captures exactly the cells in the hypercube. To perform the exploration of those cells, they require an additional operation, which is to multiply the stack size by , for any (non-constant) value of . They show that this extra operation can be implemented using one additional agent over their exponential time deterministic protocols, in both the synchronous and semi-synchronous setting. This yields a protocol that uses 5 agents in the synchronous, and 6 agents in the semi-synchronous setting, respectively.
We will show how to adapt our results from the previous sections to perform the operations MultiplyStackSize(), IsDivisible(), and DivideStackSize() on oriented grids for non-constant values of . Our implementations will only be applicable for being a power of 2, however this is sufficient for our purpose. To perform these operations, we will need one extra agent, mainly to encode the value of . In fact, we will show that we can use multiple calls to MultiplyStackSize() for constant values of , each time interpreting different agents as encoding a stack.
Once we have these stack operations for non-constant values of , we can use the result in a black-box fashion with techniques from Dobrev et al. [DNOP19a], to get a protocol with polynomial exploration time using 4 agents in the synchronous, and 5 agents in the semi-synchronous setting.
We will focus on this operation, the adaptations for the other operations are analogous. Let us also focus on the synchronous setting, in the semi-synchronous setting we can use one additional agent which acts as a synchronizer, similar to before. We will overload notation, and use MultiplyStackSize(), DivideStackSize(), and IsDivisible() to indicate that we manipulate the stack formed by agents , and , where forms the base and and are co-located at distance from . For simplicity, we will also denote the base of the stack by .